diff options
| author | Jaume Marhuenda <jaumemarhuenda@gmail.com> | 2018-06-27 23:05:12 -0700 |
|---|---|---|
| committer | Sergey Shepelev <temotor@gmail.com> | 2018-06-28 11:05:12 +0500 |
| commit | 8b2f0d23e307aa545deaa5644dc037584b8a8757 (patch) | |
| tree | a62a185d7dbed2f85f93e22ca0b5042377399a80 | |
| parent | af407c77f208ceefe5a35e39aed0cf3fdfc07cb9 (diff) | |
| download | eventlet-8b2f0d23e307aa545deaa5644dc037584b8a8757.tar.gz | |
greendns: resolving over TCP produced ValueError
| -rw-r--r-- | eventlet/support/greendns.py | 2 | ||||
| -rw-r--r-- | tests/__init__.py | 79 | ||||
| -rw-r--r-- | tests/greendns_test.py | 26 |
3 files changed, 106 insertions, 1 deletions
diff --git a/eventlet/support/greendns.py b/eventlet/support/greendns.py index 8fc3e39..84cb98c 100644 --- a/eventlet/support/greendns.py +++ b/eventlet/support/greendns.py @@ -798,7 +798,7 @@ def tcp(q, where, timeout=DNS_QUERY_TIMEOUT, port=53, _net_write(s, tcpmsg, expiration) ldata = _net_read(s, 2, expiration) (l,) = struct.unpack("!H", ldata) - wire = _net_read(s, l, expiration) + wire = bytes(_net_read(s, l, expiration)) finally: s.close() r = dns.message.from_wire(wire, keyring=q.keyring, request_mac=q.mac) diff --git a/tests/__init__.py b/tests/__init__.py index 3df192e..51b6101 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -25,6 +25,9 @@ from nose.plugins.skip import SkipTest import eventlet from eventlet import tpool import six +import socket +from threading import Thread +import struct # convenience for importers @@ -406,3 +409,79 @@ def test_run_python_pythonpath_extend(): output = run_python('', args=('-c', code), pythonpath_extend=('dira', 'dirb')) assert b'/dira\n' in output assert b'/dirb\n' in output + + +@contextlib.contextmanager +def dns_tcp_server(ip_to_give, request_count=1): + state = [0] # request count storage writable by thread + host = "localhost" + death_pill = b"DEATH_PILL" + + def extract_domain(data): + domain = b'' + kind = (data[4] >> 3) & 15 # Opcode bits + if kind == 0: # Standard query + ini = 14 + length = data[ini] + while length != 0: + domain += data[ini + 1:ini + length + 1] + b'.' + ini += length + 1 + length = data[ini] + return domain + + def answer(data, domain): + domain_length = len(domain) + packet = b'' + if domain: + # If an ip was given we return it in the answer + if ip_to_give: + packet += data[2:4] + b'\x81\x80' + packet += data[6:8] + data[6:8] + b'\x00\x00\x00\x00' # Questions and answers counts + packet += data[14: 14 + domain_length + 1] # Original domain name question + packet += b'\x00\x01\x00\x01' # Type and class + packet += b'\xc0\x0c\x00\x01' # TTL + packet += b'\x00\x01' + packet += b'\x00\x00\x00\x08' + packet += b'\x00\x04' # Resource data length -> 4 bytes + packet += bytearray(int(x) for x in ip_to_give.split(".")) + else: + packet += data[2:4] + b'\x85\x80' + packet += data[6:8] + b'\x00\x00' + b'\x00\x00\x00\x00' # Questions and answers counts + packet += data[14: 14 + domain_length + 1] # Original domain name question + packet += b'\x00\x01\x00\x01' # Type and class + + sz = struct.pack('>H', len(packet)) + return sz + packet + + def serve(server_socket): # thread target + client_sock, address = server_socket.accept() + state[0] += 1 + if state[0] <= request_count: + data = bytearray(client_sock.recv(1024)) + if data == death_pill: + client_sock.close() + return + + domain = extract_domain(data) + client_sock.sendall(answer(data, domain)) + client_sock.close() + + # Server starts + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server_socket.bind((host, 0)) + server_socket.listen(5) + server_addr = server_socket.getsockname() + + thread = Thread(target=serve, args=(server_socket, )) + thread.start() + + yield server_addr + + # Stop the server + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client.connect(server_addr) + client.send(death_pill) + client.close() + thread.join() + server_socket.close() diff --git a/tests/greendns_test.py b/tests/greendns_test.py index 7c65afb..d55ff7b 100644 --- a/tests/greendns_test.py +++ b/tests/greendns_test.py @@ -5,6 +5,7 @@ import os import socket import tempfile import time +from dns.resolver import NoAnswer, Answer, Resolver from eventlet.support import greendns from eventlet.support.greendns import dns @@ -807,6 +808,31 @@ class TestGethostbyname_ex(tests.LimitedTestCase): assert res == ('host.example.com', [], ['1.2.3.4', '1.2.3.5']) +class TinyDNSTests(tests.LimitedTestCase): + + def test_raise_dns_tcp(self): + # https://github.com/eventlet/eventlet/issues/499 + # None means we don't want the server to find the IP + with tests.dns_tcp_server(None) as dnsaddr: + resolver = Resolver() + resolver.nameservers = [dnsaddr[0]] + resolver.nameserver_ports[dnsaddr[0]] = dnsaddr[1] + + with self.assertRaises(NoAnswer): + resolver.query('host.example.com', 'a', tcp=True) + + def test_noraise_dns_tcp(self): + # https://github.com/eventlet/eventlet/issues/499 + expected_ip = "192.168.1.1" + with tests.dns_tcp_server(expected_ip) as dnsaddr: + resolver = Resolver() + resolver.nameservers = [dnsaddr[0]] + resolver.nameserver_ports[dnsaddr[0]] = dnsaddr[1] + response = resolver.query('host.example.com', 'a', tcp=True) + self.assertIsInstance(response, Answer) + self.assertEqual(response.rrset.items[0].address, expected_ip) + + def test_reverse_name(): tests.run_isolated('greendns_from_address_203.py') |
