def handleRequest(self, sock): "this is going to be called from multiple threads, so synchronize" self.lock.acquire() terminate = False try: try: raw_packet = MQTTV3.getPacket(sock) except: raise MQTTV3.MQTTException( "[MQTT-4.8.0-1] 'transient error' reading packet, closing connection" ) if raw_packet == None: # will message self.disconnect(sock, None, terminate=True) terminate = True else: packet = MQTTV3.unpackPacket(raw_packet) if packet: terminate = self.handlePacket(packet, sock) else: raise MQTTV3.MQTTException( "[MQTT-2.0.0-1] handleRequest: badly formed MQTT packet" ) finally: self.lock.release() return terminate
def isValidTopicName(aName): logger.info("[MQTT-4.7.3-1] all topic names and filters must be at least 1 char") if len(aName) < 1: raise MQTTV3.MQTTException("MQTT-4.7.3-1] all topic names and filters must be at least 1 char") return False logger.info("[MQTT-4.7.3-3] all topic names and filters must be <= 65535 bytes long") if len(aName) > 65535: raise MQTTV3.MQTTException("[MQTT-4.7.3-3] all topic names and filters must be <= 65535 bytes long") return False rc = True # '#' wildcard can be only at the end of a topic (used to be beginning as well) logger.info("[MQTT-4.7.1-2] # must be last, and next to /") if aName[0:-1].find('#') != -1: raise MQTTV3.MQTTException("[MQTT-4.7.1-2] # must be last, and next to /") rc = False logger.info("[MQTT-4.7.1-3] + can be used at any complete level") # '#' or '+' only next to a slash separator or end of name wilds = '#+' for c in wilds: pos = 0 pos = aName.find(c, pos) while pos != -1: if pos > 0: # check previous char is '/' if aName[pos-1] != '/': raise MQTTV3.MQTTException("[MQTT-4.7.1-3] + can be used at any complete level") rc = False if pos < len(aName)-1: # check that subsequent char is '/' if aName[pos+1] != '/': raise MQTTV3.MQTTException("[MQTT-4.7.1-3] + can be used at any complete level") rc = False pos = aName.find(c, pos+1) return rc
def publish(self, sock, packet): packet.receivedTime = time.monotonic() if packet.topicName.find("+") != -1 or packet.topicName.find( "#") != -1: raise MQTTV3.MQTTException( "[MQTT-3.3.2-2][MQTT-4.7.1-1] wildcards not allowed in topic name" ) if packet.fh.QoS == 0: self.broker.publish(self.clients[sock].id, packet.topicName, packet.data, packet.fh.QoS, packet.fh.RETAIN, packet.receivedTime) 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, packet.receivedTime) resp = MQTTV3.Pubacks() logger.info("[MQTT-2.3.1-6] puback messge id same as publish") resp.messageIdentifier = packet.messageIdentifier respond(sock, 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, packet.receivedTime) resp = MQTTV3.Pubrecs() logger.info("[MQTT-2.3.1-6] pubrec messge id same as publish") resp.messageIdentifier = packet.messageIdentifier respond(sock, resp)
def connect2(self, host, port, cleansession, keepalive, newsocket, protocolName, willFlag, willTopic, willMessage, willQoS, willRetain, username, password): if newsocket: try: self.sock.close() except: pass self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.settimeout(.5) self.sock.connect((host, port)) connect = MQTTV3.Connects() #mqtt login connect.ClientIdentifier = self.clientid connect.CleanSession = cleansession 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 username: connect.usernameFlag = True connect.username = username if password: connect.passwordFlag = True connect.password = password sendtosocket(self.sock, connect.pack()) response = MQTTV3.unpackPacket(MQTTV3.getPacket(self.sock)) if not response: raise MQTTV3.MQTTException( "connect failed - socket closed, no connack") assert response.fh.MessageType == MQTTV3.CONNACK self.cleansession = cleansession assert response.returnCode == 0, "connect was %s" % str(response) if self.cleansession 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: "+repr(packet)) if sock not in self.clients.keys() and packet.fh.MessageType != MQTTV3.CONNECT: self.disconnect(sock, packet) raise MQTTV3.MQTTException("[MQTT-3.1.0-1] Connect was not first packet on socket") else: getattr(self, MQTTV3.packetNames[packet.fh.MessageType].lower())(sock, packet) if sock in self.clients.keys(): self.clients[sock].lastPacket = time.time() if packet.fh.MessageType == MQTTV3.DISCONNECT: terminate = True return terminate
def handlePacket(self, packet, sock): terminate = False packet_string = str(packet) if len(packet_string) > 256: packet_string = packet_string[:255] + '...' + ( ' payload length:' + str(len(packet.data)) if hasattr(packet, "data") else "") logger.debug("in: (%d) %s", sock.fileno(), packet_string) if sock not in self.clients.keys( ) and packet.fh.MessageType != MQTTV3.CONNECT: self.disconnect(sock, packet) raise MQTTV3.MQTTException( "[MQTT-3.1.0-1] Connect was not first packet on socket") else: getattr(self, MQTTV3.packetNames[packet.fh.MessageType].lower())(sock, packet) if sock in self.clients.keys(): self.clients[sock].lastPacket = time.time() if packet.fh.MessageType == MQTTV3.DISCONNECT: terminate = True return terminate
def connect(self, sock, packet): if packet.ProtocolName != "MQTT": self.disconnect(sock, None) raise MQTTV3.MQTTException( "[MQTT-3.1.2-1] Wrong protocol name %s" % packet.ProtocolName) if packet.ProtocolVersion != 4: logger.error("[MQTT-3.1.2-2] Wrong protocol version %d", packet.ProtocolVersion) resp = MQTTV3.Connacks() resp.returnCode = 1 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 MQTTV3.MQTTException("[MQTT-3.1.0-2] Second connect packet") if len(packet.ClientIdentifier) == 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 = MQTTV3.Connacks() resp.returnCode = 2 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 = 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) 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 s in self.clients.keys(): if self.clients[s].id == packet.ClientIdentifier: logger.info("[MQTT-3.1.4-2] Disconnecting old client %s", packet.ClientIdentifier) self.disconnect(s, None) break me = None if not packet.CleanSession: me = self.broker.getClient( packet.ClientIdentifier ) # find existing state, if there is any if me: logger.info( "[MQTT-3.1.3-2] clientid used to retrieve client state") resp = MQTTV3.Connacks() resp.flags = 0x01 if me else 0x00 if me == None: me = MQTTClients(packet.ClientIdentifier, packet.CleanSession, packet.KeepAliveTimer, sock, self) else: me.socket = sock # set existing client state to new socket me.cleansession = packet.CleanSession me.keepalive = packet.KeepAliveTimer 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, resp) me.resend()