def handlePacket(self, packet, sock): terminate = False if hasattr(sock, "fileno"): packet_string = str(packet) if len(packet_string) > 256: packet_string = packet_string[0:256] + '...' + ( ' payload length:' + str(len(packet.data)) if hasattr(packet, "data") else "") logger.debug("in: (%d) %s", sock.fileno(), packet_string) if self.mscfile != None: self.mscfile.write("client%d=>broker[label=%s];\n" % (sock.fileno(), str(packet).split("(")[0])) if sock not in self.clients.keys( ) and packet.fh.PacketType != MQTTV5.PacketTypes.CONNECT: self.disconnect(sock, packet) raise MQTTV5.MQTTException( "[MQTT5-3.1.0-1-error] Connect was not first packet on socket") else: if packet.fh.PacketType == MQTTV5.PacketTypes.CONNECT: logger.info( "[MQTT5-3.1.0-1] Connect must be first packet on socket") getattr(self, MQTTV5.Packets.Names[packet.fh.PacketType].lower())(sock, packet) if sock in self.clients.keys(): self.clients[sock].lastPacket = time.monotonic() if packet.fh.PacketType == MQTTV5.PacketTypes.DISCONNECT: terminate = True return terminate
def connect2(self, host, port, newsocket,cleanstart, keepalive, protocolName,willFlag, willTopic, willMessage, willQoS, willRetain,properties, willProperties, username, password): if newsocket: try: self.sock.close() except: pass socket.setdefaulttimeout(5) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.settimeout(5) self.sock.connect((host, port)) connect = MQTTV5.Connects() connect.ClientIdentifier = self.clientid connect.CleanStart = cleanstart connect.KeepAliveTimer = keepalive if protocolName: connect.ProtocolName = protocolName if willFlag: connect.WillFlag = True connect.WillTopic = willTopic connect.WillMessage = willMessage connect.WillQoS = willQoS connect.WillRETAIN = willRetain if willProperties: connect.WillProperties = willProperties if username: connect.usernameFlag = True connect.username = username if password: connect.passwordFlag = True connect.password = password if properties: connect.properties = properties sendtosocket(self.sock, connect.pack()) response = MQTTV5.unpackPacket(MQTTV5.getPacket(self.sock)) if not response: raise MQTTV5.MQTTException("connect failed - socket closed, no connack") assert response.fh.PacketType == MQTTV5.PacketTypes.CONNACK self.cleanstart = cleanstart # print("response =") # print(response) assert response.reasonCode.getName() == "Success", "connect was %s" % str(response) if self.cleanstart or self.__receiver == None: self.__receiver = internal.Receivers(self.sock) else: self.__receiver.socket = self.sock if self.callback: id = _thread.start_new_thread(self.__receiver, (self.callback,)) return response
def handlePacket(self, packet, sock): terminate = False logger.info("in: "+str(packet)) if sock not in self.clients.keys() and packet.fh.PacketType != MQTTV5.PacketTypes.CONNECT: self.disconnect(sock, packet) raise MQTTV5.MQTTException("[MQTT-3.1.0-1] Connect was not first packet on socket") else: getattr(self, MQTTV5.Packets.Names[packet.fh.PacketType].lower())(sock, packet) if sock in self.clients.keys(): self.clients[sock].lastPacket = time.monotonic() if packet.fh.PacketType == MQTTV5.PacketTypes.DISCONNECT: terminate = True return terminate
def handleRequest(self, sock): "this is going to be called from multiple threads, so synchronize" self.lock.acquire() sendWillMessage = False try: try: raw_packet = MQTTV5.getPacket(sock) except: raise MQTTV5.MQTTException( "[MQTT-4.8.0-1] 'transient error' reading packet, closing connection" ) if raw_packet == None: # will message if sock in self.clients.keys(): self.disconnect(sock, None, sendWillMessage=True) terminate = True else: try: packet = MQTTV5.unpackPacket(raw_packet, self.maximumPacketSize) if packet: terminate = self.handlePacket(packet, sock) else: self.disconnect(sock, reasonCode="Malformed packet", sendWillMessage=True) terminate = True except MQTTV5.MalformedPacket as error: traceback.print_exc() disconnect_properties = MQTTV5.Properties( MQTTV5.PacketTypes.DISCONNECT) disconnect_properties.ReasonString = error.args[0] self.disconnect(sock, reasonCode="Malformed packet", sendWillMessage=True) terminate = True except MQTTV5.ProtocolError as error: disconnect_properties = MQTTV5.Properties( MQTTV5.PacketTypes.DISCONNECT) disconnect_properties.ReasonString = error.args[0] self.disconnect(sock, reasonCode=error.args[0], properties=disconnect_properties, sendWillMessage=True) terminate = True finally: self.lock.release() return terminate
def connect(self, sock, packet): resp = MQTTV5.Connacks() if packet.ProtocolName != "MQTT": self.disconnect(sock, None) raise MQTTV5.MQTTException("[MQTT-3.1.2-1] Wrong protocol name %s" % packet.ProtocolName) if packet.ProtocolVersion != 5: logger.error("[MQTT-3.1.2-2] Wrong protocol version %d", packet.ProtocolVersion) resp.reasonCode.set("Unsupported protocol version") respond(sock, resp) logger.info("[MQTT-3.2.2-5] must close connection after non-zero connack") self.disconnect(sock, None) logger.info("[MQTT-3.1.4-5] When rejecting connect, no more data must be processed") return if sock in self.clients.keys(): # is socket is already connected? self.disconnect(sock, None) logger.info("[MQTT-3.1.4-5] When rejecting connect, no more data must be processed") raise MQTTV5.MQTTException("[MQTT-3.1.0-2] Second connect packet") if len(packet.ClientIdentifier) == 0: if self.zero_length_clientids == False or packet.CleanStart == False: if self.zero_length_clientids: logger.info("[MQTT-3.1.3-8] Reject 0-length clientid with cleansession false") logger.info("[MQTT-3.1.3-9] if clientid is rejected, must send connack 2 and close connection") resp.reasonCode.set("Client identifier not valid") respond(sock, resp) logger.info("[MQTT-3.2.2-5] must close connection after non-zero connack") self.disconnect(sock, None) logger.info("[MQTT-3.1.4-5] When rejecting connect, no more data must be processed") return else: logger.info("[MQTT-3.1.3-7] 0-length clientid must have cleansession true") packet.ClientIdentifier = str(uuid.uuid4()) # give the client a unique clientid logger.info("[MQTT-3.1.3-6] 0-length clientid must be assigned a unique id %s", packet.ClientIdentifier) resp.properties.AssignedClientIdentifier = packet.ClientIdentifier logger.info("[MQTT-3.1.3-5] Clientids of 1 to 23 chars and ascii alphanumeric must be allowed") if packet.ClientIdentifier in [client.id for client in self.clients.values()]: # is this client already connected on a different socket? for cursock in self.clients.keys(): if self.clients[cursock].id == packet.ClientIdentifier: logger.info("[MQTT-3.1.4-2] Disconnecting old client %s", packet.ClientIdentifier) self.disconnect(cursock, None) break me = None clean = False if packet.CleanStart: clean = True else: me = self.broker.getClient(packet.ClientIdentifier) # find existing state, if there is any # has that state expired? if me and me.sessionExpiryInterval >= 0 and time.monotonic() - me.sessionEndedTime > me.sessionExpiryInterval: me = None clean = True if me: logger.info("[MQTT-3.1.3-2] clientid used to retrieve client state") resp.sessionPresent = True if me else False # Connack topic alias maximum for incoming client created topic aliases if self.topicAliasMaximum > 0: resp.properties.TopicAliasMaximum = self.topicAliasMaximum if self.maximumPacketSize < MQTTV5.MAX_PACKET_SIZE: resp.properties.MaximumPacketSize = self.maximumPacketSize if self.receiveMaximum < MQTTV5.MAX_PACKETID: resp.properties.ReceiveMaximum = self.receiveMaximum keepalive = packet.KeepAliveTimer if packet.KeepAliveTimer > 0 and self.serverKeepAlive < packet.KeepAliveTimer: keepalive = self.serverKeepAlive resp.properties.ServerKeepAlive = keepalive # Session expiry if hasattr(packet.properties, "SessionExpiryInterval"): sessionExpiryInterval = packet.properties.SessionExpiryInterval else: sessionExpiryInterval = -1 # no expiry if me == None: me = MQTTClients(packet.ClientIdentifier, packet.CleanStart, sessionExpiryInterval, keepalive, sock, self) else: me.socket = sock # set existing client state to new socket me.cleanStart = packet.CleanStart me.keepalive = keepalive me.sessionExpiryInterval = sessionExpiryInterval # the topic alias maximum in the connect properties sets the maximum outgoing topic aliases for a client me.topicAliasMaximum = packet.properties.TopicAliasMaximum if hasattr(packet.properties, "TopicAliasMaximum") else 0 me.maximumPacketSize = packet.properties.MaximumPacketSize if hasattr(packet.properties, "MaximumPacketSize") else MQTTV5.MAX_PACKET_SIZE assert me.maximumPacketSize <= MQTTV5.MAX_PACKET_SIZE # is this the correct value? me.receiveMaximum = packet.properties.ReceiveMaximum if hasattr(packet.properties, "ReceiveMaximum") else MQTTV5.MAX_PACKETID assert me.receiveMaximum <= MQTTV5.MAX_PACKETID logger.info("[MQTT-4.1.0-1] server must store data for at least as long as the network connection lasts") self.clients[sock] = me me.will = (packet.WillTopic, packet.WillQoS, packet.WillMessage, packet.WillRETAIN) if packet.WillFlag else None self.broker.connect(me, clean) logger.info("[MQTT-3.2.0-1] the first response to a client must be a connack") resp.reasonCode.set("Success") respond(sock, resp) me.resend()
def connect(self, sock, packet): resp = MQTTV5.Connacks() if packet.ProtocolName != "MQTT": self.disconnect(sock, None) raise MQTTV5.MQTTException( "[MQTT5-3.1.2-1-error] Wrong protocol name %s" % packet.ProtocolName) logger.info("[MQTT5-3.1.2-1] Protocol name must be MQTT") if packet.ProtocolVersion != 5: logger.error("[MQTT5-3.1.2-2-error] Wrong protocol version %d", packet.ProtocolVersion) resp.reasonCode.set("Unsupported protocol version") respond(sock, resp) logger.info( "[MQTT5-3.2.2-6] must set session present to 0 with non-zero connack" ) logger.info( "[MQTT5-3.2.2-7] must close connection after connack reason >= 0x80" ) self.disconnect(sock, None) logger.info( "[MQTT5-3.1.4-6] When rejecting connect, no more data must be processed" ) return logger.info("[MQTT5-3.1.2-2] Protocol version must be 5") if sock in self.clients.keys(): # is socket is already connected? self.disconnect(sock, None) logger.info( "[MQTT5-3.1.4-6] When rejecting connect, no more data must be processed" ) raise MQTTV5.MQTTException("[MQTT5-3.1.0-2] Second connect packet") if len(packet.ClientIdentifier) == 0: packet.ClientIdentifier = str( uuid.uuid4()) # give the client a unique clientid logger.info( "[MQTT5-3.1.3-6] 0-length clientid must be assigned a unique id %s", packet.ClientIdentifier) resp.properties.AssignedClientIdentifier = packet.ClientIdentifier # returns the assigned client id logger.info("[MQTT5-3.1.3-7] must return the assigned client id") else: logger.info( "[MQTT5-3.1.3-5] Clientids of 1 to 23 chars and ascii alphanumeric must be allowed" ) if False: # reject clientid test logger.info( "[MQTT5-3.1.3-8] server rejects clientid - may return connack" ) if packet.ClientIdentifier in [ client.id for client in self.clients.values() ]: # is this client already connected on a different socket? for cursock in self.clients.keys(): if self.clients[cursock].id == packet.ClientIdentifier: logger.info("[MQTT5-3.1.4-3] Disconnecting old client %s", packet.ClientIdentifier) self.disconnect(cursock, reasonCode="Session taken over") break me = None clean = False if packet.CleanStart: logger.info( "[MQTT5-3.1.2-4] discard existing session when cleanstart set to 1" ) logger.info( "[MQTT5-3.1.4-4] server must perform clean start processing") clean = True logger.info( "[MQTT5-3.2.2-2] session present must be set to 0 if cleanstart is 1" ) else: me = self.broker.getClient( packet.ClientIdentifier ) # find existing state, if there is any if not me: logger.info( "[MQTT5-3.1.2-6] no existing session and cleanstart set to 0" ) # has that state expired? if me and me.sessionExpiryInterval >= 0 and time.monotonic( ) - me.sessionEndedTime > me.sessionExpiryInterval: me = None clean = True else: logger.info( "[MQTT5-3.1.2-5] resume an existing session when cleanstart set to 0" ) if me: logger.info( "[MQTT5-3.1.3-2] clientid used to retrieve client state") logger.info("[MQTT5-3.2.2-3] session present must be set to 1") resp.sessionPresent = True if me else False # Connack topic alias maximum for incoming client created topic aliases if self.options["topicAliasMaximum"] > 0: resp.properties.TopicAliasMaximum = self.options[ "topicAliasMaximum"] if self.options["maximumPacketSize"] < MQTTV5.MAX_PACKET_SIZE: resp.properties.MaximumPacketSize = self.options[ "maximumPacketSize"] if self.options["receiveMaximum"] < MQTTV5.MAX_PACKETID: resp.properties.ReceiveMaximum = self.options["receiveMaximum"] keepalive = packet.KeepAliveTimer if packet.KeepAliveTimer > 0 and self.options[ "serverKeepAlive"] < packet.KeepAliveTimer: keepalive = self.options["serverKeepAlive"] resp.properties.ServerKeepAlive = keepalive logger.info( "[MQTT5-3.1.2-21] client must use server keep alive if returned on connack" ) # Session expiry if hasattr(packet.properties, "SessionExpiryInterval"): sessionExpiryInterval = packet.properties.SessionExpiryInterval else: sessionExpiryInterval = 0 # immediate expiry - change to spec # will delay willDelayInterval = 0 if hasattr(packet.WillProperties, "WillDelayInterval"): willDelayInterval = packet.WillProperties.WillDelayInterval delattr(packet.WillProperties, "WillDelayInterval") # must not be sent with will message if willDelayInterval > sessionExpiryInterval: willDelayInterval = sessionExpiryInterval if me == None: me = MQTTClients(packet.ClientIdentifier, packet.CleanStart, sessionExpiryInterval, willDelayInterval, keepalive, sock, self) else: me.socket = sock # set existing client state to new socket me.cleanStart = packet.CleanStart me.keepalive = keepalive me.sessionExpiryInterval = sessionExpiryInterval me.willDelayInterval = willDelayInterval if me.delayedWillTime: me.delayedWillTime = None logger.info( "[MQTT5-3.1.3-9] don't send delayed will if client connects in time" ) if me.id in self.broker.willMessageClients: self.broker.willMessageClients.remove(me.id) # the topic alias maximum in the connect properties sets the maximum outgoing topic aliases for a client me.topicAliasMaximum = packet.properties.TopicAliasMaximum if hasattr( packet.properties, "TopicAliasMaximum") else 0 me.maximumPacketSize = packet.properties.MaximumPacketSize if hasattr( packet.properties, "MaximumPacketSize") else MQTTV5.MAX_PACKET_SIZE assert me.maximumPacketSize <= MQTTV5.MAX_PACKET_SIZE # is this the correct value? me.receiveMaximum = packet.properties.ReceiveMaximum if hasattr( packet.properties, "ReceiveMaximum") else MQTTV5.MAX_PACKETID assert me.receiveMaximum <= MQTTV5.MAX_PACKETID logger.info( "[MQTT-4.1.0-1] server must store data for at least as long as the network connection lasts" ) self.clients[sock] = me me.will = (packet.WillTopic, packet.WillQoS, packet.WillMessage, packet.WillRETAIN, packet.WillProperties) if packet.WillFlag else None if me.will != None: logger.info( "[MQTT5-3.1.2-7] the will message must be stored if the WillFlag is set" ) self.broker.connect(me, clean) logger.info( "[MQTT5-3.2.0-1] the first response to a client must be a connack") logger.info( "[MQTT5-3.1.4-5] the server must acknowledge the connect with a connack success" ) resp.reasonCode.set("Success") respond(sock, resp) me.resend()