nmb.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
  1. # Copyright (c) 2003-2016 CORE Security Technologies
  2. #
  3. # This software is provided under under a slightly modified version
  4. # of the Apache Software License. See the accompanying LICENSE file
  5. # for more information.
  6. #
  7. # -*- mode: python; tab-width: 4 -*-
  8. #
  9. # Copyright (C) 2001 Michael Teo <michaelteo@bigfoot.com>
  10. # nmb.py - NetBIOS library
  11. #
  12. # This software is provided 'as-is', without any express or implied warranty.
  13. # In no event will the author be held liable for any damages arising from the
  14. # use of this software.
  15. #
  16. # Permission is granted to anyone to use this software for any purpose,
  17. # including commercial applications, and to alter it and redistribute it
  18. # freely, subject to the following restrictions:
  19. #
  20. # 1. The origin of this software must not be misrepresented; you must not
  21. # claim that you wrote the original software. If you use this software
  22. # in a product, an acknowledgment in the product documentation would be
  23. # appreciated but is not required.
  24. #
  25. # 2. Altered source versions must be plainly marked as such, and must not be
  26. # misrepresented as being the original software.
  27. #
  28. # 3. This notice cannot be removed or altered from any source distribution.
  29. #
  30. # Altered source done by Alberto Solino (@agsolino)
  31. import socket
  32. import string
  33. import re
  34. import select
  35. import errno
  36. from random import randint
  37. from struct import pack, unpack
  38. import time
  39. from structure import Structure
  40. CVS_REVISION = '$Revision: 526 $'
  41. # Taken from socket module reference
  42. INADDR_ANY = '0.0.0.0'
  43. BROADCAST_ADDR = '<broadcast>'
  44. # Default port for NetBIOS name service
  45. NETBIOS_NS_PORT = 137
  46. # Default port for NetBIOS session service
  47. NETBIOS_SESSION_PORT = 139
  48. # Default port for SMB session service
  49. SMB_SESSION_PORT = 445
  50. # Owner Node Type Constants
  51. NODE_B = 0x0000
  52. NODE_P = 0x2000
  53. NODE_M = 0x4000
  54. NODE_RESERVED = 0x6000
  55. NODE_GROUP = 0x8000
  56. NODE_UNIQUE = 0x0
  57. # Name Type Constants
  58. TYPE_UNKNOWN = 0x01
  59. TYPE_WORKSTATION = 0x00
  60. TYPE_CLIENT = 0x03
  61. TYPE_SERVER = 0x20
  62. TYPE_DOMAIN_MASTER = 0x1B
  63. TYPE_DOMAIN_CONTROLLER = 0x1C
  64. TYPE_MASTER_BROWSER = 0x1D
  65. TYPE_BROWSER = 0x1E
  66. TYPE_NETDDE = 0x1F
  67. TYPE_STATUS = 0x21
  68. # Opcodes values
  69. OPCODE_QUERY = 0
  70. OPCODE_REGISTRATION = 0x5
  71. OPCODE_RELEASE = 0x6
  72. OPCODE_WACK = 0x7
  73. OPCODE_REFRESH = 0x8
  74. OPCODE_REQUEST = 0
  75. OPCODE_RESPONSE = 0x10
  76. # NM_FLAGS
  77. NM_FLAGS_BROADCAST = 0x1
  78. NM_FLAGS_UNICAST = 0
  79. NM_FLAGS_RA = 0x8
  80. NM_FLAGS_RD = 0x10
  81. NM_FLAGS_TC = 0x20
  82. NM_FLAGS_AA = 0x40
  83. # QUESTION_TYPE
  84. QUESTION_TYPE_NB = 0x20 # NetBIOS general Name Service Resource Record
  85. QUESTION_TYPE_NBSTAT = 0x21 # NetBIOS NODE STATUS Resource Record
  86. # QUESTION_CLASS
  87. QUESTION_CLASS_IN = 0x1 # Internet class
  88. # RR_TYPE Resource Record Type code
  89. RR_TYPE_A = 0x1 # IP address Resource Record
  90. RR_TYPE_NS = 0x2 # Name Server Resource Record
  91. RR_TYPE_NULL = 0xA # NULL Resource Record
  92. RR_TYPE_NB = 0x20 # NetBIOS general Name Service Resource Record
  93. RR_TYPE_NBSTAT = 0x21 # NetBIOS NODE STATUS Resource Record
  94. # Resource Record Class
  95. RR_CLASS_IN = 1 # Internet class
  96. # RCODE values
  97. RCODE_FMT_ERR = 0x1 # Format Error. Request was invalidly formatted.
  98. RCODE_SRV_ERR = 0x2 # Server failure. Problem with NBNS, cannot process name.
  99. RCODE_IMP_ERR = 0x4 # Unsupported request error. Allowable only for challenging NBNS when gets an Update type
  100. # registration request.
  101. RCODE_RFS_ERR = 0x5 # Refused error. For policy reasons server will not register this name from this host.
  102. RCODE_ACT_ERR = 0x6 # Active error. Name is owned by another node.
  103. RCODE_CFT_ERR = 0x7 # Name in conflict error. A UNIQUE name is owned by more than one node.
  104. # NAME_FLAGS
  105. NAME_FLAGS_PRM = 0x0200 # Permanent Name Flag. If one (1) then entry is for the permanent node name. Flag is zero
  106. # (0) for all other names.
  107. NAME_FLAGS_ACT = 0x0400 # Active Name Flag. All entries have this flag set to one (1).
  108. NAME_FLAG_CNF = 0x0800 # Conflict Flag. If one (1) then name on this node is in conflict.
  109. NAME_FLAG_DRG = 0x1000 # Deregister Flag. If one (1) then this name is in the process of being deleted.
  110. NAME_TYPES = { TYPE_UNKNOWN: 'Unknown', TYPE_WORKSTATION: 'Workstation', TYPE_CLIENT: 'Client',
  111. TYPE_SERVER: 'Server', TYPE_MASTER_BROWSER: 'Master Browser', TYPE_BROWSER: 'Browser Server',
  112. TYPE_DOMAIN_MASTER: 'Domain Master' , TYPE_NETDDE: 'NetDDE Server'}
  113. # NetBIOS Session Types
  114. NETBIOS_SESSION_MESSAGE = 0x0
  115. NETBIOS_SESSION_REQUEST = 0x81
  116. NETBIOS_SESSION_POSITIVE_RESPONSE = 0x82
  117. NETBIOS_SESSION_NEGATIVE_RESPONSE = 0x83
  118. NETBIOS_SESSION_RETARGET_RESPONSE = 0x84
  119. NETBIOS_SESSION_KEEP_ALIVE = 0x85
  120. def strerror(errclass, errcode):
  121. if errclass == ERRCLASS_OS:
  122. return 'OS Error', str(errcode)
  123. elif errclass == ERRCLASS_QUERY:
  124. return 'Query Error', QUERY_ERRORS.get(errcode, 'Unknown error')
  125. elif errclass == ERRCLASS_SESSION:
  126. return 'Session Error', SESSION_ERRORS.get(errcode, 'Unknown error')
  127. else:
  128. return 'Unknown Error Class', 'Unknown Error'
  129. class NetBIOSError(Exception): pass
  130. class NetBIOSTimeout(Exception):
  131. def __init__(self, message = 'The NETBIOS connection with the remote host timed out.'):
  132. Exception.__init__(self, message)
  133. class NBResourceRecord:
  134. def __init__(self, data = 0):
  135. self._data = data
  136. try:
  137. if self._data:
  138. self.rr_name = (re.split('\x00',data))[0]
  139. offset = len(self.rr_name)+1
  140. self.rr_type = unpack('>H', self._data[offset:offset+2])[0]
  141. self.rr_class = unpack('>H', self._data[offset+2: offset+4])[0]
  142. self.ttl = unpack('>L',self._data[offset+4:offset+8])[0]
  143. self.rdlength = unpack('>H', self._data[offset+8:offset+10])[0]
  144. self.rdata = self._data[offset+10:offset+10+self.rdlength]
  145. offset = self.rdlength - 2
  146. self.unit_id = data[offset:offset+6]
  147. else:
  148. self.rr_name = ''
  149. self.rr_type = 0
  150. self.rr_class = 0
  151. self.ttl = 0
  152. self.rdlength = 0
  153. self.rdata = ''
  154. self.unit_id = ''
  155. except Exception:
  156. raise NetBIOSError( 'Wrong packet format ' )
  157. def set_rr_name(self, name):
  158. self.rr_name = name
  159. def set_rr_type(self, name):
  160. self.rr_type = name
  161. def set_rr_class(self,cl):
  162. self.rr_class = cl
  163. def set_ttl(self,ttl):
  164. self.ttl = ttl
  165. def set_rdata(self,rdata):
  166. self.rdata = rdata
  167. self.rdlength = len(rdata)
  168. def get_unit_id(self):
  169. return self.unit_id
  170. def get_rr_name(self):
  171. return self.rr_name
  172. def get_rr_class(self):
  173. return self.rr_class
  174. def get_ttl(self):
  175. return self.ttl
  176. def get_rdlength(self):
  177. return self.rdlength
  178. def get_rdata(self):
  179. return self.rdata
  180. def rawData(self):
  181. return self.rr_name + pack('!HHLH',self.rr_type, self.rr_class, self.ttl, self.rdlength) + self.rdata
  182. class NBNodeStatusResponse(NBResourceRecord):
  183. def __init__(self, data = 0):
  184. NBResourceRecord.__init__(self,data)
  185. self.num_names = 0
  186. self.node_names = [ ]
  187. self.statstics = ''
  188. self.mac = '00-00-00-00-00-00'
  189. try:
  190. if data:
  191. self._data = self.get_rdata()
  192. self.num_names = unpack('>B',self._data[:1])[0]
  193. offset = 1
  194. for i in range(0, self.num_names):
  195. name = self._data[offset:offset + 15]
  196. type,flags = unpack('>BH', self._data[offset + 15: offset + 18])
  197. offset += 18
  198. self.node_names.append(NBNodeEntry(name, type ,flags))
  199. self.set_mac_in_hexa(self.get_unit_id())
  200. except Exception:
  201. raise NetBIOSError( 'Wrong packet format ' )
  202. def set_mac_in_hexa(self, data):
  203. data_aux = ''
  204. for d in data:
  205. if data_aux == '':
  206. data_aux = '%02x' % ord(d)
  207. else:
  208. data_aux += '-%02x' % ord(d)
  209. self.mac = string.upper(data_aux)
  210. def get_num_names(self):
  211. return self.num_names
  212. def get_mac(self):
  213. return self.mac
  214. def set_num_names(self, num):
  215. self.num_names = num
  216. def get_node_names(self):
  217. return self.node_names
  218. def add_node_name(self,node_names):
  219. self.node_names.append(node_names)
  220. self.num_names += 1
  221. def rawData(self):
  222. res = pack('!B', self.num_names )
  223. for i in range(0, self.num_names):
  224. res += self.node_names[i].rawData()
  225. class NBPositiveNameQueryResponse(NBResourceRecord):
  226. def __init__(self, data = 0):
  227. NBResourceRecord.__init__(self, data)
  228. self.addr_entries = [ ]
  229. if data:
  230. self._data = self.get_rdata()
  231. _qn_length, qn_name, qn_scope = decode_name(data)
  232. self._netbios_name = string.rstrip(qn_name[:-1]) + qn_scope
  233. self._name_type = ord(qn_name[-1])
  234. self._nb_flags = unpack('!H', self._data[:2])
  235. offset = 2
  236. while offset<len(self._data):
  237. self.addr_entries.append('%d.%d.%d.%d' % unpack('4B', (self._data[offset:offset+4])))
  238. offset += 4
  239. def get_netbios_name(self):
  240. return self._netbios_name
  241. def get_name_type(self):
  242. return self._name_type
  243. def get_addr_entries(self):
  244. return self.addr_entries
  245. class NetBIOSPacket:
  246. """ This is a packet as defined in RFC 1002 """
  247. def __init__(self, data = 0):
  248. self.name_trn_id = 0x0 # Transaction ID for Name Service Transaction.
  249. # Requestor places a unique value for each active
  250. # transaction. Responder puts NAME_TRN_ID value
  251. # from request packet in response packet.
  252. self.opcode = 0 # Packet type code
  253. self.nm_flags = 0 # Flags for operation
  254. self.rcode = 0 # Result codes of request.
  255. self.qdcount = 0 # Unsigned 16 bit integer specifying the number of entries in the question section of a Name
  256. self.ancount = 0 # Unsigned 16 bit integer specifying the number of
  257. # resource records in the answer section of a Name
  258. # Service packet.
  259. self.nscount = 0 # Unsigned 16 bit integer specifying the number of
  260. # resource records in the authority section of a
  261. # Name Service packet.
  262. self.arcount = 0 # Unsigned 16 bit integer specifying the number of
  263. # resource records in the additional records
  264. # section of a Name Service packeT.
  265. self.questions = ''
  266. self.answers = ''
  267. if data == 0:
  268. self._data = ''
  269. else:
  270. try:
  271. self._data = data
  272. self.opcode = ord(data[2]) >> 3
  273. self.nm_flags = ((ord(data[2]) & 0x3) << 4) | ((ord(data[3]) & 0xf0) >> 4)
  274. self.name_trn_id = unpack('>H', self._data[:2])[0]
  275. self.rcode = ord(data[3]) & 0x0f
  276. self.qdcount = unpack('>H', self._data[4:6])[0]
  277. self.ancount = unpack('>H', self._data[6:8])[0]
  278. self.nscount = unpack('>H', self._data[8:10])[0]
  279. self.arcount = unpack('>H', self._data[10:12])[0]
  280. self.answers = self._data[12:]
  281. except Exception:
  282. raise NetBIOSError( 'Wrong packet format ' )
  283. def set_opcode(self, opcode):
  284. self.opcode = opcode
  285. def set_trn_id(self, trn):
  286. self.name_trn_id = trn
  287. def set_nm_flags(self, nm_flags):
  288. self.nm_flags = nm_flags
  289. def set_rcode(self, rcode):
  290. self.rcode = rcode
  291. def addQuestion(self, question, qtype, qclass):
  292. self.qdcount += 1
  293. self.questions += question + pack('!HH',qtype,qclass)
  294. def get_trn_id(self):
  295. return self.name_trn_id
  296. def get_rcode(self):
  297. return self.rcode
  298. def get_nm_flags(self):
  299. return self.nm_flags
  300. def get_opcode(self):
  301. return self.opcode
  302. def get_qdcount(self):
  303. return self.qdcount
  304. def get_ancount(self):
  305. return self.ancount
  306. def get_nscount(self):
  307. return self.nscount
  308. def get_arcount(self):
  309. return self.arcount
  310. def rawData(self):
  311. secondWord = self.opcode << 11
  312. secondWord |= self.nm_flags << 4
  313. secondWord |= self.rcode
  314. data = pack('!HHHHHH', self.name_trn_id, secondWord , self.qdcount, self.ancount, self.nscount, self.arcount) + self.questions + self.answers
  315. return data
  316. def get_answers(self):
  317. return self.answers
  318. class NBHostEntry:
  319. def __init__(self, nbname, nametype, ip):
  320. self.__nbname = nbname
  321. self.__nametype = nametype
  322. self.__ip = ip
  323. def get_nbname(self):
  324. return self.__nbname
  325. def get_nametype(self):
  326. return self.__nametype
  327. def get_ip(self):
  328. return self.__ip
  329. def __repr__(self):
  330. return '<NBHostEntry instance: NBname="' + self.__nbname + '", IP="' + self.__ip + '">'
  331. class NBNodeEntry:
  332. def __init__(self, nbname, nametype, flags):
  333. self.__nbname = string.ljust(nbname,17)
  334. self.__nametype = nametype
  335. self.__flags = flags
  336. self.__isgroup = flags & 0x8000
  337. self.__nodetype = flags & 0x6000
  338. self.__deleting = flags & 0x1000
  339. self.__isconflict = flags & 0x0800
  340. self.__isactive = flags & 0x0400
  341. self.__ispermanent = flags & 0x0200
  342. def get_nbname(self):
  343. return self.__nbname
  344. def get_nametype(self):
  345. return self.__nametype
  346. def is_group(self):
  347. return self.__isgroup
  348. def get_nodetype(self):
  349. return self.__nodetype
  350. def is_deleting(self):
  351. return self.__deleting
  352. def is_conflict(self):
  353. return self.__isconflict
  354. def is_active(self):
  355. return self.__isactive
  356. def is_permanent(self):
  357. return self.__ispermanent
  358. def set_nbname(self, name):
  359. self.__nbname = string.ljust(name,17)
  360. def set_nametype(self, type):
  361. self.__nametype = type
  362. def set_flags(self,flags):
  363. self.__flags = flags
  364. def __repr__(self):
  365. s = '<NBNodeEntry instance: NBname="' + self.__nbname + '" NameType="' + NAME_TYPES[self.__nametype] + '"'
  366. if self.__isactive:
  367. s += ' ACTIVE'
  368. if self.__isgroup:
  369. s += ' GROUP'
  370. if self.__isconflict:
  371. s += ' CONFLICT'
  372. if self.__deleting:
  373. s += ' DELETING'
  374. return s
  375. def rawData(self):
  376. return self.__nbname + pack('!BH',self.__nametype, self.__flags)
  377. class NetBIOS:
  378. # Creates a NetBIOS instance without specifying any default NetBIOS domain nameserver.
  379. # All queries will be sent through the servport.
  380. def __init__(self, servport = NETBIOS_NS_PORT):
  381. self.__servport = NETBIOS_NS_PORT
  382. self.__nameserver = None
  383. self.__broadcastaddr = BROADCAST_ADDR
  384. self.mac = '00-00-00-00-00-00'
  385. def _setup_connection(self, dstaddr):
  386. port = randint(10000, 60000)
  387. af, socktype, proto, _canonname, _sa = socket.getaddrinfo(dstaddr, port, socket.AF_INET, socket.SOCK_DGRAM)[0]
  388. s = socket.socket(af, socktype, proto)
  389. has_bind = 1
  390. for _i in range(0, 10):
  391. # We try to bind to a port for 10 tries
  392. try:
  393. s.bind(( INADDR_ANY, randint(10000, 60000) ))
  394. s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
  395. has_bind = 1
  396. except socket.error:
  397. pass
  398. if not has_bind:
  399. raise NetBIOSError, ( 'Cannot bind to a good UDP port', ERRCLASS_OS, errno.EAGAIN )
  400. self.__sock = s
  401. # Set the default NetBIOS domain nameserver.
  402. def set_nameserver(self, nameserver):
  403. self.__nameserver = nameserver
  404. # Return the default NetBIOS domain nameserver, or None if none is specified.
  405. def get_nameserver(self):
  406. return self.__nameserver
  407. # Set the broadcast address to be used for query.
  408. def set_broadcastaddr(self, broadcastaddr):
  409. self.__broadcastaddr = broadcastaddr
  410. # Return the broadcast address to be used, or BROADCAST_ADDR if default broadcast address is used.
  411. def get_broadcastaddr(self):
  412. return self.__broadcastaddr
  413. # Returns a NBPositiveNameQueryResponse instance containing the host information for nbname.
  414. # If a NetBIOS domain nameserver has been specified, it will be used for the query.
  415. # Otherwise, the query is broadcasted on the broadcast address.
  416. def gethostbyname(self, nbname, qtype = TYPE_WORKSTATION, scope = None, timeout = 1):
  417. return self.__queryname(nbname, self.__nameserver, qtype, scope, timeout)
  418. # Returns a list of NBNodeEntry instances containing node status information for nbname.
  419. # If destaddr contains an IP address, then this will become an unicast query on the destaddr.
  420. # Raises NetBIOSTimeout if timeout (in secs) is reached.
  421. # Raises NetBIOSError for other errors
  422. def getnodestatus(self, nbname, destaddr = None, type = TYPE_WORKSTATION, scope = None, timeout = 1):
  423. if destaddr:
  424. return self.__querynodestatus(nbname, destaddr, type, scope, timeout)
  425. else:
  426. return self.__querynodestatus(nbname, self.__nameserver, type, scope, timeout)
  427. def getnetbiosname(self, ip):
  428. entries = self.getnodestatus('*',ip)
  429. entries = filter(lambda x:x.get_nametype() == TYPE_SERVER, entries)
  430. return entries[0].get_nbname().strip()
  431. def getmacaddress(self):
  432. return self.mac
  433. def __queryname(self, nbname, destaddr, qtype, scope, timeout, retries = 0):
  434. self._setup_connection(destaddr)
  435. trn_id = randint(1, 32000)
  436. p = NetBIOSPacket()
  437. p.set_trn_id(trn_id)
  438. netbios_name = nbname.upper()
  439. qn_label = encode_name(netbios_name, qtype, scope)
  440. p.addQuestion(qn_label, QUESTION_TYPE_NB, QUESTION_CLASS_IN)
  441. p.set_nm_flags(NM_FLAGS_RD)
  442. if not destaddr:
  443. p.set_nm_flags(p.get_nm_flags() | NM_FLAGS_BROADCAST)
  444. destaddr = self.__broadcastaddr
  445. req = p.rawData()
  446. tries = retries
  447. while 1:
  448. self.__sock.sendto(req, ( destaddr, self.__servport ))
  449. try:
  450. ready, _, _ = select.select([ self.__sock.fileno() ], [ ] , [ ], timeout)
  451. if not ready:
  452. if tries:
  453. # Retry again until tries == 0
  454. tries -= 1
  455. else:
  456. raise NetBIOSTimeout
  457. else:
  458. data, _ = self.__sock.recvfrom(65536, 0)
  459. res = NetBIOSPacket(data)
  460. if res.get_trn_id() == p.get_trn_id():
  461. if res.get_rcode():
  462. if res.get_rcode() == 0x03:
  463. return None
  464. else:
  465. raise NetBIOSError, ( 'Negative name query response', ERRCLASS_QUERY, res.get_rcode() )
  466. if res.get_ancount() != 1:
  467. raise NetBIOSError( 'Malformed response')
  468. return NBPositiveNameQueryResponse(res.get_answers())
  469. except select.error, ex:
  470. if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
  471. raise NetBIOSError, ( 'Error occurs while waiting for response', ERRCLASS_OS, ex[0] )
  472. raise
  473. def __querynodestatus(self, nbname, destaddr, type, scope, timeout):
  474. self._setup_connection(destaddr)
  475. trn_id = randint(1, 32000)
  476. p = NetBIOSPacket()
  477. p.set_trn_id(trn_id)
  478. netbios_name = string.upper(nbname)
  479. qn_label = encode_name(netbios_name, type, scope)
  480. p.addQuestion(qn_label, QUESTION_TYPE_NBSTAT, QUESTION_CLASS_IN)
  481. if not destaddr:
  482. p.set_nm_flags(NM_FLAGS_BROADCAST)
  483. destaddr = self.__broadcastaddr
  484. req = p.rawData()
  485. tries = 3
  486. while 1:
  487. try:
  488. self.__sock.sendto(req, 0, ( destaddr, self.__servport ))
  489. ready, _, _ = select.select([ self.__sock.fileno() ], [ ] , [ ], timeout)
  490. if not ready:
  491. if tries:
  492. # Retry again until tries == 0
  493. tries -= 1
  494. else:
  495. raise NetBIOSTimeout
  496. else:
  497. try:
  498. data, _ = self.__sock.recvfrom(65536, 0)
  499. except Exception, e:
  500. raise NetBIOSError, "recvfrom error: %s" % str(e)
  501. self.__sock.close()
  502. res = NetBIOSPacket(data)
  503. if res.get_trn_id() == p.get_trn_id():
  504. if res.get_rcode():
  505. if res.get_rcode() == 0x03:
  506. # I'm just guessing here
  507. raise NetBIOSError, "Cannot get data from server"
  508. else:
  509. raise NetBIOSError, ( 'Negative name query response', ERRCLASS_QUERY, res.get_rcode() )
  510. answ = NBNodeStatusResponse(res.get_answers())
  511. self.mac = answ.get_mac()
  512. return answ.get_node_names()
  513. except select.error, ex:
  514. if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
  515. raise NetBIOSError, ( 'Error occurs while waiting for response', ERRCLASS_OS, ex[0] )
  516. except socket.error, ex:
  517. raise NetBIOSError, 'Connection error: %s' % str(ex)
  518. # Perform first and second level encoding of name as specified in RFC 1001 (Section 4)
  519. def encode_name(name, type, scope):
  520. if name == '*':
  521. name += '\0' * 15
  522. elif len(name) > 15:
  523. name = name[:15] + chr(type)
  524. else:
  525. name = string.ljust(name, 15) + chr(type)
  526. encoded_name = chr(len(name) * 2) + re.sub('.', _do_first_level_encoding, name)
  527. if scope:
  528. encoded_scope = ''
  529. for s in string.split(scope, '.'):
  530. encoded_scope = encoded_scope + chr(len(s)) + s
  531. return encoded_name + encoded_scope + '\0'
  532. else:
  533. return encoded_name + '\0'
  534. # Internal method for use in encode_name()
  535. def _do_first_level_encoding(m):
  536. s = ord(m.group(0))
  537. return string.uppercase[s >> 4] + string.uppercase[s & 0x0f]
  538. def decode_name(name):
  539. name_length = ord(name[0])
  540. assert name_length == 32
  541. decoded_name = re.sub('..', _do_first_level_decoding, name[1:33])
  542. if name[33] == '\0':
  543. return 34, decoded_name, ''
  544. else:
  545. decoded_domain = ''
  546. offset = 34
  547. while 1:
  548. domain_length = ord(name[offset])
  549. if domain_length == 0:
  550. break
  551. decoded_domain = '.' + name[offset:offset + domain_length]
  552. offset += domain_length
  553. return offset + 1, decoded_name, decoded_domain
  554. def _do_first_level_decoding(m):
  555. s = m.group(0)
  556. return chr(((ord(s[0]) - ord('A')) << 4) | (ord(s[1]) - ord('A')))
  557. class NetBIOSSessionPacket:
  558. def __init__(self, data = 0):
  559. self.type = 0x0
  560. self.flags = 0x0
  561. self.length = 0x0
  562. if data == 0:
  563. self._trailer = ''
  564. else:
  565. try:
  566. self.type = ord(data[0])
  567. if self.type == NETBIOS_SESSION_MESSAGE:
  568. self.length = ord(data[1]) << 16 | (unpack('!H', data[2:4])[0])
  569. else:
  570. self.flags = ord(data[1])
  571. self.length = unpack('!H', data[2:4])[0]
  572. self._trailer = data[4:]
  573. except:
  574. raise NetBIOSError( 'Wrong packet format ' )
  575. def set_type(self, type):
  576. self.type = type
  577. def get_type(self):
  578. return self.type
  579. def rawData(self):
  580. if self.type == NETBIOS_SESSION_MESSAGE:
  581. data = pack('!BBH',self.type,self.length >> 16,self.length & 0xFFFF) + self._trailer
  582. else:
  583. data = pack('!BBH',self.type,self.flags,self.length) + self._trailer
  584. return data
  585. def set_trailer(self,data):
  586. self._trailer = data
  587. self.length = len(data)
  588. def get_length(self):
  589. return self.length
  590. def get_trailer(self):
  591. return self._trailer
  592. class NetBIOSSession:
  593. def __init__(self, myname, remote_name, remote_host, remote_type = TYPE_SERVER, sess_port = NETBIOS_SESSION_PORT, timeout = None, local_type = TYPE_WORKSTATION, sock = None):
  594. if len(myname) > 15:
  595. self.__myname = string.upper(myname[:15])
  596. else:
  597. self.__myname = string.upper(myname)
  598. self.__local_type = local_type
  599. assert remote_name
  600. # if destination port SMB_SESSION_PORT and remote name *SMBSERVER, we're changing it to its IP address
  601. # helping solving the client mistake ;)
  602. if remote_name == '*SMBSERVER' and sess_port == SMB_SESSION_PORT:
  603. remote_name = remote_host
  604. # If remote name is *SMBSERVER let's try to query its name.. if can't be guessed, continue and hope for the best
  605. if remote_name == '*SMBSERVER':
  606. nb = NetBIOS()
  607. try:
  608. res = nb.getnetbiosname(remote_host)
  609. except:
  610. res = None
  611. pass
  612. if res is not None:
  613. remote_name = res
  614. if len(remote_name) > 15:
  615. self.__remote_name = string.upper(remote_name[:15])
  616. else:
  617. self.__remote_name = string.upper(remote_name)
  618. self.__remote_type = remote_type
  619. self.__remote_host = remote_host
  620. if sock is not None:
  621. # We are acting as a server
  622. self._sock = sock
  623. else:
  624. self._sock = self._setup_connection((remote_host, sess_port))
  625. if sess_port == NETBIOS_SESSION_PORT:
  626. self._request_session(remote_type, local_type, timeout)
  627. def get_myname(self):
  628. return self.__myname
  629. def get_mytype(self):
  630. return self.__local_type
  631. def get_remote_host(self):
  632. return self.__remote_host
  633. def get_remote_name(self):
  634. return self.__remote_name
  635. def get_remote_type(self):
  636. return self.__remote_type
  637. def close(self):
  638. self._sock.close()
  639. def get_socket(self):
  640. return self._sock
  641. class NetBIOSUDPSessionPacket(Structure):
  642. TYPE_DIRECT_UNIQUE = 16
  643. TYPE_DIRECT_GROUP = 17
  644. FLAGS_MORE_FRAGMENTS = 1
  645. FLAGS_FIRST_FRAGMENT = 2
  646. FLAGS_B_NODE = 0
  647. structure = (
  648. ('Type','B=16'), # Direct Unique Datagram
  649. ('Flags','B=2'), # FLAGS_FIRST_FRAGMENT
  650. ('ID','<H'),
  651. ('_SourceIP','>L'),
  652. ('SourceIP','"'),
  653. ('SourcePort','>H=138'),
  654. ('DataLegth','>H-Data'),
  655. ('Offset','>H=0'),
  656. ('SourceName','z'),
  657. ('DestinationName','z'),
  658. ('Data',':'),
  659. )
  660. def getData(self):
  661. addr = self['SourceIP'].split('.')
  662. addr = [int(x) for x in addr]
  663. addr = (((addr[0] << 8) + addr[1] << 8) + addr[2] << 8) + addr[3]
  664. self['_SourceIP'] = addr
  665. return Structure.getData(self)
  666. def get_trailer(self):
  667. return self['Data']
  668. class NetBIOSUDPSession(NetBIOSSession):
  669. def _setup_connection(self, peer):
  670. af, socktype, proto, canonname, sa = socket.getaddrinfo(peer[0], peer[1], 0, socket.SOCK_DGRAM)[0]
  671. sock = socket.socket(af, socktype, proto)
  672. sock.connect(sa)
  673. sock = socket.socket(af, socktype, proto)
  674. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  675. sock.bind((INADDR_ANY, 138))
  676. self.peer = peer
  677. return sock
  678. def _request_session(self, remote_type, local_type, timeout = None):
  679. pass
  680. def next_id(self):
  681. if hasattr(self, '__dgram_id'):
  682. answer = self.__dgram_id
  683. else:
  684. self.__dgram_id = randint(1,65535)
  685. answer = self.__dgram_id
  686. self.__dgram_id += 1
  687. return answer
  688. def send_packet(self, data):
  689. # Yes... I know...
  690. self._sock.connect(self.peer)
  691. p = NetBIOSUDPSessionPacket()
  692. p['ID'] = self.next_id()
  693. p['SourceIP'] = self._sock.getsockname()[0]
  694. p['SourceName'] = encode_name(self.get_myname(), self.get_mytype(), '')[:-1]
  695. p['DestinationName'] = encode_name(self.get_remote_name(), self.get_remote_type(), '')[:-1]
  696. p['Data'] = data
  697. self._sock.sendto(str(p), self.peer)
  698. self._sock.close()
  699. self._sock = self._setup_connection(self.peer)
  700. def recv_packet(self, timeout = None):
  701. # The next loop is a workaround for a bigger problem:
  702. # When data reaches higher layers, the lower headers are lost,
  703. # and with them, for example, the source IP. Hence, SMB users
  704. # can't know where packets are comming from... we need a better
  705. # solution, right now, we will filter everything except packets
  706. # coming from the remote_host specified in __init__()
  707. while 1:
  708. data, peer = self._sock.recvfrom(8192)
  709. # print "peer: %r self.peer: %r" % (peer, self.peer)
  710. if peer == self.peer: break
  711. return NetBIOSUDPSessionPacket(data)
  712. class NetBIOSTCPSession(NetBIOSSession):
  713. def __init__(self, myname, remote_name, remote_host, remote_type = TYPE_SERVER, sess_port = NETBIOS_SESSION_PORT, timeout = None, local_type = TYPE_WORKSTATION, sock = None, select_poll = False):
  714. self.__select_poll = select_poll
  715. if self.__select_poll:
  716. self.read_function = self.polling_read
  717. else:
  718. self.read_function = self.non_polling_read
  719. NetBIOSSession.__init__(self, myname, remote_name, remote_host, remote_type = remote_type, sess_port = sess_port, timeout = timeout, local_type = local_type, sock=sock)
  720. def _setup_connection(self, peer):
  721. try:
  722. af, socktype, proto, canonname, sa = socket.getaddrinfo(peer[0], peer[1], 0, socket.SOCK_STREAM)[0]
  723. sock = socket.socket(af, socktype, proto)
  724. sock.connect(sa)
  725. except socket.error, e:
  726. raise socket.error("Connection error (%s:%s)" % (peer[0], peer[1]), e)
  727. return sock
  728. def send_packet(self, data):
  729. p = NetBIOSSessionPacket()
  730. p.set_type(NETBIOS_SESSION_MESSAGE)
  731. p.set_trailer(data)
  732. self._sock.send(p.rawData())
  733. def recv_packet(self, timeout = None):
  734. data = self.__read(timeout)
  735. return NetBIOSSessionPacket(data)
  736. def _request_session(self, remote_type, local_type, timeout = None):
  737. p = NetBIOSSessionPacket()
  738. remote_name = encode_name(self.get_remote_name(), remote_type, '')
  739. myname = encode_name(self.get_myname(), local_type, '')
  740. p.set_type(NETBIOS_SESSION_REQUEST)
  741. p.set_trailer(remote_name + myname)
  742. self._sock.send(p.rawData())
  743. while 1:
  744. p = self.recv_packet(timeout)
  745. if p.get_type() == NETBIOS_SESSION_NEGATIVE_RESPONSE:
  746. raise NetBIOSError, ( 'Cannot request session', ERRCLASS_SESSION, ord(p.get_trailer()[0]) )
  747. elif p.get_type() == NETBIOS_SESSION_POSITIVE_RESPONSE:
  748. break
  749. else:
  750. # Ignore all other messages, most probably keepalive messages
  751. pass
  752. def polling_read(self, read_length, timeout):
  753. data = ''
  754. if timeout is None:
  755. timeout = 3600
  756. time_left = timeout
  757. CHUNK_TIME = 0.025
  758. bytes_left = read_length
  759. while bytes_left > 0:
  760. try:
  761. ready, _, _ = select.select([self._sock.fileno() ], [ ], [ ], 0)
  762. if not ready:
  763. if time_left <= 0:
  764. raise NetBIOSTimeout
  765. else:
  766. time.sleep(CHUNK_TIME)
  767. time_left -= CHUNK_TIME
  768. continue
  769. received = self._sock.recv(bytes_left)
  770. if len(received) == 0:
  771. raise NetBIOSError, ( 'Error while reading from remote', ERRCLASS_OS, None)
  772. data = data + received
  773. bytes_left = read_length - len(data)
  774. except select.error, ex:
  775. if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
  776. raise NetBIOSError, ( 'Error occurs while reading from remote', ERRCLASS_OS, ex[0] )
  777. return data
  778. def non_polling_read(self, read_length, timeout):
  779. data = ''
  780. bytes_left = read_length
  781. while bytes_left > 0:
  782. try:
  783. ready, _, _ = select.select([self._sock.fileno() ], [ ], [ ], timeout)
  784. if not ready:
  785. raise NetBIOSTimeout
  786. received = self._sock.recv(bytes_left)
  787. if len(received) == 0:
  788. raise NetBIOSError, ( 'Error while reading from remote', ERRCLASS_OS, None)
  789. data = data + received
  790. bytes_left = read_length - len(data)
  791. except select.error, ex:
  792. if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
  793. raise NetBIOSError, ( 'Error occurs while reading from remote', ERRCLASS_OS, ex[0] )
  794. return data
  795. def __read(self, timeout = None):
  796. data = self.read_function(4, timeout)
  797. type, flags, length = unpack('>ccH', data)
  798. if ord(type) == NETBIOS_SESSION_MESSAGE:
  799. length |= ord(flags) << 16
  800. else:
  801. if ord(flags) & 0x01:
  802. length |= 0x10000
  803. data2 = self.read_function(length, timeout)
  804. return data + data2
  805. ERRCLASS_QUERY = 0x00
  806. ERRCLASS_SESSION = 0xf0
  807. ERRCLASS_OS = 0xff
  808. QUERY_ERRORS = { 0x01: 'Request format error. Please file a bug report.',
  809. 0x02: 'Internal server error',
  810. 0x03: 'Name does not exist',
  811. 0x04: 'Unsupported request',
  812. 0x05: 'Request refused'
  813. }
  814. SESSION_ERRORS = { 0x80: 'Not listening on called name',
  815. 0x81: 'Not listening for calling name',
  816. 0x82: 'Called name not present',
  817. 0x83: 'Sufficient resources',
  818. 0x8f: 'Unspecified error'
  819. }
  820. def main():
  821. def get_netbios_host_by_name(name):
  822. n = NetBIOS()
  823. n.set_broadcastaddr('255.255.255.255') # To avoid use "<broadcast>" in socket
  824. for qtype in (TYPE_WORKSTATION, TYPE_CLIENT, TYPE_SERVER, TYPE_DOMAIN_MASTER, TYPE_DOMAIN_CONTROLLER):
  825. try:
  826. addrs = n.gethostbyname(name, qtype = qtype).get_addr_entries()
  827. except NetBIOSTimeout:
  828. continue
  829. else:
  830. return addrs
  831. raise Exception("Host not found")
  832. n = get_netbios_host_by_name("some-host")
  833. print n
  834. if __name__ == '__main__':
  835. main()