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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2010 Google Inc. All Rights Reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 import daemonserver
17 import errno
18 import logging
19 import socket
20 import SocketServer
21 import threading
22
23 import third_party
24 import dns.flags
25 import dns.message
26 import dns.rcode
27 import dns.resolver
28 import dns.rdatatype
29 import ipaddr
30
31
32 class DnsProxyException(Exception):
33 pass
34
35
36 class RealDnsLookup(object):
37 def __init__(self, name_servers):
38 if '127.0.0.1' in name_servers:
39 raise DnsProxyException(
40 'Invalid nameserver: 127.0.0.1 (causes an infinte loop)')
41 self.resolver = dns.resolver.get_default_resolver()
42 self.resolver.nameservers = name_servers
43 self.dns_cache_lock = threading.Lock()
44 self.dns_cache = {}
45
46 def __call__(self, hostname, rdtype=dns.rdatatype.A):
47 """Return real IP for a host.
48
49 Args:
50 host: a hostname ending with a period (e.g. "www.google.com.")
51 rdtype: the query type (1 for 'A', 28 for 'AAAA')
52 Returns:
53 the IP address as a string (e.g. "192.168.25.2")
54 """
55 self.dns_cache_lock.acquire()
56 ip = self.dns_cache.get(hostname)
57 self.dns_cache_lock.release()
58 if ip:
59 return ip
60 try:
61 answers = self.resolver.query(hostname, rdtype)
62 except dns.resolver.NXDOMAIN:
63 return None
64 except (dns.resolver.NoAnswer, dns.resolver.Timeout) as ex:
65 logging.debug('_real_dns_lookup(%s) -> None (%s)',
66 hostname, ex.__class__.__name__)
67 return None
68 if answers:
69 ip = str(answers[0])
70 self.dns_cache_lock.acquire()
71 self.dns_cache[hostname] = ip
72 self.dns_cache_lock.release()
73 return ip
74
75 def ClearCache(self):
76 """Clearn the dns cache."""
77 self.dns_cache_lock.acquire()
78 self.dns_cache.clear()
79 self.dns_cache_lock.release()
80
81
82 class PrivateIpDnsLookup(object):
83 """Resolve private hosts to their real IPs and others to the Web proxy IP.
84
85 Hosts in the given http_archive will resolve to the Web proxy IP without
86 checking the real IP.
87
88 This only supports IPv4 lookups.
89 """
90 def __init__(self, web_proxy_ip, real_dns_lookup, http_archive):
91 """Initialize PrivateIpDnsLookup.
92
93 Args:
94 web_proxy_ip: the IP address returned by __call__ for non-private hosts.
95 real_dns_lookup: a function that resolves a host to an IP.
96 http_archive: an instance of a HttpArchive
97 Hosts is in the archive will always resolve to the web_proxy_ip
98 """
99 self.web_proxy_ip = web_proxy_ip
100 self.real_dns_lookup = real_dns_lookup
101 self.http_archive = http_archive
102 self.InitializeArchiveHosts()
103
104 def __call__(self, host):
105 """Return real IPv4 for private hosts and Web proxy IP otherwise.
106
107 Args:
108 host: a hostname ending with a period (e.g. "www.google.com.")
109 Returns:
110 IP address as a string or None (if lookup fails)
111 """
112 ip = self.web_proxy_ip
113 if host not in self.archive_hosts:
114 real_ip = self.real_dns_lookup(host)
115 if real_ip:
116 if ipaddr.IPAddress(real_ip).is_private:
117 ip = real_ip
118 else:
119 ip = None
120 return ip
121
122 def InitializeArchiveHosts(self):
123 """Recompute the archive_hosts from the http_archive."""
124 self.archive_hosts = set('%s.' % req.host for req in self.http_archive)
125
126
127 class UdpDnsHandler(SocketServer.DatagramRequestHandler):
128 """Resolve DNS queries to localhost.
129
130 Possible alternative implementation:
131 http://howl.play-bow.org/pipermail/dnspython-users/2010-February/000119.html
132 """
133
134 STANDARD_QUERY_OPERATION_CODE = 0
135
136 def handle(self):
137 """Handle a DNS query.
138
139 IPv6 requests (with rdtype AAAA) receive mismatched IPv4 responses
140 (with rdtype A). To properly support IPv6, the http proxy would
141 need both types of addresses. By default, Windows XP does not
142 support IPv6.
143 """
144 self.data = self.rfile.read()
145 self.transaction_id = self.data[0]
146 self.flags = self.data[1]
147 self.qa_counts = self.data[4:6]
148 self.domain = ''
149 operation_code = (ord(self.data[2]) >> 3) & 15
150 if operation_code == self.STANDARD_QUERY_OPERATION_CODE:
151 self.wire_domain = self.data[12:]
152 self.domain = self._domain(self.wire_domain)
153 else:
154 logging.debug("DNS request with non-zero operation code: %s",
155 operation_code)
156 ip = self.server.dns_lookup(self.domain)
157 if ip is None:
158 logging.debug('dnsproxy: %s -> NXDOMAIN', self.domain)
159 response = self.get_dns_no_such_name_response()
160 else:
161 if ip == self.server.server_address[0]:
162 logging.debug('dnsproxy: %s -> %s (replay web proxy)', self.domain, ip)
163 else:
164 logging.debug('dnsproxy: %s -> %s', self.domain, ip)
165 response = self.get_dns_response(ip)
166 self.wfile.write(response)
167
168 @classmethod
169 def _domain(cls, wire_domain):
170 domain = ''
171 index = 0
172 length = ord(wire_domain[index])
173 while length:
174 domain += wire_domain[index + 1:index + length + 1] + '.'
175 index += length + 1
176 length = ord(wire_domain[index])
177 return domain
178
179 def get_dns_response(self, ip):
180 packet = ''
181 if self.domain:
182 packet = (
183 self.transaction_id +
184 self.flags +
185 '\x81\x80' + # standard query response, no error
186 self.qa_counts * 2 + '\x00\x00\x00\x00' + # Q&A counts
187 self.wire_domain +
188 '\xc0\x0c' # pointer to domain name
189 '\x00\x01' # resource record type ("A" host address)
190 '\x00\x01' # class of the data
191 '\x00\x00\x00\x3c' # ttl (seconds)
192 '\x00\x04' + # resource data length (4 bytes for ip)
193 socket.inet_aton(ip)
194 )
195 return packet
196
197 def get_dns_no_such_name_response(self):
198 query_message = dns.message.from_wire(self.data)
199 response_message = dns.message.make_response(query_message)
200 response_message.flags |= dns.flags.AA | dns.flags.RA
201 response_message.set_rcode(dns.rcode.NXDOMAIN)
202 return response_message.to_wire()
203
204 class DnsProxyServer(SocketServer.ThreadingUDPServer,
205 daemonserver.DaemonServer):
206 def __init__(self, dns_lookup=None, host='', port=53):
207 """Initialize DnsProxyServer.
208
209 Args:
210 dns_lookup: a function that resolves a host to an IP address.
211 host: a host string (name or IP) to bind the dns proxy and to which
212 DNS requests will be resolved.
213 port: an integer port on which to bind the proxy.
214 """
215 try:
216 SocketServer.ThreadingUDPServer.__init__(
217 self, (host, port), UdpDnsHandler)
218 except socket.error, (error_number, msg):
219 if error_number == errno.EACCES:
220 raise DnsProxyException(
221 'Unable to bind DNS server on (%s:%s)' % (host, port))
222 raise
223 self.dns_lookup = dns_lookup or (lambda host: self.server_address[0])
224 logging.info('Started DNS server on %s...', self.server_address)
225
226 def cleanup(self):
227 self.shutdown()
228 logging.info('Shutdown DNS server')
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698