1 # Copyright (c) 2003-2016 CORE Security Technologies
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.
9 # -*- mode: python; tab-width: 4 -*-
11 # Copyright (C) 2001 Michael Teo <michaelteo@bigfoot.com>
12 # nmb.py - NetBIOS library
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.
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:
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.
27 # 2. Altered source versions must be plainly marked as such, and must not be
28 # misrepresented as being the original software.
30 # 3. This notice cannot be removed or altered from any source distribution.
32 # Altered source done by Alberto Solino (@agsolino)
39 from random import randint
40 from struct import pack, unpack
43 from structure import Structure
45 CVS_REVISION = '$Revision: 526 $'
47 # Taken from socket module reference
48 INADDR_ANY = '0.0.0.0'
49 BROADCAST_ADDR = '<broadcast>'
51 # Default port for NetBIOS name service
53 # Default port for NetBIOS session service
54 NETBIOS_SESSION_PORT = 139
56 # Default port for SMB session service
57 SMB_SESSION_PORT = 445
59 # Owner Node Type Constants
63 NODE_RESERVED = 0x6000
69 TYPE_WORKSTATION = 0x00
72 TYPE_DOMAIN_MASTER = 0x1B
73 TYPE_DOMAIN_CONTROLLER = 0x1C
74 TYPE_MASTER_BROWSER = 0x1D
81 OPCODE_REGISTRATION = 0x5
86 OPCODE_RESPONSE = 0x10
89 NM_FLAGS_BROADCAST = 0x1
97 QUESTION_TYPE_NB = 0x20 # NetBIOS general Name Service Resource Record
98 QUESTION_TYPE_NBSTAT = 0x21 # NetBIOS NODE STATUS Resource Record
100 QUESTION_CLASS_IN = 0x1 # Internet class
102 # RR_TYPE Resource Record Type code
103 RR_TYPE_A = 0x1 # IP address Resource Record
104 RR_TYPE_NS = 0x2 # Name Server Resource Record
105 RR_TYPE_NULL = 0xA # NULL Resource Record
106 RR_TYPE_NB = 0x20 # NetBIOS general Name Service Resource Record
107 RR_TYPE_NBSTAT = 0x21 # NetBIOS NODE STATUS Resource Record
109 # Resource Record Class
110 RR_CLASS_IN = 1 # Internet class
113 RCODE_FMT_ERR = 0x1 # Format Error. Request was invalidly formatted.
114 RCODE_SRV_ERR = 0x2 # Server failure. Problem with NBNS, cannot process name.
115 RCODE_IMP_ERR = 0x4 # Unsupported request error. Allowable only for challenging NBNS when gets an Update type
116 # registration request.
117 RCODE_RFS_ERR = 0x5 # Refused error. For policy reasons server will not register this name from this host.
118 RCODE_ACT_ERR = 0x6 # Active error. Name is owned by another node.
119 RCODE_CFT_ERR = 0x7 # Name in conflict error. A UNIQUE name is owned by more than one node.
122 NAME_FLAGS_PRM = 0x0200 # Permanent Name Flag. If one (1) then entry is for the permanent node name. Flag is zero
123 # (0) for all other names.
124 NAME_FLAGS_ACT = 0x0400 # Active Name Flag. All entries have this flag set to one (1).
125 NAME_FLAG_CNF = 0x0800 # Conflict Flag. If one (1) then name on this node is in conflict.
126 NAME_FLAG_DRG = 0x1000 # Deregister Flag. If one (1) then this name is in the process of being deleted.
128 NAME_TYPES = { TYPE_UNKNOWN: 'Unknown', TYPE_WORKSTATION: 'Workstation', TYPE_CLIENT: 'Client',
129 TYPE_SERVER: 'Server', TYPE_MASTER_BROWSER: 'Master Browser', TYPE_BROWSER: 'Browser Server',
130 TYPE_DOMAIN_MASTER: 'Domain Master' , TYPE_NETDDE: 'NetDDE Server'}
131 # NetBIOS Session Types
132 NETBIOS_SESSION_MESSAGE = 0x0
133 NETBIOS_SESSION_REQUEST = 0x81
134 NETBIOS_SESSION_POSITIVE_RESPONSE = 0x82
135 NETBIOS_SESSION_NEGATIVE_RESPONSE = 0x83
136 NETBIOS_SESSION_RETARGET_RESPONSE = 0x84
137 NETBIOS_SESSION_KEEP_ALIVE = 0x85
140 def strerror(errclass, errcode):
141 if errclass == ERRCLASS_OS:
142 return 'OS Error', str(errcode)
143 elif errclass == ERRCLASS_QUERY:
144 return 'Query Error', QUERY_ERRORS.get(errcode, 'Unknown error')
145 elif errclass == ERRCLASS_SESSION:
146 return 'Session Error', SESSION_ERRORS.get(errcode, 'Unknown error')
148 return 'Unknown Error Class', 'Unknown Error'
152 class NetBIOSError(Exception): pass
153 class NetBIOSTimeout(Exception):
154 def __init__(self, message = 'The NETBIOS connection with the remote host timed out.'):
155 Exception.__init__(self, message)
157 class NBResourceRecord:
158 def __init__(self, data = 0):
162 self.rr_name = (re.split('\x00',data))[0]
163 offset = len(self.rr_name)+1
164 self.rr_type = unpack('>H', self._data[offset:offset+2])[0]
165 self.rr_class = unpack('>H', self._data[offset+2: offset+4])[0]
166 self.ttl = unpack('>L',self._data[offset+4:offset+8])[0]
167 self.rdlength = unpack('>H', self._data[offset+8:offset+10])[0]
168 self.rdata = self._data[offset+10:offset+10+self.rdlength]
169 offset = self.rdlength - 2
170 self.unit_id = data[offset:offset+6]
180 raise NetBIOSError( 'Wrong packet format ' )
182 def set_rr_name(self, name):
184 def set_rr_type(self, name):
186 def set_rr_class(self,cl):
188 def set_ttl(self,ttl):
190 def set_rdata(self,rdata):
192 self.rdlength = len(rdata)
193 def get_unit_id(self):
195 def get_rr_name(self):
197 def get_rr_class(self):
201 def get_rdlength(self):
206 return self.rr_name + pack('!HHLH',self.rr_type, self.rr_class, self.ttl, self.rdlength) + self.rdata
208 class NBNodeStatusResponse(NBResourceRecord):
209 def __init__(self, data = 0):
210 NBResourceRecord.__init__(self,data)
212 self.node_names = [ ]
214 self.mac = '00-00-00-00-00-00'
217 self._data = self.get_rdata()
218 self.num_names = unpack('>B',self._data[:1])[0]
220 for i in range(0, self.num_names):
221 name = self._data[offset:offset + 15]
222 type,flags = unpack('>BH', self._data[offset + 15: offset + 18])
224 self.node_names.append(NBNodeEntry(name, type ,flags))
225 self.set_mac_in_hexa(self.get_unit_id())
227 raise NetBIOSError( 'Wrong packet format ' )
229 def set_mac_in_hexa(self, data):
233 data_aux = '%02x' % ord(d)
235 data_aux += '-%02x' % ord(d)
236 self.mac = string.upper(data_aux)
238 def get_num_names(self):
239 return self.num_names
242 def set_num_names(self, num):
244 def get_node_names(self):
245 return self.node_names
246 def add_node_name(self,node_names):
247 self.node_names.append(node_names)
250 res = pack('!B', self.num_names )
251 for i in range(0, self.num_names):
252 res += self.node_names[i].rawData()
254 class NBPositiveNameQueryResponse(NBResourceRecord):
255 def __init__(self, data = 0):
256 NBResourceRecord.__init__(self, data)
257 self.addr_entries = [ ]
259 self._data = self.get_rdata()
260 _qn_length, qn_name, qn_scope = decode_name(data)
261 self._netbios_name = string.rstrip(qn_name[:-1]) + qn_scope
262 self._name_type = ord(qn_name[-1])
263 self._nb_flags = unpack('!H', self._data[:2])
265 while offset<len(self._data):
266 self.addr_entries.append('%d.%d.%d.%d' % unpack('4B', (self._data[offset:offset+4])))
269 def get_netbios_name(self):
270 return self._netbios_name
272 def get_name_type(self):
273 return self._name_type
275 def get_addr_entries(self):
276 return self.addr_entries
279 """ This is a packet as defined in RFC 1002 """
280 def __init__(self, data = 0):
281 self.name_trn_id = 0x0 # Transaction ID for Name Service Transaction.
282 # Requestor places a unique value for each active
283 # transaction. Responder puts NAME_TRN_ID value
284 # from request packet in response packet.
285 self.opcode = 0 # Packet type code
286 self.nm_flags = 0 # Flags for operation
287 self.rcode = 0 # Result codes of request.
288 self.qdcount = 0 # Unsigned 16 bit integer specifying the number of entries in the question section of a Name
289 self.ancount = 0 # Unsigned 16 bit integer specifying the number of
290 # resource records in the answer section of a Name
292 self.nscount = 0 # Unsigned 16 bit integer specifying the number of
293 # resource records in the authority section of a
294 # Name Service packet.
295 self.arcount = 0 # Unsigned 16 bit integer specifying the number of
296 # resource records in the additional records
297 # section of a Name Service packeT.
305 self.opcode = ord(data[2]) >> 3
306 self.nm_flags = ((ord(data[2]) & 0x3) << 4) | ((ord(data[3]) & 0xf0) >> 4)
307 self.name_trn_id = unpack('>H', self._data[:2])[0]
308 self.rcode = ord(data[3]) & 0x0f
309 self.qdcount = unpack('>H', self._data[4:6])[0]
310 self.ancount = unpack('>H', self._data[6:8])[0]
311 self.nscount = unpack('>H', self._data[8:10])[0]
312 self.arcount = unpack('>H', self._data[10:12])[0]
313 self.answers = self._data[12:]
315 raise NetBIOSError( 'Wrong packet format ' )
317 def set_opcode(self, opcode):
319 def set_trn_id(self, trn):
320 self.name_trn_id = trn
321 def set_nm_flags(self, nm_flags):
322 self.nm_flags = nm_flags
323 def set_rcode(self, rcode):
325 def addQuestion(self, question, qtype, qclass):
327 self.questions += question + pack('!HH',qtype,qclass)
328 def get_trn_id(self):
329 return self.name_trn_id
332 def get_nm_flags(self):
334 def get_opcode(self):
336 def get_qdcount(self):
338 def get_ancount(self):
340 def get_nscount(self):
342 def get_arcount(self):
345 secondWord = self.opcode << 11
346 secondWord |= self.nm_flags << 4
347 secondWord |= self.rcode
348 data = pack('!HHHHHH', self.name_trn_id, secondWord , self.qdcount, self.ancount, self.nscount, self.arcount) + self.questions + self.answers
350 def get_answers(self):
355 def __init__(self, nbname, nametype, ip):
356 self.__nbname = nbname
357 self.__nametype = nametype
360 def get_nbname(self):
363 def get_nametype(self):
364 return self.__nametype
370 return '<NBHostEntry instance: NBname="' + self.__nbname + '", IP="' + self.__ip + '">'
374 def __init__(self, nbname, nametype, flags):
375 self.__nbname = string.ljust(nbname,17)
376 self.__nametype = nametype
378 self.__isgroup = flags & 0x8000
379 self.__nodetype = flags & 0x6000
380 self.__deleting = flags & 0x1000
381 self.__isconflict = flags & 0x0800
382 self.__isactive = flags & 0x0400
383 self.__ispermanent = flags & 0x0200
385 def get_nbname(self):
388 def get_nametype(self):
389 return self.__nametype
392 return self.__isgroup
394 def get_nodetype(self):
395 return self.__nodetype
397 def is_deleting(self):
398 return self.__deleting
400 def is_conflict(self):
401 return self.__isconflict
404 return self.__isactive
406 def is_permanent(self):
407 return self.__ispermanent
409 def set_nbname(self, name):
410 self.__nbname = string.ljust(name,17)
412 def set_nametype(self, type):
413 self.__nametype = type
415 def set_flags(self,flags):
419 s = '<NBNodeEntry instance: NBname="' + self.__nbname + '" NameType="' + NAME_TYPES[self.__nametype] + '"'
424 if self.__isconflict:
430 return self.__nbname + pack('!BH',self.__nametype, self.__flags)
435 # Creates a NetBIOS instance without specifying any default NetBIOS domain nameserver.
436 # All queries will be sent through the servport.
437 def __init__(self, servport = NETBIOS_NS_PORT):
438 self.__servport = NETBIOS_NS_PORT
439 self.__nameserver = None
440 self.__broadcastaddr = BROADCAST_ADDR
441 self.mac = '00-00-00-00-00-00'
443 def _setup_connection(self, dstaddr):
444 port = randint(10000, 60000)
445 af, socktype, proto, _canonname, _sa = socket.getaddrinfo(dstaddr, port, socket.AF_INET, socket.SOCK_DGRAM)[0]
446 s = socket.socket(af, socktype, proto)
448 for _i in range(0, 10):
449 # We try to bind to a port for 10 tries
451 s.bind(( INADDR_ANY, randint(10000, 60000) ))
452 s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
457 raise NetBIOSError, ( 'Cannot bind to a good UDP port', ERRCLASS_OS, errno.EAGAIN )
460 # Set the default NetBIOS domain nameserver.
461 def set_nameserver(self, nameserver):
462 self.__nameserver = nameserver
464 # Return the default NetBIOS domain nameserver, or None if none is specified.
465 def get_nameserver(self):
466 return self.__nameserver
468 # Set the broadcast address to be used for query.
469 def set_broadcastaddr(self, broadcastaddr):
470 self.__broadcastaddr = broadcastaddr
472 # Return the broadcast address to be used, or BROADCAST_ADDR if default broadcast address is used.
473 def get_broadcastaddr(self):
474 return self.__broadcastaddr
476 # Returns a NBPositiveNameQueryResponse instance containing the host information for nbname.
477 # If a NetBIOS domain nameserver has been specified, it will be used for the query.
478 # Otherwise, the query is broadcasted on the broadcast address.
479 def gethostbyname(self, nbname, qtype = TYPE_WORKSTATION, scope = None, timeout = 1):
480 return self.__queryname(nbname, self.__nameserver, qtype, scope, timeout)
482 # Returns a list of NBNodeEntry instances containing node status information for nbname.
483 # If destaddr contains an IP address, then this will become an unicast query on the destaddr.
484 # Raises NetBIOSTimeout if timeout (in secs) is reached.
485 # Raises NetBIOSError for other errors
486 def getnodestatus(self, nbname, destaddr = None, type = TYPE_WORKSTATION, scope = None, timeout = 1):
488 return self.__querynodestatus(nbname, destaddr, type, scope, timeout)
490 return self.__querynodestatus(nbname, self.__nameserver, type, scope, timeout)
492 def getnetbiosname(self, ip):
493 entries = self.getnodestatus('*',ip)
494 entries = filter(lambda x:x.get_nametype() == TYPE_SERVER, entries)
495 return entries[0].get_nbname().strip()
497 def getmacaddress(self):
500 def __queryname(self, nbname, destaddr, qtype, scope, timeout, retries = 0):
501 self._setup_connection(destaddr)
502 trn_id = randint(1, 32000)
505 netbios_name = nbname.upper()
506 qn_label = encode_name(netbios_name, qtype, scope)
507 p.addQuestion(qn_label, QUESTION_TYPE_NB, QUESTION_CLASS_IN)
508 p.set_nm_flags(NM_FLAGS_RD)
510 p.set_nm_flags(p.get_nm_flags() | NM_FLAGS_BROADCAST)
511 destaddr = self.__broadcastaddr
516 self.__sock.sendto(req, ( destaddr, self.__servport ))
518 ready, _, _ = select.select([ self.__sock.fileno() ], [ ] , [ ], timeout)
521 # Retry again until tries == 0
526 data, _ = self.__sock.recvfrom(65536, 0)
528 res = NetBIOSPacket(data)
529 if res.get_trn_id() == p.get_trn_id():
531 if res.get_rcode() == 0x03:
534 raise NetBIOSError, ( 'Negative name query response', ERRCLASS_QUERY, res.get_rcode() )
536 if res.get_ancount() != 1:
537 raise NetBIOSError( 'Malformed response')
539 return NBPositiveNameQueryResponse(res.get_answers())
540 except select.error, ex:
541 if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
542 raise NetBIOSError, ( 'Error occurs while waiting for response', ERRCLASS_OS, ex[0] )
546 def __querynodestatus(self, nbname, destaddr, type, scope, timeout):
547 self._setup_connection(destaddr)
548 trn_id = randint(1, 32000)
551 netbios_name = string.upper(nbname)
552 qn_label = encode_name(netbios_name, type, scope)
553 p.addQuestion(qn_label, QUESTION_TYPE_NBSTAT, QUESTION_CLASS_IN)
556 p.set_nm_flags(NM_FLAGS_BROADCAST)
557 destaddr = self.__broadcastaddr
562 self.__sock.sendto(req, 0, ( destaddr, self.__servport ))
563 ready, _, _ = select.select([ self.__sock.fileno() ], [ ] , [ ], timeout)
566 # Retry again until tries == 0
572 data, _ = self.__sock.recvfrom(65536, 0)
574 raise NetBIOSError, "recvfrom error: %s" % str(e)
576 res = NetBIOSPacket(data)
577 if res.get_trn_id() == p.get_trn_id():
579 if res.get_rcode() == 0x03:
580 # I'm just guessing here
581 raise NetBIOSError, "Cannot get data from server"
583 raise NetBIOSError, ( 'Negative name query response', ERRCLASS_QUERY, res.get_rcode() )
584 answ = NBNodeStatusResponse(res.get_answers())
585 self.mac = answ.get_mac()
586 return answ.get_node_names()
587 except select.error, ex:
588 if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
589 raise NetBIOSError, ( 'Error occurs while waiting for response', ERRCLASS_OS, ex[0] )
590 except socket.error, ex:
591 raise NetBIOSError, 'Connection error: %s' % str(ex)
593 # Perform first and second level encoding of name as specified in RFC 1001 (Section 4)
594 def encode_name(name, type, scope):
598 name = name[:15] + chr(type)
600 name = string.ljust(name, 15) + chr(type)
602 encoded_name = chr(len(name) * 2) + re.sub('.', _do_first_level_encoding, name)
605 for s in string.split(scope, '.'):
606 encoded_scope = encoded_scope + chr(len(s)) + s
607 return encoded_name + encoded_scope + '\0'
609 return encoded_name + '\0'
611 # Internal method for use in encode_name()
612 def _do_first_level_encoding(m):
614 return string.uppercase[s >> 4] + string.uppercase[s & 0x0f]
616 def decode_name(name):
617 name_length = ord(name[0])
618 assert name_length == 32
620 decoded_name = re.sub('..', _do_first_level_decoding, name[1:33])
622 return 34, decoded_name, ''
627 domain_length = ord(name[offset])
628 if domain_length == 0:
630 decoded_domain = '.' + name[offset:offset + domain_length]
631 offset += domain_length
632 return offset + 1, decoded_name, decoded_domain
634 def _do_first_level_decoding(m):
636 return chr(((ord(s[0]) - ord('A')) << 4) | (ord(s[1]) - ord('A')))
640 class NetBIOSSessionPacket:
641 def __init__(self, data = 0):
649 self.type = ord(data[0])
650 if self.type == NETBIOS_SESSION_MESSAGE:
651 self.length = ord(data[1]) << 16 | (unpack('!H', data[2:4])[0])
653 self.flags = ord(data[1])
654 self.length = unpack('!H', data[2:4])[0]
656 self._trailer = data[4:]
658 raise NetBIOSError( 'Wrong packet format ' )
660 def set_type(self, type):
665 if self.type == NETBIOS_SESSION_MESSAGE:
666 data = pack('!BBH',self.type,self.length >> 16,self.length & 0xFFFF) + self._trailer
668 data = pack('!BBH',self.type,self.flags,self.length) + self._trailer
670 def set_trailer(self,data):
672 self.length = len(data)
673 def get_length(self):
675 def get_trailer(self):
678 class NetBIOSSession:
679 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):
681 self.__myname = string.upper(myname[:15])
683 self.__myname = string.upper(myname)
684 self.__local_type = local_type
687 # if destination port SMB_SESSION_PORT and remote name *SMBSERVER, we're changing it to its IP address
688 # helping solving the client mistake ;)
689 if remote_name == '*SMBSERVER' and sess_port == SMB_SESSION_PORT:
690 remote_name = remote_host
691 # If remote name is *SMBSERVER let's try to query its name.. if can't be guessed, continue and hope for the best
692 if remote_name == '*SMBSERVER':
696 res = nb.getnetbiosname(remote_host)
704 if len(remote_name) > 15:
705 self.__remote_name = string.upper(remote_name[:15])
707 self.__remote_name = string.upper(remote_name)
708 self.__remote_type = remote_type
710 self.__remote_host = remote_host
713 # We are acting as a server
716 self._sock = self._setup_connection((remote_host, sess_port))
718 if sess_port == NETBIOS_SESSION_PORT:
719 self._request_session(remote_type, local_type, timeout)
721 def get_myname(self):
724 def get_mytype(self):
725 return self.__local_type
727 def get_remote_host(self):
728 return self.__remote_host
730 def get_remote_name(self):
731 return self.__remote_name
733 def get_remote_type(self):
734 return self.__remote_type
739 def get_socket(self):
742 class NetBIOSUDPSessionPacket(Structure):
743 TYPE_DIRECT_UNIQUE = 16
744 TYPE_DIRECT_GROUP = 17
746 FLAGS_MORE_FRAGMENTS = 1
747 FLAGS_FIRST_FRAGMENT = 2
751 ('Type','B=16'), # Direct Unique Datagram
752 ('Flags','B=2'), # FLAGS_FIRST_FRAGMENT
756 ('SourcePort','>H=138'),
757 ('DataLegth','>H-Data'),
760 ('DestinationName','z'),
765 addr = self['SourceIP'].split('.')
766 addr = [int(x) for x in addr]
767 addr = (((addr[0] << 8) + addr[1] << 8) + addr[2] << 8) + addr[3]
768 self['_SourceIP'] = addr
769 return Structure.getData(self)
771 def get_trailer(self):
774 class NetBIOSUDPSession(NetBIOSSession):
775 def _setup_connection(self, peer):
776 af, socktype, proto, canonname, sa = socket.getaddrinfo(peer[0], peer[1], 0, socket.SOCK_DGRAM)[0]
777 sock = socket.socket(af, socktype, proto)
780 sock = socket.socket(af, socktype, proto)
781 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
782 sock.bind((INADDR_ANY, 138))
786 def _request_session(self, remote_type, local_type, timeout = None):
790 if hasattr(self, '__dgram_id'):
791 answer = self.__dgram_id
793 self.__dgram_id = randint(1,65535)
794 answer = self.__dgram_id
798 def send_packet(self, data):
800 self._sock.connect(self.peer)
802 p = NetBIOSUDPSessionPacket()
803 p['ID'] = self.next_id()
804 p['SourceIP'] = self._sock.getsockname()[0]
805 p['SourceName'] = encode_name(self.get_myname(), self.get_mytype(), '')[:-1]
806 p['DestinationName'] = encode_name(self.get_remote_name(), self.get_remote_type(), '')[:-1]
809 self._sock.sendto(str(p), self.peer)
812 self._sock = self._setup_connection(self.peer)
814 def recv_packet(self, timeout = None):
815 # The next loop is a workaround for a bigger problem:
816 # When data reaches higher layers, the lower headers are lost,
817 # and with them, for example, the source IP. Hence, SMB users
818 # can't know where packets are comming from... we need a better
819 # solution, right now, we will filter everything except packets
820 # coming from the remote_host specified in __init__()
823 data, peer = self._sock.recvfrom(8192)
824 # print "peer: %r self.peer: %r" % (peer, self.peer)
825 if peer == self.peer: break
827 return NetBIOSUDPSessionPacket(data)
829 class NetBIOSTCPSession(NetBIOSSession):
830 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):
831 self.__select_poll = select_poll
832 if self.__select_poll:
833 self.read_function = self.polling_read
835 self.read_function = self.non_polling_read
836 NetBIOSSession.__init__(self, myname, remote_name, remote_host, remote_type = remote_type, sess_port = sess_port, timeout = timeout, local_type = local_type, sock=sock)
839 def _setup_connection(self, peer):
841 af, socktype, proto, canonname, sa = socket.getaddrinfo(peer[0], peer[1], 0, socket.SOCK_STREAM)[0]
842 sock = socket.socket(af, socktype, proto)
844 except socket.error, e:
845 raise socket.error("Connection error (%s:%s)" % (peer[0], peer[1]), e)
848 def send_packet(self, data):
849 p = NetBIOSSessionPacket()
850 p.set_type(NETBIOS_SESSION_MESSAGE)
852 self._sock.send(p.rawData())
854 def recv_packet(self, timeout = None):
855 data = self.__read(timeout)
856 return NetBIOSSessionPacket(data)
858 def _request_session(self, remote_type, local_type, timeout = None):
859 p = NetBIOSSessionPacket()
860 remote_name = encode_name(self.get_remote_name(), remote_type, '')
861 myname = encode_name(self.get_myname(), local_type, '')
862 p.set_type(NETBIOS_SESSION_REQUEST)
863 p.set_trailer(remote_name + myname)
865 self._sock.send(p.rawData())
867 p = self.recv_packet(timeout)
868 if p.get_type() == NETBIOS_SESSION_NEGATIVE_RESPONSE:
869 raise NetBIOSError, ( 'Cannot request session', ERRCLASS_SESSION, ord(p.get_trailer()[0]) )
870 elif p.get_type() == NETBIOS_SESSION_POSITIVE_RESPONSE:
873 # Ignore all other messages, most probably keepalive messages
876 def polling_read(self, read_length, timeout):
883 bytes_left = read_length
885 while bytes_left > 0:
887 ready, _, _ = select.select([self._sock.fileno() ], [ ], [ ], 0)
893 time.sleep(CHUNK_TIME)
894 time_left -= CHUNK_TIME
897 received = self._sock.recv(bytes_left)
898 if len(received) == 0:
899 raise NetBIOSError, ( 'Error while reading from remote', ERRCLASS_OS, None)
901 data = data + received
902 bytes_left = read_length - len(data)
903 except select.error, ex:
904 if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
905 raise NetBIOSError, ( 'Error occurs while reading from remote', ERRCLASS_OS, ex[0] )
909 def non_polling_read(self, read_length, timeout):
911 bytes_left = read_length
913 while bytes_left > 0:
915 ready, _, _ = select.select([self._sock.fileno() ], [ ], [ ], timeout)
920 received = self._sock.recv(bytes_left)
921 if len(received) == 0:
922 raise NetBIOSError, ( 'Error while reading from remote', ERRCLASS_OS, None)
924 data = data + received
925 bytes_left = read_length - len(data)
926 except select.error, ex:
927 if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
928 raise NetBIOSError, ( 'Error occurs while reading from remote', ERRCLASS_OS, ex[0] )
932 def __read(self, timeout = None):
933 data = self.read_function(4, timeout)
934 type, flags, length = unpack('>ccH', data)
935 if ord(type) == NETBIOS_SESSION_MESSAGE:
936 length |= ord(flags) << 16
938 if ord(flags) & 0x01:
940 data2 = self.read_function(length, timeout)
944 ERRCLASS_QUERY = 0x00
945 ERRCLASS_SESSION = 0xf0
948 QUERY_ERRORS = { 0x01: 'Request format error. Please file a bug report.',
949 0x02: 'Internal server error',
950 0x03: 'Name does not exist',
951 0x04: 'Unsupported request',
952 0x05: 'Request refused'
955 SESSION_ERRORS = { 0x80: 'Not listening on called name',
956 0x81: 'Not listening for calling name',
957 0x82: 'Called name not present',
958 0x83: 'Sufficient resources',
959 0x8f: 'Unspecified error'
963 def get_netbios_host_by_name(name):
965 n.set_broadcastaddr('255.255.255.255') # To avoid use "<broadcast>" in socket
966 for qtype in (TYPE_WORKSTATION, TYPE_CLIENT, TYPE_SERVER, TYPE_DOMAIN_MASTER, TYPE_DOMAIN_CONTROLLER):
968 addrs = n.gethostbyname(name, qtype = qtype).get_addr_entries()
969 except NetBIOSTimeout:
973 raise Exception("Host not found")
976 n = get_netbios_host_by_name("some-host")
979 if __name__ == '__main__':