1 // -*- mode: cpp; mode: fold -*-
3 /* ######################################################################
7 ##################################################################### */
13 #include <netinet/in.h>
14 #include <arpa/nameser.h>
19 #include <apt-pkg/strutl.h>
20 #include <apt-pkg/error.h>
23 bool GetSrvRecords(std::string host
, int port
, std::vector
<SrvRec
> &Result
)
26 struct servent
*s_ent
= getservbyport(htons(port
), "tcp");
30 strprintf(target
, "_%s._tcp.%s", s_ent
->s_name
, host
.c_str());
31 return GetSrvRecords(target
, Result
);
34 bool GetSrvRecords(std::string name
, std::vector
<SrvRec
> &Result
)
36 unsigned char answer
[PACKETSZ
];
37 int answer_len
, compressed_name_len
;
41 return _error
->Errno("res_init", "Failed to init resolver");
43 answer_len
= res_query(name
.c_str(), C_IN
, T_SRV
, answer
, sizeof(answer
));
46 if (answer_len
< (int)sizeof(HEADER
))
47 return _error
->Warning("Not enough data from res_query (%i)", answer_len
);
50 HEADER
*header
= (HEADER
*)answer
;
51 if (header
->rcode
!= NOERROR
)
52 return _error
->Warning("res_query returned rcode %i", header
->rcode
);
53 answer_count
= ntohs(header
->ancount
);
54 if (answer_count
<= 0)
55 return _error
->Warning("res_query returned no answers (%i) ", answer_count
);
58 compressed_name_len
= dn_skipname(answer
+sizeof(HEADER
), answer
+answer_len
);
59 if(compressed_name_len
< 0)
60 return _error
->Warning("dn_skipname failed %i", compressed_name_len
);
62 // pt points to the first answer record, go over all of them now
63 unsigned char *pt
= answer
+sizeof(HEADER
)+compressed_name_len
+QFIXEDSZ
;
64 while ((int)Result
.size() < answer_count
&& pt
< answer
+answer_len
)
67 u_int16_t type
, klass
, priority
, weight
, port
, dlen
;
70 compressed_name_len
= dn_skipname(pt
, answer
+answer_len
);
71 if (compressed_name_len
< 0)
72 return _error
->Warning("dn_skipname failed (2): %i",
74 pt
+= compressed_name_len
;
75 if (((answer
+answer_len
) - pt
) < 16)
76 return _error
->Warning("packet too short");
78 // extract the data out of the result buffer
79 #define extract_u16(target, p) target = *p++ << 8; target |= *p++;
81 extract_u16(type
, pt
);
83 return _error
->Warning("Unexpected type excepted %x != %x",
85 extract_u16(klass
, pt
);
87 return _error
->Warning("Unexpected class excepted %x != %x",
90 extract_u16(dlen
, pt
);
91 extract_u16(priority
, pt
);
92 extract_u16(weight
, pt
);
93 extract_u16(port
, pt
);
97 compressed_name_len
= dn_expand(answer
, answer
+answer_len
, pt
, buf
, sizeof(buf
));
98 if(compressed_name_len
< 0)
99 return _error
->Warning("dn_expand failed %i", compressed_name_len
);
100 pt
+= compressed_name_len
;
102 // add it to our class
103 rec
.priority
= priority
;
107 Result
.push_back(rec
);
110 // implement load balancing as specified in RFC-2782
112 // sort them by priority
113 std::stable_sort(Result
.begin(), Result
.end());
115 // assign random number ranges
117 int prev_priority
= 0;
118 for(std::vector
<SrvRec
>::iterator I
= Result
.begin();
119 I
!= Result
.end(); ++I
)
121 if(prev_priority
!= I
->priority
)
123 I
->random_number_range_start
= prev_weight
;
124 I
->random_number_range_end
= prev_weight
+ I
->weight
;
125 prev_weight
= I
->random_number_range_end
;
126 prev_priority
= I
->priority
;
129 // go over the code in reverse order and note the max random range
132 for(std::vector
<SrvRec
>::iterator I
= Result
.end();
133 I
!= Result
.begin(); --I
)
135 if(prev_priority
!= I
->priority
)
136 max
= I
->random_number_range_end
;
137 I
->random_number_range_max
= max
;
140 // FIXME: now shuffle