Mercurial > emacs
comparison lisp/net/dns.el @ 86915:b48d018b85e2
Move here from ../gnus.
author | Glenn Morris <rgm@gnu.org> |
---|---|
date | Sat, 01 Dec 2007 20:58:03 +0000 |
parents | |
children | 439fa1afe05a |
comparison
equal
deleted
inserted
replaced
86914:444c508e6655 | 86915:b48d018b85e2 |
---|---|
1 ;;; dns.el --- Domain Name Service lookups | |
2 | |
3 ;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. | |
4 | |
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org> | |
6 ;; Keywords: network | |
7 | |
8 ;; This file is part of GNU Emacs. | |
9 | |
10 ;; GNU Emacs is free software; you can redistribute it and/or modify | |
11 ;; it under the terms of the GNU General Public License as published by | |
12 ;; the Free Software Foundation; either version 3, or (at your option) | |
13 ;; any later version. | |
14 | |
15 ;; GNU Emacs is distributed in the hope that it will be useful, | |
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 ;; GNU General Public License for more details. | |
19 | |
20 ;; You should have received a copy of the GNU General Public License | |
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the | |
22 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
23 ;; Boston, MA 02110-1301, USA. | |
24 | |
25 ;;; Commentary: | |
26 | |
27 ;;; Code: | |
28 | |
29 (eval-when-compile (require 'cl)) | |
30 | |
31 (require 'mm-util) | |
32 | |
33 (defvar dns-timeout 5 | |
34 "How many seconds to wait when doing DNS queries.") | |
35 | |
36 (defvar dns-servers nil | |
37 "Which DNS servers to query. | |
38 If nil, /etc/resolv.conf will be consulted.") | |
39 | |
40 ;;; Internal code: | |
41 | |
42 (defvar dns-query-types | |
43 '((A 1) | |
44 (NS 2) | |
45 (MD 3) | |
46 (MF 4) | |
47 (CNAME 5) | |
48 (SOA 6) | |
49 (MB 7) | |
50 (MG 8) | |
51 (MR 9) | |
52 (NULL 10) | |
53 (WKS 11) | |
54 (PTR 12) | |
55 (HINFO 13) | |
56 (MINFO 14) | |
57 (MX 15) | |
58 (TXT 16) | |
59 (AAAA 28) ; RFC3596 | |
60 (SRV 33) ; RFC2782 | |
61 (AXFR 252) | |
62 (MAILB 253) | |
63 (MAILA 254) | |
64 (* 255)) | |
65 "Names of query types and their values.") | |
66 | |
67 (defvar dns-classes | |
68 '((IN 1) | |
69 (CS 2) | |
70 (CH 3) | |
71 (HS 4)) | |
72 "Classes of queries.") | |
73 | |
74 (defun dns-write-bytes (value &optional length) | |
75 (let (bytes) | |
76 (dotimes (i (or length 1)) | |
77 (push (% value 256) bytes) | |
78 (setq value (/ value 256))) | |
79 (dolist (byte bytes) | |
80 (insert byte)))) | |
81 | |
82 (defun dns-read-bytes (length) | |
83 (let ((value 0)) | |
84 (dotimes (i length) | |
85 (setq value (logior (* value 256) (following-char))) | |
86 (forward-char 1)) | |
87 value)) | |
88 | |
89 (defun dns-get (type spec) | |
90 (cadr (assq type spec))) | |
91 | |
92 (defun dns-inverse-get (value spec) | |
93 (let ((found nil)) | |
94 (while (and (not found) | |
95 spec) | |
96 (if (eq value (cadr (car spec))) | |
97 (setq found (caar spec)) | |
98 (pop spec))) | |
99 found)) | |
100 | |
101 (defun dns-write-name (name) | |
102 (dolist (part (split-string name "\\.")) | |
103 (dns-write-bytes (length part)) | |
104 (insert part)) | |
105 (dns-write-bytes 0)) | |
106 | |
107 (defun dns-read-string-name (string buffer) | |
108 (mm-with-unibyte-buffer | |
109 (insert string) | |
110 (goto-char (point-min)) | |
111 (dns-read-name buffer))) | |
112 | |
113 (defun dns-read-name (&optional buffer) | |
114 (let ((ended nil) | |
115 (name nil) | |
116 length) | |
117 (while (not ended) | |
118 (setq length (dns-read-bytes 1)) | |
119 (if (= 192 (logand length (lsh 3 6))) | |
120 (let ((offset (+ (* (logand 63 length) 256) | |
121 (dns-read-bytes 1)))) | |
122 (save-excursion | |
123 (when buffer | |
124 (set-buffer buffer)) | |
125 (goto-char (1+ offset)) | |
126 (setq ended (dns-read-name buffer)))) | |
127 (if (zerop length) | |
128 (setq ended t) | |
129 (push (buffer-substring (point) | |
130 (progn (forward-char length) (point))) | |
131 name)))) | |
132 (if (stringp ended) | |
133 (if (null name) | |
134 ended | |
135 (concat (mapconcat 'identity (nreverse name) ".") "." ended)) | |
136 (mapconcat 'identity (nreverse name) ".")))) | |
137 | |
138 (defun dns-write (spec &optional tcp-p) | |
139 "Write a DNS packet according to SPEC. | |
140 If TCP-P, the first two bytes of the package with be the length field." | |
141 (with-temp-buffer | |
142 (dns-write-bytes (dns-get 'id spec) 2) | |
143 (dns-write-bytes | |
144 (logior | |
145 (lsh (if (dns-get 'response-p spec) 1 0) -7) | |
146 (lsh | |
147 (cond | |
148 ((eq (dns-get 'opcode spec) 'query) 0) | |
149 ((eq (dns-get 'opcode spec) 'inverse-query) 1) | |
150 ((eq (dns-get 'opcode spec) 'status) 2) | |
151 (t (error "No such opcode: %s" (dns-get 'opcode spec)))) | |
152 -3) | |
153 (lsh (if (dns-get 'authoritative-p spec) 1 0) -2) | |
154 (lsh (if (dns-get 'truncated-p spec) 1 0) -1) | |
155 (lsh (if (dns-get 'recursion-desired-p spec) 1 0) 0))) | |
156 (dns-write-bytes | |
157 (cond | |
158 ((eq (dns-get 'response-code spec) 'no-error) 0) | |
159 ((eq (dns-get 'response-code spec) 'format-error) 1) | |
160 ((eq (dns-get 'response-code spec) 'server-failure) 2) | |
161 ((eq (dns-get 'response-code spec) 'name-error) 3) | |
162 ((eq (dns-get 'response-code spec) 'not-implemented) 4) | |
163 ((eq (dns-get 'response-code spec) 'refused) 5) | |
164 (t 0))) | |
165 (dns-write-bytes (length (dns-get 'queries spec)) 2) | |
166 (dns-write-bytes (length (dns-get 'answers spec)) 2) | |
167 (dns-write-bytes (length (dns-get 'authorities spec)) 2) | |
168 (dns-write-bytes (length (dns-get 'additionals spec)) 2) | |
169 (dolist (query (dns-get 'queries spec)) | |
170 (dns-write-name (car query)) | |
171 (dns-write-bytes (cadr (assq (or (dns-get 'type query) 'A) | |
172 dns-query-types)) 2) | |
173 (dns-write-bytes (cadr (assq (or (dns-get 'class query) 'IN) | |
174 dns-classes)) 2)) | |
175 (dolist (slot '(answers authorities additionals)) | |
176 (dolist (resource (dns-get slot spec)) | |
177 (dns-write-name (car resource)) | |
178 (dns-write-bytes (cadr (assq (dns-get 'type resource) dns-query-types)) | |
179 2) | |
180 (dns-write-bytes (cadr (assq (dns-get 'class resource) dns-classes)) | |
181 2) | |
182 (dns-write-bytes (dns-get 'ttl resource) 4) | |
183 (dns-write-bytes (length (dns-get 'data resource)) 2) | |
184 (insert (dns-get 'data resource)))) | |
185 (when tcp-p | |
186 (goto-char (point-min)) | |
187 (dns-write-bytes (buffer-size) 2)) | |
188 (buffer-string))) | |
189 | |
190 (defun dns-read (packet) | |
191 (mm-with-unibyte-buffer | |
192 (let ((spec nil) | |
193 queries answers authorities additionals) | |
194 (insert packet) | |
195 (goto-char (point-min)) | |
196 (push (list 'id (dns-read-bytes 2)) spec) | |
197 (let ((byte (dns-read-bytes 1))) | |
198 (push (list 'response-p (if (zerop (logand byte (lsh 1 7))) nil t)) | |
199 spec) | |
200 (let ((opcode (logand byte (lsh 7 3)))) | |
201 (push (list 'opcode | |
202 (cond ((eq opcode 0) 'query) | |
203 ((eq opcode 1) 'inverse-query) | |
204 ((eq opcode 2) 'status))) | |
205 spec)) | |
206 (push (list 'authoritative-p (if (zerop (logand byte (lsh 1 2))) | |
207 nil t)) spec) | |
208 (push (list 'truncated-p (if (zerop (logand byte (lsh 1 2))) nil t)) | |
209 spec) | |
210 (push (list 'recursion-desired-p | |
211 (if (zerop (logand byte (lsh 1 0))) nil t)) spec)) | |
212 (let ((rc (logand (dns-read-bytes 1) 15))) | |
213 (push (list 'response-code | |
214 (cond | |
215 ((eq rc 0) 'no-error) | |
216 ((eq rc 1) 'format-error) | |
217 ((eq rc 2) 'server-failure) | |
218 ((eq rc 3) 'name-error) | |
219 ((eq rc 4) 'not-implemented) | |
220 ((eq rc 5) 'refused))) | |
221 spec)) | |
222 (setq queries (dns-read-bytes 2)) | |
223 (setq answers (dns-read-bytes 2)) | |
224 (setq authorities (dns-read-bytes 2)) | |
225 (setq additionals (dns-read-bytes 2)) | |
226 (let ((qs nil)) | |
227 (dotimes (i queries) | |
228 (push (list (dns-read-name) | |
229 (list 'type (dns-inverse-get (dns-read-bytes 2) | |
230 dns-query-types)) | |
231 (list 'class (dns-inverse-get (dns-read-bytes 2) | |
232 dns-classes))) | |
233 qs)) | |
234 (push (list 'queries qs) spec)) | |
235 (dolist (slot '(answers authorities additionals)) | |
236 (let ((qs nil) | |
237 type) | |
238 (dotimes (i (symbol-value slot)) | |
239 (push (list (dns-read-name) | |
240 (list 'type | |
241 (setq type (dns-inverse-get (dns-read-bytes 2) | |
242 dns-query-types))) | |
243 (list 'class (dns-inverse-get (dns-read-bytes 2) | |
244 dns-classes)) | |
245 (list 'ttl (dns-read-bytes 4)) | |
246 (let ((length (dns-read-bytes 2))) | |
247 (list 'data | |
248 (dns-read-type | |
249 (buffer-substring | |
250 (point) | |
251 (progn (forward-char length) (point))) | |
252 type)))) | |
253 qs)) | |
254 (push (list slot qs) spec))) | |
255 (nreverse spec)))) | |
256 | |
257 (defun dns-read-int32 () | |
258 ;; Full 32 bit Integers can't be handled by Emacs. If we use | |
259 ;; floats, it works. | |
260 (format "%.0f" (+ (* (dns-read-bytes 1) 16777216.0) | |
261 (dns-read-bytes 3)))) | |
262 | |
263 (defun dns-read-type (string type) | |
264 (let ((buffer (current-buffer)) | |
265 (point (point))) | |
266 (prog1 | |
267 (mm-with-unibyte-buffer | |
268 (insert string) | |
269 (goto-char (point-min)) | |
270 (cond | |
271 ((eq type 'A) | |
272 (let ((bytes nil)) | |
273 (dotimes (i 4) | |
274 (push (dns-read-bytes 1) bytes)) | |
275 (mapconcat 'number-to-string (nreverse bytes) "."))) | |
276 ((eq type 'AAAA) | |
277 (let (hextets) | |
278 (dotimes (i 8) | |
279 (push (dns-read-bytes 2) hextets)) | |
280 (mapconcat (lambda (n) (format "%x" n)) (nreverse hextets) ":"))) | |
281 ((eq type 'SOA) | |
282 (list (list 'mname (dns-read-name buffer)) | |
283 (list 'rname (dns-read-name buffer)) | |
284 (list 'serial (dns-read-int32)) | |
285 (list 'refresh (dns-read-int32)) | |
286 (list 'retry (dns-read-int32)) | |
287 (list 'expire (dns-read-int32)) | |
288 (list 'minimum (dns-read-int32)))) | |
289 ((eq type 'SRV) | |
290 (list (list 'priority (dns-read-bytes 2)) | |
291 (list 'weight (dns-read-bytes 2)) | |
292 (list 'port (dns-read-bytes 2)) | |
293 (list 'target (dns-read-name buffer)))) | |
294 ((eq type 'MX) | |
295 (cons (dns-read-bytes 2) (dns-read-name buffer))) | |
296 ((or (eq type 'CNAME) (eq type 'NS) (eq type 'PTR)) | |
297 (dns-read-string-name string buffer)) | |
298 (t string))) | |
299 (goto-char point)))) | |
300 | |
301 (defun dns-parse-resolv-conf () | |
302 (when (file-exists-p "/etc/resolv.conf") | |
303 (with-temp-buffer | |
304 (insert-file-contents "/etc/resolv.conf") | |
305 (goto-char (point-min)) | |
306 (while (re-search-forward "^nameserver[\t ]+\\([^ \t\n]+\\)" nil t) | |
307 (push (match-string 1) dns-servers)) | |
308 (setq dns-servers (nreverse dns-servers))))) | |
309 | |
310 (defun dns-read-txt (string) | |
311 (if (> (length string) 1) | |
312 (substring string 1) | |
313 string)) | |
314 | |
315 (defun dns-get-txt-answer (answers) | |
316 (let ((result "") | |
317 (do-next nil)) | |
318 (dolist (answer answers) | |
319 (dolist (elem answer) | |
320 (when (consp elem) | |
321 (cond | |
322 ((eq (car elem) 'type) | |
323 (setq do-next (eq (cadr elem) 'TXT))) | |
324 ((eq (car elem) 'data) | |
325 (when do-next | |
326 (setq result (concat result (dns-read-txt (cadr elem)))))))))) | |
327 result)) | |
328 | |
329 ;;; Interface functions. | |
330 (defmacro dns-make-network-process (server) | |
331 (if (featurep 'xemacs) | |
332 `(let ((coding-system-for-read 'binary) | |
333 (coding-system-for-write 'binary)) | |
334 (open-network-stream "dns" (current-buffer) | |
335 ,server "domain" 'udp)) | |
336 `(let ((server ,server) | |
337 (coding-system-for-read 'binary) | |
338 (coding-system-for-write 'binary)) | |
339 (if (fboundp 'make-network-process) | |
340 (make-network-process | |
341 :name "dns" | |
342 :coding 'binary | |
343 :buffer (current-buffer) | |
344 :host server | |
345 :service "domain" | |
346 :type 'datagram) | |
347 ;; Older versions of Emacs doesn't have | |
348 ;; `make-network-process', so we fall back on opening a TCP | |
349 ;; connection to the DNS server. | |
350 (open-network-stream "dns" (current-buffer) server "domain"))))) | |
351 | |
352 (defvar dns-cache (make-vector 4096 0)) | |
353 | |
354 (defun query-dns-cached (name &optional type fullp reversep) | |
355 (let* ((key (format "%s:%s:%s:%s" name type fullp reversep)) | |
356 (sym (intern-soft key dns-cache))) | |
357 (if (and sym | |
358 (boundp sym)) | |
359 (symbol-value sym) | |
360 (let ((result (query-dns name type fullp reversep))) | |
361 (set (intern key dns-cache) result) | |
362 result)))) | |
363 | |
364 (defun query-dns (name &optional type fullp reversep) | |
365 "Query a DNS server for NAME of TYPE. | |
366 If FULLP, return the entire record returned. | |
367 If REVERSEP, look up an IP address." | |
368 (setq type (or type 'A)) | |
369 (unless dns-servers | |
370 (dns-parse-resolv-conf)) | |
371 | |
372 (when reversep | |
373 (setq name (concat | |
374 (mapconcat 'identity (nreverse (split-string name "\\.")) ".") | |
375 ".in-addr.arpa") | |
376 type 'PTR)) | |
377 | |
378 (if (not dns-servers) | |
379 (message "No DNS server configuration found") | |
380 (mm-with-unibyte-buffer | |
381 (let ((process (condition-case () | |
382 (dns-make-network-process (car dns-servers)) | |
383 (error | |
384 (message "dns: Got an error while trying to talk to %s" | |
385 (car dns-servers)) | |
386 nil))) | |
387 (tcp-p (and (not (fboundp 'make-network-process)) | |
388 (not (featurep 'xemacs)))) | |
389 (step 100) | |
390 (times (* dns-timeout 1000)) | |
391 (id (random 65000))) | |
392 (when process | |
393 (process-send-string | |
394 process | |
395 (dns-write `((id ,id) | |
396 (opcode query) | |
397 (queries ((,name (type ,type)))) | |
398 (recursion-desired-p t)) | |
399 tcp-p)) | |
400 (while (and (zerop (buffer-size)) | |
401 (> times 0)) | |
402 (sit-for (/ step 1000.0)) | |
403 (accept-process-output process 0 step) | |
404 (decf times step)) | |
405 (ignore-errors | |
406 (delete-process process)) | |
407 (when (and tcp-p | |
408 (>= (buffer-size) 2)) | |
409 (goto-char (point-min)) | |
410 (delete-region (point) (+ (point) 2))) | |
411 (when (and (>= (buffer-size) 2) | |
412 ;; We had a time-out. | |
413 (> times 0)) | |
414 (let ((result (dns-read (buffer-string)))) | |
415 (if fullp | |
416 result | |
417 (let ((answer (car (dns-get 'answers result)))) | |
418 (when (eq type (dns-get 'type answer)) | |
419 (if (eq type 'TXT) | |
420 (dns-get-txt-answer (dns-get 'answers result)) | |
421 (dns-get 'data answer)))))))))))) | |
422 | |
423 (provide 'dns) | |
424 | |
425 ;;; arch-tag: d0edd0c4-4cce-4538-ae92-06c3356ee80a | |
426 ;;; dns.el ends here |