Hide keyboard shortcuts

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# 

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 

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 

46 

47from impacket.structure import Structure 

48 

49# Our random number generator 

50try: 

51 rand = random.SystemRandom() 

52except NotImplementedError: 

53 rand = random 

54 pass 

55 

56 

57################################################################################ 

58# CONSTANTS 

59################################################################################ 

60# Taken from socket module reference 

61INADDR_ANY = '0.0.0.0' 

62BROADCAST_ADDR = '<broadcast>' 

63 

64# Default port for NetBIOS name service 

65NETBIOS_NS_PORT = 137 

66# Default port for NetBIOS session service 

67NETBIOS_SESSION_PORT = 139 

68 

69# Default port for SMB session service 

70SMB_SESSION_PORT = 445 

71 

72# Owner Node Type Constants 

73NODE_B = 0x0000 

74NODE_P = 0x2000 

75NODE_M = 0x4000 

76NODE_RESERVED = 0x6000 

77NODE_GROUP = 0x8000 

78NODE_UNIQUE = 0x0 

79 

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 

91 

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 

100 

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 

108 

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 

114 

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 

121 

122# RESOURCE RECORD RR_CLASS field definitions 

123RR_CLASS_IN = 1 # Internet class 

124 

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. 

133 

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. 

140 

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 

146 

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'} 

151 

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 

159 

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) 

167  

168 :param string name: the name to encode 

169 :param integer nametype: the name type constants 

170 :param string scope: the name's scope  

171  

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) 

180 

181 encoded_name = chr(len(name) * 2) + re.sub('.', _do_first_level_encoding, name) 

182 

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 

192 

193 return b(encoded_name + encoded_scope) + b'\0' 

194 else: 

195 return b(encoded_name) + b'\0' 

196 

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] 

201 

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) 

206 

207 :param string/bytes name: the name to decode 

208 

209 :return string: the decoded name. 

210 """ 

211 

212 name_length = ord(name[0:1]) 

213 assert name_length == 32 

214 

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 

228 

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'))) 

232 

233ERRCLASS_QUERY = 0x00 

234ERRCLASS_SESSION = 0xf0 

235ERRCLASS_OS = 0xff 

236 

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.', 

244 

245 } 

246 

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 } 

253 

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 

259 

260 def get_error_code(self): 

261 return self.error 

262 

263 def getErrorCode(self): 

264 return self.get_error_code() 

265 

266 def get_error_string(self): 

267 return str(self) 

268 

269 def getErrorString(self): 

270 return str(self) 

271 

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 

282 

283class NetBIOSTimeout(Exception): 

284 def __init__(self, message = 'The NETBIOS connection with the remote host timed out.'): 

285 Exception.__init__(self, message) 

286 

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 ) 

299 

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']) 

313 

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() 

322 

323 def get_mac(self): 

324 return self.mac 

325 

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() 

330 

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'])) 

340 

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 ) 

354 

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 ) 

362 

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 ) 

373 

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 

395 

396 self['QUESTION_TYPE'] = QUESTION_TYPE_NB 

397 self['QUESTION_CLASS'] = QUESTION_CLASS_IN 

398 

399 self['RR_TYPE'] = RR_TYPE_NB 

400 self['RR_CLASS'] = RR_CLASS_IN 

401 

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 

411 

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 

421 

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) 

428 

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) 

433 

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 

437 

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 

453 

454 self['QUESTION_TYPE'] = QUESTION_TYPE_NB 

455 self['QUESTION_CLASS'] = QUESTION_CLASS_IN 

456 

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 ) 

463 

464# ToDo: 4.2.15. REDIRECT NAME QUERY RESPONSE 

465# ToDo: 4.2.16. WAIT FOR ACKNOWLEDGEMENT (WACK) RESPONSE 

466 

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) 

471 

472 self['FLAGS'] = 0 

473 self['QUESTION_TYPE'] = QUESTION_TYPE_NBSTAT 

474 

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 ) 

482 

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 ) 

506 

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' 

515 

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 

532 

533 def send(self, request, destaddr, timeout): 

534 self._setup_connection(destaddr) 

535 

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)) 

563 

564 # Set the default NetBIOS domain nameserver. 

565 def set_nameserver(self, nameserver): 

566 self.__nameserver = nameserver 

567 

568 # Return the default NetBIOS domain nameserver, or None if none is specified. 

569 def get_nameserver(self): 

570 return self.__nameserver 

571 

572 # Set the broadcast address to be used for query. 

573 def set_broadcastaddr(self, broadcastaddr): 

574 self.__broadcastaddr = broadcastaddr 

575 

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 

579 

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 

586 

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) 

596 

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') 

601 

602 def getmacaddress(self): 

603 return self.mac 

604 

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) 

608 

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 

619 

620 res = self.send(p, destaddr, 1) 

621 return res 

622 

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) 

626 

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 

633 

634 destaddr = self.__broadcastaddr 

635 

636 res = self.send(p, destaddr, timeout) 

637 return NBPositiveNameQueryResponse(res['ANSWERS']) 

638 

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' 

645 

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 

649 

650 res = self.send(p, destaddr, timeout) 

651 answ = NBNodeStatusResponse(res['ANSWERS']) 

652 self.mac = answ.get_mac() 

653 return answ.entries 

654 

655################################################################################ 

656# 4.2 SESSION SERVICE PACKETS 

657################################################################################ 

658 

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] 

674 

675 self._trailer = data[4:] 

676 except: 

677 raise NetBIOSError('Wrong packet format ') 

678 

679 def set_type(self, type): 

680 self.type = type 

681 

682 def get_type(self): 

683 return self.type 

684 

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 

691 

692 def set_trailer(self, data): 

693 self._trailer = data 

694 self.length = len(data) 

695 

696 def get_length(self): 

697 return self.length 

698 

699 def get_trailer(self): 

700 return self._trailer 

701 

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 """ 

706 

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 

721 

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 

727 

728 # If remote name is *SMBSERVER let's try to query its name.. if can't be guessed, continue and hope for the best 

729 

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 

737 

738 if res is not None: 

739 remote_name = res 

740 

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 

747 

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) 

753 

754 if sess_port == NETBIOS_SESSION_PORT: 

755 self._request_session(remote_type, local_type, timeout) 

756 

757 def _request_session(self, remote_type, local_type, timeout): 

758 raise NotImplementedError('Not Implemented!') 

759 

760 def _setup_connection(self, peer, timeout=None): 

761 raise NotImplementedError('Not Implemented!') 

762 

763 def get_myname(self): 

764 return self.__myname 

765 

766 def get_mytype(self): 

767 return self.__local_type 

768 

769 def get_remote_host(self): 

770 return self.__remote_host 

771 

772 def get_remote_name(self): 

773 return self.__remote_name 

774 

775 def get_remote_type(self): 

776 return self.__remote_type 

777 

778 def close(self): 

779 self._sock.close() 

780 

781 def get_socket(self): 

782 return self._sock 

783 

784class NetBIOSUDPSessionPacket(Structure): 

785 TYPE_DIRECT_UNIQUE = 16 

786 TYPE_DIRECT_GROUP = 17 

787 

788 FLAGS_MORE_FRAGMENTS = 1 

789 FLAGS_FIRST_FRAGMENT = 2 

790 FLAGS_B_NODE = 0 

791 

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 ) 

805 

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) 

812 

813 def get_trailer(self): 

814 return self['Data'] 

815 

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) 

821 

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 

827 

828 def _request_session(self, remote_type, local_type, timeout = None): 

829 pass 

830 

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 

839 

840 def send_packet(self, data): 

841 # Yes... I know... 

842 self._sock.connect(self.peer) 

843 

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 

850 

851 self._sock.sendto(str(p), self.peer) 

852 self._sock.close() 

853 

854 self._sock = self._setup_connection(self.peer) 

855 

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__() 

863 

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 

869 

870 return NetBIOSUDPSessionPacket(data) 

871 

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 """ 

876  

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) 

894 

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 

906 

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()) 

912 

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) 

920 

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) 

927 

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 

938 

939 def polling_read(self, read_length, timeout): 

940 data = b'' 

941 if timeout is None: 

942 timeout = 3600 

943 

944 time_left = timeout 

945 CHUNK_TIME = 0.025 

946 bytes_left = read_length 

947 

948 while bytes_left > 0: 

949 try: 

950 ready, _, _ = select.select([self._sock.fileno()], [], [], 0) 

951 

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 

959 

960 received = self._sock.recv(bytes_left) 

961 if len(received) == 0: 

962 raise NetBIOSError('Error while reading from remote', ERRCLASS_OS, None) 

963 

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) 

969 

970 return bytes(data) 

971 

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 

976 

977 start_time = time.time() 

978 bytes_left = read_length 

979 

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) 

988 

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 

991 

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) 

994 

995 data = data + received 

996 bytes_left = read_length - len(data) 

997 

998 return bytes(data) 

999 

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) 

1009 

1010 return data + data2