|
34 | 34 | XShortField, |
35 | 35 | XStrFixedLenField |
36 | 36 | ) |
| 37 | +from scapy.interfaces import _GlobInterfaceType |
| 38 | +from scapy.sendrecv import sr1 |
37 | 39 | from scapy.layers.inet import IP, UDP, TCP |
38 | 40 | from scapy.layers.l2 import Ether, SourceMACField |
39 | 41 |
|
| 42 | +# Typing imports |
| 43 | +from typing import ( |
| 44 | + List, |
| 45 | + Union, |
| 46 | +) |
| 47 | + |
40 | 48 |
|
41 | 49 | class NetBIOS_DS(Packet): |
42 | 50 | name = "NetBIOS datagram service" |
@@ -398,6 +406,93 @@ def tcp_reassemble(cls, data, *args, **kwargs): |
398 | 406 | bind_layers(TCP, NBTSession, dport=139, sport=139) |
399 | 407 |
|
400 | 408 |
|
| 409 | +_nbns_cache = conf.netcache.new_cache("nbns_cache", 300) |
| 410 | + |
| 411 | + |
| 412 | +@conf.commands.register |
| 413 | +def nbns_resolve( |
| 414 | + qname: str, |
| 415 | + iface: Union[_GlobInterfaceType, List[_GlobInterfaceType]] = None, |
| 416 | + raw: bool = False, |
| 417 | + timeout: int = 3, |
| 418 | + **kwargs, |
| 419 | +) -> List[str]: |
| 420 | + """ |
| 421 | + Perform a simple NBNS (NetBios Name Services) resolution with caching |
| 422 | +
|
| 423 | + :param qname: the name to query |
| 424 | + :param iface: the interfaces to use. (default: all) |
| 425 | + :param raw: return the whole netbios packet (default False) |
| 426 | + :param timeout: seconds until timeout (per server) |
| 427 | + :raise TimeoutError: if no DNS servers were reached in time. |
| 428 | + """ |
| 429 | + kwargs.setdefault("verbose", 0) |
| 430 | + |
| 431 | + # Unify types (for caching) |
| 432 | + qname = NBNSQueryRequest.QUESTION_NAME.any2i(None, qname) |
| 433 | + |
| 434 | + # Check cache |
| 435 | + cache_ident = qname + b"raw" if raw else b"" |
| 436 | + result = _nbns_cache.get(cache_ident) |
| 437 | + if result: |
| 438 | + return result |
| 439 | + |
| 440 | + if iface is None: |
| 441 | + ifaces = [ |
| 442 | + x |
| 443 | + for name, x in conf.ifaces.items() |
| 444 | + if x.is_valid() and name != conf.loopback_name |
| 445 | + ] |
| 446 | + elif isinstance(iface, list): |
| 447 | + ifaces = iface |
| 448 | + else: |
| 449 | + ifaces = [iface] |
| 450 | + |
| 451 | + # Builds a request for each broadcast address of each interface |
| 452 | + requests = [] |
| 453 | + for iface in ifaces: |
| 454 | + for bdcst in conf.route.get_if_bcast(iface): |
| 455 | + if bdcst == "255.255.255.255": |
| 456 | + continue |
| 457 | + requests.append( |
| 458 | + IP(dst=bdcst) / |
| 459 | + UDP() / |
| 460 | + NBNSHeader() / |
| 461 | + NBNSQueryRequest(QUESTION_NAME=qname) |
| 462 | + ) |
| 463 | + |
| 464 | + if not requests: |
| 465 | + return None |
| 466 | + |
| 467 | + # Perform requests, get the first response |
| 468 | + try: |
| 469 | + old_checkIPAddr = conf.checkIPaddr |
| 470 | + conf.checkIPaddr = False |
| 471 | + |
| 472 | + res = sr1( |
| 473 | + requests, |
| 474 | + timeout=timeout, |
| 475 | + first=True, |
| 476 | + **kwargs, |
| 477 | + ) |
| 478 | + finally: |
| 479 | + conf.checkIPaddr = old_checkIPAddr |
| 480 | + |
| 481 | + if res is not None: |
| 482 | + if raw: |
| 483 | + # Raw |
| 484 | + result = res |
| 485 | + else: |
| 486 | + # Get IP |
| 487 | + result = [x.NB_ADDRESS for x in res.ADDR_ENTRY] |
| 488 | + if result: |
| 489 | + # Cache it |
| 490 | + _nbns_cache[cache_ident] = result |
| 491 | + return result |
| 492 | + else: |
| 493 | + raise TimeoutError |
| 494 | + |
| 495 | + |
401 | 496 | class NBNS_am(AnsweringMachine): |
402 | 497 | function_name = "nbnsd" |
403 | 498 | filter = "udp port 137" |
|
0 commit comments