Commit 6cd50fa2ad6d2600a8201dc591f2a403f3b3e8ee
1 parent
5a7d3066
Reverse if-guard to remove indentation
Diff with 'ignore whitespace' looks clean.
Showing
1 changed file
with
264 additions
and
266 deletions
mqttpacket.cpp
| ... | ... | @@ -332,68 +332,134 @@ void MqttPacket::handleConnect() |
| 332 | 332 | |
| 333 | 333 | const Settings &settings = *ThreadGlobals::getSettings(); |
| 334 | 334 | |
| 335 | - if (variable_header_length == 4 || variable_header_length == 6) | |
| 335 | + if (!(variable_header_length == 4 || variable_header_length == 6)) | |
| 336 | 336 | { |
| 337 | - char *c = readBytes(variable_header_length); | |
| 338 | - std::string magic_marker(c, variable_header_length); | |
| 337 | + throw ProtocolError("Invalid variable header length. Garbage?", ReasonCodes::MalformedPacket); | |
| 338 | + } | |
| 339 | 339 | |
| 340 | - char protocol_level = readByte(); | |
| 340 | + char *c = readBytes(variable_header_length); | |
| 341 | + std::string magic_marker(c, variable_header_length); | |
| 341 | 342 | |
| 342 | - if (magic_marker == "MQTT") | |
| 343 | - { | |
| 344 | - if (protocol_level == 0x04) | |
| 345 | - protocolVersion = ProtocolVersion::Mqtt311; | |
| 346 | - if (protocol_level == 0x05) | |
| 347 | - protocolVersion = ProtocolVersion::Mqtt5; | |
| 348 | - } | |
| 349 | - else if (magic_marker == "MQIsdp" && protocol_level == 0x03) | |
| 350 | - { | |
| 351 | - protocolVersion = ProtocolVersion::Mqtt31; | |
| 352 | - } | |
| 353 | - else | |
| 354 | - { | |
| 355 | - // The specs are unclear when to use the version 3 codes or version 5 codes. | |
| 356 | - ProtocolVersion fuzzyProtocolVersion = protocol_level < 0x05 ? ProtocolVersion::Mqtt31 : ProtocolVersion::Mqtt5; | |
| 343 | + char protocol_level = readByte(); | |
| 357 | 344 | |
| 358 | - ConnAck connAck(fuzzyProtocolVersion, ReasonCodes::UnsupportedProtocolVersion); | |
| 359 | - MqttPacket response(connAck); | |
| 360 | - sender->setReadyForDisconnect(); | |
| 361 | - sender->writeMqttPacket(response); | |
| 362 | - logger->logf(LOG_ERR, "Rejecting because of invalid protocol version: %s", sender->repr().c_str()); | |
| 363 | - return; | |
| 364 | - } | |
| 345 | + if (magic_marker == "MQTT") | |
| 346 | + { | |
| 347 | + if (protocol_level == 0x04) | |
| 348 | + protocolVersion = ProtocolVersion::Mqtt311; | |
| 349 | + if (protocol_level == 0x05) | |
| 350 | + protocolVersion = ProtocolVersion::Mqtt5; | |
| 351 | + } | |
| 352 | + else if (magic_marker == "MQIsdp" && protocol_level == 0x03) | |
| 353 | + { | |
| 354 | + protocolVersion = ProtocolVersion::Mqtt31; | |
| 355 | + } | |
| 356 | + else | |
| 357 | + { | |
| 358 | + // The specs are unclear when to use the version 3 codes or version 5 codes. | |
| 359 | + ProtocolVersion fuzzyProtocolVersion = protocol_level < 0x05 ? ProtocolVersion::Mqtt31 : ProtocolVersion::Mqtt5; | |
| 360 | + | |
| 361 | + ConnAck connAck(fuzzyProtocolVersion, ReasonCodes::UnsupportedProtocolVersion); | |
| 362 | + MqttPacket response(connAck); | |
| 363 | + sender->setReadyForDisconnect(); | |
| 364 | + sender->writeMqttPacket(response); | |
| 365 | + logger->logf(LOG_ERR, "Rejecting because of invalid protocol version: %s", sender->repr().c_str()); | |
| 366 | + return; | |
| 367 | + } | |
| 365 | 368 | |
| 366 | - char flagByte = readByte(); | |
| 367 | - bool reserved = !!(flagByte & 0b00000001); | |
| 369 | + char flagByte = readByte(); | |
| 370 | + bool reserved = !!(flagByte & 0b00000001); | |
| 368 | 371 | |
| 369 | - if (reserved) | |
| 370 | - throw ProtocolError("Protocol demands reserved flag in CONNECT is 0", ReasonCodes::MalformedPacket); | |
| 372 | + if (reserved) | |
| 373 | + throw ProtocolError("Protocol demands reserved flag in CONNECT is 0", ReasonCodes::MalformedPacket); | |
| 371 | 374 | |
| 372 | 375 | |
| 373 | - bool user_name_flag = !!(flagByte & 0b10000000); | |
| 374 | - bool password_flag = !!(flagByte & 0b01000000); | |
| 375 | - bool will_retain = !!(flagByte & 0b00100000); | |
| 376 | - char will_qos = (flagByte & 0b00011000) >> 3; | |
| 377 | - bool will_flag = !!(flagByte & 0b00000100); | |
| 378 | - bool clean_start = !!(flagByte & 0b00000010); | |
| 376 | + bool user_name_flag = !!(flagByte & 0b10000000); | |
| 377 | + bool password_flag = !!(flagByte & 0b01000000); | |
| 378 | + bool will_retain = !!(flagByte & 0b00100000); | |
| 379 | + char will_qos = (flagByte & 0b00011000) >> 3; | |
| 380 | + bool will_flag = !!(flagByte & 0b00000100); | |
| 381 | + bool clean_start = !!(flagByte & 0b00000010); | |
| 379 | 382 | |
| 380 | - if (will_qos > 2) | |
| 381 | - throw ProtocolError("Invalid QoS for will.", ReasonCodes::MalformedPacket); | |
| 383 | + if (will_qos > 2) | |
| 384 | + throw ProtocolError("Invalid QoS for will.", ReasonCodes::MalformedPacket); | |
| 382 | 385 | |
| 383 | - uint16_t keep_alive = readTwoBytesToUInt16(); | |
| 386 | + uint16_t keep_alive = readTwoBytesToUInt16(); | |
| 384 | 387 | |
| 385 | - uint16_t max_qos_packets = settings.maxQosMsgPendingPerClient; | |
| 386 | - uint32_t session_expire = settings.getExpireSessionAfterSeconds(); | |
| 387 | - uint32_t max_outgoing_packet_size = settings.maxPacketSize; | |
| 388 | - uint16_t max_outgoing_topic_aliases = 0; // Default MUST BE 0, meaning server won't initiate aliases | |
| 389 | - bool request_response_information = false; | |
| 390 | - bool request_problem_information = false; | |
| 388 | + uint16_t max_qos_packets = settings.maxQosMsgPendingPerClient; | |
| 389 | + uint32_t session_expire = settings.getExpireSessionAfterSeconds(); | |
| 390 | + uint32_t max_outgoing_packet_size = settings.maxPacketSize; | |
| 391 | + uint16_t max_outgoing_topic_aliases = 0; // Default MUST BE 0, meaning server won't initiate aliases | |
| 392 | + bool request_response_information = false; | |
| 393 | + bool request_problem_information = false; | |
| 391 | 394 | |
| 392 | - std::string authenticationMethod; | |
| 393 | - std::string authenticationData; | |
| 395 | + std::string authenticationMethod; | |
| 396 | + std::string authenticationData; | |
| 394 | 397 | |
| 398 | + if (protocolVersion == ProtocolVersion::Mqtt5) | |
| 399 | + { | |
| 400 | + const size_t proplen = decodeVariableByteIntAtPos(); | |
| 401 | + const size_t prop_end_at = pos + proplen; | |
| 402 | + | |
| 403 | + while (pos < prop_end_at) | |
| 404 | + { | |
| 405 | + const Mqtt5Properties prop = static_cast<Mqtt5Properties>(readByte()); | |
| 406 | + | |
| 407 | + switch (prop) | |
| 408 | + { | |
| 409 | + case Mqtt5Properties::SessionExpiryInterval: | |
| 410 | + session_expire = std::min<uint32_t>(readFourBytesToUint32(), session_expire); | |
| 411 | + break; | |
| 412 | + case Mqtt5Properties::ReceiveMaximum: | |
| 413 | + max_qos_packets = std::min<int16_t>(readTwoBytesToUInt16(), max_qos_packets); | |
| 414 | + break; | |
| 415 | + case Mqtt5Properties::MaximumPacketSize: | |
| 416 | + max_outgoing_packet_size = std::min<uint32_t>(readFourBytesToUint32(), max_outgoing_packet_size); | |
| 417 | + break; | |
| 418 | + case Mqtt5Properties::TopicAliasMaximum: | |
| 419 | + max_outgoing_topic_aliases = std::min<uint16_t>(readTwoBytesToUInt16(), settings.maxOutgoingTopicAliasValue); | |
| 420 | + break; | |
| 421 | + case Mqtt5Properties::RequestResponseInformation: | |
| 422 | + request_response_information = !!readByte(); | |
| 423 | + UNUSED(request_response_information); | |
| 424 | + break; | |
| 425 | + case Mqtt5Properties::RequestProblemInformation: | |
| 426 | + request_problem_information = !!readByte(); | |
| 427 | + UNUSED(request_problem_information); | |
| 428 | + break; | |
| 429 | + case Mqtt5Properties::UserProperty: | |
| 430 | + readUserProperty(); | |
| 431 | + break; | |
| 432 | + case Mqtt5Properties::AuthenticationMethod: | |
| 433 | + { | |
| 434 | + authenticationMethod = readBytesToString(); | |
| 435 | + break; | |
| 436 | + } | |
| 437 | + case Mqtt5Properties::AuthenticationData: | |
| 438 | + { | |
| 439 | + authenticationData = readBytesToString(false); | |
| 440 | + break; | |
| 441 | + } | |
| 442 | + default: | |
| 443 | + throw ProtocolError("Invalid connect property.", ReasonCodes::ProtocolError); | |
| 444 | + } | |
| 445 | + } | |
| 446 | + } | |
| 447 | + | |
| 448 | + std::string client_id = readBytesToString(); | |
| 449 | + | |
| 450 | + std::string username; | |
| 451 | + std::string password; | |
| 452 | + | |
| 453 | + WillPublish willpublish; | |
| 454 | + willpublish.qos = will_qos; | |
| 455 | + willpublish.retain = will_retain; | |
| 456 | + | |
| 457 | + if (will_flag) | |
| 458 | + { | |
| 395 | 459 | if (protocolVersion == ProtocolVersion::Mqtt5) |
| 396 | 460 | { |
| 461 | + willpublish.constructPropertyBuilder(); | |
| 462 | + | |
| 397 | 463 | const size_t proplen = decodeVariableByteIntAtPos(); |
| 398 | 464 | const size_t prop_end_at = pos + proplen; |
| 399 | 465 | |
| ... | ... | @@ -403,277 +469,209 @@ void MqttPacket::handleConnect() |
| 403 | 469 | |
| 404 | 470 | switch (prop) |
| 405 | 471 | { |
| 406 | - case Mqtt5Properties::SessionExpiryInterval: | |
| 407 | - session_expire = std::min<uint32_t>(readFourBytesToUint32(), session_expire); | |
| 408 | - break; | |
| 409 | - case Mqtt5Properties::ReceiveMaximum: | |
| 410 | - max_qos_packets = std::min<int16_t>(readTwoBytesToUInt16(), max_qos_packets); | |
| 472 | + case Mqtt5Properties::WillDelayInterval: | |
| 473 | + willpublish.will_delay = readFourBytesToUint32(); | |
| 411 | 474 | break; |
| 412 | - case Mqtt5Properties::MaximumPacketSize: | |
| 413 | - max_outgoing_packet_size = std::min<uint32_t>(readFourBytesToUint32(), max_outgoing_packet_size); | |
| 475 | + case Mqtt5Properties::PayloadFormatIndicator: | |
| 476 | + willpublish.propertyBuilder->writePayloadFormatIndicator(readByte()); | |
| 414 | 477 | break; |
| 415 | - case Mqtt5Properties::TopicAliasMaximum: | |
| 416 | - max_outgoing_topic_aliases = std::min<uint16_t>(readTwoBytesToUInt16(), settings.maxOutgoingTopicAliasValue); | |
| 417 | - break; | |
| 418 | - case Mqtt5Properties::RequestResponseInformation: | |
| 419 | - request_response_information = !!readByte(); | |
| 420 | - UNUSED(request_response_information); | |
| 478 | + case Mqtt5Properties::ContentType: | |
| 479 | + { | |
| 480 | + const std::string contentType = readBytesToString(); | |
| 481 | + willpublish.propertyBuilder->writeContentType(contentType); | |
| 421 | 482 | break; |
| 422 | - case Mqtt5Properties::RequestProblemInformation: | |
| 423 | - request_problem_information = !!readByte(); | |
| 424 | - UNUSED(request_problem_information); | |
| 483 | + } | |
| 484 | + case Mqtt5Properties::ResponseTopic: | |
| 485 | + { | |
| 486 | + const std::string responseTopic = readBytesToString(); | |
| 487 | + willpublish.propertyBuilder->writeResponseTopic(responseTopic); | |
| 425 | 488 | break; |
| 426 | - case Mqtt5Properties::UserProperty: | |
| 427 | - readUserProperty(); | |
| 489 | + } | |
| 490 | + case Mqtt5Properties::MessageExpiryInterval: | |
| 491 | + { | |
| 492 | + const uint32_t expiresAfter = readFourBytesToUint32(); | |
| 493 | + willpublish.setExpireAfter(expiresAfter); | |
| 428 | 494 | break; |
| 429 | - case Mqtt5Properties::AuthenticationMethod: | |
| 495 | + } | |
| 496 | + case Mqtt5Properties::CorrelationData: | |
| 430 | 497 | { |
| 431 | - authenticationMethod = readBytesToString(); | |
| 498 | + const std::string correlationData = readBytesToString(false); | |
| 499 | + willpublish.propertyBuilder->writeCorrelationData(correlationData); | |
| 432 | 500 | break; |
| 433 | 501 | } |
| 434 | - case Mqtt5Properties::AuthenticationData: | |
| 502 | + case Mqtt5Properties::UserProperty: | |
| 435 | 503 | { |
| 436 | - authenticationData = readBytesToString(false); | |
| 504 | + readUserProperty(); | |
| 437 | 505 | break; |
| 438 | 506 | } |
| 439 | 507 | default: |
| 440 | - throw ProtocolError("Invalid connect property.", ReasonCodes::ProtocolError); | |
| 508 | + throw ProtocolError("Invalid will property in connect.", ReasonCodes::ProtocolError); | |
| 441 | 509 | } |
| 442 | 510 | } |
| 443 | 511 | } |
| 444 | 512 | |
| 445 | - std::string client_id = readBytesToString(); | |
| 513 | + willpublish.topic = readBytesToString(true, true); | |
| 446 | 514 | |
| 447 | - std::string username; | |
| 448 | - std::string password; | |
| 515 | + uint16_t will_payload_length = readTwoBytesToUInt16(); | |
| 516 | + willpublish.payload = std::string(readBytes(will_payload_length), will_payload_length); | |
| 517 | + } | |
| 518 | + if (user_name_flag) | |
| 519 | + { | |
| 520 | + username = readBytesToString(false); | |
| 449 | 521 | |
| 450 | - WillPublish willpublish; | |
| 451 | - willpublish.qos = will_qos; | |
| 452 | - willpublish.retain = will_retain; | |
| 522 | + if (username.empty()) | |
| 523 | + throw ProtocolError("Username flagged as present, but it's 0 bytes.", ReasonCodes::MalformedPacket); | |
| 453 | 524 | |
| 454 | - if (will_flag) | |
| 525 | + if (!settings.allowUnsafeUsernameChars && containsDangerousCharacters(username)) | |
| 526 | + throw ProtocolError(formatString("Username '%s' contains unsafe characters and 'allow_unsafe_username_chars' is false.", username.c_str()), | |
| 527 | + ReasonCodes::BadUserNameOrPassword); | |
| 528 | + } | |
| 529 | + if (password_flag) | |
| 530 | + { | |
| 531 | + if (this->protocolVersion <= ProtocolVersion::Mqtt311 && !user_name_flag) | |
| 455 | 532 | { |
| 456 | - if (protocolVersion == ProtocolVersion::Mqtt5) | |
| 457 | - { | |
| 458 | - willpublish.constructPropertyBuilder(); | |
| 459 | - | |
| 460 | - const size_t proplen = decodeVariableByteIntAtPos(); | |
| 461 | - const size_t prop_end_at = pos + proplen; | |
| 462 | - | |
| 463 | - while (pos < prop_end_at) | |
| 464 | - { | |
| 465 | - const Mqtt5Properties prop = static_cast<Mqtt5Properties>(readByte()); | |
| 466 | - | |
| 467 | - switch (prop) | |
| 468 | - { | |
| 469 | - case Mqtt5Properties::WillDelayInterval: | |
| 470 | - willpublish.will_delay = readFourBytesToUint32(); | |
| 471 | - break; | |
| 472 | - case Mqtt5Properties::PayloadFormatIndicator: | |
| 473 | - willpublish.propertyBuilder->writePayloadFormatIndicator(readByte()); | |
| 474 | - break; | |
| 475 | - case Mqtt5Properties::ContentType: | |
| 476 | - { | |
| 477 | - const std::string contentType = readBytesToString(); | |
| 478 | - willpublish.propertyBuilder->writeContentType(contentType); | |
| 479 | - break; | |
| 480 | - } | |
| 481 | - case Mqtt5Properties::ResponseTopic: | |
| 482 | - { | |
| 483 | - const std::string responseTopic = readBytesToString(); | |
| 484 | - willpublish.propertyBuilder->writeResponseTopic(responseTopic); | |
| 485 | - break; | |
| 486 | - } | |
| 487 | - case Mqtt5Properties::MessageExpiryInterval: | |
| 488 | - { | |
| 489 | - const uint32_t expiresAfter = readFourBytesToUint32(); | |
| 490 | - willpublish.setExpireAfter(expiresAfter); | |
| 491 | - break; | |
| 492 | - } | |
| 493 | - case Mqtt5Properties::CorrelationData: | |
| 494 | - { | |
| 495 | - const std::string correlationData = readBytesToString(false); | |
| 496 | - willpublish.propertyBuilder->writeCorrelationData(correlationData); | |
| 497 | - break; | |
| 498 | - } | |
| 499 | - case Mqtt5Properties::UserProperty: | |
| 500 | - { | |
| 501 | - readUserProperty(); | |
| 502 | - break; | |
| 503 | - } | |
| 504 | - default: | |
| 505 | - throw ProtocolError("Invalid will property in connect.", ReasonCodes::ProtocolError); | |
| 506 | - } | |
| 507 | - } | |
| 508 | - } | |
| 533 | + throw ProtocolError("MQTT 3.1.1: If the User Name Flag is set to 0, the Password Flag MUST be set to 0."); | |
| 534 | + } | |
| 509 | 535 | |
| 510 | - willpublish.topic = readBytesToString(true, true); | |
| 536 | + password = readBytesToString(false); | |
| 511 | 537 | |
| 512 | - uint16_t will_payload_length = readTwoBytesToUInt16(); | |
| 513 | - willpublish.payload = std::string(readBytes(will_payload_length), will_payload_length); | |
| 514 | - } | |
| 515 | - if (user_name_flag) | |
| 516 | - { | |
| 517 | - username = readBytesToString(false); | |
| 538 | + if (password.empty()) | |
| 539 | + throw ProtocolError("Password flagged as present, but it's 0 bytes.", ReasonCodes::MalformedPacket); | |
| 540 | + } | |
| 518 | 541 | |
| 519 | - if (username.empty()) | |
| 520 | - throw ProtocolError("Username flagged as present, but it's 0 bytes.", ReasonCodes::MalformedPacket); | |
| 542 | + // I deferred the initial UTF8 check on username to be able to give an appropriate connack here, but to me, the specs | |
| 543 | + // are actually vague whether 'BadUserNameOrPassword' should be given on invalid UTF8. | |
| 544 | + if (!isValidUtf8(username)) | |
| 545 | + { | |
| 546 | + ConnAck connAck(protocolVersion, ReasonCodes::BadUserNameOrPassword); | |
| 547 | + MqttPacket response(connAck); | |
| 548 | + sender->setReadyForDisconnect(); | |
| 549 | + sender->writeMqttPacket(response); | |
| 550 | + logger->logf(LOG_ERR, "Username has invalid UTF8: %s", username.c_str()); | |
| 551 | + return; | |
| 552 | + } | |
| 521 | 553 | |
| 522 | - if (!settings.allowUnsafeUsernameChars && containsDangerousCharacters(username)) | |
| 523 | - throw ProtocolError(formatString("Username '%s' contains unsafe characters and 'allow_unsafe_username_chars' is false.", username.c_str()), | |
| 524 | - ReasonCodes::BadUserNameOrPassword); | |
| 525 | - } | |
| 526 | - if (password_flag) | |
| 527 | - { | |
| 528 | - if (this->protocolVersion <= ProtocolVersion::Mqtt311 && !user_name_flag) | |
| 529 | - { | |
| 530 | - throw ProtocolError("MQTT 3.1.1: If the User Name Flag is set to 0, the Password Flag MUST be set to 0."); | |
| 531 | - } | |
| 554 | + bool validClientId = true; | |
| 532 | 555 | |
| 533 | - password = readBytesToString(false); | |
| 556 | + // Check for wildcard chars in case the client_id ever appears in topics. | |
| 557 | + if (!settings.allowUnsafeClientidChars && containsDangerousCharacters(client_id)) | |
| 558 | + { | |
| 559 | + logger->logf(LOG_ERR, "ClientID '%s' has + or # in the id and 'allow_unsafe_clientid_chars' is false.", client_id.c_str()); | |
| 560 | + validClientId = false; | |
| 561 | + } | |
| 562 | + else if (!clean_start && client_id.empty()) | |
| 563 | + { | |
| 564 | + logger->logf(LOG_ERR, "ClientID empty and clean start 0, which is incompatible"); | |
| 565 | + validClientId = false; | |
| 566 | + } | |
| 567 | + else if (protocolVersion < ProtocolVersion::Mqtt311 && client_id.empty()) | |
| 568 | + { | |
| 569 | + logger->logf(LOG_ERR, "Empty clientID. Connect with protocol 3.1.1 or higher to have one generated securely."); | |
| 570 | + validClientId = false; | |
| 571 | + } | |
| 534 | 572 | |
| 535 | - if (password.empty()) | |
| 536 | - throw ProtocolError("Password flagged as present, but it's 0 bytes.", ReasonCodes::MalformedPacket); | |
| 537 | - } | |
| 573 | + if (!validClientId) | |
| 574 | + { | |
| 575 | + ConnAck connAck(protocolVersion, ReasonCodes::ClientIdentifierNotValid); | |
| 576 | + MqttPacket response(connAck); | |
| 577 | + sender->setDisconnectReason("Invalid clientID"); | |
| 578 | + sender->setReadyForDisconnect(); | |
| 579 | + sender->writeMqttPacket(response); | |
| 580 | + return; | |
| 581 | + } | |
| 538 | 582 | |
| 539 | - // I deferred the initial UTF8 check on username to be able to give an appropriate connack here, but to me, the specs | |
| 540 | - // are actually vague whether 'BadUserNameOrPassword' should be given on invalid UTF8. | |
| 541 | - if (!isValidUtf8(username)) | |
| 542 | - { | |
| 543 | - ConnAck connAck(protocolVersion, ReasonCodes::BadUserNameOrPassword); | |
| 544 | - MqttPacket response(connAck); | |
| 545 | - sender->setReadyForDisconnect(); | |
| 546 | - sender->writeMqttPacket(response); | |
| 547 | - logger->logf(LOG_ERR, "Username has invalid UTF8: %s", username.c_str()); | |
| 548 | - return; | |
| 549 | - } | |
| 583 | + bool clientIdGenerated = false; | |
| 584 | + if (client_id.empty()) | |
| 585 | + { | |
| 586 | + client_id = getSecureRandomString(23); | |
| 587 | + clientIdGenerated = true; | |
| 588 | + } | |
| 550 | 589 | |
| 551 | - bool validClientId = true; | |
| 590 | + sender->setClientProperties(protocolVersion, client_id, username, true, keep_alive, max_outgoing_packet_size, max_outgoing_topic_aliases); | |
| 552 | 591 | |
| 553 | - // Check for wildcard chars in case the client_id ever appears in topics. | |
| 554 | - if (!settings.allowUnsafeClientidChars && containsDangerousCharacters(client_id)) | |
| 555 | - { | |
| 556 | - logger->logf(LOG_ERR, "ClientID '%s' has + or # in the id and 'allow_unsafe_clientid_chars' is false.", client_id.c_str()); | |
| 557 | - validClientId = false; | |
| 558 | - } | |
| 559 | - else if (!clean_start && client_id.empty()) | |
| 560 | - { | |
| 561 | - logger->logf(LOG_ERR, "ClientID empty and clean start 0, which is incompatible"); | |
| 562 | - validClientId = false; | |
| 563 | - } | |
| 564 | - else if (protocolVersion < ProtocolVersion::Mqtt311 && client_id.empty()) | |
| 565 | - { | |
| 566 | - logger->logf(LOG_ERR, "Empty clientID. Connect with protocol 3.1.1 or higher to have one generated securely."); | |
| 567 | - validClientId = false; | |
| 568 | - } | |
| 592 | + if (will_flag) | |
| 593 | + sender->setWill(std::move(willpublish)); | |
| 569 | 594 | |
| 570 | - if (!validClientId) | |
| 571 | - { | |
| 572 | - ConnAck connAck(protocolVersion, ReasonCodes::ClientIdentifierNotValid); | |
| 573 | - MqttPacket response(connAck); | |
| 574 | - sender->setDisconnectReason("Invalid clientID"); | |
| 575 | - sender->setReadyForDisconnect(); | |
| 576 | - sender->writeMqttPacket(response); | |
| 577 | - return; | |
| 578 | - } | |
| 595 | + // Stage connack, for immediate or delayed use when auth succeeds. | |
| 596 | + { | |
| 597 | + bool sessionPresent = false; | |
| 598 | + std::shared_ptr<Session> existingSession; | |
| 579 | 599 | |
| 580 | - bool clientIdGenerated = false; | |
| 581 | - if (client_id.empty()) | |
| 600 | + if (protocolVersion >= ProtocolVersion::Mqtt311 && !clean_start) | |
| 582 | 601 | { |
| 583 | - client_id = getSecureRandomString(23); | |
| 584 | - clientIdGenerated = true; | |
| 602 | + existingSession = subscriptionStore->lockSession(client_id); | |
| 603 | + if (existingSession) | |
| 604 | + sessionPresent = true; | |
| 585 | 605 | } |
| 586 | 606 | |
| 587 | - sender->setClientProperties(protocolVersion, client_id, username, true, keep_alive, max_outgoing_packet_size, max_outgoing_topic_aliases); | |
| 607 | + std::unique_ptr<ConnAck> connAck = std::make_unique<ConnAck>(protocolVersion, ReasonCodes::Success, sessionPresent); | |
| 588 | 608 | |
| 589 | - if (will_flag) | |
| 590 | - sender->setWill(std::move(willpublish)); | |
| 591 | - | |
| 592 | - // Stage connack, for immediate or delayed use when auth succeeds. | |
| 609 | + if (protocolVersion >= ProtocolVersion::Mqtt5) | |
| 593 | 610 | { |
| 594 | - bool sessionPresent = false; | |
| 595 | - std::shared_ptr<Session> existingSession; | |
| 596 | - | |
| 597 | - if (protocolVersion >= ProtocolVersion::Mqtt311 && !clean_start) | |
| 598 | - { | |
| 599 | - existingSession = subscriptionStore->lockSession(client_id); | |
| 600 | - if (existingSession) | |
| 601 | - sessionPresent = true; | |
| 602 | - } | |
| 603 | - | |
| 604 | - std::unique_ptr<ConnAck> connAck = std::make_unique<ConnAck>(protocolVersion, ReasonCodes::Success, sessionPresent); | |
| 605 | - | |
| 606 | - if (protocolVersion >= ProtocolVersion::Mqtt5) | |
| 611 | + connAck->propertyBuilder = std::make_shared<Mqtt5PropertyBuilder>(); | |
| 612 | + connAck->propertyBuilder->writeSessionExpiry(session_expire); | |
| 613 | + connAck->propertyBuilder->writeReceiveMax(max_qos_packets); | |
| 614 | + connAck->propertyBuilder->writeRetainAvailable(1); | |
| 615 | + connAck->propertyBuilder->writeMaxPacketSize(sender->getMaxIncomingPacketSize()); | |
| 616 | + if (clientIdGenerated) | |
| 617 | + connAck->propertyBuilder->writeAssignedClientId(client_id); | |
| 618 | + connAck->propertyBuilder->writeMaxTopicAliases(settings.maxIncomingTopicAliasValue); | |
| 619 | + connAck->propertyBuilder->writeWildcardSubscriptionAvailable(1); | |
| 620 | + connAck->propertyBuilder->writeSubscriptionIdentifiersAvailable(0); | |
| 621 | + connAck->propertyBuilder->writeSharedSubscriptionAvailable(0); | |
| 622 | + | |
| 623 | + if (!authenticationMethod.empty()) | |
| 607 | 624 | { |
| 608 | - connAck->propertyBuilder = std::make_shared<Mqtt5PropertyBuilder>(); | |
| 609 | - connAck->propertyBuilder->writeSessionExpiry(session_expire); | |
| 610 | - connAck->propertyBuilder->writeReceiveMax(max_qos_packets); | |
| 611 | - connAck->propertyBuilder->writeRetainAvailable(1); | |
| 612 | - connAck->propertyBuilder->writeMaxPacketSize(sender->getMaxIncomingPacketSize()); | |
| 613 | - if (clientIdGenerated) | |
| 614 | - connAck->propertyBuilder->writeAssignedClientId(client_id); | |
| 615 | - connAck->propertyBuilder->writeMaxTopicAliases(settings.maxIncomingTopicAliasValue); | |
| 616 | - connAck->propertyBuilder->writeWildcardSubscriptionAvailable(1); | |
| 617 | - connAck->propertyBuilder->writeSubscriptionIdentifiersAvailable(0); | |
| 618 | - connAck->propertyBuilder->writeSharedSubscriptionAvailable(0); | |
| 619 | - | |
| 620 | - if (!authenticationMethod.empty()) | |
| 621 | - { | |
| 622 | - connAck->propertyBuilder->writeAuthenticationMethod(authenticationMethod); | |
| 623 | - } | |
| 625 | + connAck->propertyBuilder->writeAuthenticationMethod(authenticationMethod); | |
| 624 | 626 | } |
| 625 | - | |
| 626 | - sender->stageConnack(std::move(connAck)); | |
| 627 | 627 | } |
| 628 | 628 | |
| 629 | - sender->setRegistrationData(clean_start, max_qos_packets, session_expire); | |
| 629 | + sender->stageConnack(std::move(connAck)); | |
| 630 | + } | |
| 630 | 631 | |
| 631 | - Authentication &authentication = *ThreadGlobals::getAuth(); | |
| 632 | - AuthResult authResult = AuthResult::login_denied; | |
| 632 | + sender->setRegistrationData(clean_start, max_qos_packets, session_expire); | |
| 633 | 633 | |
| 634 | - if (!user_name_flag && authenticationMethod.empty() && settings.allowAnonymous) | |
| 635 | - { | |
| 636 | - authResult = AuthResult::success; | |
| 637 | - } | |
| 638 | - else if (!authenticationMethod.empty()) | |
| 639 | - { | |
| 640 | - sender->setExtendedAuthenticationMethod(authenticationMethod); | |
| 634 | + Authentication &authentication = *ThreadGlobals::getAuth(); | |
| 635 | + AuthResult authResult = AuthResult::login_denied; | |
| 641 | 636 | |
| 642 | - std::string returnData; | |
| 643 | - authResult = authentication.extendedAuth(client_id, ExtendedAuthStage::Auth, authenticationMethod, authenticationData, | |
| 644 | - getUserProperties(), returnData, sender->getMutableUsername()); | |
| 637 | + if (!user_name_flag && authenticationMethod.empty() && settings.allowAnonymous) | |
| 638 | + { | |
| 639 | + authResult = AuthResult::success; | |
| 640 | + } | |
| 641 | + else if (!authenticationMethod.empty()) | |
| 642 | + { | |
| 643 | + sender->setExtendedAuthenticationMethod(authenticationMethod); | |
| 645 | 644 | |
| 646 | - if (authResult == AuthResult::auth_continue) | |
| 647 | - { | |
| 648 | - Auth auth(ReasonCodes::ContinueAuthentication, authenticationMethod, returnData); | |
| 649 | - MqttPacket pack(auth); | |
| 650 | - sender->writeMqttPacket(pack); | |
| 651 | - return; | |
| 652 | - } | |
| 653 | - if (authResult == AuthResult::success) | |
| 654 | - { | |
| 655 | - sender->addAuthReturnDataToStagedConnAck(returnData); | |
| 656 | - } | |
| 657 | - } | |
| 658 | - else | |
| 659 | - { | |
| 660 | - authResult = authentication.unPwdCheck(username, password, getUserProperties()); | |
| 661 | - } | |
| 645 | + std::string returnData; | |
| 646 | + authResult = authentication.extendedAuth(client_id, ExtendedAuthStage::Auth, authenticationMethod, authenticationData, | |
| 647 | + getUserProperties(), returnData, sender->getMutableUsername()); | |
| 662 | 648 | |
| 663 | - if (authResult == AuthResult::success) | |
| 649 | + if (authResult == AuthResult::auth_continue) | |
| 664 | 650 | { |
| 665 | - sender->sendConnackSuccess(); | |
| 666 | - subscriptionStore->registerClientAndKickExistingOne(sender); | |
| 651 | + Auth auth(ReasonCodes::ContinueAuthentication, authenticationMethod, returnData); | |
| 652 | + MqttPacket pack(auth); | |
| 653 | + sender->writeMqttPacket(pack); | |
| 654 | + return; | |
| 667 | 655 | } |
| 668 | - else | |
| 656 | + if (authResult == AuthResult::success) | |
| 669 | 657 | { |
| 670 | - const ReasonCodes reason = authResultToReasonCode(authResult); | |
| 671 | - sender->sendConnackDeny(reason); | |
| 658 | + sender->addAuthReturnDataToStagedConnAck(returnData); | |
| 672 | 659 | } |
| 673 | 660 | } |
| 674 | 661 | else |
| 675 | 662 | { |
| 676 | - throw ProtocolError("Invalid variable header length. Garbage?", ReasonCodes::MalformedPacket); | |
| 663 | + authResult = authentication.unPwdCheck(username, password, getUserProperties()); | |
| 664 | + } | |
| 665 | + | |
| 666 | + if (authResult == AuthResult::success) | |
| 667 | + { | |
| 668 | + sender->sendConnackSuccess(); | |
| 669 | + subscriptionStore->registerClientAndKickExistingOne(sender); | |
| 670 | + } | |
| 671 | + else | |
| 672 | + { | |
| 673 | + const ReasonCodes reason = authResultToReasonCode(authResult); | |
| 674 | + sender->sendConnackDeny(reason); | |
| 677 | 675 | } |
| 678 | 676 | } |
| 679 | 677 | ... | ... |