Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2313)

Unified Diff: Tools/Scripts/webkitpy/thirdparty/webpagereplay/dnsproxy.py

Issue 18418010: Check in the thirdparty libs needed for webkitpy. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Tools/Scripts/webkitpy/thirdparty/webpagereplay/dnsproxy.py
diff --git a/Tools/Scripts/webkitpy/thirdparty/webpagereplay/dnsproxy.py b/Tools/Scripts/webkitpy/thirdparty/webpagereplay/dnsproxy.py
new file mode 100644
index 0000000000000000000000000000000000000000..b8fe9516abf053e18d128e78e62ab7c377807e45
--- /dev/null
+++ b/Tools/Scripts/webkitpy/thirdparty/webpagereplay/dnsproxy.py
@@ -0,0 +1,228 @@
+#!/usr/bin/env python
+# Copyright 2010 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import daemonserver
+import errno
+import logging
+import socket
+import SocketServer
+import threading
+
+import third_party
+import dns.flags
+import dns.message
+import dns.rcode
+import dns.resolver
+import dns.rdatatype
+import ipaddr
+
+
+class DnsProxyException(Exception):
+ pass
+
+
+class RealDnsLookup(object):
+ def __init__(self, name_servers):
+ if '127.0.0.1' in name_servers:
+ raise DnsProxyException(
+ 'Invalid nameserver: 127.0.0.1 (causes an infinte loop)')
+ self.resolver = dns.resolver.get_default_resolver()
+ self.resolver.nameservers = name_servers
+ self.dns_cache_lock = threading.Lock()
+ self.dns_cache = {}
+
+ def __call__(self, hostname, rdtype=dns.rdatatype.A):
+ """Return real IP for a host.
+
+ Args:
+ host: a hostname ending with a period (e.g. "www.google.com.")
+ rdtype: the query type (1 for 'A', 28 for 'AAAA')
+ Returns:
+ the IP address as a string (e.g. "192.168.25.2")
+ """
+ self.dns_cache_lock.acquire()
+ ip = self.dns_cache.get(hostname)
+ self.dns_cache_lock.release()
+ if ip:
+ return ip
+ try:
+ answers = self.resolver.query(hostname, rdtype)
+ except dns.resolver.NXDOMAIN:
+ return None
+ except (dns.resolver.NoAnswer, dns.resolver.Timeout) as ex:
+ logging.debug('_real_dns_lookup(%s) -> None (%s)',
+ hostname, ex.__class__.__name__)
+ return None
+ if answers:
+ ip = str(answers[0])
+ self.dns_cache_lock.acquire()
+ self.dns_cache[hostname] = ip
+ self.dns_cache_lock.release()
+ return ip
+
+ def ClearCache(self):
+ """Clearn the dns cache."""
+ self.dns_cache_lock.acquire()
+ self.dns_cache.clear()
+ self.dns_cache_lock.release()
+
+
+class PrivateIpDnsLookup(object):
+ """Resolve private hosts to their real IPs and others to the Web proxy IP.
+
+ Hosts in the given http_archive will resolve to the Web proxy IP without
+ checking the real IP.
+
+ This only supports IPv4 lookups.
+ """
+ def __init__(self, web_proxy_ip, real_dns_lookup, http_archive):
+ """Initialize PrivateIpDnsLookup.
+
+ Args:
+ web_proxy_ip: the IP address returned by __call__ for non-private hosts.
+ real_dns_lookup: a function that resolves a host to an IP.
+ http_archive: an instance of a HttpArchive
+ Hosts is in the archive will always resolve to the web_proxy_ip
+ """
+ self.web_proxy_ip = web_proxy_ip
+ self.real_dns_lookup = real_dns_lookup
+ self.http_archive = http_archive
+ self.InitializeArchiveHosts()
+
+ def __call__(self, host):
+ """Return real IPv4 for private hosts and Web proxy IP otherwise.
+
+ Args:
+ host: a hostname ending with a period (e.g. "www.google.com.")
+ Returns:
+ IP address as a string or None (if lookup fails)
+ """
+ ip = self.web_proxy_ip
+ if host not in self.archive_hosts:
+ real_ip = self.real_dns_lookup(host)
+ if real_ip:
+ if ipaddr.IPAddress(real_ip).is_private:
+ ip = real_ip
+ else:
+ ip = None
+ return ip
+
+ def InitializeArchiveHosts(self):
+ """Recompute the archive_hosts from the http_archive."""
+ self.archive_hosts = set('%s.' % req.host for req in self.http_archive)
+
+
+class UdpDnsHandler(SocketServer.DatagramRequestHandler):
+ """Resolve DNS queries to localhost.
+
+ Possible alternative implementation:
+ http://howl.play-bow.org/pipermail/dnspython-users/2010-February/000119.html
+ """
+
+ STANDARD_QUERY_OPERATION_CODE = 0
+
+ def handle(self):
+ """Handle a DNS query.
+
+ IPv6 requests (with rdtype AAAA) receive mismatched IPv4 responses
+ (with rdtype A). To properly support IPv6, the http proxy would
+ need both types of addresses. By default, Windows XP does not
+ support IPv6.
+ """
+ self.data = self.rfile.read()
+ self.transaction_id = self.data[0]
+ self.flags = self.data[1]
+ self.qa_counts = self.data[4:6]
+ self.domain = ''
+ operation_code = (ord(self.data[2]) >> 3) & 15
+ if operation_code == self.STANDARD_QUERY_OPERATION_CODE:
+ self.wire_domain = self.data[12:]
+ self.domain = self._domain(self.wire_domain)
+ else:
+ logging.debug("DNS request with non-zero operation code: %s",
+ operation_code)
+ ip = self.server.dns_lookup(self.domain)
+ if ip is None:
+ logging.debug('dnsproxy: %s -> NXDOMAIN', self.domain)
+ response = self.get_dns_no_such_name_response()
+ else:
+ if ip == self.server.server_address[0]:
+ logging.debug('dnsproxy: %s -> %s (replay web proxy)', self.domain, ip)
+ else:
+ logging.debug('dnsproxy: %s -> %s', self.domain, ip)
+ response = self.get_dns_response(ip)
+ self.wfile.write(response)
+
+ @classmethod
+ def _domain(cls, wire_domain):
+ domain = ''
+ index = 0
+ length = ord(wire_domain[index])
+ while length:
+ domain += wire_domain[index + 1:index + length + 1] + '.'
+ index += length + 1
+ length = ord(wire_domain[index])
+ return domain
+
+ def get_dns_response(self, ip):
+ packet = ''
+ if self.domain:
+ packet = (
+ self.transaction_id +
+ self.flags +
+ '\x81\x80' + # standard query response, no error
+ self.qa_counts * 2 + '\x00\x00\x00\x00' + # Q&A counts
+ self.wire_domain +
+ '\xc0\x0c' # pointer to domain name
+ '\x00\x01' # resource record type ("A" host address)
+ '\x00\x01' # class of the data
+ '\x00\x00\x00\x3c' # ttl (seconds)
+ '\x00\x04' + # resource data length (4 bytes for ip)
+ socket.inet_aton(ip)
+ )
+ return packet
+
+ def get_dns_no_such_name_response(self):
+ query_message = dns.message.from_wire(self.data)
+ response_message = dns.message.make_response(query_message)
+ response_message.flags |= dns.flags.AA | dns.flags.RA
+ response_message.set_rcode(dns.rcode.NXDOMAIN)
+ return response_message.to_wire()
+
+class DnsProxyServer(SocketServer.ThreadingUDPServer,
+ daemonserver.DaemonServer):
+ def __init__(self, dns_lookup=None, host='', port=53):
+ """Initialize DnsProxyServer.
+
+ Args:
+ dns_lookup: a function that resolves a host to an IP address.
+ host: a host string (name or IP) to bind the dns proxy and to which
+ DNS requests will be resolved.
+ port: an integer port on which to bind the proxy.
+ """
+ try:
+ SocketServer.ThreadingUDPServer.__init__(
+ self, (host, port), UdpDnsHandler)
+ except socket.error, (error_number, msg):
+ if error_number == errno.EACCES:
+ raise DnsProxyException(
+ 'Unable to bind DNS server on (%s:%s)' % (host, port))
+ raise
+ self.dns_lookup = dns_lookup or (lambda host: self.server_address[0])
+ logging.info('Started DNS server on %s...', self.server_address)
+
+ def cleanup(self):
+ self.shutdown()
+ logging.info('Shutdown DNS server')

Powered by Google App Engine
This is Rietveld 408576698