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@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 34import socket 35import string 36import re 37import select 38import errno 39from random import randint 40from struct import pack, unpack 41import time 42 43from structure import Structure 44 45CVS_REVISION = '$Revision: 526 $' 46 47# Taken from socket module reference 48INADDR_ANY = '0.0.0.0' 49BROADCAST_ADDR = '<broadcast>' 50 51# Default port for NetBIOS name service 52NETBIOS_NS_PORT = 137 53# Default port for NetBIOS session service 54NETBIOS_SESSION_PORT = 139 55 56# Default port for SMB session service 57SMB_SESSION_PORT = 445 58 59# Owner Node Type Constants 60NODE_B = 0x0000 61NODE_P = 0x2000 62NODE_M = 0x4000 63NODE_RESERVED = 0x6000 64NODE_GROUP = 0x8000 65NODE_UNIQUE = 0x0 66 67# Name Type Constants 68TYPE_UNKNOWN = 0x01 69TYPE_WORKSTATION = 0x00 70TYPE_CLIENT = 0x03 71TYPE_SERVER = 0x20 72TYPE_DOMAIN_MASTER = 0x1B 73TYPE_DOMAIN_CONTROLLER = 0x1C 74TYPE_MASTER_BROWSER = 0x1D 75TYPE_BROWSER = 0x1E 76TYPE_NETDDE = 0x1F 77TYPE_STATUS = 0x21 78 79# Opcodes values 80OPCODE_QUERY = 0 81OPCODE_REGISTRATION = 0x5 82OPCODE_RELEASE = 0x6 83OPCODE_WACK = 0x7 84OPCODE_REFRESH = 0x8 85OPCODE_REQUEST = 0 86OPCODE_RESPONSE = 0x10 87 88# NM_FLAGS 89NM_FLAGS_BROADCAST = 0x1 90NM_FLAGS_UNICAST = 0 91NM_FLAGS_RA = 0x8 92NM_FLAGS_RD = 0x10 93NM_FLAGS_TC = 0x20 94NM_FLAGS_AA = 0x40 95 96# QUESTION_TYPE 97QUESTION_TYPE_NB = 0x20 # NetBIOS general Name Service Resource Record 98QUESTION_TYPE_NBSTAT = 0x21 # NetBIOS NODE STATUS Resource Record 99# QUESTION_CLASS 100QUESTION_CLASS_IN = 0x1 # Internet class 101 102# RR_TYPE Resource Record Type code 103RR_TYPE_A = 0x1 # IP address Resource Record 104RR_TYPE_NS = 0x2 # Name Server Resource Record 105RR_TYPE_NULL = 0xA # NULL Resource Record 106RR_TYPE_NB = 0x20 # NetBIOS general Name Service Resource Record 107RR_TYPE_NBSTAT = 0x21 # NetBIOS NODE STATUS Resource Record 108 109# Resource Record Class 110RR_CLASS_IN = 1 # Internet class 111 112# RCODE values 113RCODE_FMT_ERR = 0x1 # Format Error. Request was invalidly formatted. 114RCODE_SRV_ERR = 0x2 # Server failure. Problem with NBNS, cannot process name. 115RCODE_IMP_ERR = 0x4 # Unsupported request error. Allowable only for challenging NBNS when gets an Update type 116 # registration request. 117RCODE_RFS_ERR = 0x5 # Refused error. For policy reasons server will not register this name from this host. 118RCODE_ACT_ERR = 0x6 # Active error. Name is owned by another node. 119RCODE_CFT_ERR = 0x7 # Name in conflict error. A UNIQUE name is owned by more than one node. 120 121# NAME_FLAGS 122NAME_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. 124NAME_FLAGS_ACT = 0x0400 # Active Name Flag. All entries have this flag set to one (1). 125NAME_FLAG_CNF = 0x0800 # Conflict Flag. If one (1) then name on this node is in conflict. 126NAME_FLAG_DRG = 0x1000 # Deregister Flag. If one (1) then this name is in the process of being deleted. 127 128NAME_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 132NETBIOS_SESSION_MESSAGE = 0x0 133NETBIOS_SESSION_REQUEST = 0x81 134NETBIOS_SESSION_POSITIVE_RESPONSE = 0x82 135NETBIOS_SESSION_NEGATIVE_RESPONSE = 0x83 136NETBIOS_SESSION_RETARGET_RESPONSE = 0x84 137NETBIOS_SESSION_KEEP_ALIVE = 0x85 138 139 140def 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 152class NetBIOSError(Exception): pass 153class NetBIOSTimeout(Exception): 154 def __init__(self, message = 'The NETBIOS connection with the remote host timed out.'): 155 Exception.__init__(self, message) 156 157class 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 208class 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 254class 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 278class 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 353class 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 372class 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 433class 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) 594def 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() 612def _do_first_level_encoding(m): 613 s = ord(m.group(0)) 614 return string.uppercase[s >> 4] + string.uppercase[s & 0x0f] 615 616def 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 634def _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 640class 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 678class 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 742class 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 774class 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 829class 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 944ERRCLASS_QUERY = 0x00 945ERRCLASS_SESSION = 0xf0 946ERRCLASS_OS = 0xff 947 948QUERY_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 955SESSION_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 962def 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 979if __name__ == '__main__': 980 main() 981