Merge branch 'master' of https://git.gnus.org/gnus
[gnus] / lisp / dns.el
index 9f8776e..2d4c2d8 100644 (file)
@@ -1,27 +1,25 @@
 ;;; dns.el --- Domain Name Service lookups
 
-;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007
+;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
 ;;   Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
-;; Keywords: network
+;; Keywords: network comm
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -31,8 +29,8 @@
   "How many seconds to wait when doing DNS queries.")
 
 (defvar dns-servers nil
-  "Which DNS servers to query.
-If nil, /etc/resolv.conf will be consulted.")
+  "List of DNS servers to query.
+If nil, /etc/resolv.conf and nslookup will be consulted.")
 
 ;;; Internal code:
 
@@ -102,11 +100,11 @@ If nil, /etc/resolv.conf will be consulted.")
   (dns-write-bytes 0))
 
 (defun dns-read-string-name (string buffer)
-  (let (default-enable-multibyte-characters)
-    (with-temp-buffer
-      (insert string)
-      (goto-char (point-min))
-      (dns-read-name buffer))))
+  (with-temp-buffer
+    (unless (featurep 'xemacs) (set-buffer-multibyte nil))
+    (insert string)
+    (goto-char (point-min))
+    (dns-read-name buffer)))
 
 (defun dns-read-name (&optional buffer)
   (let ((ended nil)
@@ -137,6 +135,7 @@ If nil, /etc/resolv.conf will be consulted.")
   "Write a DNS packet according to SPEC.
 If TCP-P, the first two bytes of the package with be the length field."
   (with-temp-buffer
+    (unless (featurep 'xemacs) (set-buffer-multibyte nil))
     (dns-write-bytes (dns-get 'id spec) 2)
     (dns-write-bytes
      (logior
@@ -152,7 +151,7 @@ If TCP-P, the first two bytes of the package with be the length field."
       (lsh (if (dns-get 'truncated-p spec) 1 0) -1)
       (lsh (if (dns-get 'recursion-desired-p spec) 1 0) 0)))
     (dns-write-bytes
-     (cond 
+     (cond
       ((eq (dns-get 'response-code spec) 'no-error) 0)
       ((eq (dns-get 'response-code spec) 'format-error) 1)
       ((eq (dns-get 'response-code spec) 'server-failure) 2)
@@ -186,72 +185,72 @@ If TCP-P, the first two bytes of the package with be the length field."
     (buffer-string)))
 
 (defun dns-read (packet)
-  (let (default-enable-multibyte-characters)
-    (with-temp-buffer
-      (let ((spec nil)
-            queries answers authorities additionals)
-        (insert packet)
-        (goto-char (point-min))
-        (push (list 'id (dns-read-bytes 2)) spec)
-        (let ((byte (dns-read-bytes 1)))
-          (push (list 'response-p (if (zerop (logand byte (lsh 1 7))) nil t))
-                spec)
-          (let ((opcode (logand byte (lsh 7 3))))
-            (push (list 'opcode
-                        (cond ((eq opcode 0) 'query)
-                              ((eq opcode 1) 'inverse-query)
-                              ((eq opcode 2) 'status)))
-                  spec))
-          (push (list 'authoritative-p (if (zerop (logand byte (lsh 1 2)))
-                                           nil t)) spec)
-          (push (list 'truncated-p (if (zerop (logand byte (lsh 1 2))) nil t))
-                spec)
-          (push (list 'recursion-desired-p
-                      (if (zerop (logand byte (lsh 1 0))) nil t)) spec))
-        (let ((rc (logand (dns-read-bytes 1) 15)))
-          (push (list 'response-code
-                      (cond
-                       ((eq rc 0) 'no-error)
-                       ((eq rc 1) 'format-error)
-                       ((eq rc 2) 'server-failure)
-                       ((eq rc 3) 'name-error)
-                       ((eq rc 4) 'not-implemented)
-                       ((eq rc 5) 'refused)))
+  (with-temp-buffer
+    (unless (featurep 'xemacs) (set-buffer-multibyte nil))
+    (let ((spec nil)
+          queries answers authorities additionals)
+      (insert packet)
+      (goto-char (point-min))
+      (push (list 'id (dns-read-bytes 2)) spec)
+      (let ((byte (dns-read-bytes 1)))
+        (push (list 'response-p (if (zerop (logand byte (lsh 1 7))) nil t))
+              spec)
+        (let ((opcode (logand byte (lsh 7 3))))
+          (push (list 'opcode
+                      (cond ((eq opcode 0) 'query)
+                            ((eq opcode 1) 'inverse-query)
+                            ((eq opcode 2) 'status)))
                 spec))
-        (setq queries (dns-read-bytes 2))
-        (setq answers (dns-read-bytes 2))
-        (setq authorities (dns-read-bytes 2))
-        (setq additionals (dns-read-bytes 2))
-        (let ((qs nil))
-          (dotimes (i queries)
+        (push (list 'authoritative-p (if (zerop (logand byte (lsh 1 2)))
+                                         nil t)) spec)
+        (push (list 'truncated-p (if (zerop (logand byte (lsh 1 2))) nil t))
+              spec)
+        (push (list 'recursion-desired-p
+                    (if (zerop (logand byte (lsh 1 0))) nil t)) spec))
+      (let ((rc (logand (dns-read-bytes 1) 15)))
+        (push (list 'response-code
+                    (cond
+                     ((eq rc 0) 'no-error)
+                     ((eq rc 1) 'format-error)
+                     ((eq rc 2) 'server-failure)
+                     ((eq rc 3) 'name-error)
+                     ((eq rc 4) 'not-implemented)
+                     ((eq rc 5) 'refused)))
+              spec))
+      (setq queries (dns-read-bytes 2))
+      (setq answers (dns-read-bytes 2))
+      (setq authorities (dns-read-bytes 2))
+      (setq additionals (dns-read-bytes 2))
+      (let ((qs nil))
+        (dotimes (i queries)
+          (push (list (dns-read-name)
+                      (list 'type (dns-inverse-get (dns-read-bytes 2)
+                                                   dns-query-types))
+                      (list 'class (dns-inverse-get (dns-read-bytes 2)
+                                                    dns-classes)))
+                qs))
+        (push (list 'queries qs) spec))
+      (dolist (slot '(answers authorities additionals))
+        (let ((qs nil)
+              type)
+          (dotimes (i (symbol-value slot))
             (push (list (dns-read-name)
-                        (list 'type (dns-inverse-get (dns-read-bytes 2)
-                                                     dns-query-types))
+                        (list 'type
+                              (setq type (dns-inverse-get (dns-read-bytes 2)
+                                                          dns-query-types)))
                         (list 'class (dns-inverse-get (dns-read-bytes 2)
-                                                      dns-classes)))
+                                                      dns-classes))
+                        (list 'ttl (dns-read-bytes 4))
+                        (let ((length (dns-read-bytes 2)))
+                          (list 'data
+                                (dns-read-type
+                                 (buffer-substring
+                                  (point)
+                                  (progn (forward-char length) (point)))
+                                 type))))
                   qs))
-          (push (list 'queries qs) spec))
-        (dolist (slot '(answers authorities additionals))
-          (let ((qs nil)
-                type)
-            (dotimes (i (symbol-value slot))
-              (push (list (dns-read-name)
-                          (list 'type
-                                (setq type (dns-inverse-get (dns-read-bytes 2)
-                                                            dns-query-types)))
-                          (list 'class (dns-inverse-get (dns-read-bytes 2)
-                                                        dns-classes))
-                          (list 'ttl (dns-read-bytes 4))
-                          (let ((length (dns-read-bytes 2)))
-                            (list 'data
-                                  (dns-read-type
-                                   (buffer-substring
-                                    (point)
-                                    (progn (forward-char length) (point)))
-                                   type))))
-                    qs))
-            (push (list slot qs) spec)))
-        (nreverse spec)))))
+          (push (list slot qs) spec)))
+      (nreverse spec))))
 
 (defun dns-read-int32 ()
   ;; Full 32 bit Integers can't be handled by Emacs.  If we use
@@ -263,50 +262,60 @@ If TCP-P, the first two bytes of the package with be the length field."
   (let ((buffer (current-buffer))
        (point (point)))
     (prog1
-        (let (default-enable-multibyte-characters)
-          (with-temp-buffer
-            (insert string)
-            (goto-char (point-min))
-            (cond
-             ((eq type 'A)
-              (let ((bytes nil))
-                (dotimes (i 4)
-                  (push (dns-read-bytes 1) bytes))
-                (mapconcat 'number-to-string (nreverse bytes) ".")))
-             ((eq type 'AAAA)
-              (let (hextets)
-                (dotimes (i 8)
-                  (push (dns-read-bytes 2) hextets))
-                (mapconcat (lambda (n) (format "%x" n))
-                           (nreverse hextets) ":")))
-             ((eq type 'SOA)
-              (list (list 'mname (dns-read-name buffer))
-                    (list 'rname (dns-read-name buffer))
-                    (list 'serial (dns-read-int32))
-                    (list 'refresh (dns-read-int32))
-                    (list 'retry (dns-read-int32))
-                    (list 'expire (dns-read-int32))
-                    (list 'minimum (dns-read-int32))))
-             ((eq type 'SRV)
-              (list (list 'priority (dns-read-bytes 2))
-                    (list 'weight (dns-read-bytes 2))
-                    (list 'port (dns-read-bytes 2))
-                    (list 'target (dns-read-name buffer))))
-             ((eq type 'MX)
-              (cons (dns-read-bytes 2) (dns-read-name buffer)))
-             ((or (eq type 'CNAME) (eq type 'NS) (eq type 'PTR))
-              (dns-read-string-name string buffer))
-             (t string))))
+        (with-temp-buffer
+         (unless (featurep 'xemacs) (set-buffer-multibyte nil))
+          (insert string)
+          (goto-char (point-min))
+          (cond
+           ((eq type 'A)
+            (let ((bytes nil))
+              (dotimes (i 4)
+                (push (dns-read-bytes 1) bytes))
+              (mapconcat 'number-to-string (nreverse bytes) ".")))
+           ((eq type 'AAAA)
+            (let (hextets)
+              (dotimes (i 8)
+                (push (dns-read-bytes 2) hextets))
+              (mapconcat (lambda (n) (format "%x" n))
+                         (nreverse hextets) ":")))
+           ((eq type 'SOA)
+            (list (list 'mname (dns-read-name buffer))
+                  (list 'rname (dns-read-name buffer))
+                  (list 'serial (dns-read-int32))
+                  (list 'refresh (dns-read-int32))
+                  (list 'retry (dns-read-int32))
+                  (list 'expire (dns-read-int32))
+                  (list 'minimum (dns-read-int32))))
+           ((eq type 'SRV)
+            (list (list 'priority (dns-read-bytes 2))
+                  (list 'weight (dns-read-bytes 2))
+                  (list 'port (dns-read-bytes 2))
+                  (list 'target (dns-read-name buffer))))
+           ((eq type 'MX)
+            (cons (dns-read-bytes 2) (dns-read-name buffer)))
+           ((or (eq type 'CNAME) (eq type 'NS) (eq type 'PTR))
+            (dns-read-string-name string buffer))
+           (t string)))
       (goto-char point))))
 
-(defun dns-parse-resolv-conf ()
-  (when (file-exists-p "/etc/resolv.conf")
-    (with-temp-buffer
-      (insert-file-contents "/etc/resolv.conf")
-      (goto-char (point-min))
-      (while (re-search-forward "^nameserver[\t ]+\\([^ \t\n]+\\)" nil t)
-       (push (match-string 1) dns-servers))
-      (setq dns-servers (nreverse dns-servers)))))
+(defun dns-set-servers ()
+  "Set `dns-servers' to a list of DNS servers or nil if none are found.
+Parses \"/etc/resolv.conf\" or calls \"nslookup\"."
+  (or (when (file-exists-p "/etc/resolv.conf")
+       (setq dns-servers nil)
+       (with-temp-buffer
+         (insert-file-contents "/etc/resolv.conf")
+         (goto-char (point-min))
+         (while (re-search-forward "^nameserver[\t ]+\\([^ \t\n]+\\)" nil t)
+           (push (match-string 1) dns-servers))
+         (setq dns-servers (nreverse dns-servers))))
+      (when (executable-find "nslookup")
+       (with-temp-buffer
+         (call-process "nslookup" nil t nil "localhost")
+         (goto-char (point-min))
+         (re-search-forward
+          "^Address:[ \t]*\\([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\)" nil t)
+         (setq dns-servers (list (match-string 1)))))))
 
 (defun dns-read-txt (string)
   (if (> (length string) 1)
@@ -352,23 +361,26 @@ If TCP-P, the first two bytes of the package with be the length field."
 
 (defvar dns-cache (make-vector 4096 0))
 
-(defun query-dns-cached (name &optional type fullp reversep)
+(defun dns-query-cached (name &optional type fullp reversep)
   (let* ((key (format "%s:%s:%s:%s" name type fullp reversep))
         (sym (intern-soft key dns-cache)))
     (if (and sym
             (boundp sym))
        (symbol-value sym)
-      (let ((result (query-dns name type fullp reversep)))
+      (let ((result (dns-query name type fullp reversep)))
        (set (intern key dns-cache) result)
        result))))
 
-(defun query-dns (name &optional type fullp reversep)
+;; The old names `query-dns' and `query-dns-cached' weren't used in Emacs 23
+;; yet, so no alias are provided.  --rsteib
+
+(defun dns-query (name &optional type fullp reversep)
   "Query a DNS server for NAME of TYPE.
 If FULLP, return the entire record returned.
 If REVERSEP, look up an IP address."
   (setq type (or type 'A))
   (unless dns-servers
-    (dns-parse-resolv-conf))
+    (dns-set-servers))
 
   (when reversep
     (setq name (concat
@@ -378,53 +390,52 @@ If REVERSEP, look up an IP address."
 
   (if (not dns-servers)
       (message "No DNS server configuration found")
-    (let (default-enable-multibyte-characters)
-      (with-temp-buffer
-        (let ((process (condition-case ()
-                           (dns-make-network-process (car dns-servers))
-                         (error
-                          (message
-                           "dns: Got an error while trying to talk to %s"
-                           (car dns-servers))
-                          nil)))
-              (tcp-p (and (not (fboundp 'make-network-process))
-                          (not (featurep 'xemacs))))
-              (step 100)
-              (times (* dns-timeout 1000))
-              (id (random 65000)))
-          (when process
-            (process-send-string
-             process
-             (dns-write `((id ,id)
-                          (opcode query)
-                          (queries ((,name (type ,type))))
-                          (recursion-desired-p t))
-                        tcp-p))
-            (while (and (zerop (buffer-size))
-                        (> times 0))
-              (sit-for (/ step 1000.0))
-              (accept-process-output process 0 step)
-              (setq times (- times step)))
-            (condition-case nil
-                (delete-process process)
-              (error nil))
-            (when (and tcp-p
-                       (>= (buffer-size) 2))
-              (goto-char (point-min))
-              (delete-region (point) (+ (point) 2)))
-            (when (and (>= (buffer-size) 2)
-                       ;; We had a time-out.
-                       (> times 0))
-              (let ((result (dns-read (buffer-string))))
-                (if fullp
-                    result
-                  (let ((answer (car (dns-get 'answers result))))
-                    (when (eq type (dns-get 'type answer))
-                      (if (eq type 'TXT)
-                          (dns-get-txt-answer (dns-get 'answers result))
-                        (dns-get 'data answer)))))))))))))
+    (with-temp-buffer
+      (unless (featurep 'xemacs) (set-buffer-multibyte nil))
+      (let ((process (condition-case ()
+                         (dns-make-network-process (car dns-servers))
+                       (error
+                        (message
+                         "dns: Got an error while trying to talk to %s"
+                         (car dns-servers))
+                        nil)))
+            (tcp-p (and (not (fboundp 'make-network-process))
+                        (not (featurep 'xemacs))))
+            (step 100)
+            (times (* dns-timeout 1000))
+            (id (random 65000)))
+        (when process
+          (process-send-string
+           process
+           (dns-write `((id ,id)
+                        (opcode query)
+                        (queries ((,name (type ,type))))
+                        (recursion-desired-p t))
+                      tcp-p))
+          (while (and (zerop (buffer-size))
+                      (> times 0))
+            (sit-for (/ step 1000.0))
+            (accept-process-output process 0 step)
+            (setq times (- times step)))
+          (condition-case nil
+              (delete-process process)
+            (error nil))
+          (when (and tcp-p
+                     (>= (buffer-size) 2))
+            (goto-char (point-min))
+            (delete-region (point) (+ (point) 2)))
+          (when (and (>= (buffer-size) 2)
+                     ;; We had a time-out.
+                     (> times 0))
+            (let ((result (dns-read (buffer-string))))
+              (if fullp
+                  result
+                (let ((answer (car (dns-get 'answers result))))
+                  (when (eq type (dns-get 'type answer))
+                    (if (eq type 'TXT)
+                        (dns-get-txt-answer (dns-get 'answers result))
+                      (dns-get 'data answer))))))))))))
 
 (provide 'dns)
 
-;;; arch-tag: d0edd0c4-4cce-4538-ae92-06c3356ee80a
 ;;; dns.el ends here