1 # Copyright (c) 2003-2016 CORE Security Technologies 2 # 3 # This software is provided under under a slightly modified version 4 # of the Apache Software License. See the accompanying LICENSE file 5 # for more information. 6 # 7 8 9 # -*- mode: python; tab-width: 4 -*- 10 # 11 # Copyright (C) 2001 Michael Teo <michaelteo (at] 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) 33 34 import socket 35 import string 36 import re 37 import select 38 import errno 39 from random import randint 40 from struct import pack, unpack 41 import time 42 43 from structure import Structure 44 45 CVS_REVISION = '$Revision: 526 $' 46 47 # Taken from socket module reference 48 INADDR_ANY = '0.0.0.0' 49 BROADCAST_ADDR = '<broadcast>' 50 51 # Default port for NetBIOS name service 52 NETBIOS_NS_PORT = 137 53 # Default port for NetBIOS session service 54 NETBIOS_SESSION_PORT = 139 55 56 # Default port for SMB session service 57 SMB_SESSION_PORT = 445 58 59 # Owner Node Type Constants 60 NODE_B = 0x0000 61 NODE_P = 0x2000 62 NODE_M = 0x4000 63 NODE_RESERVED = 0x6000 64 NODE_GROUP = 0x8000 65 NODE_UNIQUE = 0x0 66 67 # Name Type Constants 68 TYPE_UNKNOWN = 0x01 69 TYPE_WORKSTATION = 0x00 70 TYPE_CLIENT = 0x03 71 TYPE_SERVER = 0x20 72 TYPE_DOMAIN_MASTER = 0x1B 73 TYPE_DOMAIN_CONTROLLER = 0x1C 74 TYPE_MASTER_BROWSER = 0x1D 75 TYPE_BROWSER = 0x1E 76 TYPE_NETDDE = 0x1F 77 TYPE_STATUS = 0x21 78 79 # Opcodes values 80 OPCODE_QUERY = 0 81 OPCODE_REGISTRATION = 0x5 82 OPCODE_RELEASE = 0x6 83 OPCODE_WACK = 0x7 84 OPCODE_REFRESH = 0x8 85 OPCODE_REQUEST = 0 86 OPCODE_RESPONSE = 0x10 87 88 # NM_FLAGS 89 NM_FLAGS_BROADCAST = 0x1 90 NM_FLAGS_UNICAST = 0 91 NM_FLAGS_RA = 0x8 92 NM_FLAGS_RD = 0x10 93 NM_FLAGS_TC = 0x20 94 NM_FLAGS_AA = 0x40 95 96 # QUESTION_TYPE 97 QUESTION_TYPE_NB = 0x20 # NetBIOS general Name Service Resource Record 98 QUESTION_TYPE_NBSTAT = 0x21 # NetBIOS NODE STATUS Resource Record 99 # QUESTION_CLASS 100 QUESTION_CLASS_IN = 0x1 # Internet class 101 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 108 109 # Resource Record Class 110 RR_CLASS_IN = 1 # Internet class 111 112 # RCODE values 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. 120 121 # NAME_FLAGS 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. 127 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 138 139 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') 147 else: 148 return 'Unknown Error Class', 'Unknown Error' 149 150 151 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) 156 157 class NBResourceRecord: 158 def __init__(self, data = 0): 159 self._data = data 160 try: 161 if self._data: 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] 171 else: 172 self.rr_name = '' 173 self.rr_type = 0 174 self.rr_class = 0 175 self.ttl = 0 176 self.rdlength = 0 177 self.rdata = '' 178 self.unit_id = '' 179 except Exception: 180 raise NetBIOSError( 'Wrong packet format ' ) 181 182 def set_rr_name(self, name): 183 self.rr_name = name 184 def set_rr_type(self, name): 185 self.rr_type = name 186 def set_rr_class(self,cl): 187 self.rr_class = cl 188 def set_ttl(self,ttl): 189 self.ttl = ttl 190 def set_rdata(self,rdata): 191 self.rdata = rdata 192 self.rdlength = len(rdata) 193 def get_unit_id(self): 194 return self.unit_id 195 def get_rr_name(self): 196 return self.rr_name 197 def get_rr_class(self): 198 return self.rr_class 199 def get_ttl(self): 200 return self.ttl 201 def get_rdlength(self): 202 return self.rdlength 203 def get_rdata(self): 204 return self.rdata 205 def rawData(self): 206 return self.rr_name + pack('!HHLH',self.rr_type, self.rr_class, self.ttl, self.rdlength) + self.rdata 207 208 class NBNodeStatusResponse(NBResourceRecord): 209 def __init__(self, data = 0): 210 NBResourceRecord.__init__(self,data) 211 self.num_names = 0 212 self.node_names = [ ] 213 self.statstics = '' 214 self.mac = '00-00-00-00-00-00' 215 try: 216 if data: 217 self._data = self.get_rdata() 218 self.num_names = unpack('>B',self._data[:1])[0] 219 offset = 1 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]) 223 offset += 18 224 self.node_names.append(NBNodeEntry(name, type ,flags)) 225 self.set_mac_in_hexa(self.get_unit_id()) 226 except Exception: 227 raise NetBIOSError( 'Wrong packet format ' ) 228 229 def set_mac_in_hexa(self, data): 230 data_aux = '' 231 for d in data: 232 if data_aux == '': 233 data_aux = '%02x' % ord(d) 234 else: 235 data_aux += '-%02x' % ord(d) 236 self.mac = string.upper(data_aux) 237 238 def get_num_names(self): 239 return self.num_names 240 def get_mac(self): 241 return self.mac 242 def set_num_names(self, num): 243 self.num_names = 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) 248 self.num_names += 1 249 def rawData(self): 250 res = pack('!B', self.num_names ) 251 for i in range(0, self.num_names): 252 res += self.node_names[i].rawData() 253 254 class NBPositiveNameQueryResponse(NBResourceRecord): 255 def __init__(self, data = 0): 256 NBResourceRecord.__init__(self, data) 257 self.addr_entries = [ ] 258 if data: 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]) 264 offset = 2 265 while offset<len(self._data): 266 self.addr_entries.append('%d.%d.%d.%d' % unpack('4B', (self._data[offset:offset+4]))) 267 offset += 4 268 269 def get_netbios_name(self): 270 return self._netbios_name 271 272 def get_name_type(self): 273 return self._name_type 274 275 def get_addr_entries(self): 276 return self.addr_entries 277 278 class NetBIOSPacket: 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 291 # Service packet. 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. 298 self.questions = '' 299 self.answers = '' 300 if data == 0: 301 self._data = '' 302 else: 303 try: 304 self._data = data 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:] 314 except Exception: 315 raise NetBIOSError( 'Wrong packet format ' ) 316 317 def set_opcode(self, opcode): 318 self.opcode = 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): 324 self.rcode = rcode 325 def addQuestion(self, question, qtype, qclass): 326 self.qdcount += 1 327 self.questions += question + pack('!HH',qtype,qclass) 328 def get_trn_id(self): 329 return self.name_trn_id 330 def get_rcode(self): 331 return self.rcode 332 def get_nm_flags(self): 333 return self.nm_flags 334 def get_opcode(self): 335 return self.opcode 336 def get_qdcount(self): 337 return self.qdcount 338 def get_ancount(self): 339 return self.ancount 340 def get_nscount(self): 341 return self.nscount 342 def get_arcount(self): 343 return self.arcount 344 def rawData(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 349 return data 350 def get_answers(self): 351 return self.answers 352 353 class NBHostEntry: 354 355 def __init__(self, nbname, nametype, ip): 356 self.__nbname = nbname 357 self.__nametype = nametype 358 self.__ip = ip 359 360 def get_nbname(self): 361 return self.__nbname 362 363 def get_nametype(self): 364 return self.__nametype 365 366 def get_ip(self): 367 return self.__ip 368 369 def __repr__(self): 370 return '<NBHostEntry instance: NBname="' + self.__nbname + '", IP="' + self.__ip + '">' 371 372 class NBNodeEntry: 373 374 def __init__(self, nbname, nametype, flags): 375 self.__nbname = string.ljust(nbname,17) 376 self.__nametype = nametype 377 self.__flags = flags 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 384 385 def get_nbname(self): 386 return self.__nbname 387 388 def get_nametype(self): 389 return self.__nametype 390 391 def is_group(self): 392 return self.__isgroup 393 394 def get_nodetype(self): 395 return self.__nodetype 396 397 def is_deleting(self): 398 return self.__deleting 399 400 def is_conflict(self): 401 return self.__isconflict 402 403 def is_active(self): 404 return self.__isactive 405 406 def is_permanent(self): 407 return self.__ispermanent 408 409 def set_nbname(self, name): 410 self.__nbname = string.ljust(name,17) 411 412 def set_nametype(self, type): 413 self.__nametype = type 414 415 def set_flags(self,flags): 416 self.__flags = flags 417 418 def __repr__(self): 419 s = '<NBNodeEntry instance: NBname="' + self.__nbname + '" NameType="' + NAME_TYPES[self.__nametype] + '"' 420 if self.__isactive: 421 s += ' ACTIVE' 422 if self.__isgroup: 423 s += ' GROUP' 424 if self.__isconflict: 425 s += ' CONFLICT' 426 if self.__deleting: 427 s += ' DELETING' 428 return s 429 def rawData(self): 430 return self.__nbname + pack('!BH',self.__nametype, self.__flags) 431 432 433 class NetBIOS: 434 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' 442 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) 447 has_bind = 1 448 for _i in range(0, 10): 449 # We try to bind to a port for 10 tries 450 try: 451 s.bind(( INADDR_ANY, randint(10000, 60000) )) 452 s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) 453 has_bind = 1 454 except socket.error: 455 pass 456 if not has_bind: 457 raise NetBIOSError, ( 'Cannot bind to a good UDP port', ERRCLASS_OS, errno.EAGAIN ) 458 self.__sock = s 459 460 # Set the default NetBIOS domain nameserver. 461 def set_nameserver(self, nameserver): 462 self.__nameserver = nameserver 463 464 # Return the default NetBIOS domain nameserver, or None if none is specified. 465 def get_nameserver(self): 466 return self.__nameserver 467 468 # Set the broadcast address to be used for query. 469 def set_broadcastaddr(self, broadcastaddr): 470 self.__broadcastaddr = broadcastaddr 471 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 475 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) 481 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): 487 if destaddr: 488 return self.__querynodestatus(nbname, destaddr, type, scope, timeout) 489 else: 490 return self.__querynodestatus(nbname, self.__nameserver, type, scope, timeout) 491 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() 496 497 def getmacaddress(self): 498 return self.mac 499 500 def __queryname(self, nbname, destaddr, qtype, scope, timeout, retries = 0): 501 self._setup_connection(destaddr) 502 trn_id = randint(1, 32000) 503 p = NetBIOSPacket() 504 p.set_trn_id(trn_id) 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) 509 if not destaddr: 510 p.set_nm_flags(p.get_nm_flags() | NM_FLAGS_BROADCAST) 511 destaddr = self.__broadcastaddr 512 req = p.rawData() 513 514 tries = retries 515 while 1: 516 self.__sock.sendto(req, ( destaddr, self.__servport )) 517 try: 518 ready, _, _ = select.select([ self.__sock.fileno() ], [ ] , [ ], timeout) 519 if not ready: 520 if tries: 521 # Retry again until tries == 0 522 tries -= 1 523 else: 524 raise NetBIOSTimeout 525 else: 526 data, _ = self.__sock.recvfrom(65536, 0) 527 528 res = NetBIOSPacket(data) 529 if res.get_trn_id() == p.get_trn_id(): 530 if res.get_rcode(): 531 if res.get_rcode() == 0x03: 532 return None 533 else: 534 raise NetBIOSError, ( 'Negative name query response', ERRCLASS_QUERY, res.get_rcode() ) 535 536 if res.get_ancount() != 1: 537 raise NetBIOSError( 'Malformed response') 538 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] ) 543 raise 544 545 546 def __querynodestatus(self, nbname, destaddr, type, scope, timeout): 547 self._setup_connection(destaddr) 548 trn_id = randint(1, 32000) 549 p = NetBIOSPacket() 550 p.set_trn_id(trn_id) 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) 554 555 if not destaddr: 556 p.set_nm_flags(NM_FLAGS_BROADCAST) 557 destaddr = self.__broadcastaddr 558 req = p.rawData() 559 tries = 3 560 while 1: 561 try: 562 self.__sock.sendto(req, 0, ( destaddr, self.__servport )) 563 ready, _, _ = select.select([ self.__sock.fileno() ], [ ] , [ ], timeout) 564 if not ready: 565 if tries: 566 # Retry again until tries == 0 567 tries -= 1 568 else: 569 raise NetBIOSTimeout 570 else: 571 try: 572 data, _ = self.__sock.recvfrom(65536, 0) 573 except Exception, e: 574 raise NetBIOSError, "recvfrom error: %s" % str(e) 575 self.__sock.close() 576 res = NetBIOSPacket(data) 577 if res.get_trn_id() == p.get_trn_id(): 578 if res.get_rcode(): 579 if res.get_rcode() == 0x03: 580 # I'm just guessing here 581 raise NetBIOSError, "Cannot get data from server" 582 else: 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) 592 593 # Perform first and second level encoding of name as specified in RFC 1001 (Section 4) 594 def encode_name(name, type, scope): 595 if name == '*': 596 name += '\0' * 15 597 elif len(name) > 15: 598 name = name[:15] + chr(type) 599 else: 600 name = string.ljust(name, 15) + chr(type) 601 602 encoded_name = chr(len(name) * 2) + re.sub('.', _do_first_level_encoding, name) 603 if scope: 604 encoded_scope = '' 605 for s in string.split(scope, '.'): 606 encoded_scope = encoded_scope + chr(len(s)) + s 607 return encoded_name + encoded_scope + '\0' 608 else: 609 return encoded_name + '\0' 610 611 # Internal method for use in encode_name() 612 def _do_first_level_encoding(m): 613 s = ord(m.group(0)) 614 return string.uppercase[s >> 4] + string.uppercase[s & 0x0f] 615 616 def decode_name(name): 617 name_length = ord(name[0]) 618 assert name_length == 32 619 620 decoded_name = re.sub('..', _do_first_level_decoding, name[1:33]) 621 if name[33] == '\0': 622 return 34, decoded_name, '' 623 else: 624 decoded_domain = '' 625 offset = 34 626 while 1: 627 domain_length = ord(name[offset]) 628 if domain_length == 0: 629 break 630 decoded_domain = '.' + name[offset:offset + domain_length] 631 offset += domain_length 632 return offset + 1, decoded_name, decoded_domain 633 634 def _do_first_level_decoding(m): 635 s = m.group(0) 636 return chr(((ord(s[0]) - ord('A')) << 4) | (ord(s[1]) - ord('A'))) 637 638 639 640 class NetBIOSSessionPacket: 641 def __init__(self, data = 0): 642 self.type = 0x0 643 self.flags = 0x0 644 self.length = 0x0 645 if data == 0: 646 self._trailer = '' 647 else: 648 try: 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]) 652 else: 653 self.flags = ord(data[1]) 654 self.length = unpack('!H', data[2:4])[0] 655 656 self._trailer = data[4:] 657 except: 658 raise NetBIOSError( 'Wrong packet format ' ) 659 660 def set_type(self, type): 661 self.type = type 662 def get_type(self): 663 return self.type 664 def rawData(self): 665 if self.type == NETBIOS_SESSION_MESSAGE: 666 data = pack('!BBH',self.type,self.length >> 16,self.length & 0xFFFF) + self._trailer 667 else: 668 data = pack('!BBH',self.type,self.flags,self.length) + self._trailer 669 return data 670 def set_trailer(self,data): 671 self._trailer = data 672 self.length = len(data) 673 def get_length(self): 674 return self.length 675 def get_trailer(self): 676 return self._trailer 677 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): 680 if len(myname) > 15: 681 self.__myname = string.upper(myname[:15]) 682 else: 683 self.__myname = string.upper(myname) 684 self.__local_type = local_type 685 686 assert remote_name 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': 693 nb = NetBIOS() 694 695 try: 696 res = nb.getnetbiosname(remote_host) 697 except: 698 res = None 699 pass 700 701 if res is not None: 702 remote_name = res 703 704 if len(remote_name) > 15: 705 self.__remote_name = string.upper(remote_name[:15]) 706 else: 707 self.__remote_name = string.upper(remote_name) 708 self.__remote_type = remote_type 709 710 self.__remote_host = remote_host 711 712 if sock is not None: 713 # We are acting as a server 714 self._sock = sock 715 else: 716 self._sock = self._setup_connection((remote_host, sess_port)) 717 718 if sess_port == NETBIOS_SESSION_PORT: 719 self._request_session(remote_type, local_type, timeout) 720 721 def get_myname(self): 722 return self.__myname 723 724 def get_mytype(self): 725 return self.__local_type 726 727 def get_remote_host(self): 728 return self.__remote_host 729 730 def get_remote_name(self): 731 return self.__remote_name 732 733 def get_remote_type(self): 734 return self.__remote_type 735 736 def close(self): 737 self._sock.close() 738 739 def get_socket(self): 740 return self._sock 741 742 class NetBIOSUDPSessionPacket(Structure): 743 TYPE_DIRECT_UNIQUE = 16 744 TYPE_DIRECT_GROUP = 17 745 746 FLAGS_MORE_FRAGMENTS = 1 747 FLAGS_FIRST_FRAGMENT = 2 748 FLAGS_B_NODE = 0 749 750 structure = ( 751 ('Type','B=16'), # Direct Unique Datagram 752 ('Flags','B=2'), # FLAGS_FIRST_FRAGMENT 753 ('ID','<H'), 754 ('_SourceIP','>L'), 755 ('SourceIP','"'), 756 ('SourcePort','>H=138'), 757 ('DataLegth','>H-Data'), 758 ('Offset','>H=0'), 759 ('SourceName','z'), 760 ('DestinationName','z'), 761 ('Data',':'), 762 ) 763 764 def getData(self): 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) 770 771 def get_trailer(self): 772 return self['Data'] 773 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) 778 sock.connect(sa) 779 780 sock = socket.socket(af, socktype, proto) 781 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 782 sock.bind((INADDR_ANY, 138)) 783 self.peer = peer 784 return sock 785 786 def _request_session(self, remote_type, local_type, timeout = None): 787 pass 788 789 def next_id(self): 790 if hasattr(self, '__dgram_id'): 791 answer = self.__dgram_id 792 else: 793 self.__dgram_id = randint(1,65535) 794 answer = self.__dgram_id 795 self.__dgram_id += 1 796 return answer 797 798 def send_packet(self, data): 799 # Yes... I know... 800 self._sock.connect(self.peer) 801 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] 807 p['Data'] = data 808 809 self._sock.sendto(str(p), self.peer) 810 self._sock.close() 811 812 self._sock = self._setup_connection(self.peer) 813 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__() 821 822 while 1: 823 data, peer = self._sock.recvfrom(8192) 824 # print "peer: %r self.peer: %r" % (peer, self.peer) 825 if peer == self.peer: break 826 827 return NetBIOSUDPSessionPacket(data) 828 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 834 else: 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) 837 838 839 def _setup_connection(self, peer): 840 try: 841 af, socktype, proto, canonname, sa = socket.getaddrinfo(peer[0], peer[1], 0, socket.SOCK_STREAM)[0] 842 sock = socket.socket(af, socktype, proto) 843 sock.connect(sa) 844 except socket.error, e: 845 raise socket.error("Connection error (%s:%s)" % (peer[0], peer[1]), e) 846 return sock 847 848 def send_packet(self, data): 849 p = NetBIOSSessionPacket() 850 p.set_type(NETBIOS_SESSION_MESSAGE) 851 p.set_trailer(data) 852 self._sock.send(p.rawData()) 853 854 def recv_packet(self, timeout = None): 855 data = self.__read(timeout) 856 return NetBIOSSessionPacket(data) 857 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) 864 865 self._sock.send(p.rawData()) 866 while 1: 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: 871 break 872 else: 873 # Ignore all other messages, most probably keepalive messages 874 pass 875 876 def polling_read(self, read_length, timeout): 877 data = '' 878 if timeout is None: 879 timeout = 3600 880 881 time_left = timeout 882 CHUNK_TIME = 0.025 883 bytes_left = read_length 884 885 while bytes_left > 0: 886 try: 887 ready, _, _ = select.select([self._sock.fileno() ], [ ], [ ], 0) 888 889 if not ready: 890 if time_left <= 0: 891 raise NetBIOSTimeout 892 else: 893 time.sleep(CHUNK_TIME) 894 time_left -= CHUNK_TIME 895 continue 896 897 received = self._sock.recv(bytes_left) 898 if len(received) == 0: 899 raise NetBIOSError, ( 'Error while reading from remote', ERRCLASS_OS, None) 900 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] ) 906 907 return data 908 909 def non_polling_read(self, read_length, timeout): 910 data = '' 911 bytes_left = read_length 912 913 while bytes_left > 0: 914 try: 915 ready, _, _ = select.select([self._sock.fileno() ], [ ], [ ], timeout) 916 917 if not ready: 918 raise NetBIOSTimeout 919 920 received = self._sock.recv(bytes_left) 921 if len(received) == 0: 922 raise NetBIOSError, ( 'Error while reading from remote', ERRCLASS_OS, None) 923 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] ) 929 930 return data 931 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 937 else: 938 if ord(flags) & 0x01: 939 length |= 0x10000 940 data2 = self.read_function(length, timeout) 941 942 return data + data2 943 944 ERRCLASS_QUERY = 0x00 945 ERRCLASS_SESSION = 0xf0 946 ERRCLASS_OS = 0xff 947 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' 953 } 954 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' 960 } 961 962 def main(): 963 def get_netbios_host_by_name(name): 964 n = NetBIOS() 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): 967 try: 968 addrs = n.gethostbyname(name, qtype = qtype).get_addr_entries() 969 except NetBIOSTimeout: 970 continue 971 else: 972 return addrs 973 raise Exception("Host not found") 974 975 976 n = get_netbios_host_by_name("some-host") 977 print n 978 979 if __name__ == '__main__': 980 main() 981