Remove nnml-retrieve-groups that is unnecessary and somewhat problematic
[gnus] / lisp / dns.el
index e5a30ec..ba6523f 100644 (file)
@@ -1,40 +1,41 @@
 ;;; dns.el --- Domain Name Service lookups
-;; Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
+
+;; Copyright (C) 2002-2015 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 2, 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:
 
 ;;; Code:
 
-(eval-when-compile (require 'cl))
-
-(require 'mm-util)
-
 (defvar dns-timeout 5
   "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.")
+
+(defvar dns-servers-valid-for-interfaces nil
+  "The return value of `network-interface-list' when `dns-servers' was set.
+If the set of network interfaces and/or their IP addresses
+change, then presumably the list of DNS servers needs to be
+updated.  Set this variable to t to disable the check.")
 
 ;;; Internal code:
 
@@ -104,7 +105,8 @@ If nil, /etc/resolv.conf will be consulted.")
   (dns-write-bytes 0))
 
 (defun dns-read-string-name (string buffer)
-  (mm-with-unibyte-buffer
+  (with-temp-buffer
+    (unless (featurep 'xemacs) (set-buffer-multibyte nil))
     (insert string)
     (goto-char (point-min))
     (dns-read-name buffer)))
@@ -138,6 +140,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
@@ -153,7 +156,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)
@@ -187,75 +190,76 @@ If TCP-P, the first two bytes of the package with be the length field."
     (buffer-string)))
 
 (defun dns-read (packet)
-  (mm-with-unibyte-buffer
+  (with-temp-buffer
+    (unless (featurep 'xemacs) (set-buffer-multibyte nil))
     (let ((spec nil)
-         queries answers authorities additionals)
+          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))
+        (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)))
-             spec))
+        (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
-                           (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))))
+        (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
+                              (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))))
 
 (defun dns-read-int32 ()
-  ;; Full 32 bit Integers can't be handled by Emacs.  If we use
-  ;; floats, it works.
+  ;; Full 32 bit Integers can't be handled by 32-bit Emacsen.  If we
+  ;; use floats, it works.
   (format "%.0f" (+ (* (dns-read-bytes 1) 16777216.0)
                    (dns-read-bytes 3))))
 
@@ -263,48 +267,73 @@ If TCP-P, the first two bytes of the package with be the length field."
   (let ((buffer (current-buffer))
        (point (point)))
     (prog1
-       (mm-with-unibyte-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)))))
+(declare-function network-interface-list "process.c")
+
+(defun dns-servers-up-to-date-p ()
+  "Return false if we need to recheck the list of DNS servers."
+  (and dns-servers
+       (or (eq dns-servers-valid-for-interfaces t)
+          ;; `network-interface-list' was introduced in Emacs 22.1.
+          (not (fboundp 'network-interface-list))
+          (equal dns-servers-valid-for-interfaces
+                 (network-interface-list)))))
+
+(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))))))
+  (when (fboundp 'network-interface-list)
+    (setq dns-servers-valid-for-interfaces (network-interface-list))))
 
 (defun dns-read-txt (string)
   (if (> (length string) 1)
@@ -350,23 +379,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))
+  (unless (dns-servers-up-to-date-p)
+    (dns-set-servers))
 
   (when reversep
     (setq name (concat
@@ -376,47 +408,52 @@ If REVERSEP, look up an IP address."
 
   (if (not dns-servers)
       (message "No DNS server configuration found")
-    (mm-with-unibyte-buffer
+    (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)
-           (decf times step))
-         (ignore-errors
-           (delete-process process))
-         (when tcp-p
-           (goto-char (point-min))
-           (delete-region (point) (+ (point) 2)))
-         (unless (zerop (buffer-size))
-           (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))))))))))))
+                         (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