def _recv_handler(self): recv_buffer = [] while True: try: data_from_server = self._relay_conn.sock.recv(1024) except (socket.timeout, OSError) as e: Log.p(f'RECV HANDLER: {e}') break else: self._reset_fail_detection() if (not data_from_server): Log.p('RECV HANDLER: PIPELINE CLOSED BY REMOTE SERVER!') break recv_buffer.append(data_from_server) while recv_buffer: current_data = b''.join(recv_buffer)[2:] data_len = short_unpackf(recv_buffer[0])[0] if (len(current_data) == data_len): recv_buffer = [] elif (len(current_data) > data_len): recv_buffer = [current_data[data_len:]] else: break if not self.is_keepalive(current_data): self.DNSRelay.responder.add(current_data[:data_len]) self._relay_conn.sock.close()
def __fail_detection(self): if (fast_time() - self._last_sent >= FIVE_SEC and self._send_cnt >= HEARTBEAT_FAIL_LIMIT): self.mark_server_down() Log.p( f'NOTICE: fail detection | now={fast_time()}, last_sent={self._last_sent}, send_count={self._send_cnt}' )
def _register_new_socket(self, client_query=None): for secure_server in self.DNSRelay.dns_servers: if (not secure_server[self._protocol]): continue if self._tls_connect(secure_server['ip']): return True self.mark_server_down() else: Log.p('NO SECURE SERVERS AVAILABLE!') self.DNSRelay.tls_up = False
def mark_server_down(self): if (self.socket_available): self._relay_conn.sock.close() Log.p( f'NOTICE: {self._relay_conn.remote_ip} failed to respond to 3 messages. marking as down.' ) for server in self.DNSRelay.dns_servers: if (server['ip'] == self._relay_conn.remote_ip): server[self._protocol] = False
def __send_query(self, client_query): for attempt in range(2): try: self._relay_conn.sock.send(client_query.send_data) Log.console(f'SENT SECURE[{attempt}]: {client_query.request}') except OSError: if not self._register_new_socket(): break threading.Thread(target=self._recv_handler).start() else: self._increment_fail_detection() break
def _register(self, listener): '''will register interface with listener. requires subclass property for listener_sock returning valid socket object. once registration is complete the thread will exit.''' Log.console(f'REGISTERING: {self._l_ip}.') l_sock = self.listener_sock listener._registered_socks[l_sock.fileno()] = l_sock listener._epoll.register(l_sock.fileno(), select.EPOLLIN) Log.console(f'COMPLETE: {self._l_ip}.')
def _tls_connect(self, secure_server): Log.p(f'Opening Secure socket to {secure_server}: 853') sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) dns_sock = self._tls_context.wrap_socket(sock, server_hostname=secure_server) try: dns_sock.connect((secure_server, PROTO.DNS_TLS)) except OSError: return None else: return True finally: self._relay_conn = RELAY_CONN(secure_server, dns_sock)
def tls(self): if (not self.is_enabled): return TEN_SEC for secure_server in self.DNSRelay.dns_servers: if (secure_server[self._protocol]): continue # not checking if server/proto is known up if self._tls_reachable(secure_server['ip']): secure_server[self._protocol] = True self.DNSRelay.tls_up = True Log.p('NOTICE: TLS server {} has recovered.'.format( secure_server['ip'])) return THIRTY_SEC
def __init__(self, DNSRelay): '''general constructor. can only be reached through subclass. May be expanded. ''' if (self.__run is False): raise TypeError( f'{self.__class__.__name__} must be started through run class method.' ) Log.console(f'INITIALIZING: {self.__class__.__name__}') self.DNSRelay = DNSRelay self._relay_conn = RELAY_CONN(None, socket.socket()) self._send_cnt = 0 self._last_sent = 0 threading.Thread(target=self.__fail_detection).start() threading.Thread(target=self.relay).start()
def run(cls, listening_addresses): Log.console('INITIALIZING: Primary service.') # initializing dns cache/ sending in reference to needed methods for top domains cls._records_cache = DNSCache( packet=ClientRequest.generate_local_query, request_handler=cls._handle_query) # defining main epoll/ socket loop prior for register methods reference. service_loop = cls(None) # starting a registration thread for all available interfaces # upon registration the threads will exit for ip in listening_addresses: self = cls(ip) threading.Thread(target=self._register, args=(service_loop, )).start() TLSRelay.run(cls) # engaging epoll listener. local traffic processing starts here. service_loop._listener()
parser.add_argument('-v', '--verbose', help='prints output to screen', action='store_true') args = parser.parse_args(argv[1:]) l_addrs = args.ip_addrs servers = args.servers VERBOSE = args.verbose if (servers): SERVERS = tuple(servers.split(',')) if (l_addrs): LISTENING_ADDRESSES = tuple(l_addrs.split(',')) try: argument_validation() except ValueError as E: sys.stdout.err(E) os._exit(1) if (os.getuid() or DISABLED): sys.stdout.err('DNS over TLS Relay must be ran as root.') os._exit(1) display_banner() Log.setup(verbose=VERBOSE) DNSRelay.run(LISTENING_ADDRESSES)
def add(self, request, data_to_cache): '''add query to cache after calculating expiration time.''' self[request] = data_to_cache Log.p(f'CACHE ADD | NAME: {request} TTL: {data_to_cache.ttl}')