def publish(self, sock, packet, callback): if packet.Flags.QoS == 0: if packet.Flags.TopicIdType == 2: topic = MQTTSN.writeInt16(packet.TopicId).decode() print("topic", topic) self.broker.publish(self.clients[sock].id, topic, packet.Data, packet.Flags.QoS, packet.Flags.RETAIN) elif packet.fh.QoS == 1: if packet.fh.DUP: logger.info( "[MQTT-3.3.1-3] Incoming publish DUP 1 ==> outgoing publish with DUP 0" ) logger.info( "[MQTT-4.3.2-2] server must store message in accordance with QoS 1" ) self.broker.publish(self.clients[sock].id, packet.topicName, packet.data, packet.fh.QoS, packet.fh.RETAIN) resp = MQTTSN.Pubacks() logger.info("[MQTT-2.3.1-6] puback messge id same as publish") resp.messageIdentifier = packet.messageIdentifier respond(sock, callback, resp) elif packet.fh.QoS == 2: myclient = self.clients[sock] if self.publish_on_pubrel: if packet.messageIdentifier in myclient.inbound.keys(): if packet.fh.DUP == 0: logger.error( "[MQTT-3.3.1-2] duplicate QoS 2 message id %d found with DUP 0", packet.messageIdentifier) else: logger.info( "[MQTT-3.3.1-2] DUP flag is 1 on redelivery") else: myclient.inbound[packet.messageIdentifier] = packet else: if packet.messageIdentifier in myclient.inbound: if packet.fh.DUP == 0: logger.error( "[MQTT-3.3.1-2] duplicate QoS 2 message id %d found with DUP 0", packet.messageIdentifier) else: logger.info( "[MQTT-3.3.1-2] DUP flag is 1 on redelivery") else: myclient.inbound.append(packet.messageIdentifier) logger.info( "[MQTT-4.3.3-2] server must store message in accordance with QoS 2" ) self.broker.publish(myclient, packet.topicName, packet.data, packet.fh.QoS, packet.fh.RETAIN) resp = MQTTSN.Pubrecs() logger.info("[MQTT-2.3.1-6] pubrec messge id same as publish") resp.messageIdentifier = packet.messageIdentifier respond(sock, callback, resp)
def subscribe(self, sock, packet): topics = [] qoss = [] respqoss = [] for p in packet.data: if p[0] == "test/nosubscribe": respqoss.append(0x80) else: if p[0] == "test/QoS 1 only": respqoss.append(min(1), p[1]) elif p[0] == "test/QoS 0 only": respqoss.append(min(0), p[1]) else: respqoss.append(p[1]) topics.append(p[0]) qoss.append(respqoss[-1]) if len(topics) > 0: self.broker.subscribe(self.clients[sock].id, topics, qoss) resp = MQTTSN.Subacks() logger.info( "[MQTT-2.3.1-7][MQTT-3.8.4-2] Suback has same message id as subscribe" ) logger.info("[MQTT-3.8.4-1] Must respond with suback") resp.messageIdentifier = packet.messageIdentifier logger.info( "[MQTT-3.8.4-5] return code must be returned for each topic in subscribe" ) logger.info( "[MQTT-3.9.3-1] the order of return codes must match order of topics in subscribe" ) resp.data = respqoss respond(sock, resp)
def pubrec(self, sock, packet): "confirmed reception of qos 2" myclient = self.clients[sock] if myclient.pubrec(packet.messageIdentifier): logger.info( "[MQTT-3.5.4-1] must reply with pubrel in response to pubrec") resp = MQTTSN.Pubrels() resp.messageIdentifier = packet.messageIdentifier respond(sock, resp)
def handleRequest(self, raw_packet, client_address, callback): "this is going to be called from multiple threads, so synchronize" self.lock.acquire() terminate = False try: if raw_packet == None: # will message self.disconnect(sock, None, terminate=True) terminate = True else: packet = MQTTSN.unpackPacket(raw_packet) if packet: terminate = self.handlePacket(packet, client_address, callback) else: raise MQTTSN.MQTTSNException( "[MQTT-2.0.0-1] handleRequest: badly formed MQTT packet" ) finally: self.lock.release() return terminate
def pubrel(self, sock, packet): myclient = self.clients[sock] pub = myclient.pubrel(packet.messageIdentifier) if pub: if self.publish_on_pubrel: self.broker.publish(myclient.id, pub.topicName, pub.data, pub.fh.QoS, pub.fh.RETAIN) del myclient.inbound[packet.messageIdentifier] else: myclient.inbound.remove(packet.messageIdentifier) resp = MQTTSN.Pubcomps() logger.info("[MQTT-2.3.1-6] pubcomp messge id same as publish") resp.messageIdentifier = packet.messageIdentifier respond(sock, resp)
def unsubscribe(self, sock, packet): self.broker.unsubscribe(self.clients[sock].id, packet.data) resp = MQTTSN.Unsubacks() logger.info( "[MQTT-2.3.1-7] Unsuback has same message id as unsubscribe") logger.info( "[MQTT-3.10.4-4] Unsuback must be sent - same message id as unsubscribe" ) me = self.clients[sock] if len(me.outbound) > 0: logger.info( "[MQTT-3.10.4-3] sending unsuback has no effect on outward inflight messages" ) resp.messageIdentifier = packet.messageIdentifier respond(sock, resp)
def handlePacket(self, packet, sock, callback): terminate = False logger.info("in: " + str(packet)) if sock not in self.clients.keys() and not isinstance( packet, MQTTSN.Connects): print(self.clients.keys(), sock) self.disconnect(sock, packet) raise MQTTSN.MQTTSNException( "[MQTT-3.1.0-1] Connect was not first packet on socket") else: getattr(self, MQTTSN.Messages.Names[packet.messageType].lower())( sock, packet, callback) if sock in self.clients.keys(): self.clients[sock].lastPacket = time.time() if packet.messageType == MQTTSN.MessageTypes.DISCONNECT: terminate = True return terminate
def publishArrived(self, topic, msg, qos, retained=False): pub = MQTTSN.Publishes() logger.info( "[MQTT-3.2.3-3] topic name must match the subscription's topic filter" ) pub.topicName = topic pub.data = msg pub.fh.QoS = qos pub.fh.RETAIN = retained if retained: logger.info( "[MQTT-2.1.2-7] Last retained message on matching topics sent on subscribe" ) if pub.fh.RETAIN: logger.info( "[MQTT-2.1.2-9] Set retained flag on retained messages") if qos == 2: pub.qos2state = "PUBREC" if qos in [1, 2]: pub.messageIdentifier = self.msgid logger.debug("client id: %d msgid: %d", self.id, self.msgid) if self.msgid == 65535: self.msgid = 1 else: self.msgid += 1 self.outbound.append(pub) self.outmsgs[pub.messageIdentifier] = pub logger.info( "[MQTT-4.6.0-6] publish packets must be sent in order of receipt from any given client" ) if self.connected: respond(self.socket, pub) else: if qos == 0 and not self.broker.dropQoS0: self.outbound.append(pub) if qos in [1, 2]: logger.info( "[MQTT-3.1.2-5] storing of QoS 1 and 2 messages for disconnected client %s", self.id)
def resend(self): logger.debug("resending unfinished publications %s", str(self.outbound)) if len(self.outbound) > 0: logger.info( "[MQTT-4.4.0-1] resending inflight QoS 1 and 2 messages") for pub in self.outbound: logger.debug("resending", pub) logger.info("[MQTT-4.4.0-2] dup flag must be set on in re-publish") if pub.fh.QoS == 0: respond(self.socket, pub) elif pub.fh.QoS == 1: pub.fh.DUP = 1 logger.info( "[MQTT-2.1.2-3] Dup when resending QoS 1 publish id %d", pub.messageIdentifier) logger.info( "[MQTT-2.3.1-4] Message id same as original publish on resend" ) logger.info("[MQTT-4.3.2-1] Resending QoS 1 with DUP flag") respond(self.socket, pub) elif pub.fh.QoS == 2: if pub.qos2state == "PUBREC": logger.info( "[MQTT-2.1.2-3] Dup when resending QoS 2 publish id %d", pub.messageIdentifier) pub.fh.DUP = 1 logger.info( "[MQTT-2.3.1-4] Message id same as original publish on resend" ) logger.info("[MQTT-4.3.3-1] Resending QoS 2 with DUP flag") respond(self.socket, pub) else: resp = MQTTSN.Pubrels() logger.info( "[MQTT-2.3.1-4] Message id same as original publish on resend" ) resp.messageIdentifier = pub.messageIdentifier respond(self.socket, resp)
def pingreq(self, sock, packet): resp = MQTTSN.Pingresps() logger.info("[MQTT-3.12.4-1] sending pingresp in response to pingreq") respond(sock, resp)
def connect(self, sock, packet, callback): if packet.ProtocolId != 1: logger.error("[MQTT-3.1.2-2] Wrong protocol version %d", packet.ProtocolVersion) resp = MQTTSN.Connacks() resp.ReturnCode = 1 respond(sock, callback, 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 MQTTSN.MQTTSNException( "[MQTT-3.1.0-2] Second connect packet") if len(packet.ClientId) == 0: if self.zero_length_clientids == False or packet.CleanSession == 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 = MQTTSN.Connacks() resp.returnCode = 2 respond(sock, callback, 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.ClientId = 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.ClientId) logger.info( "[MQTT-3.1.3-5] Clientids of 1 to 23 chars and ascii alphanumeric must be allowed" ) if packet.ClientId in [ client.id for client in self.clients.values() ]: # is this client already connected on a different socket? for s in self.clients.keys(): if self.clients[s].id == packet.ClientId: logger.info("[MQTT-3.1.4-2] Disconnecting old client %s", packet.ClientId) self.disconnect(s, None) break me = None if not packet.Flags.CleanSession: me = self.broker.getClient( packet.ClientId) # find existing state, if there is any if me: logger.info( "[MQTT-3.1.3-2] clientid used to retrieve client state") resp = MQTTSN.Connacks() if me == None: me = MQTTSNClients(packet.ClientId, packet.Flags.CleanSession, packet.Duration, sock, self) else: me.socket = sock # set existing client state to new socket me.cleansession = packet.Flags.CleanSession me.keepalive = packet.Duration 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) logger.info( "[MQTT-3.2.0-1] the first response to a client must be a connack") resp.ReturnCode = 0 respond(sock, callback, resp) me.resend()