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# Impacket - Collection of Python classes for working with network protocols. 

2# 

3# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved. 

4# 

5# This software is provided under a slightly modified version 

6# of the Apache Software License. See the accompanying LICENSE file 

7# for more information. 

8# 

9# Author: 

10# Altered source done by Alberto Solino (@agsolino) 

11# 

12# Copyright and license note from Pysmb: 

13# 

14# Copyright (C) 2001 Michael Teo <michaelteo@bigfoot.com> 

15# nmb.py - NetBIOS library 

16# 

17# This software is provided 'as-is', without any express or implied warranty.  

18# In no event will the author be held liable for any damages arising from the  

19# use of this software. 

20# 

21# Permission is granted to anyone to use this software for any purpose,  

22# including commercial applications, and to alter it and redistribute it  

23# freely, subject to the following restrictions: 

24# 

25# 1. The origin of this software must not be misrepresented; you must not  

26# claim that you wrote the original software. If you use this software  

27# in a product, an acknowledgment in the product documentation would be 

28# appreciated but is not required. 

29# 

30# 2. Altered source versions must be plainly marked as such, and must not be  

31# misrepresented as being the original software. 

32# 

33# 3. This notice cannot be removed or altered from any source distribution. 

34# 

35from __future__ import division 

36from __future__ import print_function 

37from __future__ import absolute_import 

38import errno 

39import re 

40import select 

41import socket 

42import string 

43import time 

44import random 

45from struct import pack, unpack 

46from six import byte2int, indexbytes, b 

47 

48from impacket.structure import Structure 

49 

50# Our random number generator 

51try: 

52 rand = random.SystemRandom() 

53except NotImplementedError: 

54 rand = random 

55 pass 

56 

57 

58################################################################################ 

59# CONSTANTS 

60################################################################################ 

61# Taken from socket module reference 

62INADDR_ANY = '0.0.0.0' 

63BROADCAST_ADDR = '<broadcast>' 

64 

65# Default port for NetBIOS name service 

66NETBIOS_NS_PORT = 137 

67# Default port for NetBIOS session service 

68NETBIOS_SESSION_PORT = 139 

69 

70# Default port for SMB session service 

71SMB_SESSION_PORT = 445 

72 

73# Owner Node Type Constants 

74NODE_B = 0x0000 

75NODE_P = 0x2000 

76NODE_M = 0x4000 

77NODE_RESERVED = 0x6000 

78NODE_GROUP = 0x8000 

79NODE_UNIQUE = 0x0 

80 

81# Name Type Constants 

82TYPE_UNKNOWN = 0x01 

83TYPE_WORKSTATION = 0x00 

84TYPE_CLIENT = 0x03 

85TYPE_SERVER = 0x20 

86TYPE_DOMAIN_MASTER = 0x1B 

87TYPE_DOMAIN_CONTROLLER = 0x1C 

88TYPE_MASTER_BROWSER = 0x1D 

89TYPE_BROWSER = 0x1E 

90TYPE_NETDDE = 0x1F 

91TYPE_STATUS = 0x21 

92 

93# Opcodes values 

94OPCODE_QUERY = 0 

95OPCODE_REGISTRATION = 0x5 << 11 

96OPCODE_RELEASE = 0x6 << 11 

97OPCODE_WACK = 0x7 << 11 

98OPCODE_REFRESH = 0x8 << 11 

99OPCODE_REQUEST = 0 << 11 

100OPCODE_RESPONSE = 0x10 << 11 

101 

102# NM_FLAGS 

103NM_FLAGS_BROADCAST = 0x1 << 4 

104NM_FLAGS_UNICAST = 0 << 4 

105NM_FLAGS_RA = 0x8 << 4 

106NM_FLAGS_RD = 0x10 << 4 

107NM_FLAGS_TC = 0x20 << 4 

108NM_FLAGS_AA = 0x40 << 4 

109 

110# QUESTION_TYPE 

111QUESTION_TYPE_NB = 0x20 # NetBIOS general Name Service Resource Record 

112QUESTION_TYPE_NBSTAT = 0x21 # NetBIOS NODE STATUS Resource Record 

113# QUESTION_CLASS 

114QUESTION_CLASS_IN = 0x1 # Internet class 

115 

116# RESOURCE RECORD RR_TYPE field definitions 

117RR_TYPE_A = 0x1 # IP address Resource Record 

118RR_TYPE_NS = 0x2 # Name Server Resource Record 

119RR_TYPE_NULL = 0xA # NULL Resource Record 

120RR_TYPE_NB = 0x20 # NetBIOS general Name Service Resource Record 

121RR_TYPE_NBSTAT = 0x21 # NetBIOS NODE STATUS Resource Record 

122 

123# RESOURCE RECORD RR_CLASS field definitions 

124RR_CLASS_IN = 1 # Internet class 

125 

126# RCODE values 

127RCODE_FMT_ERR = 0x1 # Format Error. Request was invalidly formatted. 

128RCODE_SRV_ERR = 0x2 # Server failure. Problem with NBNS, cannot process name. 

129RCODE_IMP_ERR = 0x4 # Unsupported request error. Allowable only for challenging NBNS when gets an Update type 

130 # registration request. 

131RCODE_RFS_ERR = 0x5 # Refused error. For policy reasons server will not register this name from this host. 

132RCODE_ACT_ERR = 0x6 # Active error. Name is owned by another node. 

133RCODE_CFT_ERR = 0x7 # Name in conflict error. A UNIQUE name is owned by more than one node. 

134 

135# NAME_FLAGS 

136NAME_FLAGS_PRM = 0x0200 # Permanent Name Flag. If one (1) then entry is for the permanent node name. Flag is zero 

137 # (0) for all other names. 

138NAME_FLAGS_ACT = 0x0400 # Active Name Flag. All entries have this flag set to one (1). 

139NAME_FLAG_CNF = 0x0800 # Conflict Flag. If one (1) then name on this node is in conflict. 

140NAME_FLAG_DRG = 0x1000 # Deregister Flag. If one (1) then this name is in the process of being deleted. 

141 

142# NB_FLAGS 

143NB_FLAGS_ONT_B = 0 

144NB_FLAGS_ONT_P = 1 << 13 

145NB_FLAGS_ONT_M = 2 << 13 

146NB_FLAGS_G = 1 << 15 

147 

148NAME_TYPES = {TYPE_UNKNOWN: 'Unknown', TYPE_WORKSTATION: 'Workstation', TYPE_CLIENT: 'Client', 

149 TYPE_SERVER: 'Server', TYPE_DOMAIN_MASTER: 'Domain Master', TYPE_DOMAIN_CONTROLLER: 'Domain Controller', 

150 TYPE_MASTER_BROWSER: 'Master Browser', TYPE_BROWSER: 'Browser Server', TYPE_NETDDE: 'NetDDE Server', 

151 TYPE_STATUS: 'Status'} 

152 

153# NetBIOS Session Types 

154NETBIOS_SESSION_MESSAGE = 0x0 

155NETBIOS_SESSION_REQUEST = 0x81 

156NETBIOS_SESSION_POSITIVE_RESPONSE = 0x82 

157NETBIOS_SESSION_NEGATIVE_RESPONSE = 0x83 

158NETBIOS_SESSION_RETARGET_RESPONSE = 0x84 

159NETBIOS_SESSION_KEEP_ALIVE = 0x85 

160 

161################################################################################ 

162# HELPERS 

163################################################################################ 

164def encode_name(name, nametype, scope): 

165 # ToDo: Rewrite this simpler, we're using less than written 

166 """ 

167 Perform first and second level encoding of name as specified in RFC 1001 (Section 4) 

168  

169 :param string name: the name to encode 

170 :param integer nametype: the name type constants 

171 :param string scope: the name's scope  

172  

173 :return string/bytes: the encoded name. 

174 """ 

175 if name == '*': 

176 name += '\0' * 15 

177 elif len(name) > 15: 

178 name = name[:15] + chr(nametype) 

179 else: 

180 name = name.ljust(15) + chr(nametype) 

181 

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

183 

184 try: 

185 if isinstance(encoded_name, unicode): 

186 encoded_name = encoded_name.encode('utf-8') 

187 except NameError: 

188 pass 

189 if scope: 189 ↛ 190line 189 didn't jump to line 190, because the condition on line 189 was never true

190 encoded_scope = '' 

191 for s in scope.split('.'): 

192 encoded_scope = encoded_scope + chr(len(s)) + s 

193 

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

195 else: 

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

197 

198# Internal method for use in encode_name() 

199def _do_first_level_encoding(m): 

200 s = ord(m.group(0)) 

201 return string.ascii_uppercase[s >> 4] + string.ascii_uppercase[s & 0x0f] 

202 

203def decode_name(name): 

204 # ToDo: Rewrite this simpler, we're using less than written 

205 """ 

206 Perform first and second level decoding of name as specified in RFC 1001 (Section 4) 

207 

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

209 

210 :return string: the decoded name. 

211 """ 

212 

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

214 assert name_length == 32 

215 

216 decoded_name = re.sub('..', _do_first_level_decoding, name[1:33].decode('utf-8')) 

217 if name[33:34] == b'\0': 217 ↛ 220line 217 didn't jump to line 220, because the condition on line 217 was never false

218 return 34, decoded_name, '' 

219 else: 

220 decoded_domain = '' 

221 offset = 34 

222 while 1: 

223 domain_length = byte2int(name[offset:offset+1]) 

224 if domain_length == 0: 

225 break 

226 decoded_domain = '.' + name[offset:offset + domain_length].decode('utf-8') 

227 offset += domain_length 

228 return offset + 1, decoded_name, decoded_domain 

229 

230def _do_first_level_decoding(m): 

231 s = m.group(0) 

232 return chr(((ord(s[0]) - ord('A')) << 4) | (ord(s[1]) - ord('A'))) 

233 

234ERRCLASS_QUERY = 0x00 

235ERRCLASS_SESSION = 0xf0 

236ERRCLASS_OS = 0xff 

237 

238QUERY_ERRORS = {0x01: 'Format Error. Request was invalidly formatted', 

239 0x02: 'Server failure. Problem with NBNS, cannot process name.', 

240 0x03: 'Name does not exist', 

241 0x04: 'Unsupported request error. Allowable only for challenging NBNS when gets an Update type registration request.', 

242 0x05: 'Refused error. For policy reasons server will not register this name from this host.', 

243 0x06: 'Active error. Name is owned by another node.', 

244 0x07: 'Name in conflict error. A UNIQUE name is owned by more than one node.', 

245 

246 } 

247 

248SESSION_ERRORS = {0x80: 'Not listening on called name', 

249 0x81: 'Not listening for calling name', 

250 0x82: 'Called name not present', 

251 0x83: 'Sufficient resources', 

252 0x8f: 'Unspecified error' 

253 } 

254 

255class NetBIOSError(Exception): 

256 def __init__(self, error_message='', error_class=None, error_code=None): 

257 self.error_class = error_class 

258 self.error_code = error_code 

259 self.error_msg = error_message 

260 

261 def get_error_code(self): 

262 return self.error 

263 

264 def getErrorCode(self): 

265 return self.get_error_code() 

266 

267 def get_error_string(self): 

268 return str(self) 

269 

270 def getErrorString(self): 

271 return str(self) 

272 

273 def __str__(self): 

274 if self.error_code is not None: 

275 if self.error_code in QUERY_ERRORS: 

276 return '%s-%s(%s)' % (self.error_msg, QUERY_ERRORS[self.error_code], self.error_code) 

277 elif self.error_code in SESSION_ERRORS: 

278 return '%s-%s(%s)' % (self.error_msg, SESSION_ERRORS[self.error_code], self.error_code) 

279 else: 

280 return '%s(%s)' % (self.error_msg, self.error_code) 

281 else: 

282 return '%s' % self.error_msg 

283 

284class NetBIOSTimeout(Exception): 

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

286 Exception.__init__(self, message) 

287 

288################################################################################ 

289# 4.2 NAME SERVER PACKETS 

290################################################################################ 

291class NBNSResourceRecord(Structure): 

292 structure = ( 

293 ('RR_NAME','z=\x00'), 

294 ('RR_TYPE','>H=0'), 

295 ('RR_CLASS','>H=0'), 

296 ('TTL','>L=0'), 

297 ('RDLENGTH','>H-RDATA'), 

298 ('RDATA',':=""'), 

299 ) 

300 

301class NBNodeStatusResponse(NBNSResourceRecord): 

302 def __init__(self, data = 0): 

303 NBNSResourceRecord.__init__(self, data) 

304 self.mac = b'00-00-00-00-00-00' 

305 self.num_names = unpack('B', self['RDATA'][:1])[0] 

306 self.entries = list() 

307 data = self['RDATA'][1:] 

308 for _ in range(self.num_names): 

309 entry = NODE_NAME_ENTRY(data) 

310 data = data[len(entry):] 

311 self.entries.append(entry) 

312 self.statistics = STATISTICS(data) 

313 self.set_mac_in_hexa(self.statistics['UNIT_ID']) 

314 

315 def set_mac_in_hexa(self, data): 

316 data_aux = u'' 

317 for d in bytearray(data): 

318 if data_aux == '': 

319 data_aux = '%02x' % d 

320 else: 

321 data_aux += '-%02x' % d 

322 self.mac = data_aux.upper() 

323 

324 def get_mac(self): 

325 return self.mac 

326 

327 def rawData(self): 

328 res = pack('!B', self.num_names ) 

329 for i in range(0, self.num_names): 

330 res += self.entries[i].getData() 

331 

332class NBPositiveNameQueryResponse(NBNSResourceRecord): 

333 def __init__(self, data = 0): 

334 NBNSResourceRecord.__init__(self, data) 

335 self.entries = [ ] 

336 rdata = self['RDATA'] 

337 while len(rdata) > 0: 

338 entry = ADDR_ENTRY(rdata) 

339 rdata = rdata[len(entry):] 

340 self.entries.append(socket.inet_ntoa(entry['NB_ADDRESS'])) 

341 

342# 4.2.1. GENERAL FORMAT OF NAME SERVICE PACKETS 

343class NAME_SERVICE_PACKET(Structure): 

344 commonHdr = ( 

345 ('NAME_TRN_ID','>H=0'), 

346 ('FLAGS','>H=0'), 

347 ('QDCOUNT','>H=0'), 

348 ('ANCOUNT','>H=0'), 

349 ('NSCOUNT','>H=0'), 

350 ('ARCOUNT','>H=0'), 

351 ) 

352 structure = ( 

353 ('ANSWERS',':'), 

354 ) 

355 

356# 4.2.1.2. QUESTION SECTION 

357class QUESTION_ENTRY(Structure): 

358 commonHdr = ( 

359 ('QUESTION_NAME','z'), 

360 ('QUESTION_TYPE','>H=0'), 

361 ('QUESTION_CLASS','>H=0'), 

362 ) 

363 

364# 4.2.1.3. RESOURCE RECORD 

365class RESOURCE_RECORD(Structure): 

366 structure = ( 

367 ('RR_NAME','z=\x00'), 

368 ('RR_TYPE','>H=0'), 

369 ('RR_CLASS','>H=0'), 

370 ('TTL','>L=0'), 

371 ('RDLENGTH','>H-RDATA'), 

372 ('RDATA',':=""'), 

373 ) 

374 

375# 4.2.2. NAME REGISTRATION REQUEST 

376class NAME_REGISTRATION_REQUEST(NAME_SERVICE_PACKET): 

377 structure = ( 

378 ('QUESTION_NAME', ':'), 

379 ('QUESTION_TYPE', '>H=0'), 

380 ('QUESTION_CLASS', '>H=0'), 

381 ('RR_NAME',':', ), 

382 ('RR_TYPE', '>H=0'), 

383 ('RR_CLASS','>H=0'), 

384 ('TTL', '>L=0'), 

385 ('RDLENGTH', '>H=6'), 

386 ('NB_FLAGS', '>H=0'), 

387 ('NB_ADDRESS', '4s=b""'), 

388 ) 

389 def __init__(self, data=None): 

390 NAME_SERVICE_PACKET.__init__(self,data) 

391 self['FLAGS'] = OPCODE_REQUEST | NM_FLAGS_RD | OPCODE_REGISTRATION 

392 self['QDCOUNT'] = 1 

393 self['ANCOUNT'] = 0 

394 self['NSCOUNT'] = 0 

395 self['ARCOUNT'] = 1 

396 

397 self['QUESTION_TYPE'] = QUESTION_TYPE_NB 

398 self['QUESTION_CLASS'] = QUESTION_CLASS_IN 

399 

400 self['RR_TYPE'] = RR_TYPE_NB 

401 self['RR_CLASS'] = RR_CLASS_IN 

402 

403# 4.2.3. NAME OVERWRITE REQUEST & DEMAND 

404class NAME_OVERWRITE_REQUEST(NAME_REGISTRATION_REQUEST): 

405 def __init__(self, data=None): 

406 NAME_REGISTRATION_REQUEST.__init__(self,data) 

407 self['FLAGS'] = OPCODE_REQUEST | OPCODE_REGISTRATION 

408 self['QDCOUNT'] = 1 

409 self['ANCOUNT'] = 0 

410 self['NSCOUNT'] = 0 

411 self['ARCOUNT'] = 1 

412 

413# 4.2.4. NAME REFRESH REQUEST 

414class NAME_REFRESH_REQUEST(NAME_REGISTRATION_REQUEST): 

415 def __init__(self, data=None): 

416 NAME_REGISTRATION_REQUEST.__init__(self,data) 

417 self['FLAGS'] = OPCODE_REFRESH | 0x1 

418 self['QDCOUNT'] = 1 

419 self['ANCOUNT'] = 0 

420 self['NSCOUNT'] = 0 

421 self['ARCOUNT'] = 1 

422 

423# 4.2.5. POSITIVE NAME REGISTRATION RESPONSE 

424# 4.2.6. NEGATIVE NAME REGISTRATION RESPONSE 

425# 4.2.7. END-NODE CHALLENGE REGISTRATION RESPONSE 

426class NAME_REGISTRATION_RESPONSE(NAME_REGISTRATION_REQUEST): 

427 def __init__(self, data=None): 

428 NAME_REGISTRATION_REQUEST.__init__(self,data) 

429 

430# 4.2.8. NAME CONFLICT DEMAND 

431class NAME_CONFLICT_DEMAND(NAME_REGISTRATION_REQUEST): 

432 def __init__(self, data=None): 

433 NAME_REGISTRATION_REQUEST.__init__(self,data) 

434 

435# ToDo: 4.2.9. NAME RELEASE REQUEST & DEMAND 

436# ToDo: 4.2.10. POSITIVE NAME RELEASE RESPONSE 

437# ToDo: 4.2.11. NEGATIVE NAME RELEASE RESPONSE 

438 

439# 4.2.12. NAME QUERY REQUEST 

440class NAME_QUERY_REQUEST(NAME_SERVICE_PACKET): 

441 structure = ( 

442 ('QUESTION_NAME', ':'), 

443 ('QUESTION_TYPE', '>H=0'), 

444 ('QUESTION_CLASS', '>H=0'), 

445 ) 

446 def __init__(self, data=None): 

447 NAME_SERVICE_PACKET.__init__(self,data) 

448 self['FLAGS'] = OPCODE_REQUEST | OPCODE_REGISTRATION | NM_FLAGS_RD 

449 self['RCODE'] = 0 

450 self['QDCOUNT'] = 1 

451 self['ANCOUNT'] = 0 

452 self['NSCOUNT'] = 0 

453 self['ARCOUNT'] = 0 

454 

455 self['QUESTION_TYPE'] = QUESTION_TYPE_NB 

456 self['QUESTION_CLASS'] = QUESTION_CLASS_IN 

457 

458# 4.2.13. POSITIVE NAME QUERY RESPONSE 

459class ADDR_ENTRY(Structure): 

460 structure = ( 

461 ('NB_FLAGS', '>H=0'), 

462 ('NB_ADDRESS', '4s=b""'), 

463 ) 

464 

465# ToDo: 4.2.15. REDIRECT NAME QUERY RESPONSE 

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

467 

468# 4.2.17. NODE STATUS REQUEST 

469class NODE_STATUS_REQUEST(NAME_QUERY_REQUEST): 

470 def __init__(self, data=None): 

471 NAME_QUERY_REQUEST.__init__(self,data) 

472 

473 self['FLAGS'] = 0 

474 self['QUESTION_TYPE'] = QUESTION_TYPE_NBSTAT 

475 

476# 4.2.18. NODE STATUS RESPONSE 

477class NODE_NAME_ENTRY(Structure): 

478 structure = ( 

479 ('NAME','15s=b""'), 

480 ('TYPE','B=0'), 

481 ('NAME_FLAGS','>H'), 

482 ) 

483 

484class STATISTICS(Structure): 

485 structure = ( 

486 ('UNIT_ID','6s=b""'), 

487 ('JUMPERS','B'), 

488 ('TEST_RESULT','B'), 

489 ('VERSION_NUMBER','>H'), 

490 ('PERIOD_OF_STATISTICS','>H'), 

491 ('NUMBER_OF_CRCs','>H'), 

492 ('NUMBER_ALIGNMENT_ERRORS','>H'), 

493 ('NUMBER_OF_COLLISIONS','>H'), 

494 ('NUMBER_SEND_ABORTS','>H'), 

495 ('NUMBER_GOOD_SENDS','>L'), 

496 ('NUMBER_GOOD_RECEIVES','>L'), 

497 ('NUMBER_RETRANSMITS','>H'), 

498 ('NUMBER_NO_RESOURCE_CONDITIONS','>H'), 

499 ('NUMBER_FREE_COMMAND_BLOCKS','>H'), 

500 ('TOTAL_NUMBER_COMMAND_BLOCKS','>H'), 

501 ('MAX_TOTAL_NUMBER_COMMAND_BLOCKS','>H'), 

502 ('NUMBER_PENDING_SESSIONS','>H'), 

503 ('MAX_NUMBER_PENDING_SESSIONS','>H'), 

504 ('MAX_TOTAL_SESSIONS_POSSIBLE','>H'), 

505 ('SESSION_DATA_PACKET_SIZE','>H'), 

506 ) 

507 

508class NetBIOS: 

509 # Creates a NetBIOS instance without specifying any default NetBIOS domain nameserver. 

510 # All queries will be sent through the servport. 

511 def __init__(self, servport = NETBIOS_NS_PORT): 

512 self.__servport = NETBIOS_NS_PORT 

513 self.__nameserver = None 

514 self.__broadcastaddr = BROADCAST_ADDR 

515 self.mac = b'00-00-00-00-00-00' 

516 

517 def _setup_connection(self, dstaddr, timeout=None): 

518 port = rand.randint(10000, 60000) 

519 af, socktype, proto, _canonname, _sa = socket.getaddrinfo(dstaddr, port, socket.AF_INET, socket.SOCK_DGRAM)[0] 

520 s = socket.socket(af, socktype, proto) 

521 has_bind = 1 

522 for _i in range(0, 10): 

523 # We try to bind to a port for 10 tries 

524 try: 

525 s.bind((INADDR_ANY, rand.randint(10000, 60000))) 

526 s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) 

527 has_bind = 1 

528 except socket.error: 

529 pass 

530 if not has_bind: 530 ↛ 531line 530 didn't jump to line 531, because the condition on line 530 was never true

531 raise NetBIOSError('Cannot bind to a good UDP port', ERRCLASS_OS, errno.EAGAIN) 

532 self.__sock = s 

533 

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

535 self._setup_connection(destaddr) 

536 

537 tries = 3 

538 while 1: 

539 try: 

540 self.__sock.sendto(request.getData(), 0, (destaddr, self.__servport)) 

541 ready, _, _ = select.select([self.__sock.fileno()], [], [], timeout) 

542 if not ready: 

543 if tries: 

544 # Retry again until tries == 0 

545 tries -= 1 

546 else: 

547 raise NetBIOSTimeout 

548 else: 

549 try: 

550 data, _ = self.__sock.recvfrom(65536, 0) 

551 except Exception as e: 

552 raise NetBIOSError("recvfrom error: %s" % str(e)) 

553 self.__sock.close() 

554 res = NAME_SERVICE_PACKET(data) 

555 if res['NAME_TRN_ID'] == request['NAME_TRN_ID']: 555 ↛ 539line 555 didn't jump to line 539, because the condition on line 555 was never false

556 if (res['FLAGS'] & 0xf) > 0: 556 ↛ 557line 556 didn't jump to line 557, because the condition on line 556 was never true

557 raise NetBIOSError('Negative response', ERRCLASS_QUERY, res['FLAGS'] & 0xf) 

558 return res 

559 except select.error as ex: 559 ↛ 560line 559 didn't jump to line 560, because the exception caught by line 559 didn't happen

560 if ex.errno != errno.EINTR and ex.errno != errno.EAGAIN: 

561 raise NetBIOSError('Error occurs while waiting for response', ERRCLASS_OS, ex.errno) 

562 except socket.error as ex: 

563 raise NetBIOSError('Connection error: %s' % str(ex)) 

564 

565 # Set the default NetBIOS domain nameserver. 

566 def set_nameserver(self, nameserver): 

567 self.__nameserver = nameserver 

568 

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

570 def get_nameserver(self): 

571 return self.__nameserver 

572 

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

574 def set_broadcastaddr(self, broadcastaddr): 

575 self.__broadcastaddr = broadcastaddr 

576 

577 # Return the broadcast address to be used, or BROADCAST_ADDR if default broadcast address is used.  

578 def get_broadcastaddr(self): 

579 return self.__broadcastaddr 

580 

581 # Returns a NBPositiveNameQueryResponse instance containing the host information for nbname. 

582 # If a NetBIOS domain nameserver has been specified, it will be used for the query. 

583 # Otherwise, the query is broadcasted on the broadcast address. 

584 def gethostbyname(self, nbname, qtype = TYPE_WORKSTATION, scope = None, timeout = 1): 

585 resp = self.name_query_request(nbname, self.__nameserver, qtype, scope, timeout) 

586 return resp 

587 

588 # Returns a list of NBNodeEntry instances containing node status information for nbname. 

589 # If destaddr contains an IP address, then this will become an unicast query on the destaddr. 

590 # Raises NetBIOSTimeout if timeout (in secs) is reached. 

591 # Raises NetBIOSError for other errors 

592 def getnodestatus(self, nbname, destaddr = None, type = TYPE_WORKSTATION, scope = None, timeout = 1): 

593 if destaddr: 593 ↛ 596line 593 didn't jump to line 596, because the condition on line 593 was never false

594 return self.node_status_request(nbname, destaddr, type, scope, timeout) 

595 else: 

596 return self.node_status_request(nbname, self.__nameserver, type, scope, timeout) 

597 

598 def getnetbiosname(self, ip): 

599 entries = self.getnodestatus('*',ip) 

600 entries = [x for x in entries if x['TYPE'] == TYPE_SERVER] 

601 return entries[0]['NAME'].strip().decode('latin-1') 

602 

603 def getmacaddress(self): 

604 return self.mac 

605 

606 def name_registration_request(self, nbname, destaddr, qtype, scope, nb_flags=0, nb_address='0.0.0.0'): 

607 netbios_name = nbname.upper() 

608 qn_label = encode_name(netbios_name, qtype, scope) 

609 

610 p = NAME_REGISTRATION_REQUEST() 

611 p['NAME_TRN_ID'] = rand.randint(1, 32000) 

612 p['QUESTION_NAME'] = qn_label[:-1] + b'\x00' 

613 p['RR_NAME'] = qn_label[:-1] + b'\x00' 

614 p['TTL'] = 0xffff 

615 p['NB_FLAGS'] = nb_flags 

616 p['NB_ADDRESS'] = socket.inet_aton(nb_address) 

617 if not destaddr: 617 ↛ 618line 617 didn't jump to line 618, because the condition on line 617 was never true

618 p['FLAGS'] |= NM_FLAGS_BROADCAST 

619 destaddr = self.__broadcastaddr 

620 

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

622 return res 

623 

624 def name_query_request(self, nbname, destaddr = None, qtype = TYPE_SERVER, scope = None, timeout = 1): 

625 netbios_name = nbname.upper() 

626 qn_label = encode_name(netbios_name, qtype, scope) 

627 

628 p = NAME_QUERY_REQUEST() 

629 p['NAME_TRN_ID'] = rand.randint(1, 32000) 

630 p['QUESTION_NAME'] = qn_label[:-1] + b'\x00' 

631 p['FLAGS'] = NM_FLAGS_RD 

632 if not destaddr: 632 ↛ 633line 632 didn't jump to line 633, because the condition on line 632 was never true

633 p['FLAGS'] |= NM_FLAGS_BROADCAST 

634 

635 destaddr = self.__broadcastaddr 

636 

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

638 return NBPositiveNameQueryResponse(res['ANSWERS']) 

639 

640 def node_status_request(self, nbname, destaddr, type, scope, timeout): 

641 netbios_name = nbname.upper() 

642 qn_label = encode_name(netbios_name, type, scope) 

643 p = NODE_STATUS_REQUEST() 

644 p['NAME_TRN_ID'] = rand.randint(1, 32000) 

645 p['QUESTION_NAME'] = qn_label[:-1] + b'\x00' 

646 

647 if not destaddr: 647 ↛ 648line 647 didn't jump to line 648, because the condition on line 647 was never true

648 p['FLAGS'] = NM_FLAGS_BROADCAST 

649 destaddr = self.__broadcastaddr 

650 

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

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

653 self.mac = answ.get_mac() 

654 return answ.entries 

655 

656################################################################################ 

657# 4.2 SESSION SERVICE PACKETS 

658################################################################################ 

659 

660class NetBIOSSessionPacket: 

661 def __init__(self, data=0): 

662 self.type = 0x0 

663 self.flags = 0x0 

664 self.length = 0x0 

665 if data == 0: 

666 self._trailer = b'' 

667 else: 

668 try: 

669 self.type = indexbytes(data,0) 

670 if self.type == NETBIOS_SESSION_MESSAGE: 

671 self.length = indexbytes(data,1) << 16 | (unpack('!H', data[2:4])[0]) 

672 else: 

673 self.flags = data[1] 

674 self.length = unpack('!H', data[2:4])[0] 

675 

676 self._trailer = data[4:] 

677 except: 

678 raise NetBIOSError('Wrong packet format ') 

679 

680 def set_type(self, type): 

681 self.type = type 

682 

683 def get_type(self): 

684 return self.type 

685 

686 def rawData(self): 

687 if self.type == NETBIOS_SESSION_MESSAGE: 

688 data = pack('!BBH', self.type, self.length >> 16, self.length & 0xFFFF) + self._trailer 

689 else: 

690 data = pack('!BBH', self.type, self.flags, self.length) + self._trailer 

691 return data 

692 

693 def set_trailer(self, data): 

694 self._trailer = data 

695 self.length = len(data) 

696 

697 def get_length(self): 

698 return self.length 

699 

700 def get_trailer(self): 

701 return self._trailer 

702 

703class NetBIOSSession: 

704 def __init__(self, myname, remote_name, remote_host, remote_type=TYPE_SERVER, sess_port=NETBIOS_SESSION_PORT, 

705 timeout=None, local_type=TYPE_WORKSTATION, sock=None): 

706 """ 

707 

708 :param unicode myname: My local NetBIOS name 

709 :param unicode remote_name: Remote NetBIOS name 

710 :param unicode remote_host: Remote IP Address 

711 :param integer remote_type: NetBIOS Host type 

712 :param integer sess_port: Session port to connect (139,445) 

713 :param integer timeout: Timeout for connection 

714 :param integer local_type: My Local Host Type 

715 :param socket sock: Socket for already established connection 

716 """ 

717 if len(myname) > 15: 717 ↛ 718line 717 didn't jump to line 718, because the condition on line 717 was never true

718 self.__myname = myname[:15].upper() 

719 else: 

720 self.__myname = myname.upper() 

721 self.__local_type = local_type 

722 

723 assert remote_name 

724 # if destination port SMB_SESSION_PORT and remote name *SMBSERVER, we're changing it to its IP address 

725 # helping solving the client mistake ;) 

726 if remote_name == '*SMBSERVER' and sess_port == SMB_SESSION_PORT: 726 ↛ 727line 726 didn't jump to line 727, because the condition on line 726 was never true

727 remote_name = remote_host 

728 

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

730 

731 if remote_name == '*SMBSERVER': 731 ↛ 732line 731 didn't jump to line 732, because the condition on line 731 was never true

732 nb = NetBIOS() 

733 try: 

734 res = nb.getnetbiosname(remote_host) 

735 except: 

736 res = None 

737 pass 

738 

739 if res is not None: 

740 remote_name = res 

741 

742 if len(remote_name) > 15: 742 ↛ 743line 742 didn't jump to line 743, because the condition on line 742 was never true

743 self.__remote_name = remote_name[:15].upper() 

744 else: 

745 self.__remote_name = remote_name.upper() 

746 self.__remote_type = remote_type 

747 self.__remote_host = remote_host 

748 

749 if sock is not None: 749 ↛ 751line 749 didn't jump to line 751, because the condition on line 749 was never true

750 # We are acting as a server 

751 self._sock = sock 

752 else: 

753 self._sock = self._setup_connection((remote_host, sess_port), timeout) 

754 

755 if sess_port == NETBIOS_SESSION_PORT: 

756 self._request_session(remote_type, local_type, timeout) 

757 

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

759 raise NotImplementedError('Not Implemented!') 

760 

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

762 raise NotImplementedError('Not Implemented!') 

763 

764 def get_myname(self): 

765 return self.__myname 

766 

767 def get_mytype(self): 

768 return self.__local_type 

769 

770 def get_remote_host(self): 

771 return self.__remote_host 

772 

773 def get_remote_name(self): 

774 return self.__remote_name 

775 

776 def get_remote_type(self): 

777 return self.__remote_type 

778 

779 def close(self): 

780 self._sock.close() 

781 

782 def get_socket(self): 

783 return self._sock 

784 

785class NetBIOSUDPSessionPacket(Structure): 

786 TYPE_DIRECT_UNIQUE = 16 

787 TYPE_DIRECT_GROUP = 17 

788 

789 FLAGS_MORE_FRAGMENTS = 1 

790 FLAGS_FIRST_FRAGMENT = 2 

791 FLAGS_B_NODE = 0 

792 

793 structure = ( 

794 ('Type','B=16'), # Direct Unique Datagram 

795 ('Flags','B=2'), # FLAGS_FIRST_FRAGMENT 

796 ('ID','<H'), 

797 ('_SourceIP','>L'), 

798 ('SourceIP','"'), 

799 ('SourcePort','>H=138'), 

800 ('DataLegth','>H-Data'), 

801 ('Offset','>H=0'), 

802 ('SourceName','z'), 

803 ('DestinationName','z'), 

804 ('Data',':'), 

805 ) 

806 

807 def getData(self): 

808 addr = self['SourceIP'].split('.') 

809 addr = [int(x) for x in addr] 

810 addr = (((addr[0] << 8) + addr[1] << 8) + addr[2] << 8) + addr[3] 

811 self['_SourceIP'] = addr 

812 return Structure.getData(self) 

813 

814 def get_trailer(self): 

815 return self['Data'] 

816 

817class NetBIOSUDPSession(NetBIOSSession): 

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

819 af, socktype, proto, canonname, sa = socket.getaddrinfo(peer[0], peer[1], 0, socket.SOCK_DGRAM)[0] 

820 sock = socket.socket(af, socktype, proto) 

821 sock.connect(sa) 

822 

823 sock = socket.socket(af, socktype, proto) 

824 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 

825 sock.bind((INADDR_ANY, 138)) 

826 self.peer = peer 

827 return sock 

828 

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

830 pass 

831 

832 def next_id(self): 

833 if hasattr(self, '__dgram_id'): 

834 answer = self.__dgram_id 

835 else: 

836 self.__dgram_id = rand.randint(1,65535) 

837 answer = self.__dgram_id 

838 self.__dgram_id += 1 

839 return answer 

840 

841 def send_packet(self, data): 

842 # Yes... I know... 

843 self._sock.connect(self.peer) 

844 

845 p = NetBIOSUDPSessionPacket() 

846 p['ID'] = self.next_id() 

847 p['SourceIP'] = self._sock.getsockname()[0] 

848 p['SourceName'] = encode_name(self.get_myname(), self.get_mytype(), '')[:-1] 

849 p['DestinationName'] = encode_name(self.get_remote_name(), self.get_remote_type(), '')[:-1] 

850 p['Data'] = data 

851 

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

853 self._sock.close() 

854 

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

856 

857 def recv_packet(self, timeout = None): 

858 # The next loop is a workaround for a bigger problem: 

859 # When data reaches higher layers, the lower headers are lost, 

860 # and with them, for example, the source IP. Hence, SMB users 

861 # can't know where packets are coming from... we need a better 

862 # solution, right now, we will filter everything except packets 

863 # coming from the remote_host specified in __init__() 

864 

865 while 1: 

866 data, peer = self._sock.recvfrom(8192) 

867# print "peer: %r self.peer: %r" % (peer, self.peer) 

868 if peer == self.peer: 

869 break 

870 

871 return NetBIOSUDPSessionPacket(data) 

872 

873class NetBIOSTCPSession(NetBIOSSession): 

874 def __init__(self, myname, remote_name, remote_host, remote_type=TYPE_SERVER, sess_port=NETBIOS_SESSION_PORT, 

875 timeout=None, local_type=TYPE_WORKSTATION, sock=None, select_poll=False): 

876 """ 

877  

878 :param unicode myname: My local NetBIOS name 

879 :param unicode remote_name: Remote NetBIOS name 

880 :param unicode remote_host: Remote IP Address 

881 :param integer remote_type: NetBIOS Host type 

882 :param integer sess_port: Session port to connect (139,445) 

883 :param integer timeout: Timeout for connection 

884 :param integer local_type: My Local Host Type 

885 :param socket sock: Socket for already established connection 

886 :param boolean select_poll: Type of polling mechanism 

887 """ 

888 self.__select_poll = select_poll 

889 if self.__select_poll: 889 ↛ 890line 889 didn't jump to line 890, because the condition on line 889 was never true

890 self.read_function = self.polling_read 

891 else: 

892 self.read_function = self.non_polling_read 

893 NetBIOSSession.__init__(self, myname, remote_name, remote_host, remote_type=remote_type, sess_port=sess_port, 

894 timeout=timeout, local_type=local_type, sock=sock) 

895 

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

897 try: 

898 af, socktype, proto, canonname, sa = socket.getaddrinfo(peer[0], peer[1], 0, socket.SOCK_STREAM)[0] 

899 sock = socket.socket(af, socktype, proto) 

900 oldtimeout = sock.gettimeout() 

901 sock.settimeout(timeout) 

902 sock.connect(sa) 

903 sock.settimeout(oldtimeout) 

904 except socket.error as e: 

905 raise socket.error("Connection error (%s:%s)" % (peer[0], peer[1]), e) 

906 return sock 

907 

908 def send_packet(self, data): 

909 p = NetBIOSSessionPacket() 

910 p.set_type(NETBIOS_SESSION_MESSAGE) 

911 p.set_trailer(data) 

912 self._sock.sendall(p.rawData()) 

913 

914 def recv_packet(self, timeout = None): 

915 data = self.__read(timeout) 

916 NBSPacket = NetBIOSSessionPacket(data) 

917 if NBSPacket.get_type() == NETBIOS_SESSION_KEEP_ALIVE: 917 ↛ 919line 917 didn't jump to line 919, because the condition on line 917 was never true

918 # Discard packet 

919 return self.recv_packet(timeout) 

920 return NetBIOSSessionPacket(data) 

921 

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

923 p = NetBIOSSessionPacket() 

924 remote_name = encode_name(self.get_remote_name(), remote_type, '') 

925 myname = encode_name(self.get_myname(), local_type, '') 

926 p.set_type(NETBIOS_SESSION_REQUEST) 

927 p.set_trailer(remote_name + myname) 

928 

929 self._sock.sendall(p.rawData()) 

930 while 1: 

931 p = self.recv_packet(timeout) 

932 if p.get_type() == NETBIOS_SESSION_NEGATIVE_RESPONSE: 932 ↛ 933line 932 didn't jump to line 933, because the condition on line 932 was never true

933 raise NetBIOSError('Cannot request session (Called Name:%s)' % self.get_remote_name()) 

934 elif p.get_type() == NETBIOS_SESSION_POSITIVE_RESPONSE: 934 ↛ 938line 934 didn't jump to line 938, because the condition on line 934 was never false

935 break 

936 else: 

937 # Ignore all other messages, most probably keepalive messages 

938 pass 

939 

940 def polling_read(self, read_length, timeout): 

941 data = b'' 

942 if timeout is None: 

943 timeout = 3600 

944 

945 time_left = timeout 

946 CHUNK_TIME = 0.025 

947 bytes_left = read_length 

948 

949 while bytes_left > 0: 

950 try: 

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

952 

953 if not ready: 

954 if time_left <= 0: 

955 raise NetBIOSTimeout 

956 else: 

957 time.sleep(CHUNK_TIME) 

958 time_left -= CHUNK_TIME 

959 continue 

960 

961 received = self._sock.recv(bytes_left) 

962 if len(received) == 0: 

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

964 

965 data = data + received 

966 bytes_left = read_length - len(data) 

967 except select.error as ex: 

968 if ex.errno != errno.EINTR and ex.errno != errno.EAGAIN: 

969 raise NetBIOSError('Error occurs while reading from remote', ERRCLASS_OS, ex.errno) 

970 

971 return bytes(data) 

972 

973 def non_polling_read(self, read_length, timeout): 

974 data = b'' 

975 if timeout is None: 975 ↛ 976line 975 didn't jump to line 976, because the condition on line 975 was never true

976 timeout = 3600 

977 

978 start_time = time.time() 

979 bytes_left = read_length 

980 

981 while bytes_left > 0: 

982 self._sock.settimeout(timeout) 

983 try: 

984 received = self._sock.recv(bytes_left) 

985 except socket.timeout: 

986 raise NetBIOSTimeout 

987 except Exception as ex: 

988 raise NetBIOSError('Error occurs while reading from remote', ERRCLASS_OS, ex.errno) 

989 

990 if (time.time() - start_time) > timeout: 990 ↛ 991line 990 didn't jump to line 991, because the condition on line 990 was never true

991 raise NetBIOSTimeout 

992 

993 if len(received) == 0: 993 ↛ 994line 993 didn't jump to line 994, because the condition on line 993 was never true

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

995 

996 data = data + received 

997 bytes_left = read_length - len(data) 

998 

999 return bytes(data) 

1000 

1001 def __read(self, timeout = None): 

1002 data = self.read_function(4, timeout) 

1003 type, flags, length = unpack('>ccH', data) 

1004 if ord(type) == NETBIOS_SESSION_MESSAGE: 

1005 length |= ord(flags) << 16 

1006 else: 

1007 if ord(flags) & 0x01: 1007 ↛ 1008line 1007 didn't jump to line 1008, because the condition on line 1007 was never true

1008 length |= 0x10000 

1009 data2 = self.read_function(length, timeout) 

1010 

1011 return data + data2