def checkMessageProxyProtocol(self, receivedProxyPayload, source, destination, isTCP, values=[]): proxy = ProxyProtocol() self.assertTrue(proxy.parseHeader(receivedProxyPayload)) self.assertEquals(proxy.version, 0x02) self.assertEquals(proxy.command, 0x01) self.assertEquals(proxy.family, 0x01) if not isTCP: self.assertEquals(proxy.protocol, 0x02) else: self.assertEquals(proxy.protocol, 0x01) self.assertGreater(proxy.contentLen, 0) self.assertTrue(proxy.parseAddressesAndPorts(receivedProxyPayload)) self.assertEquals(proxy.source, source) self.assertEquals(proxy.destination, destination) #self.assertEquals(proxy.sourcePort, sourcePort) self.assertEquals(proxy.destinationPort, self._dnsDistPort) self.assertTrue(proxy.parseAdditionalValues(receivedProxyPayload)) proxy.values.sort() values.sort() self.assertEquals(proxy.values, values)
def ProxyProtocolUDPResponder(port, fromQueue, toQueue): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) try: sock.bind(("127.0.0.1", port)) except socket.error as e: print("Error binding in the Proxy Protocol UDP responder: %s" % str(e)) sys.exit(1) while True: data, addr = sock.recvfrom(4096) proxy = ProxyProtocol() if len(data) < proxy.HEADER_SIZE: continue if not proxy.parseHeader(data): continue if proxy.local: # likely a healthcheck data = data[proxy.HEADER_SIZE:] request = dns.message.from_wire(data) response = dns.message.make_response(request) wire = response.to_wire() sock.settimeout(2.0) sock.sendto(wire, addr) sock.settimeout(None) continue payload = data[:(proxy.HEADER_SIZE + proxy.contentLen)] dnsData = data[(proxy.HEADER_SIZE + proxy.contentLen):] toQueue.put([payload, dnsData], True, 2.0) # computing the correct ID for the response request = dns.message.from_wire(dnsData) response = fromQueue.get(True, 2.0) response.id = request.id sock.settimeout(2.0) sock.sendto(response.to_wire(), addr) sock.settimeout(None) sock.close()
def ProxyProtocolTCPResponder(port, fromQueue, toQueue): # be aware that this responder will not accept a new connection # until the last one has been closed. This is done on purpose to # to check for connection reuse, making sure that a lot of connections # are not opened in parallel. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) try: sock.bind(("127.0.0.1", port)) except socket.error as e: print("Error binding in the TCP responder: %s" % str(e)) sys.exit(1) sock.listen(100) while True: (conn, _) = sock.accept() conn.settimeout(5.0) # try to read the entire Proxy Protocol header proxy = ProxyProtocol() header = conn.recv(proxy.HEADER_SIZE) if not header: conn.close() continue if not proxy.parseHeader(header): conn.close() continue proxyContent = conn.recv(proxy.contentLen) if not proxyContent: conn.close() continue payload = header + proxyContent while True: try: data = conn.recv(2) except socket.timeout: data = None if not data: conn.close() break (datalen,) = struct.unpack("!H", data) data = conn.recv(datalen) toQueue.put([payload, data], True, 2.0) response = copy.deepcopy(fromQueue.get(True, 2.0)) if not response: conn.close() break # computing the correct ID for the response request = dns.message.from_wire(data) response.id = request.id wire = response.to_wire() conn.send(struct.pack("!H", len(wire))) conn.send(wire) conn.close() sock.close()
def handleDoHConnection(cls, config, conn, fromQueue, toQueue, trailingDataResponse, multipleResponses, callback, tlsContext, useProxyProtocol): ignoreTrailing = trailingDataResponse is True try: h2conn = h2.connection.H2Connection(config=config) h2conn.initiate_connection() conn.sendall(h2conn.data_to_send()) except ssl.SSLEOFError as e: print("Unexpected EOF: %s" % (e)) return dnsData = {} if useProxyProtocol: # try to read the entire Proxy Protocol header proxy = ProxyProtocol() header = conn.recv(proxy.HEADER_SIZE) if not header: print('unable to get header') conn.close() return if not proxy.parseHeader(header): print('unable to parse header') print(header) conn.close() return proxyContent = conn.recv(proxy.contentLen) if not proxyContent: print('unable to get content') conn.close() return payload = header + proxyContent toQueue.put(payload, True, cls._queueTimeout) # be careful, HTTP/2 headers and data might be in different recv() results requestHeaders = None while True: data = conn.recv(65535) if not data: break events = h2conn.receive_data(data) for event in events: if isinstance(event, h2.events.RequestReceived): requestHeaders = event.headers if isinstance(event, h2.events.DataReceived): h2conn.acknowledge_received_data( event.flow_controlled_length, event.stream_id) if not event.stream_id in dnsData: dnsData[event.stream_id] = b'' dnsData[event.stream_id] = dnsData[event.stream_id] + ( event.data) if event.stream_ended: forceRcode = None status = 200 try: request = dns.message.from_wire( dnsData[event.stream_id], ignore_trailing=ignoreTrailing) except dns.message.TrailingJunk as e: if trailingDataResponse is False or forceRcode is True: raise print( "DOH query with trailing data, synthesizing response" ) request = dns.message.from_wire( dnsData[event.stream_id], ignore_trailing=True) forceRcode = trailingDataResponse if callback: status, wire = callback(request, requestHeaders, fromQueue, toQueue) else: response = cls._getResponse(request, fromQueue, toQueue, synthesize=forceRcode) if response: wire = response.to_wire(max_size=65535) if not wire: conn.close() conn = None break headers = [ (':status', str(status)), ('content-length', str(len(wire))), ('content-type', 'application/dns-message'), ] h2conn.send_headers(stream_id=event.stream_id, headers=headers) h2conn.send_data(stream_id=event.stream_id, data=wire, end_stream=True) data_to_send = h2conn.data_to_send() if data_to_send: conn.sendall(data_to_send) if conn is None: break if conn is not None: conn.close()
def ProxyProtocolTCPResponder(port, fromQueue, toQueue): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) try: sock.bind(("127.0.0.1", port)) except socket.error as e: print("Error binding in the TCP responder: %s" % str(e)) sys.exit(1) sock.listen(100) while True: (conn, _) = sock.accept() conn.settimeout(5.0) # try to read the entire Proxy Protocol header proxy = ProxyProtocol() header = conn.recv(proxy.HEADER_SIZE) if not header: conn.close() continue if not proxy.parseHeader(header): conn.close() continue proxyContent = conn.recv(proxy.contentLen) if not proxyContent: conn.close() continue payload = header + proxyContent while True: try: data = conn.recv(2) except socket.timeout: data = None if not data: conn.close() break (datalen, ) = struct.unpack("!H", data) data = conn.recv(datalen) toQueue.put([payload, data], True, 2.0) response = fromQueue.get(True, 2.0) if not response: conn.close() break # computing the correct ID for the response request = dns.message.from_wire(data) response.id = request.id wire = response.to_wire() conn.send(struct.pack("!H", len(wire))) conn.send(wire) conn.close() sock.close()
def DOHResponder(cls, port, fromQueue, toQueue, trailingDataResponse=False, multipleResponses=False, callback=None, tlsContext=None, useProxyProtocol=False): # trailingDataResponse=True means "ignore trailing data". # Other values are either False (meaning "raise an exception") # or are interpreted as a response RCODE for queries with trailing data. # callback is invoked for every -even healthcheck ones- query and should return a raw response ignoreTrailing = trailingDataResponse is True sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) try: sock.bind(("127.0.0.1", port)) except socket.error as e: print("Error binding in the TCP responder: %s" % str(e)) sys.exit(1) sock.listen(100) if tlsContext: sock = tlsContext.wrap_socket(sock, server_side=True) config = h2.config.H2Configuration(client_side=False) while True: try: (conn, _) = sock.accept() except ssl.SSLError: continue except ConnectionResetError: continue conn.settimeout(5.0) h2conn = h2.connection.H2Connection(config=config) h2conn.initiate_connection() conn.sendall(h2conn.data_to_send()) dnsData = {} if useProxyProtocol: # try to read the entire Proxy Protocol header proxy = ProxyProtocol() header = conn.recv(proxy.HEADER_SIZE) if not header: print('unable to get header') conn.close() continue if not proxy.parseHeader(header): print('unable to parse header') print(header) conn.close() continue proxyContent = conn.recv(proxy.contentLen) if not proxyContent: print('unable to get content') conn.close() continue payload = header + proxyContent toQueue.put(payload, True, cls._queueTimeout) while True: data = conn.recv(65535) if not data: break events = h2conn.receive_data(data) for event in events: if isinstance(event, h2.events.DataReceived): h2conn.acknowledge_received_data( event.flow_controlled_length, event.stream_id) if not event.stream_id in dnsData: dnsData[event.stream_id] = b'' dnsData[event.stream_id] = dnsData[event.stream_id] + ( event.data) if event.stream_ended: forceRcode = None status = 200 try: request = dns.message.from_wire( dnsData[event.stream_id], ignore_trailing=ignoreTrailing) except dns.message.TrailingJunk as e: if trailingDataResponse is False or forceRcode is True: raise print( "DOH query with trailing data, synthesizing response" ) request = dns.message.from_wire( dnsData[event.stream_id], ignore_trailing=True) forceRcode = trailingDataResponse if callback: status, wire = callback(request) else: response = cls._getResponse( request, fromQueue, toQueue, synthesize=forceRcode) if response: wire = response.to_wire(max_size=65535) if not wire: conn.close() conn = None break headers = [ (':status', str(status)), ('content-length', str(len(wire))), ('content-type', 'application/dns-message'), ] h2conn.send_headers(stream_id=event.stream_id, headers=headers) h2conn.send_data(stream_id=event.stream_id, data=wire, end_stream=True) data_to_send = h2conn.data_to_send() if data_to_send: conn.sendall(data_to_send) if conn is None: break if conn is not None: conn.close() sock.close()