Coverage for /root/GitHubProjects/impacket/impacket/nmb.py : 70%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Impacket - Collection of Python classes for working with network protocols.
2#
3# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved.
4#
5# This software is provided under a slightly modified version
6# of the Apache Software License. See the accompanying LICENSE file
7# for more information.
8#
9# Author:
10# Altered source done by Alberto Solino (@agsolino)
11#
12# Copyright and license note from Pysmb:
13#
14# Copyright (C) 2001 Michael Teo <michaelteo@bigfoot.com>
15# nmb.py - NetBIOS library
16#
17# This software is provided 'as-is', without any express or implied warranty.
18# In no event will the author be held liable for any damages arising from the
19# use of this software.
20#
21# Permission is granted to anyone to use this software for any purpose,
22# including commercial applications, and to alter it and redistribute it
23# freely, subject to the following restrictions:
24#
25# 1. The origin of this software must not be misrepresented; you must not
26# claim that you wrote the original software. If you use this software
27# in a product, an acknowledgment in the product documentation would be
28# appreciated but is not required.
29#
30# 2. Altered source versions must be plainly marked as such, and must not be
31# misrepresented as being the original software.
32#
33# 3. This notice cannot be removed or altered from any source distribution.
34#
35from __future__ import division
36from __future__ import print_function
37from __future__ import absolute_import
38import errno
39import re
40import select
41import socket
42import string
43import time
44import random
45from struct import pack, unpack
46from six import byte2int, indexbytes, b
48from impacket.structure import Structure
50# Our random number generator
51try:
52 rand = random.SystemRandom()
53except NotImplementedError:
54 rand = random
55 pass
58################################################################################
59# CONSTANTS
60################################################################################
61# Taken from socket module reference
62INADDR_ANY = '0.0.0.0'
63BROADCAST_ADDR = '<broadcast>'
65# Default port for NetBIOS name service
66NETBIOS_NS_PORT = 137
67# Default port for NetBIOS session service
68NETBIOS_SESSION_PORT = 139
70# Default port for SMB session service
71SMB_SESSION_PORT = 445
73# Owner Node Type Constants
74NODE_B = 0x0000
75NODE_P = 0x2000
76NODE_M = 0x4000
77NODE_RESERVED = 0x6000
78NODE_GROUP = 0x8000
79NODE_UNIQUE = 0x0
81# Name Type Constants
82TYPE_UNKNOWN = 0x01
83TYPE_WORKSTATION = 0x00
84TYPE_CLIENT = 0x03
85TYPE_SERVER = 0x20
86TYPE_DOMAIN_MASTER = 0x1B
87TYPE_DOMAIN_CONTROLLER = 0x1C
88TYPE_MASTER_BROWSER = 0x1D
89TYPE_BROWSER = 0x1E
90TYPE_NETDDE = 0x1F
91TYPE_STATUS = 0x21
93# Opcodes values
94OPCODE_QUERY = 0
95OPCODE_REGISTRATION = 0x5 << 11
96OPCODE_RELEASE = 0x6 << 11
97OPCODE_WACK = 0x7 << 11
98OPCODE_REFRESH = 0x8 << 11
99OPCODE_REQUEST = 0 << 11
100OPCODE_RESPONSE = 0x10 << 11
102# NM_FLAGS
103NM_FLAGS_BROADCAST = 0x1 << 4
104NM_FLAGS_UNICAST = 0 << 4
105NM_FLAGS_RA = 0x8 << 4
106NM_FLAGS_RD = 0x10 << 4
107NM_FLAGS_TC = 0x20 << 4
108NM_FLAGS_AA = 0x40 << 4
110# QUESTION_TYPE
111QUESTION_TYPE_NB = 0x20 # NetBIOS general Name Service Resource Record
112QUESTION_TYPE_NBSTAT = 0x21 # NetBIOS NODE STATUS Resource Record
113# QUESTION_CLASS
114QUESTION_CLASS_IN = 0x1 # Internet class
116# RESOURCE RECORD RR_TYPE field definitions
117RR_TYPE_A = 0x1 # IP address Resource Record
118RR_TYPE_NS = 0x2 # Name Server Resource Record
119RR_TYPE_NULL = 0xA # NULL Resource Record
120RR_TYPE_NB = 0x20 # NetBIOS general Name Service Resource Record
121RR_TYPE_NBSTAT = 0x21 # NetBIOS NODE STATUS Resource Record
123# RESOURCE RECORD RR_CLASS field definitions
124RR_CLASS_IN = 1 # Internet class
126# RCODE values
127RCODE_FMT_ERR = 0x1 # Format Error. Request was invalidly formatted.
128RCODE_SRV_ERR = 0x2 # Server failure. Problem with NBNS, cannot process name.
129RCODE_IMP_ERR = 0x4 # Unsupported request error. Allowable only for challenging NBNS when gets an Update type
130 # registration request.
131RCODE_RFS_ERR = 0x5 # Refused error. For policy reasons server will not register this name from this host.
132RCODE_ACT_ERR = 0x6 # Active error. Name is owned by another node.
133RCODE_CFT_ERR = 0x7 # Name in conflict error. A UNIQUE name is owned by more than one node.
135# NAME_FLAGS
136NAME_FLAGS_PRM = 0x0200 # Permanent Name Flag. If one (1) then entry is for the permanent node name. Flag is zero
137 # (0) for all other names.
138NAME_FLAGS_ACT = 0x0400 # Active Name Flag. All entries have this flag set to one (1).
139NAME_FLAG_CNF = 0x0800 # Conflict Flag. If one (1) then name on this node is in conflict.
140NAME_FLAG_DRG = 0x1000 # Deregister Flag. If one (1) then this name is in the process of being deleted.
142# NB_FLAGS
143NB_FLAGS_ONT_B = 0
144NB_FLAGS_ONT_P = 1 << 13
145NB_FLAGS_ONT_M = 2 << 13
146NB_FLAGS_G = 1 << 15
148NAME_TYPES = {TYPE_UNKNOWN: 'Unknown', TYPE_WORKSTATION: 'Workstation', TYPE_CLIENT: 'Client',
149 TYPE_SERVER: 'Server', TYPE_DOMAIN_MASTER: 'Domain Master', TYPE_DOMAIN_CONTROLLER: 'Domain Controller',
150 TYPE_MASTER_BROWSER: 'Master Browser', TYPE_BROWSER: 'Browser Server', TYPE_NETDDE: 'NetDDE Server',
151 TYPE_STATUS: 'Status'}
153# NetBIOS Session Types
154NETBIOS_SESSION_MESSAGE = 0x0
155NETBIOS_SESSION_REQUEST = 0x81
156NETBIOS_SESSION_POSITIVE_RESPONSE = 0x82
157NETBIOS_SESSION_NEGATIVE_RESPONSE = 0x83
158NETBIOS_SESSION_RETARGET_RESPONSE = 0x84
159NETBIOS_SESSION_KEEP_ALIVE = 0x85
161################################################################################
162# HELPERS
163################################################################################
164def encode_name(name, nametype, scope):
165 # ToDo: Rewrite this simpler, we're using less than written
166 """
167 Perform first and second level encoding of name as specified in RFC 1001 (Section 4)
169 :param string name: the name to encode
170 :param integer nametype: the name type constants
171 :param string scope: the name's scope
173 :return string/bytes: the encoded name.
174 """
175 if name == '*':
176 name += '\0' * 15
177 elif len(name) > 15:
178 name = name[:15] + chr(nametype)
179 else:
180 name = name.ljust(15) + chr(nametype)
182 encoded_name = chr(len(name) * 2) + re.sub('.', _do_first_level_encoding, name)
184 try:
185 if isinstance(encoded_name, unicode):
186 encoded_name = encoded_name.encode('utf-8')
187 except NameError:
188 pass
189 if scope: 189 ↛ 190line 189 didn't jump to line 190, because the condition on line 189 was never true
190 encoded_scope = ''
191 for s in scope.split('.'):
192 encoded_scope = encoded_scope + chr(len(s)) + s
194 return b(encoded_name + encoded_scope) + b'\0'
195 else:
196 return b(encoded_name) + b'\0'
198# Internal method for use in encode_name()
199def _do_first_level_encoding(m):
200 s = ord(m.group(0))
201 return string.ascii_uppercase[s >> 4] + string.ascii_uppercase[s & 0x0f]
203def decode_name(name):
204 # ToDo: Rewrite this simpler, we're using less than written
205 """
206 Perform first and second level decoding of name as specified in RFC 1001 (Section 4)
208 :param string/bytes name: the name to decode
210 :return string: the decoded name.
211 """
213 name_length = ord(name[0:1])
214 assert name_length == 32
216 decoded_name = re.sub('..', _do_first_level_decoding, name[1:33].decode('utf-8'))
217 if name[33:34] == b'\0': 217 ↛ 220line 217 didn't jump to line 220, because the condition on line 217 was never false
218 return 34, decoded_name, ''
219 else:
220 decoded_domain = ''
221 offset = 34
222 while 1:
223 domain_length = byte2int(name[offset:offset+1])
224 if domain_length == 0:
225 break
226 decoded_domain = '.' + name[offset:offset + domain_length].decode('utf-8')
227 offset += domain_length
228 return offset + 1, decoded_name, decoded_domain
230def _do_first_level_decoding(m):
231 s = m.group(0)
232 return chr(((ord(s[0]) - ord('A')) << 4) | (ord(s[1]) - ord('A')))
234ERRCLASS_QUERY = 0x00
235ERRCLASS_SESSION = 0xf0
236ERRCLASS_OS = 0xff
238QUERY_ERRORS = {0x01: 'Format Error. Request was invalidly formatted',
239 0x02: 'Server failure. Problem with NBNS, cannot process name.',
240 0x03: 'Name does not exist',
241 0x04: 'Unsupported request error. Allowable only for challenging NBNS when gets an Update type registration request.',
242 0x05: 'Refused error. For policy reasons server will not register this name from this host.',
243 0x06: 'Active error. Name is owned by another node.',
244 0x07: 'Name in conflict error. A UNIQUE name is owned by more than one node.',
246 }
248SESSION_ERRORS = {0x80: 'Not listening on called name',
249 0x81: 'Not listening for calling name',
250 0x82: 'Called name not present',
251 0x83: 'Sufficient resources',
252 0x8f: 'Unspecified error'
253 }
255class NetBIOSError(Exception):
256 def __init__(self, error_message='', error_class=None, error_code=None):
257 self.error_class = error_class
258 self.error_code = error_code
259 self.error_msg = error_message
261 def get_error_code(self):
262 return self.error
264 def getErrorCode(self):
265 return self.get_error_code()
267 def get_error_string(self):
268 return str(self)
270 def getErrorString(self):
271 return str(self)
273 def __str__(self):
274 if self.error_code is not None:
275 if self.error_code in QUERY_ERRORS:
276 return '%s-%s(%s)' % (self.error_msg, QUERY_ERRORS[self.error_code], self.error_code)
277 elif self.error_code in SESSION_ERRORS:
278 return '%s-%s(%s)' % (self.error_msg, SESSION_ERRORS[self.error_code], self.error_code)
279 else:
280 return '%s(%s)' % (self.error_msg, self.error_code)
281 else:
282 return '%s' % self.error_msg
284class NetBIOSTimeout(Exception):
285 def __init__(self, message = 'The NETBIOS connection with the remote host timed out.'):
286 Exception.__init__(self, message)
288################################################################################
289# 4.2 NAME SERVER PACKETS
290################################################################################
291class NBNSResourceRecord(Structure):
292 structure = (
293 ('RR_NAME','z=\x00'),
294 ('RR_TYPE','>H=0'),
295 ('RR_CLASS','>H=0'),
296 ('TTL','>L=0'),
297 ('RDLENGTH','>H-RDATA'),
298 ('RDATA',':=""'),
299 )
301class NBNodeStatusResponse(NBNSResourceRecord):
302 def __init__(self, data = 0):
303 NBNSResourceRecord.__init__(self, data)
304 self.mac = b'00-00-00-00-00-00'
305 self.num_names = unpack('B', self['RDATA'][:1])[0]
306 self.entries = list()
307 data = self['RDATA'][1:]
308 for _ in range(self.num_names):
309 entry = NODE_NAME_ENTRY(data)
310 data = data[len(entry):]
311 self.entries.append(entry)
312 self.statistics = STATISTICS(data)
313 self.set_mac_in_hexa(self.statistics['UNIT_ID'])
315 def set_mac_in_hexa(self, data):
316 data_aux = u''
317 for d in bytearray(data):
318 if data_aux == '':
319 data_aux = '%02x' % d
320 else:
321 data_aux += '-%02x' % d
322 self.mac = data_aux.upper()
324 def get_mac(self):
325 return self.mac
327 def rawData(self):
328 res = pack('!B', self.num_names )
329 for i in range(0, self.num_names):
330 res += self.entries[i].getData()
332class NBPositiveNameQueryResponse(NBNSResourceRecord):
333 def __init__(self, data = 0):
334 NBNSResourceRecord.__init__(self, data)
335 self.entries = [ ]
336 rdata = self['RDATA']
337 while len(rdata) > 0:
338 entry = ADDR_ENTRY(rdata)
339 rdata = rdata[len(entry):]
340 self.entries.append(socket.inet_ntoa(entry['NB_ADDRESS']))
342# 4.2.1. GENERAL FORMAT OF NAME SERVICE PACKETS
343class NAME_SERVICE_PACKET(Structure):
344 commonHdr = (
345 ('NAME_TRN_ID','>H=0'),
346 ('FLAGS','>H=0'),
347 ('QDCOUNT','>H=0'),
348 ('ANCOUNT','>H=0'),
349 ('NSCOUNT','>H=0'),
350 ('ARCOUNT','>H=0'),
351 )
352 structure = (
353 ('ANSWERS',':'),
354 )
356# 4.2.1.2. QUESTION SECTION
357class QUESTION_ENTRY(Structure):
358 commonHdr = (
359 ('QUESTION_NAME','z'),
360 ('QUESTION_TYPE','>H=0'),
361 ('QUESTION_CLASS','>H=0'),
362 )
364# 4.2.1.3. RESOURCE RECORD
365class RESOURCE_RECORD(Structure):
366 structure = (
367 ('RR_NAME','z=\x00'),
368 ('RR_TYPE','>H=0'),
369 ('RR_CLASS','>H=0'),
370 ('TTL','>L=0'),
371 ('RDLENGTH','>H-RDATA'),
372 ('RDATA',':=""'),
373 )
375# 4.2.2. NAME REGISTRATION REQUEST
376class NAME_REGISTRATION_REQUEST(NAME_SERVICE_PACKET):
377 structure = (
378 ('QUESTION_NAME', ':'),
379 ('QUESTION_TYPE', '>H=0'),
380 ('QUESTION_CLASS', '>H=0'),
381 ('RR_NAME',':', ),
382 ('RR_TYPE', '>H=0'),
383 ('RR_CLASS','>H=0'),
384 ('TTL', '>L=0'),
385 ('RDLENGTH', '>H=6'),
386 ('NB_FLAGS', '>H=0'),
387 ('NB_ADDRESS', '4s=b""'),
388 )
389 def __init__(self, data=None):
390 NAME_SERVICE_PACKET.__init__(self,data)
391 self['FLAGS'] = OPCODE_REQUEST | NM_FLAGS_RD | OPCODE_REGISTRATION
392 self['QDCOUNT'] = 1
393 self['ANCOUNT'] = 0
394 self['NSCOUNT'] = 0
395 self['ARCOUNT'] = 1
397 self['QUESTION_TYPE'] = QUESTION_TYPE_NB
398 self['QUESTION_CLASS'] = QUESTION_CLASS_IN
400 self['RR_TYPE'] = RR_TYPE_NB
401 self['RR_CLASS'] = RR_CLASS_IN
403# 4.2.3. NAME OVERWRITE REQUEST & DEMAND
404class NAME_OVERWRITE_REQUEST(NAME_REGISTRATION_REQUEST):
405 def __init__(self, data=None):
406 NAME_REGISTRATION_REQUEST.__init__(self,data)
407 self['FLAGS'] = OPCODE_REQUEST | OPCODE_REGISTRATION
408 self['QDCOUNT'] = 1
409 self['ANCOUNT'] = 0
410 self['NSCOUNT'] = 0
411 self['ARCOUNT'] = 1
413# 4.2.4. NAME REFRESH REQUEST
414class NAME_REFRESH_REQUEST(NAME_REGISTRATION_REQUEST):
415 def __init__(self, data=None):
416 NAME_REGISTRATION_REQUEST.__init__(self,data)
417 self['FLAGS'] = OPCODE_REFRESH | 0x1
418 self['QDCOUNT'] = 1
419 self['ANCOUNT'] = 0
420 self['NSCOUNT'] = 0
421 self['ARCOUNT'] = 1
423# 4.2.5. POSITIVE NAME REGISTRATION RESPONSE
424# 4.2.6. NEGATIVE NAME REGISTRATION RESPONSE
425# 4.2.7. END-NODE CHALLENGE REGISTRATION RESPONSE
426class NAME_REGISTRATION_RESPONSE(NAME_REGISTRATION_REQUEST):
427 def __init__(self, data=None):
428 NAME_REGISTRATION_REQUEST.__init__(self,data)
430# 4.2.8. NAME CONFLICT DEMAND
431class NAME_CONFLICT_DEMAND(NAME_REGISTRATION_REQUEST):
432 def __init__(self, data=None):
433 NAME_REGISTRATION_REQUEST.__init__(self,data)
435# ToDo: 4.2.9. NAME RELEASE REQUEST & DEMAND
436# ToDo: 4.2.10. POSITIVE NAME RELEASE RESPONSE
437# ToDo: 4.2.11. NEGATIVE NAME RELEASE RESPONSE
439# 4.2.12. NAME QUERY REQUEST
440class NAME_QUERY_REQUEST(NAME_SERVICE_PACKET):
441 structure = (
442 ('QUESTION_NAME', ':'),
443 ('QUESTION_TYPE', '>H=0'),
444 ('QUESTION_CLASS', '>H=0'),
445 )
446 def __init__(self, data=None):
447 NAME_SERVICE_PACKET.__init__(self,data)
448 self['FLAGS'] = OPCODE_REQUEST | OPCODE_REGISTRATION | NM_FLAGS_RD
449 self['RCODE'] = 0
450 self['QDCOUNT'] = 1
451 self['ANCOUNT'] = 0
452 self['NSCOUNT'] = 0
453 self['ARCOUNT'] = 0
455 self['QUESTION_TYPE'] = QUESTION_TYPE_NB
456 self['QUESTION_CLASS'] = QUESTION_CLASS_IN
458# 4.2.13. POSITIVE NAME QUERY RESPONSE
459class ADDR_ENTRY(Structure):
460 structure = (
461 ('NB_FLAGS', '>H=0'),
462 ('NB_ADDRESS', '4s=b""'),
463 )
465# ToDo: 4.2.15. REDIRECT NAME QUERY RESPONSE
466# ToDo: 4.2.16. WAIT FOR ACKNOWLEDGEMENT (WACK) RESPONSE
468# 4.2.17. NODE STATUS REQUEST
469class NODE_STATUS_REQUEST(NAME_QUERY_REQUEST):
470 def __init__(self, data=None):
471 NAME_QUERY_REQUEST.__init__(self,data)
473 self['FLAGS'] = 0
474 self['QUESTION_TYPE'] = QUESTION_TYPE_NBSTAT
476# 4.2.18. NODE STATUS RESPONSE
477class NODE_NAME_ENTRY(Structure):
478 structure = (
479 ('NAME','15s=b""'),
480 ('TYPE','B=0'),
481 ('NAME_FLAGS','>H'),
482 )
484class STATISTICS(Structure):
485 structure = (
486 ('UNIT_ID','6s=b""'),
487 ('JUMPERS','B'),
488 ('TEST_RESULT','B'),
489 ('VERSION_NUMBER','>H'),
490 ('PERIOD_OF_STATISTICS','>H'),
491 ('NUMBER_OF_CRCs','>H'),
492 ('NUMBER_ALIGNMENT_ERRORS','>H'),
493 ('NUMBER_OF_COLLISIONS','>H'),
494 ('NUMBER_SEND_ABORTS','>H'),
495 ('NUMBER_GOOD_SENDS','>L'),
496 ('NUMBER_GOOD_RECEIVES','>L'),
497 ('NUMBER_RETRANSMITS','>H'),
498 ('NUMBER_NO_RESOURCE_CONDITIONS','>H'),
499 ('NUMBER_FREE_COMMAND_BLOCKS','>H'),
500 ('TOTAL_NUMBER_COMMAND_BLOCKS','>H'),
501 ('MAX_TOTAL_NUMBER_COMMAND_BLOCKS','>H'),
502 ('NUMBER_PENDING_SESSIONS','>H'),
503 ('MAX_NUMBER_PENDING_SESSIONS','>H'),
504 ('MAX_TOTAL_SESSIONS_POSSIBLE','>H'),
505 ('SESSION_DATA_PACKET_SIZE','>H'),
506 )
508class NetBIOS:
509 # Creates a NetBIOS instance without specifying any default NetBIOS domain nameserver.
510 # All queries will be sent through the servport.
511 def __init__(self, servport = NETBIOS_NS_PORT):
512 self.__servport = NETBIOS_NS_PORT
513 self.__nameserver = None
514 self.__broadcastaddr = BROADCAST_ADDR
515 self.mac = b'00-00-00-00-00-00'
517 def _setup_connection(self, dstaddr, timeout=None):
518 port = rand.randint(10000, 60000)
519 af, socktype, proto, _canonname, _sa = socket.getaddrinfo(dstaddr, port, socket.AF_INET, socket.SOCK_DGRAM)[0]
520 s = socket.socket(af, socktype, proto)
521 has_bind = 1
522 for _i in range(0, 10):
523 # We try to bind to a port for 10 tries
524 try:
525 s.bind((INADDR_ANY, rand.randint(10000, 60000)))
526 s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
527 has_bind = 1
528 except socket.error:
529 pass
530 if not has_bind: 530 ↛ 531line 530 didn't jump to line 531, because the condition on line 530 was never true
531 raise NetBIOSError('Cannot bind to a good UDP port', ERRCLASS_OS, errno.EAGAIN)
532 self.__sock = s
534 def send(self, request, destaddr, timeout):
535 self._setup_connection(destaddr)
537 tries = 3
538 while 1:
539 try:
540 self.__sock.sendto(request.getData(), 0, (destaddr, self.__servport))
541 ready, _, _ = select.select([self.__sock.fileno()], [], [], timeout)
542 if not ready:
543 if tries:
544 # Retry again until tries == 0
545 tries -= 1
546 else:
547 raise NetBIOSTimeout
548 else:
549 try:
550 data, _ = self.__sock.recvfrom(65536, 0)
551 except Exception as e:
552 raise NetBIOSError("recvfrom error: %s" % str(e))
553 self.__sock.close()
554 res = NAME_SERVICE_PACKET(data)
555 if res['NAME_TRN_ID'] == request['NAME_TRN_ID']: 555 ↛ 539line 555 didn't jump to line 539, because the condition on line 555 was never false
556 if (res['FLAGS'] & 0xf) > 0: 556 ↛ 557line 556 didn't jump to line 557, because the condition on line 556 was never true
557 raise NetBIOSError('Negative response', ERRCLASS_QUERY, res['FLAGS'] & 0xf)
558 return res
559 except select.error as ex: 559 ↛ 560line 559 didn't jump to line 560, because the exception caught by line 559 didn't happen
560 if ex.errno != errno.EINTR and ex.errno != errno.EAGAIN:
561 raise NetBIOSError('Error occurs while waiting for response', ERRCLASS_OS, ex.errno)
562 except socket.error as ex:
563 raise NetBIOSError('Connection error: %s' % str(ex))
565 # Set the default NetBIOS domain nameserver.
566 def set_nameserver(self, nameserver):
567 self.__nameserver = nameserver
569 # Return the default NetBIOS domain nameserver, or None if none is specified.
570 def get_nameserver(self):
571 return self.__nameserver
573 # Set the broadcast address to be used for query.
574 def set_broadcastaddr(self, broadcastaddr):
575 self.__broadcastaddr = broadcastaddr
577 # Return the broadcast address to be used, or BROADCAST_ADDR if default broadcast address is used.
578 def get_broadcastaddr(self):
579 return self.__broadcastaddr
581 # Returns a NBPositiveNameQueryResponse instance containing the host information for nbname.
582 # If a NetBIOS domain nameserver has been specified, it will be used for the query.
583 # Otherwise, the query is broadcasted on the broadcast address.
584 def gethostbyname(self, nbname, qtype = TYPE_WORKSTATION, scope = None, timeout = 1):
585 resp = self.name_query_request(nbname, self.__nameserver, qtype, scope, timeout)
586 return resp
588 # Returns a list of NBNodeEntry instances containing node status information for nbname.
589 # If destaddr contains an IP address, then this will become an unicast query on the destaddr.
590 # Raises NetBIOSTimeout if timeout (in secs) is reached.
591 # Raises NetBIOSError for other errors
592 def getnodestatus(self, nbname, destaddr = None, type = TYPE_WORKSTATION, scope = None, timeout = 1):
593 if destaddr: 593 ↛ 596line 593 didn't jump to line 596, because the condition on line 593 was never false
594 return self.node_status_request(nbname, destaddr, type, scope, timeout)
595 else:
596 return self.node_status_request(nbname, self.__nameserver, type, scope, timeout)
598 def getnetbiosname(self, ip):
599 entries = self.getnodestatus('*',ip)
600 entries = [x for x in entries if x['TYPE'] == TYPE_SERVER]
601 return entries[0]['NAME'].strip().decode('latin-1')
603 def getmacaddress(self):
604 return self.mac
606 def name_registration_request(self, nbname, destaddr, qtype, scope, nb_flags=0, nb_address='0.0.0.0'):
607 netbios_name = nbname.upper()
608 qn_label = encode_name(netbios_name, qtype, scope)
610 p = NAME_REGISTRATION_REQUEST()
611 p['NAME_TRN_ID'] = rand.randint(1, 32000)
612 p['QUESTION_NAME'] = qn_label[:-1] + b'\x00'
613 p['RR_NAME'] = qn_label[:-1] + b'\x00'
614 p['TTL'] = 0xffff
615 p['NB_FLAGS'] = nb_flags
616 p['NB_ADDRESS'] = socket.inet_aton(nb_address)
617 if not destaddr: 617 ↛ 618line 617 didn't jump to line 618, because the condition on line 617 was never true
618 p['FLAGS'] |= NM_FLAGS_BROADCAST
619 destaddr = self.__broadcastaddr
621 res = self.send(p, destaddr, 1)
622 return res
624 def name_query_request(self, nbname, destaddr = None, qtype = TYPE_SERVER, scope = None, timeout = 1):
625 netbios_name = nbname.upper()
626 qn_label = encode_name(netbios_name, qtype, scope)
628 p = NAME_QUERY_REQUEST()
629 p['NAME_TRN_ID'] = rand.randint(1, 32000)
630 p['QUESTION_NAME'] = qn_label[:-1] + b'\x00'
631 p['FLAGS'] = NM_FLAGS_RD
632 if not destaddr: 632 ↛ 633line 632 didn't jump to line 633, because the condition on line 632 was never true
633 p['FLAGS'] |= NM_FLAGS_BROADCAST
635 destaddr = self.__broadcastaddr
637 res = self.send(p, destaddr, timeout)
638 return NBPositiveNameQueryResponse(res['ANSWERS'])
640 def node_status_request(self, nbname, destaddr, type, scope, timeout):
641 netbios_name = nbname.upper()
642 qn_label = encode_name(netbios_name, type, scope)
643 p = NODE_STATUS_REQUEST()
644 p['NAME_TRN_ID'] = rand.randint(1, 32000)
645 p['QUESTION_NAME'] = qn_label[:-1] + b'\x00'
647 if not destaddr: 647 ↛ 648line 647 didn't jump to line 648, because the condition on line 647 was never true
648 p['FLAGS'] = NM_FLAGS_BROADCAST
649 destaddr = self.__broadcastaddr
651 res = self.send(p, destaddr, timeout)
652 answ = NBNodeStatusResponse(res['ANSWERS'])
653 self.mac = answ.get_mac()
654 return answ.entries
656################################################################################
657# 4.2 SESSION SERVICE PACKETS
658################################################################################
660class NetBIOSSessionPacket:
661 def __init__(self, data=0):
662 self.type = 0x0
663 self.flags = 0x0
664 self.length = 0x0
665 if data == 0:
666 self._trailer = b''
667 else:
668 try:
669 self.type = indexbytes(data,0)
670 if self.type == NETBIOS_SESSION_MESSAGE:
671 self.length = indexbytes(data,1) << 16 | (unpack('!H', data[2:4])[0])
672 else:
673 self.flags = data[1]
674 self.length = unpack('!H', data[2:4])[0]
676 self._trailer = data[4:]
677 except:
678 raise NetBIOSError('Wrong packet format ')
680 def set_type(self, type):
681 self.type = type
683 def get_type(self):
684 return self.type
686 def rawData(self):
687 if self.type == NETBIOS_SESSION_MESSAGE:
688 data = pack('!BBH', self.type, self.length >> 16, self.length & 0xFFFF) + self._trailer
689 else:
690 data = pack('!BBH', self.type, self.flags, self.length) + self._trailer
691 return data
693 def set_trailer(self, data):
694 self._trailer = data
695 self.length = len(data)
697 def get_length(self):
698 return self.length
700 def get_trailer(self):
701 return self._trailer
703class NetBIOSSession:
704 def __init__(self, myname, remote_name, remote_host, remote_type=TYPE_SERVER, sess_port=NETBIOS_SESSION_PORT,
705 timeout=None, local_type=TYPE_WORKSTATION, sock=None):
706 """
708 :param unicode myname: My local NetBIOS name
709 :param unicode remote_name: Remote NetBIOS name
710 :param unicode remote_host: Remote IP Address
711 :param integer remote_type: NetBIOS Host type
712 :param integer sess_port: Session port to connect (139,445)
713 :param integer timeout: Timeout for connection
714 :param integer local_type: My Local Host Type
715 :param socket sock: Socket for already established connection
716 """
717 if len(myname) > 15: 717 ↛ 718line 717 didn't jump to line 718, because the condition on line 717 was never true
718 self.__myname = myname[:15].upper()
719 else:
720 self.__myname = myname.upper()
721 self.__local_type = local_type
723 assert remote_name
724 # if destination port SMB_SESSION_PORT and remote name *SMBSERVER, we're changing it to its IP address
725 # helping solving the client mistake ;)
726 if remote_name == '*SMBSERVER' and sess_port == SMB_SESSION_PORT: 726 ↛ 727line 726 didn't jump to line 727, because the condition on line 726 was never true
727 remote_name = remote_host
729 # If remote name is *SMBSERVER let's try to query its name.. if can't be guessed, continue and hope for the best
731 if remote_name == '*SMBSERVER': 731 ↛ 732line 731 didn't jump to line 732, because the condition on line 731 was never true
732 nb = NetBIOS()
733 try:
734 res = nb.getnetbiosname(remote_host)
735 except:
736 res = None
737 pass
739 if res is not None:
740 remote_name = res
742 if len(remote_name) > 15: 742 ↛ 743line 742 didn't jump to line 743, because the condition on line 742 was never true
743 self.__remote_name = remote_name[:15].upper()
744 else:
745 self.__remote_name = remote_name.upper()
746 self.__remote_type = remote_type
747 self.__remote_host = remote_host
749 if sock is not None: 749 ↛ 751line 749 didn't jump to line 751, because the condition on line 749 was never true
750 # We are acting as a server
751 self._sock = sock
752 else:
753 self._sock = self._setup_connection((remote_host, sess_port), timeout)
755 if sess_port == NETBIOS_SESSION_PORT:
756 self._request_session(remote_type, local_type, timeout)
758 def _request_session(self, remote_type, local_type, timeout):
759 raise NotImplementedError('Not Implemented!')
761 def _setup_connection(self, peer, timeout=None):
762 raise NotImplementedError('Not Implemented!')
764 def get_myname(self):
765 return self.__myname
767 def get_mytype(self):
768 return self.__local_type
770 def get_remote_host(self):
771 return self.__remote_host
773 def get_remote_name(self):
774 return self.__remote_name
776 def get_remote_type(self):
777 return self.__remote_type
779 def close(self):
780 self._sock.close()
782 def get_socket(self):
783 return self._sock
785class NetBIOSUDPSessionPacket(Structure):
786 TYPE_DIRECT_UNIQUE = 16
787 TYPE_DIRECT_GROUP = 17
789 FLAGS_MORE_FRAGMENTS = 1
790 FLAGS_FIRST_FRAGMENT = 2
791 FLAGS_B_NODE = 0
793 structure = (
794 ('Type','B=16'), # Direct Unique Datagram
795 ('Flags','B=2'), # FLAGS_FIRST_FRAGMENT
796 ('ID','<H'),
797 ('_SourceIP','>L'),
798 ('SourceIP','"'),
799 ('SourcePort','>H=138'),
800 ('DataLegth','>H-Data'),
801 ('Offset','>H=0'),
802 ('SourceName','z'),
803 ('DestinationName','z'),
804 ('Data',':'),
805 )
807 def getData(self):
808 addr = self['SourceIP'].split('.')
809 addr = [int(x) for x in addr]
810 addr = (((addr[0] << 8) + addr[1] << 8) + addr[2] << 8) + addr[3]
811 self['_SourceIP'] = addr
812 return Structure.getData(self)
814 def get_trailer(self):
815 return self['Data']
817class NetBIOSUDPSession(NetBIOSSession):
818 def _setup_connection(self, peer, timeout=None):
819 af, socktype, proto, canonname, sa = socket.getaddrinfo(peer[0], peer[1], 0, socket.SOCK_DGRAM)[0]
820 sock = socket.socket(af, socktype, proto)
821 sock.connect(sa)
823 sock = socket.socket(af, socktype, proto)
824 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
825 sock.bind((INADDR_ANY, 138))
826 self.peer = peer
827 return sock
829 def _request_session(self, remote_type, local_type, timeout = None):
830 pass
832 def next_id(self):
833 if hasattr(self, '__dgram_id'):
834 answer = self.__dgram_id
835 else:
836 self.__dgram_id = rand.randint(1,65535)
837 answer = self.__dgram_id
838 self.__dgram_id += 1
839 return answer
841 def send_packet(self, data):
842 # Yes... I know...
843 self._sock.connect(self.peer)
845 p = NetBIOSUDPSessionPacket()
846 p['ID'] = self.next_id()
847 p['SourceIP'] = self._sock.getsockname()[0]
848 p['SourceName'] = encode_name(self.get_myname(), self.get_mytype(), '')[:-1]
849 p['DestinationName'] = encode_name(self.get_remote_name(), self.get_remote_type(), '')[:-1]
850 p['Data'] = data
852 self._sock.sendto(str(p), self.peer)
853 self._sock.close()
855 self._sock = self._setup_connection(self.peer)
857 def recv_packet(self, timeout = None):
858 # The next loop is a workaround for a bigger problem:
859 # When data reaches higher layers, the lower headers are lost,
860 # and with them, for example, the source IP. Hence, SMB users
861 # can't know where packets are coming from... we need a better
862 # solution, right now, we will filter everything except packets
863 # coming from the remote_host specified in __init__()
865 while 1:
866 data, peer = self._sock.recvfrom(8192)
867# print "peer: %r self.peer: %r" % (peer, self.peer)
868 if peer == self.peer:
869 break
871 return NetBIOSUDPSessionPacket(data)
873class NetBIOSTCPSession(NetBIOSSession):
874 def __init__(self, myname, remote_name, remote_host, remote_type=TYPE_SERVER, sess_port=NETBIOS_SESSION_PORT,
875 timeout=None, local_type=TYPE_WORKSTATION, sock=None, select_poll=False):
876 """
878 :param unicode myname: My local NetBIOS name
879 :param unicode remote_name: Remote NetBIOS name
880 :param unicode remote_host: Remote IP Address
881 :param integer remote_type: NetBIOS Host type
882 :param integer sess_port: Session port to connect (139,445)
883 :param integer timeout: Timeout for connection
884 :param integer local_type: My Local Host Type
885 :param socket sock: Socket for already established connection
886 :param boolean select_poll: Type of polling mechanism
887 """
888 self.__select_poll = select_poll
889 if self.__select_poll: 889 ↛ 890line 889 didn't jump to line 890, because the condition on line 889 was never true
890 self.read_function = self.polling_read
891 else:
892 self.read_function = self.non_polling_read
893 NetBIOSSession.__init__(self, myname, remote_name, remote_host, remote_type=remote_type, sess_port=sess_port,
894 timeout=timeout, local_type=local_type, sock=sock)
896 def _setup_connection(self, peer, timeout=None):
897 try:
898 af, socktype, proto, canonname, sa = socket.getaddrinfo(peer[0], peer[1], 0, socket.SOCK_STREAM)[0]
899 sock = socket.socket(af, socktype, proto)
900 oldtimeout = sock.gettimeout()
901 sock.settimeout(timeout)
902 sock.connect(sa)
903 sock.settimeout(oldtimeout)
904 except socket.error as e:
905 raise socket.error("Connection error (%s:%s)" % (peer[0], peer[1]), e)
906 return sock
908 def send_packet(self, data):
909 p = NetBIOSSessionPacket()
910 p.set_type(NETBIOS_SESSION_MESSAGE)
911 p.set_trailer(data)
912 self._sock.sendall(p.rawData())
914 def recv_packet(self, timeout = None):
915 data = self.__read(timeout)
916 NBSPacket = NetBIOSSessionPacket(data)
917 if NBSPacket.get_type() == NETBIOS_SESSION_KEEP_ALIVE: 917 ↛ 919line 917 didn't jump to line 919, because the condition on line 917 was never true
918 # Discard packet
919 return self.recv_packet(timeout)
920 return NetBIOSSessionPacket(data)
922 def _request_session(self, remote_type, local_type, timeout = None):
923 p = NetBIOSSessionPacket()
924 remote_name = encode_name(self.get_remote_name(), remote_type, '')
925 myname = encode_name(self.get_myname(), local_type, '')
926 p.set_type(NETBIOS_SESSION_REQUEST)
927 p.set_trailer(remote_name + myname)
929 self._sock.sendall(p.rawData())
930 while 1:
931 p = self.recv_packet(timeout)
932 if p.get_type() == NETBIOS_SESSION_NEGATIVE_RESPONSE: 932 ↛ 933line 932 didn't jump to line 933, because the condition on line 932 was never true
933 raise NetBIOSError('Cannot request session (Called Name:%s)' % self.get_remote_name())
934 elif p.get_type() == NETBIOS_SESSION_POSITIVE_RESPONSE: 934 ↛ 938line 934 didn't jump to line 938, because the condition on line 934 was never false
935 break
936 else:
937 # Ignore all other messages, most probably keepalive messages
938 pass
940 def polling_read(self, read_length, timeout):
941 data = b''
942 if timeout is None:
943 timeout = 3600
945 time_left = timeout
946 CHUNK_TIME = 0.025
947 bytes_left = read_length
949 while bytes_left > 0:
950 try:
951 ready, _, _ = select.select([self._sock.fileno()], [], [], 0)
953 if not ready:
954 if time_left <= 0:
955 raise NetBIOSTimeout
956 else:
957 time.sleep(CHUNK_TIME)
958 time_left -= CHUNK_TIME
959 continue
961 received = self._sock.recv(bytes_left)
962 if len(received) == 0:
963 raise NetBIOSError('Error while reading from remote', ERRCLASS_OS, None)
965 data = data + received
966 bytes_left = read_length - len(data)
967 except select.error as ex:
968 if ex.errno != errno.EINTR and ex.errno != errno.EAGAIN:
969 raise NetBIOSError('Error occurs while reading from remote', ERRCLASS_OS, ex.errno)
971 return bytes(data)
973 def non_polling_read(self, read_length, timeout):
974 data = b''
975 if timeout is None: 975 ↛ 976line 975 didn't jump to line 976, because the condition on line 975 was never true
976 timeout = 3600
978 start_time = time.time()
979 bytes_left = read_length
981 while bytes_left > 0:
982 self._sock.settimeout(timeout)
983 try:
984 received = self._sock.recv(bytes_left)
985 except socket.timeout:
986 raise NetBIOSTimeout
987 except Exception as ex:
988 raise NetBIOSError('Error occurs while reading from remote', ERRCLASS_OS, ex.errno)
990 if (time.time() - start_time) > timeout: 990 ↛ 991line 990 didn't jump to line 991, because the condition on line 990 was never true
991 raise NetBIOSTimeout
993 if len(received) == 0: 993 ↛ 994line 993 didn't jump to line 994, because the condition on line 993 was never true
994 raise NetBIOSError('Error while reading from remote', ERRCLASS_OS, None)
996 data = data + received
997 bytes_left = read_length - len(data)
999 return bytes(data)
1001 def __read(self, timeout = None):
1002 data = self.read_function(4, timeout)
1003 type, flags, length = unpack('>ccH', data)
1004 if ord(type) == NETBIOS_SESSION_MESSAGE:
1005 length |= ord(flags) << 16
1006 else:
1007 if ord(flags) & 0x01: 1007 ↛ 1008line 1007 didn't jump to line 1008, because the condition on line 1007 was never true
1008 length |= 0x10000
1009 data2 = self.read_function(length, timeout)
1011 return data + data2