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