def addp_conf_req(self, frame, address, local_ip, local_mac): addp_ver = 0x0100 # ADDPv1, may be overwritten in command mac_addr = frame.payload[:6] # check if mac_addr matches ours or is equal to broadcast MAC if mac_addr != "\xff\xff\xff\xff\xff\xff" and mac_addr != local_mac: logger.debug("Message has wrong address.") return None index = 6 logger.info("Received 'Configuration' request from: %s" % str(address)) # pull out any OP commands in frame while len(frame.payload) > index + 2: op_code, length = struct.unpack(">BB", frame.payload[index, index + 2]) index += 2 if op_code == ADDP_OP_VERSION: addp_ver, = struct.unpack(">H", frame.payload[index:index + 2]) index += 2 else: # unsupported OP code index += length # Create response response = ADDP_Frame(ADDP_CMD_CONF_REPLY) # add MAC address response.payload += struct.pack(">BB", ADDP_OP_MAC, 6) response.payload += self.mac # add IP address, submask, and gateway IP response.payload += struct.pack(">BBI", ADDP_OP_IPADDR, 4, local_ip) #NOTE: add more parameters to response (Python netifaces module would work well for this). #response.payload += struct.pack(">BBI", ADDP_OP_SUBMASK, 4, 0x00000000) #response.payload += struct.pack(">BBI", ADDP_OP_GATEWAY, 4, 0x00000000) # add DNS servers #response.payload += struct.pack(">BBI", ADDP_OP_DNS, 4, 0x00000000) #response.payload += struct.pack(">BBI", ADDP_OP_DNS, 4, 0x00000000) # add DHCP settings (DHCP server or 0) #response.payload += struct.pack(">BBI", ADDP_OP_DHCP, 4, 0x00000000) # add device name device_name = settings.get('device_name', '') response.payload += struct.pack(">BB", ADDP_OP_NAME, len(device_name)) response.payload += device_name # add hardware name device_type = settings.get('device_type') response.payload += struct.pack(">BB", ADDP_OP_HWNAME, len(device_type)) response.payload += device_type if addp_ver == 0x0100: # add version # get the time of when the program was started (year, mon, mday, hour, min, sec, wday, yday, isdst) = time.gmtime(time.time() - time.clock()) version = "Version %s %d/%02d/%d" % (settings.get( 'version', '0.0.0'), mon, mday, year) response.payload += struct.pack(">BB", ADDP_OP_FEPREV, len(version)) response.payload += version return response
def addp_conf_req(self, frame, address, local_ip, local_mac): addp_ver = 0x0100 # ADDPv1, may be overwritten in command mac_addr = frame.payload[:6] # check if mac_addr matches ours or is equal to broadcast MAC if mac_addr != "\xff\xff\xff\xff\xff\xff" and mac_addr != local_mac: logger.debug("Message has wrong address.") return None index = 6 logger.info("Received 'Configuration' request from: %s" % str(address)) # pull out any OP commands in frame while len(frame.payload) > index + 2: op_code, length = struct.unpack(">BB", frame.payload[index, index + 2]) index += 2 if op_code == ADDP_OP_VERSION: addp_ver, = struct.unpack(">H", frame.payload[index: index + 2]) index += 2 else: # unsupported OP code index += length # Create response response = ADDP_Frame(ADDP_CMD_CONF_REPLY) # add MAC address response.payload += struct.pack(">BB", ADDP_OP_MAC, 6) response.payload += self.mac # add IP address, submask, and gateway IP response.payload += struct.pack(">BBI", ADDP_OP_IPADDR, 4, local_ip) #NOTE: add more parameters to response (Python netifaces module would work well for this). #response.payload += struct.pack(">BBI", ADDP_OP_SUBMASK, 4, 0x00000000) #response.payload += struct.pack(">BBI", ADDP_OP_GATEWAY, 4, 0x00000000) # add DNS servers #response.payload += struct.pack(">BBI", ADDP_OP_DNS, 4, 0x00000000) #response.payload += struct.pack(">BBI", ADDP_OP_DNS, 4, 0x00000000) # add DHCP settings (DHCP server or 0) #response.payload += struct.pack(">BBI", ADDP_OP_DHCP, 4, 0x00000000) # add device name device_name = settings.get('device_name', '') response.payload += struct.pack(">BB", ADDP_OP_NAME, len(device_name)) response.payload += device_name # add hardware name device_type = settings.get('device_type') response.payload += struct.pack(">BB", ADDP_OP_HWNAME, len(device_type)) response.payload += device_type if addp_ver == 0x0100: # add version # get the time of when the program was started (year, mon, mday, hour, min, sec, wday, yday, isdst) = time.gmtime(time.time() - time.clock()) version = "Version %s %d/%02d/%d" % (settings.get('version', '0.0.0'), mon, mday, year) response.payload += struct.pack(">BB", ADDP_OP_FEPREV, len(version)) response.payload += version return response
def run(self): while(1): #NOTE: we take no action if the local port is changed dynamically local_port = settings.get('local_port') if not local_port: time.sleep(1) continue logger.info("Starting web server at http://localhost:%d" % local_port) make_server('', local_port, self, handler_class=RCIWSGIRequestHandler).serve_forever()
def run(self): while (1): #NOTE: we take no action if the local port is changed dynamically local_port = settings.get('local_port') if not local_port: time.sleep(1) continue logger.info("Starting web server at http://localhost:%d" % local_port) make_server('', local_port, self, handler_class=RCIWSGIRequestHandler).serve_forever()
def __call__(self, request): if request.method == 'GET': key = request.GET.get('key') if key: notify = request.GET.get('notify') if notify is not None: if notify and notify not in self.callbacks: # add a notify callback callback = lambda new_value, old_value, key=key: self.callback( key, new_value, old_value) settings.add_callback(key, callback) self.callbacks[key] = callback elif not notify and notify in self.callbacks: # remove callback settings.remove_callback(key, self.callbacks[key]) del self.callbacks[key] # return the value of a setting value = str(settings.get(key, '')) return webob.Response(json.dumps(value), content_type='json') else: # return all of the keys return webob.Response(json.dumps(settings), content_type='json') if request.method == 'POST': # set a value key = request.POST.get('key') if key: value = request.POST.get('value') # remove the setting if no value is given if value is None or value == 'undefined': settings.pop(key, None) # figure out if the value is an int try: value = int(value) except: try: value = float(value) except: pass # set the value settings[key] = value return #success else: return webob.exc.HTTPMethodNotAllowed()
def __call__(self, request): if request.method == 'GET': key = request.GET.get('key') if key: notify = request.GET.get('notify') if notify is not None: if notify and notify not in self.callbacks: # add a notify callback callback = lambda new_value, old_value, key=key: self.callback(key, new_value, old_value) settings.add_callback(key, callback) self.callbacks[key] = callback elif not notify and notify in self.callbacks: # remove callback settings.remove_callback(key, self.callbacks[key]) del self.callbacks[key] # return the value of a setting value = str(settings.get(key, '')) return webob.Response(json.dumps(value), content_type='json') else: # return all of the keys return webob.Response(json.dumps(settings), content_type='json') if request.method == 'POST': # set a value key = request.POST.get('key') if key: value = request.POST.get('value') # remove the setting if no value is given if value is None or value == 'undefined': settings.pop(key, None) # figure out if the value is an int try: value = int(value) except: try: value = float(value) except: pass # set the value settings[key] = value return #success else: return webob.exc.HTTPMethodNotAllowed()
def handle_msg(self, msg): """self.msg_type is current message type, msg contains the message.""" # reset server KA timer self.tx_ka = time.time() + self.tx_intvl if self.msg_type != EDP_KEEPALIVE: logger.debug("received message type=0x%04X len=%u" % (self.msg_type, len(msg))) #TODO: decode type if self.msg_type == EDP_PAYLOAD: if self.phase == self.PHASE_WAIT_VERS_OK: if len(msg) != 1 or ord(msg[0]): logger.error("bad version - code %d (msg len %d) waiting for inner vers OK" % (ord(msg[0]), len(msg))) self._handle_error("Bad version") else: # Success, proceed to security self.phase = self.PHASE_SECURING # We're using simple identification here... self.send_msg(EDP_PAYLOAD, "\x80\x00") payload = "\x81" # Device ID payload += self._device_id_str(settings['device_id']) self.send_msg(EDP_PAYLOAD, payload) # Connection URI if self.red_uri is not None: conn_uri = self.red_uri else: conn_uri = self.uri payload = struct.pack("!BH", 0x86, len(conn_uri)) payload += conn_uri self.send_msg(EDP_PAYLOAD, payload) # Simple form does not require waiting for any response, # so proceed to discovery... self.phase = self.PHASE_DISCOVERY payload = "\x00" # Security layer payload if settings.get('vendor_id'): # Send Vendor ID (needs to be done before type) payload = "\x06" payload += struct.pack("!I", settings['vendor_id']) self.send_msg(EDP_PAYLOAD, payload) payload += "\x04" # Device type discovery message payload += struct.pack("!H", len(settings['device_type'])) payload += settings['device_type'] self.send_msg(EDP_PAYLOAD, payload) # Initialization: send a few odd messages if self.red_uri is not None: payload = "\x04\x01\x0DRedirected OK" # Redirected OK payload += struct.pack("!H", len(self.uri)) payload += self.uri self.send_fac(EDP_FACILITY_CONN_CONTROL, payload) else: payload = "\x04\x00\x00\x00\x00" # Not redirected self.send_fac(EDP_FACILITY_CONN_CONTROL, payload) # Connection report payload = "\x05\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" # append IP IP_list = [int(num) for num in socket.gethostbyname(socket.gethostname()).split(".")] payload += struct.pack("!BBBBB", IP_list[0], IP_list[1], IP_list[2], IP_list[3], 0x01) payload += struct.pack("!Q", settings['mac'])[-6:] self.send_fac(EDP_FACILITY_CONN_CONTROL, payload) # Announce RCI compression. #self.send_fac(EDP_FACILITY_RCI, "\xB0\x01\x01\xFF") #ZLIB, reply self.send_fac(EDP_FACILITY_RCI, "\xB0\x00\x00") # None, no reply # Done init self.send_msg(EDP_PAYLOAD, "\x00\x05") self.phase = self.PHASE_FACILITY elif self.phase == self.PHASE_SECURING: self.phase = self.PHASE_FACILITY elif self.phase == self.PHASE_DISCOVERY: self.phase = self.PHASE_FACILITY elif self.phase == self.PHASE_FACILITY: if len(msg) < 5: logger.error("bad message length %u" % len(msg)) self._handle_error() fac = struct.unpack("!H", msg[2:4])[0] if fac == EDP_FACILITY_CLIENT_LOOPBACK: self.send_msg(EDP_PAYLOAD, msg) elif fac == EDP_FACILITY_CONN_CONTROL: opcode = ord(msg[4]) if opcode == 0x00: # disconnect (server has nothing to send) logger.warning("got disconnect request - ignoring for now") # We should theoretically close, but server seems to # send this even though we have successfully started # and should not close. Just ignore it for now, until # situation is clarified. #self.close(1) #time.sleep(RECONNECT_TIME) # give a few seconds before trying to reconnect... elif opcode == 0x03: # redirect # Ignore the URL count. Just use the 1st URL, since # we have a nameserver. urllen = struct.unpack("!H", msg[6:8])[0] if len(msg) < 13 or ord(msg[5]) < 1 or urllen+8 > len(msg): logger.error("Redirect error - no URL provided, or bad URL length") self._handle_error("Redirect Error") else: self.red_uri = msg[8:8+urllen] self.close(2) #close and redirect elif fac == EDP_FACILITY_RCI: self.handle_rci(msg[4:]) else: handler = self.fac.get(fac) if handler is None: logger.warning("got unhandled facility code 0x%04X" % fac) else: handler(msg[4:]) elif self.msg_type == EDP_VERSION_OK: if self.phase == self.PHASE_INIT: #Send inner versioning info self.send_msg(EDP_PAYLOAD, "\x00\x00\x01\x20") self.phase = self.PHASE_WAIT_VERS_OK elif self.msg_type == EDP_VERSION_BAD: logger.error("server error - bad version") self._handle_error() elif self.msg_type == EDP_SERVER_OVERLOAD: logger.warning("server error - overloaded") self._handle_error() else: # Ignore anything unknown. (This also handles keepalives) if self.msg_type != EDP_KEEPALIVE: logger.warning("unexpected message type 0x%04X" % self.msg_type) return 0
def handle_msg(self, msg): """self.msg_type is current message type, msg contains the message.""" # reset server KA timer self.tx_ka = time.time() + self.tx_intvl if self.msg_type != EDP_KEEPALIVE: logger.debug("received message type=0x%04X len=%u" % (self.msg_type, len(msg))) #TODO: decode type if self.msg_type == EDP_PAYLOAD: if self.phase == self.PHASE_WAIT_VERS_OK: if len(msg) != 1 or ord(msg[0]): logger.error( "bad version - code %d (msg len %d) waiting for inner vers OK" % (ord(msg[0]), len(msg))) self._handle_error("Bad version") else: # Success, proceed to security self.phase = self.PHASE_SECURING # We're using simple identification here... self.send_msg(EDP_PAYLOAD, "\x80\x00") payload = "\x81" # Device ID payload += self._device_id_str(settings['device_id']) self.send_msg(EDP_PAYLOAD, payload) # Connection URI if self.red_uri is not None: conn_uri = self.red_uri else: conn_uri = self.uri payload = struct.pack("!BH", 0x86, len(conn_uri)) payload += conn_uri self.send_msg(EDP_PAYLOAD, payload) # Simple form does not require waiting for any response, # so proceed to discovery... self.phase = self.PHASE_DISCOVERY payload = "\x00" # Security layer payload if settings.get('vendor_id'): # Send Vendor ID (needs to be done before type) payload = "\x06" payload += struct.pack("!I", settings['vendor_id']) self.send_msg(EDP_PAYLOAD, payload) payload += "\x04" # Device type discovery message payload += struct.pack("!H", len(settings['device_type'])) payload += settings['device_type'] self.send_msg(EDP_PAYLOAD, payload) # Initialization: send a few odd messages if self.red_uri is not None: payload = "\x04\x01\x0DRedirected OK" # Redirected OK payload += struct.pack("!H", len(self.uri)) payload += self.uri self.send_fac(EDP_FACILITY_CONN_CONTROL, payload) else: payload = "\x04\x00\x00\x00\x00" # Not redirected self.send_fac(EDP_FACILITY_CONN_CONTROL, payload) # Connection report payload = "\x05\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF" # append IP try: IP_list = [ int(num) for num in socket.gethostbyname( socket.gethostname()).split(".") ] except socket.gaierror: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("my.idigi.com", self.PORT)) IP_list = s.getsockname()[0].split(".") del (s) payload += struct.pack("!BBBBB", IP_list[0], IP_list[1], IP_list[2], IP_list[3], 0x01) payload += struct.pack("!Q", settings['mac'])[-6:] self.send_fac(EDP_FACILITY_CONN_CONTROL, payload) if settings.get('firmware_version'): try: payload = struct.pack("!B", 0x00) payload += struct.pack( "!BI", 0, settings['firmware_version']) self.send_fac(EDP_FACILITY_FIRMWARE, payload) except (TypeError, ValueError, struct.error), e: logger.error( "bad firmware version: %s\terror: %s" % (settings['firmware_version'], str(e))) # Announce RCI compression. #self.send_fac(EDP_FACILITY_RCI, "\xB0\x01\x01\xFF") #ZLIB, reply self.send_fac(EDP_FACILITY_RCI, "\xB0\x00\x00") # None, no reply # Done init self.send_msg(EDP_PAYLOAD, "\x00\x05") self.phase = self.PHASE_FACILITY elif self.phase == self.PHASE_SECURING: self.phase = self.PHASE_FACILITY elif self.phase == self.PHASE_DISCOVERY: self.phase = self.PHASE_FACILITY elif self.phase == self.PHASE_FACILITY: if len(msg) < 5: logger.error("bad message length %u" % len(msg)) self._handle_error() fac = struct.unpack("!H", msg[2:4])[0] if fac == EDP_FACILITY_CLIENT_LOOPBACK: self.send_msg(EDP_PAYLOAD, msg) elif fac == EDP_FACILITY_CONN_CONTROL: opcode = ord(msg[4]) if opcode == 0x00: # disconnect (server has nothing to send) logger.warning( "got disconnect request - ignoring for now") # We should theoretically close, but server seems to # send this even though we have successfully started # and should not close. Just ignore it for now, until # situation is clarified. #self.close(1) #time.sleep(RECONNECT_TIME) # give a few seconds before trying to reconnect... elif opcode == 0x03: # redirect # Ignore the URL count. Just use the 1st URL, since # we have a nameserver. urllen = struct.unpack("!H", msg[6:8])[0] if len(msg) < 13 or ord( msg[5]) < 1 or urllen + 8 > len(msg): logger.error( "Redirect error - no URL provided, or bad URL length" ) self._handle_error("Redirect Error") else: self.red_uri = msg[8:8 + urllen] self.close(2) #close and redirect elif fac == EDP_FACILITY_RCI: self.handle_rci(msg[4:]) else: handler = self.fac.get(fac) if handler is None: logger.warning("got unhandled facility code 0x%04X" % fac) else: handler(msg[4:])
def create_accessor(name, default=''): return lambda: str(settings.get(name, default) ) #make sure return value is string
http_server = HTTPHandler() http_server.start() # Create RCI tree that responds to RCI requests rci_tree = DeviceRoot() #-- RCI Descriptor --# rci_tree.attach(RciDescriptor(rci_tree)) #-- RCI STATE --# rci_tree.attach(RciState().attach( BranchNode('device_info', 'Device Information').attach( SimpleLeafNode( 'mac', dtype=DTYPE.MAC_ADDR, desc="MAC Address", accessor=lambda: ":".join("%02X" % ( (settings.get('mac', 0) >> (i * 8)) & 0xFF) for i in xrange(6)))).attach( SimpleLeafNode( 'product', dtype=DTYPE.STRING, desc="product", accessor=create_accessor('device_type'))).attach( SimpleLeafNode( 'company', dtype=DTYPE.STRING, desc="company", accessor=create_accessor('company'))).attach( SimpleLeafNode( 'os_name', dtype=DTYPE.STRING, desc="Name of host operating system",
def create_accessor(name, default=''): return lambda: str(settings.get(name, default)) #make sure return value is string
#NOTE: code will only run on first import - Python only imports files once # start HTTP server thread for processing RCI requests http_server = HTTPHandler() http_server.start() # Create RCI tree that responds to RCI requests rci_tree = DeviceRoot() #-- RCI Descriptor --# rci_tree.attach(RciDescriptor(rci_tree)) #-- RCI STATE --# rci_tree.attach(RciState() .attach(BranchNode('device_info', 'Device Information') .attach(SimpleLeafNode('mac', dtype=DTYPE.MAC_ADDR, desc="MAC Address", accessor=lambda: ":".join("%02X" % ((settings.get('mac', 0) >> (i*8)) & 0xFF) for i in xrange(6)))) .attach(SimpleLeafNode('product', dtype=DTYPE.STRING, desc="product", accessor=create_accessor('device_type'))) .attach(SimpleLeafNode('company', dtype=DTYPE.STRING, desc="company", accessor=create_accessor('company'))) .attach(SimpleLeafNode('os_name', dtype=DTYPE.STRING, desc="Name of host operating system", accessor=lambda: sys.platform)) ) .attach(BranchNode('boot_stats', 'Primary interface') .attach(SimpleLeafNode('ip', dtype=DTYPE.IPV4, desc='IP Address', accessor=create_accessor('ip_address', '0.0.0.0'))) ) .attach(BranchNode('zigbee_state', 'Gateway XBee') .attach(SimpleLeafNode('gateway_addr', dtype=DTYPE.XBEE_EXT_ADDR, desc='XBee extended address', accessor=lambda: ':'.join("%02x" % ord(x) for x in xbee.ddo_get_param(None, 'SH')+xbee.ddo_get_param(None, 'SL'))))