Revision: mange@freemail.hu--2005/emacs-jabber--cvs-head--0--patch-556
[emacs-jabber.git] / srv.el
blob42665e7c0a0c5adcbbd360dd14c445a831e884c1
1 ;;; srv.el --- perform SRV DNS requests
3 ;; Copyright (C) 2005, 2007 Magnus Henoch
5 ;; Author: Magnus Henoch <mange@freemail.hu>
6 ;; Keywords: comm
7 ;; Version: 0.1
9 ;; This file is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
14 ;; This file is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING. If not, write to
21 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
24 ;;; Commentary:
26 ;; This code implements RFC 2782 (SRV records). It requires a version
27 ;; of dns.el that supports SRV records; look in Gnus CVS if you don't
28 ;; have one.
30 ;;; Code:
32 (condition-case nil
33 (require 'dns)
34 (error nil))
35 (eval-when-compile (require 'cl))
37 (defun srv-lookup (target)
38 "Perform SRV lookup of TARGET and return list of connection candidiates.
39 TARGET is a string of the form \"_Service._Proto.Name\".
41 Returns a list with elements of the form (HOST . PORT), where HOST is
42 a hostname and PORT is a numeric port. The caller is supposed to
43 make connection attempts in the order given, starting from the beginning
44 of the list. The list is empty if no SRV records were found."
45 (unless (boundp 'dns-query-types)
46 (error "No dns.el available"))
47 (unless (assq 'SRV dns-query-types)
48 (error "dns.el doesn't support SRV lookups"))
49 (let* ((result (query-dns target 'SRV t))
50 (answers (mapcar #'(lambda (a)
51 (cadr (assq 'data a)))
52 (cadr (assq 'answers result))))
53 answers-by-priority weighted-result)
54 (if (or (null answers)
55 ;; Special case for "service decidedly not available"
56 (and (eq (length answers) 1)
57 (string= (cadr (assq 'target (car answers))) ".")))
58 nil
59 ;; Sort answers into groups of same priority.
60 (dolist (a answers)
61 (let* ((priority (cadr (assq 'priority a)))
62 (entry (assq priority answers-by-priority)))
63 (if entry
64 (push a (cdr entry))
65 (push (cons priority (list a)) answers-by-priority))))
66 ;; Sort by priority.
67 (setq answers-by-priority
68 (sort answers-by-priority
69 #'(lambda (a b) (< (car a) (car b)))))
70 ;; Randomize by weight within priority groups. See
71 ;; algorithm in RFC 2782.
72 (dolist (p answers-by-priority)
73 (let ((weight-acc 0)
74 weight-order)
75 ;; Assign running sum of weight to each entry.
76 (dolist (a (cdr p))
77 (incf weight-acc (cadr (assq 'weight a)))
78 (push (cons weight-acc a) weight-order))
79 (setq weight-order (nreverse weight-order))
81 ;; While elements remain, pick a random number between 0 and
82 ;; weight-acc inclusive, and select the first entry whose
83 ;; running sum is greater than or equal to this number.
84 (while weight-order
85 (let* ((r (random (1+ weight-acc)))
86 (next-entry (dolist (a weight-order)
87 (if (>= (car a) r)
88 (return a)))))
89 (push (cdr next-entry) weighted-result)
90 (setq weight-order
91 (delq next-entry weight-order))))))
92 ;; Extract hostnames and ports
93 (mapcar #'(lambda (a) (cons (cadr (assq 'target a))
94 (cadr (assq 'port a))))
95 (nreverse weighted-result)))))
97 (provide 'srv)
98 ;; arch-tag: b43358f2-d241-11da-836e-000a95c2fcd0
99 ;;; srv.el ends here