// // Created by stephane bourque on 2022-07-26. // #include "AP_WS_Connection.h" #include "AP_WS_Server.h" #include "CentralConfig.h" #include "Daemon.h" #include "FindCountry.h" #include "StorageService.h" #include "CommandManager.h" #include "framework/KafkaManager.h" #include "framework/utils.h" #include "firmware_revision_cache.h" #include "UI_GW_WebSocketNotifications.h" #include namespace OpenWifi { [[maybe_unused]] static void SendKafkaFirmwareUpdate(const std::string &SerialNumber, const std::string &OldFirmware, const std::string &NewFirmware) { if (KafkaManager()->Enabled()) { Poco::JSON::Object EventDetails; EventDetails.set("oldFirmware", OldFirmware); EventDetails.set("newFirmware", NewFirmware); Poco::JSON::Object Event; Event.set("type", "device.firmware_change"); Event.set("timestamp", Utils::Now()); Event.set("payload", EventDetails); KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, Event); } } [[maybe_unused]] static void SendKafkaDeviceNotProvisioned( const std::string &SerialNumber, const std::string &Firmware, const std::string &DeviceType, const std::string &IP) { if (KafkaManager()->Enabled()) { Poco::JSON::Object EventDetails; EventDetails.set("firmware", Firmware); EventDetails.set("deviceType", DeviceType); EventDetails.set("IP", IP); Poco::JSON::Object Event; Event.set("type", "device.not_provisioned"); Event.set("timestamp", Utils::Now()); Event.set("payload", EventDetails); KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, Event); } } void AP_WS_Connection::Process_connect(Poco::JSON::Object::Ptr ParamsObj, const std::string &Serial) { if (ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::FIRMWARE) && ParamsObj->has(uCentralProtocol::CAPABILITIES)) { uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID); auto Firmware = ParamsObj->get(uCentralProtocol::FIRMWARE).toString(); auto Capabilities = ParamsObj->getObject(uCentralProtocol::CAPABILITIES); std::string DevicePassword; if(ParamsObj->has("password")) { DevicePassword = ParamsObj->get("password").toString(); } SerialNumber_ = Serial; SerialNumberInt_ = Utils::SerialNumberToInt(SerialNumber_); CommandManager()->ClearQueue(SerialNumberInt_); AP_WS_Server()->StartSession(State_.sessionId, SerialNumberInt_); Config::Capabilities Caps(Capabilities); Compatible_ = Caps.Compatible(); State_.UUID = UUID; State_.Firmware = Firmware; State_.PendingUUID = 0; State_.Address = Utils::FormatIPv6(WS_->peerAddress().toString()); CId_ = SerialNumber_ + "@" + CId_; auto Platform = Poco::toLower(Caps.Platform()); if(ParamsObj->has("reason")) { State_.connectReason = ParamsObj->get("reason").toString(); } auto IP = PeerAddress_.toString(); if (IP.substr(0, 7) == "::ffff:") { IP = IP.substr(7); } bool RestrictedDevice = false; if (Capabilities->has("restrictions")) { RestrictedDevice = true; Poco::JSON::Object::Ptr RestrictionObject = Capabilities->getObject("restrictions"); Restrictions_.from_json(RestrictionObject); } if (Capabilities->has("developer") && !Capabilities->isNull("developer")) { Restrictions_.developer = Capabilities->getValue("developer"); } if(Capabilities->has("secure-rtty")) { RTTYMustBeSecure_ = Capabilities->getValue("secure-rtty"); } State_.locale = FindCountryFromIP()->Get(IP); GWObjects::Device DeviceInfo; std::lock_guard DbSessionLock(DbSession_->Mutex()); auto DeviceExists = StorageService()->GetDevice(DbSession_->Session(), SerialNumber_, DeviceInfo); if (Daemon()->AutoProvisioning() && !DeviceExists) { // check the firmware version. if this is too old, we cannot let that device connect yet, we must // force a firmware upgrade GWObjects::DefaultFirmware MinimumFirmware; if(FirmwareRevisionCache()->DeviceMustUpgrade(Compatible_, Firmware, MinimumFirmware)) { Poco::JSON::Object UpgradeCommand, Params; UpgradeCommand.set(uCentralProtocol::JSONRPC,uCentralProtocol::JSONRPC_VERSION); UpgradeCommand.set(uCentralProtocol::METHOD,uCentralProtocol::UPGRADE); Params.set(uCentralProtocol::SERIALNUMBER, SerialNumber_); Params.set(uCentralProtocol::WHEN, 0); Params.set(uCentralProtocol::URI, MinimumFirmware.uri); Params.set(uCentralProtocol::KEEP_REDIRECTOR,1); UpgradeCommand.set(uCentralProtocol::PARAMS, Params); UpgradeCommand.set(uCentralProtocol::ID, 1); std::ostringstream Command; UpgradeCommand.stringify(Command); if(Send(Command.str())) { poco_information( Logger(), fmt::format( "Forcing device {} to upgrade to {} before connection is allowed.", SerialNumber_, MinimumFirmware.revision)); } else { poco_error( Logger(), fmt::format( "Could not force device {} to upgrade to {} before connection is allowed.", SerialNumber_, MinimumFirmware.revision)); } return; } else { StorageService()->CreateDefaultDevice( DbSession_->Session(), SerialNumber_, Caps, Firmware, PeerAddress_, State_.VerifiedCertificate == GWObjects::SIMULATED); } } else if (!Daemon()->AutoProvisioning() && !DeviceExists) { SendKafkaDeviceNotProvisioned(SerialNumber_, Firmware, Compatible_, CId_); poco_warning(Logger(),fmt::format("Device {} is a {} from {} and cannot be provisioned.",SerialNumber_,Compatible_, CId_)); return EndConnection(); } else if (DeviceExists) { StorageService()->UpdateDeviceCapabilities(DbSession_->Session(), SerialNumber_, Caps); int Updated{0}; if (!Firmware.empty()) { if (Firmware != DeviceInfo.Firmware) { DeviceFirmwareChangeKafkaEvent KEvent(SerialNumberInt_, Utils::Now(), DeviceInfo.Firmware, Firmware); DeviceInfo.Firmware = Firmware; DeviceInfo.LastFWUpdate = Utils::Now(); ++Updated; GWWebSocketNotifications::SingleDeviceFirmwareChange_t Notification; Notification.content.serialNumber = SerialNumber_; Notification.content.newFirmware = Firmware; GWWebSocketNotifications::DeviceFirmwareUpdated(Notification); } else if (DeviceInfo.LastFWUpdate == 0) { DeviceInfo.LastFWUpdate = Utils::Now(); ++Updated; } } if(ParamsObj->has("reason")) { State_.connectReason = ParamsObj->get("reason").toString(); DeviceInfo.connectReason = State_.connectReason; ++Updated; } if(DeviceInfo.DevicePassword!=DevicePassword) { DeviceInfo.DevicePassword = DevicePassword.empty() ? "openwifi" : DevicePassword ; ++Updated; } if (DeviceInfo.lastRecordedContact==0) { DeviceInfo.lastRecordedContact = Utils::Now(); ++Updated; } if (DeviceInfo.simulated && (State_.VerifiedCertificate!=GWObjects::SIMULATED)) { DeviceInfo.simulated = false; ++Updated; } if (!DeviceInfo.simulated && (State_.VerifiedCertificate==GWObjects::SIMULATED)) { DeviceInfo.simulated = true; ++Updated; } if (DeviceInfo.locale != State_.locale) { DeviceInfo.locale = State_.locale; ++Updated; } if (Compatible_ != DeviceInfo.Compatible) { DeviceInfo.Compatible = Compatible_; ++Updated; } if (Platform != DeviceInfo.DeviceType) { DeviceInfo.DeviceType = Platform; ++Updated; } if (RestrictedDevice != DeviceInfo.restrictedDevice) { DeviceInfo.restrictedDevice = RestrictedDevice; ++Updated; } if (Restrictions_ != DeviceInfo.restrictionDetails) { DeviceInfo.restrictionDetails = Restrictions_; ++Updated; } if(DeviceInfo.certificateExpiryDate!=State_.certificateExpiryDate) { DeviceInfo.certificateExpiryDate = State_.certificateExpiryDate; ++Updated; } if (Updated) { StorageService()->UpdateDevice(DbSession_->Session(), DeviceInfo); } } if(!Simulated_) { uint64_t UpgradedUUID = 0; if (LookForUpgrade(DbSession_->Session(), UUID, UpgradedUUID)) { State_.UUID = UpgradedUUID; } } State_.Compatible = Compatible_; State_.Connected = true; ConnectionCompletionTime_ = std::chrono::high_resolution_clock::now() - ConnectionStart_; State_.connectionCompletionTime = ConnectionCompletionTime_.count(); if (State_.VerifiedCertificate == GWObjects::VALID_CERTIFICATE) { if ((Utils::SerialNumberMatch(CN_, SerialNumber_, (int)AP_WS_Server()->MismatchDepth())) || AP_WS_Server()->IsSimSerialNumber(CN_)) { State_.VerifiedCertificate = GWObjects::VERIFIED; poco_information(Logger_, fmt::format("CONNECT({}): Fully validated and authenticated " "device. Session={} ConnectionCompletion Time={}", CId_, State_.sessionId, State_.connectionCompletionTime)); } else { State_.VerifiedCertificate = GWObjects::MISMATCH_SERIAL; if (AP_WS_Server()->AllowSerialNumberMismatch()) { poco_information( Logger_, fmt::format("CONNECT({}): Serial number mismatch allowed. CN={} " "Serial={} Session={} ConnectionCompletion Time={}", CId_, CN_, SerialNumber_, State_.sessionId, State_.connectionCompletionTime)); } else { poco_information( Logger_, fmt::format("CONNECT({}): Serial number mismatch disallowed. " "Device rejected. CN={} Serial={} Session={}", CId_, CN_, SerialNumber_, State_.sessionId)); return EndConnection(); } } } else { poco_information(Logger_, fmt::format("CONNECT({}): Simulator device. " "Session={} ConnectionCompletion Time={}", CId_, State_.sessionId, State_.connectionCompletionTime)); } GWWebSocketNotifications::SingleDevice_t Notification; Notification.content.serialNumber = SerialNumber_; GWWebSocketNotifications::DeviceConnected(Notification); if (KafkaManager()->Enabled()) { ParamsObj->set(uCentralProtocol::CONNECTIONIP, CId_); ParamsObj->set("locale", State_.locale); ParamsObj->set(uCentralProtocol::TIMESTAMP, Utils::Now()); ParamsObj->set(uCentralProtocol::UUID, uuid_); KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber_, *ParamsObj); } } else { poco_warning( Logger_, fmt::format("INVALID-PROTOCOL({}): Missing one of uuid, firmware, or capabilities", CId_)); Errors_++; } } } // namespace OpenWifi