Source code for pathspider.helpers.dns

import struct
import socket

from dnslib.dns import DNSError, DNSRecord, DNSQuestion, QTYPE
from scapy.all import RandShort

from pathspider.base import CONN_OK
from pathspider.base import CONN_TIMEOUT
from pathspider.base import CONN_FAILED


[docs]class PSDNSRecord(DNSRecord):
[docs] def spider_send(self, source, job, conn_timeout, tcp=False, sockopts=None): """ Send packet to nameserver and return response and source port. :param str source: source address for the connection :param dict job: dictionary containing the job details :param bool tcp: perform connection using TCP if *True*, otherwise UDP :param list(tuple) sockopts: list of socket options to set on the connection socket as tuples containing the level, option and value """ data = self.pack() if ':' in job['dip']: inet = socket.AF_INET6 else: inet = socket.AF_INET if tcp: if len(data) > 65535: raise ValueError("Packet length too long: %d" % len(data)) data = struct.pack("!H", len(data)) + data sock = socket.socket(inet, socket.SOCK_STREAM) if ':' in job['dip']: sock.bind((source[1], 0)) else: sock.bind((source[0], 0)) sock.settimeout(conn_timeout) sockopts = sockopts or [] for sockopt in sockopts: sock.setsockopt(*sockopt) sock.connect((job['dip'], job['dp'])) sock.sendall(data) sp = sock.getsockname()[1] response = None try: response = sock.recv(8192) length = struct.unpack("!H",bytes(response[:2]))[0] while len(response) - 2 < length: response += sock.recv(8192) except socket.timeout: pass if response is not None and len(response) > 2: try: response = response[2:] PSDNSRecord().parse(response) except DNSError: response = None sock.close() else: sp = RandShort() sock = socket.socket(inet, socket.SOCK_DGRAM) if ':' in job['dip']: sock.bind((source[1], sp)) else: sock.bind((source[0], sp)) sp = sock.getsockname()[1] sock.settimeout(conn_timeout) sockopts = sockopts or [] for sockopt in sockopts: sock.setsockopt(*sockopt) sock.sendto(self.pack(), (job['dip'], job['dp'])) response = None try: response, server = sock.recvfrom(8192) except socket.timeout: pass if response is not None: try: PSDNSRecord().parse(response) except DNSError: response = None sock.close() return (response, sp)
[docs]def connect_dns_tcp(*args, **kwargs): """ This helper function will perform a DNS query over a TCP connection. It will not perform any special action in the event that this is the experimental flow, it only performs a DNS query connection. .. deprecated:: 2.1.0 Use :py:func:`pathspider.helpers.dns.connect_dns` instead. :param list args: positional arguments for :py:func:`pathspider.helpers.dns.connect_dns` :param dict kwargs: keyword arguments for :py:func:`pathspider.helpers.dns.connect_dns` """ return connect_dns(*args, tcp=True, **kwargs)
[docs]def connect_dns_udp(*args, **kwargs): """ This helper function will perform a DNS query over a TCP connection. It will not perform any special action in the event that this is the experimental flow, it only performs a DNS query connection. .. deprecated:: 2.1.0 Use :py:func:`pathspider.helpers.dns.connect_dns` instead. :param list args: positional arguments for :py:func:`pathspider.helpers.dns.connect_dns` :param dict kwargs: keyword arguments for :py:func:`pathspider.helpers.dns.connect_dns` """ return connect_dns(*args, tcp=False, **kwargs)
[docs]def connect_dns(source, job, conn_timeout, tcp=False, sockopts=None): """ This helper function will perform a DNS query over a TCP connection. It will not perform any special action in the event that this is the experimental flow, it only performs a DNS query connection. :param str source: source address for the connection :param dict job: dictionary containing the job details :param bool tcp: perform connection using TCP if *True*, otherwise UDP :param list(tuple) sockopts: list of socket options to set on the connection socket as tuples containing the level, option and value """ try: q = PSDNSRecord(q=DNSQuestion(job['domain'], QTYPE.A)) response, sp = q.spider_send(source, job, conn_timeout, tcp=tcp, sockopts=sockopts) if response is None: return {'sp': sp, 'spdr_state': CONN_FAILED} return {'sp': sp, 'spdr_state': CONN_OK} except TimeoutError: return {'sp': 0, 'spdr_state': CONN_TIMEOUT} except TypeError: # Caused by not having a v4/v6 address when trying to bind return {'sp': 0, 'spdr_state': CONN_FAILED} except OSError: return {'sp': 0, 'spdr_state': CONN_FAILED} except ValueError: # Caused by domain names that don't fit in a DNS query (this should never happen) return {'sp': 0, 'spdr_state': CONN_FAILED}