]>
Commit | Line | Data |
---|---|---|
f106fecc MV |
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 <apt-pkg/error.h> | |
16 | #include "srvrec.h" | |
17 | ||
18 | bool GetSrvRecords(std::string name, std::vector<SrvRec> &Result) | |
19 | { | |
20 | unsigned char answer[PACKETSZ]; | |
21 | int answer_len, compressed_name_len; | |
22 | int answer_count; | |
23 | ||
24 | if (res_init() != 0) | |
25 | return _error->Errno("res_init", "Failed to init resolver"); | |
26 | ||
27 | answer_len = res_query(name.c_str(), C_IN, T_SRV, answer, sizeof(answer)); | |
28 | if (answer_len < (int)sizeof(HEADER)) | |
29 | return _error->Warning("Not enough data from res_query (%i)", answer_len); | |
30 | ||
31 | // check the header | |
32 | HEADER *header = (HEADER*)answer; | |
33 | if (header->rcode != NOERROR) | |
34 | return _error->Warning("res_query returned rcode %i", header->rcode); | |
35 | answer_count = ntohs(header->ancount); | |
36 | if (answer_count <= 0) | |
37 | return _error->Warning("res_query returned no answers (%i) ", answer_count); | |
38 | ||
39 | // skip the header | |
40 | compressed_name_len = dn_skipname(answer+sizeof(HEADER), answer+answer_len); | |
41 | if(compressed_name_len < 0) | |
42 | return _error->Warning("dn_skipname failed %i", compressed_name_len); | |
43 | ||
44 | // pt points to the first answer record, go over all of them now | |
45 | unsigned char *pt = answer+sizeof(HEADER)+compressed_name_len+QFIXEDSZ; | |
46 | while ((int)Result.size() < answer_count && pt < answer+answer_len) | |
47 | { | |
48 | SrvRec rec; | |
49 | u_int16_t type, klass, priority, weight, port, dlen; | |
50 | char buf[MAXDNAME]; | |
51 | ||
52 | compressed_name_len = dn_skipname(pt, answer+answer_len); | |
53 | if (compressed_name_len < 0) | |
54 | return _error->Warning("dn_skipname failed (2): %i", | |
55 | compressed_name_len); | |
56 | pt += compressed_name_len; | |
57 | if (((answer+answer_len) - pt) < 16) | |
58 | return _error->Warning("packet too short"); | |
59 | ||
60 | // extract the data out of the result buffer | |
61 | #define extract_u16(target, p) target = *p++ << 8; target |= *p++; | |
62 | ||
63 | extract_u16(type, pt); | |
64 | if(type != T_SRV) | |
65 | return _error->Warning("Unexpected type excepted %x != %x", | |
66 | T_SRV, type); | |
67 | extract_u16(klass, pt); | |
68 | if(klass != C_IN) | |
69 | return _error->Warning("Unexpected class excepted %x != %x", | |
70 | C_IN, klass); | |
71 | pt += 4; // ttl | |
72 | extract_u16(dlen, pt); | |
73 | extract_u16(priority, pt); | |
74 | extract_u16(weight, pt); | |
75 | extract_u16(port, pt); | |
76 | ||
77 | #undef extract_u16 | |
78 | ||
79 | compressed_name_len = dn_expand(answer, answer+answer_len, pt, buf, sizeof(buf)); | |
80 | if(compressed_name_len < 0) | |
81 | return _error->Warning("dn_expand failed %i", compressed_name_len); | |
82 | pt += compressed_name_len; | |
83 | ||
84 | // add it to our class | |
85 | rec.priority = priority; | |
86 | rec.weight = weight; | |
87 | rec.port = port; | |
88 | rec.target = buf; | |
89 | Result.push_back(rec); | |
90 | } | |
91 | ||
92 | return true; | |
93 | } |