]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/srvrec.cc
352a569704c23a5d41eeb5be8e3fc4b5d68f21d8
[apt.git] / apt-pkg / contrib / srvrec.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 /* ######################################################################
4
5 SRV record support
6
7 ##################################################################### */
8 /*}}}*/
9 #include <config.h>
10
11 #include <netinet/in.h>
12 #include <arpa/nameser.h>
13 #include <resolv.h>
14
15 #include <algorithm>
16
17 #include <apt-pkg/error.h>
18 #include "srvrec.h"
19
20 bool GetSrvRecords(std::string name, std::vector<SrvRec> &Result)
21 {
22 unsigned char answer[PACKETSZ];
23 int answer_len, compressed_name_len;
24 int answer_count;
25
26 if (res_init() != 0)
27 return _error->Errno("res_init", "Failed to init resolver");
28
29 answer_len = res_query(name.c_str(), C_IN, T_SRV, answer, sizeof(answer));
30 if (answer_len < (int)sizeof(HEADER))
31 return _error->Warning("Not enough data from res_query (%i)", answer_len);
32
33 // check the header
34 HEADER *header = (HEADER*)answer;
35 if (header->rcode != NOERROR)
36 return _error->Warning("res_query returned rcode %i", header->rcode);
37 answer_count = ntohs(header->ancount);
38 if (answer_count <= 0)
39 return _error->Warning("res_query returned no answers (%i) ", answer_count);
40
41 // skip the header
42 compressed_name_len = dn_skipname(answer+sizeof(HEADER), answer+answer_len);
43 if(compressed_name_len < 0)
44 return _error->Warning("dn_skipname failed %i", compressed_name_len);
45
46 // pt points to the first answer record, go over all of them now
47 unsigned char *pt = answer+sizeof(HEADER)+compressed_name_len+QFIXEDSZ;
48 while ((int)Result.size() < answer_count && pt < answer+answer_len)
49 {
50 SrvRec rec;
51 u_int16_t type, klass, priority, weight, port, dlen;
52 char buf[MAXDNAME];
53
54 compressed_name_len = dn_skipname(pt, answer+answer_len);
55 if (compressed_name_len < 0)
56 return _error->Warning("dn_skipname failed (2): %i",
57 compressed_name_len);
58 pt += compressed_name_len;
59 if (((answer+answer_len) - pt) < 16)
60 return _error->Warning("packet too short");
61
62 // extract the data out of the result buffer
63 #define extract_u16(target, p) target = *p++ << 8; target |= *p++;
64
65 extract_u16(type, pt);
66 if(type != T_SRV)
67 return _error->Warning("Unexpected type excepted %x != %x",
68 T_SRV, type);
69 extract_u16(klass, pt);
70 if(klass != C_IN)
71 return _error->Warning("Unexpected class excepted %x != %x",
72 C_IN, klass);
73 pt += 4; // ttl
74 extract_u16(dlen, pt);
75 extract_u16(priority, pt);
76 extract_u16(weight, pt);
77 extract_u16(port, pt);
78
79 #undef extract_u16
80
81 compressed_name_len = dn_expand(answer, answer+answer_len, pt, buf, sizeof(buf));
82 if(compressed_name_len < 0)
83 return _error->Warning("dn_expand failed %i", compressed_name_len);
84 pt += compressed_name_len;
85
86 // add it to our class
87 rec.priority = priority;
88 rec.weight = weight;
89 rec.port = port;
90 rec.target = buf;
91 Result.push_back(rec);
92 }
93
94 // sort them
95 std::stable_sort(Result.begin(), Result.end());
96
97 // FIXME: implement weight selection as specified in RFC-2782
98
99 return true;
100 }