# -*- coding: binary -*- module Rex module Proto # # This provides constants, encoding, and decoding routines for Digi International's ADDP protocol # class ADDP require "rex/socket" # # See the following URLs for more information: # - http://qbeukes.blogspot.com/2009/11/advanced-digi-discovery-protocol_21.html # - http://www.digi.com/wiki/developer/index.php/Advanced_Device_Discovery_Protocol_%28ADDP%29 # MAGICS = %W{ DIGI DVKT DGDP } ERRORS = %W{ no_response unknown success authenticaton_failed unit_has_address invalid_value invalid_data unsupported_command } WLAN_ENC_MODES = %W{ unknown none wep40 wep128 } WLAN_AUTH_MODES = %W{ unknown open shared_key open_shared_key } HWTYPES = %W{ unknown ps3_desk8 ps3_desk16 ps3_desk32 ps3_rack16 ps2_desk16 ps2_rack16 lets_desk1 lets_desk2 lets_desk4 dorpia_dinrail1 nubox01 nubox02 nubox04 digione_sp digione_ia digione_em } CMD_CONF_REQ = 1 CMD_CONF_REP = 2 CMD_SET_ADDR_REQ = 3 CMD_SET_ADDR_REP = 4 CMD_REBOOT_REQ = 5 CMD_REBOOT_REP = 6 CMD_SET_DHCP_REQ = 7 CMD_SET_DHCP_REP = 8 CMD_SET_WL_REQ = 9 CMD_SET_WL_REP = 10 CMD_SET_WL_COUNTRIES_REQ = 11 CMD_SET_WL_COUNTRIES_REP = 12 CMD_EDP = 13 CMD_CNT = 14 def self.encode_password(pwd="dbps") [pwd.length].pack("C") + pwd end def self.request_config(magic, dmac="\xff\xff\xff\xff\xff\xff") mac = (dmac.length == 6) ? dmac : Rex::Socket.eth_aton(dmac) req = magic + [ CMD_CONF_REQ, 6].pack("nn") + mac return req end def self.request_config_all(dmac="\xff\xff\xff\xff\xff\xff") mac = (dmac.length == 6) ? dmac : Rex::Socket.eth_aton(dmac) res = [] MAGICS.each { |m| res << self.request_config(m, dmac) } return res end def self.request_static_ip(magic, dmac, ip, mask, gw, pwd="dbps") mac = (dmac.length == 6) ? dmac : Rex::Socket.eth_aton(dmac) buf = Rex::Socket.addr_aton(ip) + Rex::Socket.addr_aton(mask) + Rex::Socket.addr_aton(gw) + mac + self.encode_password(pwd) req = magic + [CMD_SET_ADDR_REQ, buf.length].pack("nn") + buf return req end def self.request_dhcp(magic, dmac, enabled, pwd="dbps") mac = (dmac.length == 6) ? dmac : Rex::Socket.eth_aton(dmac) buf = [ enabled ? 1 : 0 ].pack("C") + mac + self.encode_password(pwd) req = magic + [CMD_SET_DHCP_REQ, buf.length].pack("nn") + buf return req end def self.request_reboot(magic, dmac, pwd="dbps") mac = (dmac.length == 6) ? dmac : Rex::Socket.eth_aton(dmac) buf = mac + self.encode_password(pwd) req = magic + [CMD_REBOOT_REQ, buf.length].pack("nn") + buf return req end def self.decode_reply(data) res = {} r_magic = data[0,4] r_ptype = data[4,2].unpack("n").first r_plen = data[6,2].unpack("n").first buff = data[8, r_plen] bidx = 0 res[:magic] = data[0,4] res[:cmd] = r_ptype while bidx < (buff.length - 2) i_type, i_len = buff[bidx, 2].unpack("CC") i_data = buff[bidx + 2, i_len] break if i_data.length != i_len case i_type when 0x01 res[:mac] = Rex::Socket.eth_ntoa(i_data) when 0x02 res[:ip] = Rex::Socket.addr_ntoa(i_data) when 0x03 res[:mask] = Rex::Socket.addr_ntoa(i_data) when 0x04 res[:hostname] = i_data when 0x05 res[:domain] = i_data when 0x06 res[:hwtype] = HWTYPES[ i_data.unpack("C").first ] || HWTYPES[ 0 ] when 0x07 res[:hwrev] = i_data.unpack("C").first when 0x08 res[:fwrev] = i_data when 0x09 res[:msg] = i_data when 0x0a res[:result] = i_data.unpack("C").first when 0x0b res[:gw] = Rex::Socket.addr_ntoa(i_data) when 0x0c res[:advisory] = i_data.unpack("n").first when 0x0d res[:hwname] = i_data when 0x0e res[:realport] = i_data.unpack("N").first when 0x0f res[:dns] = Rex::Socket.addr_ntoa(i_data) when 0x10 res[:dhcp] = (i_data.unpack("C").first == 0) ? false : true when 0x11 res[:error] = ERRORS[ i_data.unpack("C").first ] || ERRORS[0] when 0x12 res[:ports] = i_data.unpack("C").first when 0x13 res[:realport_enc] = (i_data.unpack("C").first == 0) ? false : true when 0x14 res[:version] = i_data.unpack("n").first when 0x15 res[:vendor_guid] = i_data.unpack("H*") # GUID when 0x16 res[:iftype] = i_data.unpack("C").first when 0x17 res[:challenge] = i_data # Unknown format when 0x18 res[:cap_port] = i_data.unpack("n").first when 0x19 res[:edp_devid] = i_data.unpack("H*").first # Unknown format when 0x1a res[:edp_enabled] = (i_data.unpack("C").first == 0) ? false : true when 0x1b res[:edp_url] = i_data when 0x1c res[:wl_ssid] = i_data when 0x1d res[:wl_auto_ssid] = (i_data.unpack("n").first == 0) ? false : true when 0x1e res[:wl_tx_enh_power] = i_data.unpack("n").first when 0x1f res[:wl_auth_mode] = WLAN_AUTH_MODES[ i_data.unpack("n").first ] || WLAN_AUTH_MODES[ 0 ] when 0x20 res[:wl_enc_mode] = WLAN_ENC_MODES[ i_data.unpack("n").first ] || WLAN_ENC_MODES[ 0 ] when 0x21 res[:wl_enc_key] = i_data when 0x22 res[:wl_cur_country] = i_data when 0x23 res[:wl_country_list] = i_data else # Store unknown responses res["unknown_0x#{"%.2x" % i_type}".to_sym] = i_data end bidx = bidx + 2 + i_len end return res end def self.reply_to_string(res) str = "" fields = [ :hwname, :hwtype, :hwrev, :fwrev, :mac, :ip, :mask, :gw, :hostname, :domain, :dns, :dhcp, :msg, :result, :error, :advisory, :ports, :realport, :realport_enc, :version, :vendor_guid, :iftype, :challenge, :cap_port, :edp_devid, :edp_enabled, :edp_url, :wl_ssid, :wl_auto_ssid, :wl_tx_enh_power, :wl_auth_mode, :wl_enc_mode, :wl_enc_key, :wl_cur_country, :wl_country_list, :magic ] fields.each do |fname| next unless res.has_key?(fname) str << "#{fname}:#{res[fname]} " end return str end end end end