comparison libpurple/dnssrv.c @ 27590:a08e84032814

merge of '2348ff22f0ff3453774b8b25b36238465580c609' and 'e76f11543c2a4aa05bdf584f087cbe3439029661'
author Paul Aurich <paul@darkrain42.org>
date Sun, 12 Jul 2009 05:43:38 +0000
parents e400cd35542b
children 7fbf964c6c6c
comparison
equal deleted inserted replaced
27104:048bcf41deef 27590:a08e84032814
64 static void (WINAPI *MyDnsRecordListFree) (PDNS_RECORD pRecordList, 64 static void (WINAPI *MyDnsRecordListFree) (PDNS_RECORD pRecordList,
65 DNS_FREE_TYPE FreeType) = NULL; 65 DNS_FREE_TYPE FreeType) = NULL;
66 #endif 66 #endif
67 67
68 struct _PurpleTxtResponse { 68 struct _PurpleTxtResponse {
69 char *content; 69 char *content;
70 }; 70 };
71 71
72 struct _PurpleSrvQueryData { 72 struct _PurpleSrvQueryData {
73 union { 73 union {
74 PurpleSrvCallback srv; 74 PurpleSrvCallback srv;
80 int type; 80 int type;
81 #ifdef _WIN32 81 #ifdef _WIN32
82 GThread *resolver; 82 GThread *resolver;
83 char *query; 83 char *query;
84 char *error_message; 84 char *error_message;
85 GSList *results; 85 GList *results;
86 #else 86 #else
87 int fd_in, fd_out; 87 int fd_in, fd_out;
88 pid_t pid; 88 pid_t pid;
89 #endif 89 #endif
90 }; 90 };
92 typedef struct _PurpleSrvInternalQuery { 92 typedef struct _PurpleSrvInternalQuery {
93 int type; 93 int type;
94 char query[256]; 94 char query[256];
95 } PurpleSrvInternalQuery; 95 } PurpleSrvInternalQuery;
96 96
97 typedef struct _PurpleSrvResponseContainer {
98 PurpleSrvResponse *response;
99 int sum;
100 } PurpleSrvResponseContainer;
101
102 /**
103 * Sort by priority, then by weight. Strictly numerically--no
104 * randomness. Technically we only need to sort by pref and then
105 * make sure any records with weight 0 are at the beginning of
106 * their group, but it's just as easy to sort by weight.
107 */
97 static gint 108 static gint
98 responsecompare(gconstpointer ar, gconstpointer br) 109 responsecompare(gconstpointer ar, gconstpointer br)
99 { 110 {
100 PurpleSrvResponse *a = (PurpleSrvResponse*)ar; 111 PurpleSrvResponse *a = (PurpleSrvResponse*)ar;
101 PurpleSrvResponse *b = (PurpleSrvResponse*)br; 112 PurpleSrvResponse *b = (PurpleSrvResponse*)br;
110 if(a->pref < b->pref) 121 if(a->pref < b->pref)
111 return -1; 122 return -1;
112 return 1; 123 return 1;
113 } 124 }
114 125
126 /**
127 * Iterate over a list of PurpleSrvResponseContainer making the sum
128 * the running total of the sums. Select a random integer in the range
129 * (1, sum+1), then find the first element greater than or equal to the
130 * number selected. From RFC 2782.
131 *
132 * @param list The list of PurpleSrvResponseContainer. This function
133 * removes a node from this list and returns the new list.
134 * @param container_ptr The PurpleSrvResponseContainer that was chosen
135 * will be returned here.
136 */
137 static GList *
138 select_random_response(GList *list, PurpleSrvResponseContainer **container_ptr)
139 {
140 GList *cur;
141 size_t runningtotal;
142 int r;
143
144 runningtotal = 0;
145 cur = list;
146
147 while (cur) {
148 PurpleSrvResponseContainer *container = cur->data;
149 runningtotal += container->response->weight;
150 container->sum = runningtotal;
151 cur = cur->next;
152 }
153
154 /*
155 * If the running total is greater than 0, pick a number between
156 * 1 and the runningtotal inclusive. (This is not precisely what
157 * the RFC algorithm describes, but we wish to deal with integers
158 * and avoid floats. This is functionally equivalent.)
159 * If running total is 0, then choose r = 0.
160 */
161 r = runningtotal ? g_random_int_range(1, runningtotal + 1) : 0;
162 cur = list;
163 while (r > ((PurpleSrvResponseContainer *)cur->data)->sum) {
164 cur = cur->next;
165 }
166
167 /* Set the return parameter and remove cur from the list */
168 *container_ptr = cur->data;
169 return g_list_delete_link(list, cur);
170 }
171
172 /**
173 * Reorder a GList of PurpleSrvResponses that have the same priority
174 * (aka "pref").
175 */
176 static void
177 srv_reorder(GList *list, int num)
178 {
179 int i;
180 GList *cur, *container_list = NULL;
181 PurpleSrvResponseContainer *container;
182
183 if (num < 2)
184 /* Nothing to sort */
185 return;
186
187 /* First build a list of container structs */
188 for (i = 0, cur = list; i < num; i++, cur = cur->next) {
189 container = g_new(PurpleSrvResponseContainer, 1);
190 container->response = cur->data;
191 container_list = g_list_prepend(container_list, container);
192 }
193 container_list = g_list_reverse(container_list);
194
195 /*
196 * Re-order the list that was passed in as a parameter. We leave
197 * the list nodes in place, but replace their data pointers.
198 */
199 cur = list;
200 while (container_list) {
201 container_list = select_random_response(container_list, &container);
202 cur->data = container->response;
203 g_free(container);
204 cur = cur->next;
205 }
206 }
207
208 /**
209 * Sorts a GList of PurpleSrvResponses according to the
210 * algorithm described in RFC 2782.
211 *
212 * @param response GList of PurpleSrvResponse's
213 * @param The original list, resorted
214 */
215 static GList *
216 purple_srv_sort(GList *list)
217 {
218 int pref, count;
219 GList *cur, *start;
220
221 if (!list || !list->next) {
222 /* Nothing to sort */
223 return list;
224 }
225
226 list = g_list_sort(list, responsecompare);
227
228 start = cur = list;
229 count = 1;
230 while (cur) {
231 PurpleSrvResponse *next_response;
232 pref = ((PurpleSrvResponse *)cur->data)->pref;
233 next_response = cur->next ? cur->next->data : NULL;
234 if (!next_response || next_response->pref != pref) {
235 /*
236 * The 'count' records starting at 'start' all have the same
237 * priority. Sort them by weight.
238 */
239 srv_reorder(start, count);
240 start = cur->next;
241 count = 0;
242 }
243 count++;
244 cur = cur->next;
245 }
246
247 return list;
248 }
249
115 #ifndef _WIN32 250 #ifndef _WIN32
116 251
117 G_GNUC_NORETURN static void 252 G_GNUC_NORETURN static void
118 resolve(int in, int out) 253 resolve(int in, int out)
119 { 254 {
120 GList *ret = NULL; 255 GList *ret = NULL;
121 PurpleSrvResponse *srvres; 256 PurpleSrvResponse *srvres;
122 PurpleTxtResponse *txtres; 257 PurpleTxtResponse *txtres;
123 queryans answer; 258 queryans answer;
124 int size; 259 int size, qdcount, ancount;
125 int qdcount; 260 guchar *end, *cp;
126 int ancount;
127 guchar *end;
128 guchar *cp;
129 gchar name[256]; 261 gchar name[256];
130 guint16 type, dlen, pref, weight, port; 262 guint16 type, dlen, pref, weight, port;
131 PurpleSrvInternalQuery query; 263 PurpleSrvInternalQuery query;
132 264
133 #ifdef HAVE_SIGNAL_H 265 #ifdef HAVE_SIGNAL_H
189 strcpy(srvres->hostname, name); 321 strcpy(srvres->hostname, name);
190 srvres->pref = pref; 322 srvres->pref = pref;
191 srvres->port = port; 323 srvres->port = port;
192 srvres->weight = weight; 324 srvres->weight = weight;
193 325
194 ret = g_list_insert_sorted(ret, srvres, responsecompare); 326 ret = g_list_prepend(ret, srvres);
195 } else if (query.type == T_TXT) { 327 } else if (query.type == T_TXT) {
196 txtres = g_new0(PurpleTxtResponse, 1); 328 txtres = g_new0(PurpleTxtResponse, 1);
197 txtres->content = g_strndup((gchar*)(++cp), dlen-1); 329 txtres->content = g_strndup((gchar*)(++cp), dlen-1);
198 ret = g_list_append(ret, txtres); 330 ret = g_list_append(ret, txtres);
199 cp += dlen - 1; 331 cp += dlen - 1;
202 } 334 }
203 } 335 }
204 336
205 end: 337 end:
206 size = g_list_length(ret); 338 size = g_list_length(ret);
339
340 if (query.type == T_SRV)
341 ret = purple_srv_sort(ret);
342
207 /* TODO: Check return value */ 343 /* TODO: Check return value */
208 write(out, &(query.type), sizeof(query.type)); 344 write(out, &(query.type), sizeof(query.type));
209 write(out, &size, sizeof(size)); 345 write(out, &size, sizeof(size));
210 while (ret != NULL) 346 while (ret != NULL)
211 { 347 {
212 /* TODO: Check return value */ 348 /* TODO: Check return value */
213 if (query.type == T_SRV) write(out, ret->data, sizeof(PurpleSrvResponse)); 349 if (query.type == T_SRV)
214 if (query.type == T_TXT) write(out, ret->data, sizeof(PurpleTxtResponse)); 350 write(out, ret->data, sizeof(PurpleSrvResponse));
351 if (query.type == T_TXT)
352 write(out, ret->data, sizeof(PurpleTxtResponse));
353
215 g_free(ret->data); 354 g_free(ret->data);
216 ret = g_list_remove(ret, ret->data); 355 ret = g_list_remove(ret, ret->data);
217 } 356 }
218 357
219 close(out); 358 close(out);
270 } 409 }
271 } 410 }
272 411
273 cb(res, size, query_data->extradata); 412 cb(res, size, query_data->extradata);
274 } else if (type == T_TXT) { 413 } else if (type == T_TXT) {
275 GSList *responses = NULL; 414 GList *responses = NULL;
276 PurpleTxtResponse *res; 415 PurpleTxtResponse *res;
277 PurpleTxtCallback cb = query_data->cb.txt; 416 PurpleTxtCallback cb = query_data->cb.txt;
278 ssize_t red; 417 ssize_t red;
279 purple_debug_info("dnssrv","found %d TXT entries\n", size); 418 purple_debug_info("dnssrv","found %d TXT entries\n", size);
280 res = g_new0(PurpleTxtResponse, 1); 419 res = g_new0(PurpleTxtResponse, 1);
283 if (red != sizeof(PurpleTxtResponse)) { 422 if (red != sizeof(PurpleTxtResponse)) {
284 purple_debug_error("dnssrv","unable to read txt " 423 purple_debug_error("dnssrv","unable to read txt "
285 "response: %s\n", g_strerror(errno)); 424 "response: %s\n", g_strerror(errno));
286 size = 0; 425 size = 0;
287 g_free(res); 426 g_free(res);
288 g_slist_foreach(responses, (GFunc)purple_txt_response_destroy, NULL); 427 g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL);
289 g_slist_free(responses); 428 g_list_free(responses);
290 responses = NULL; 429 responses = NULL;
291 break; 430 break;
292 } 431 }
293 } 432 }
294 433
310 449
311 static gboolean 450 static gboolean
312 res_main_thread_cb(gpointer data) 451 res_main_thread_cb(gpointer data)
313 { 452 {
314 PurpleSrvResponse *srvres = NULL; 453 PurpleSrvResponse *srvres = NULL;
315 int size = 0;
316 PurpleSrvQueryData *query_data = data; 454 PurpleSrvQueryData *query_data = data;
317 if(query_data->error_message != NULL) 455 if(query_data->error_message != NULL)
318 purple_debug_error("dnssrv", query_data->error_message); 456 purple_debug_error("dnssrv", query_data->error_message);
319 else { 457 else {
320 if (query_data->type == DNS_TYPE_SRV) { 458 if (query_data->type == DNS_TYPE_SRV) {
321 PurpleSrvResponse *srvres_tmp = NULL; 459 PurpleSrvResponse *srvres_tmp = NULL;
322 GSList *lst = query_data->results; 460 GList *lst = query_data->results;
323 461 int size = g_list_length(lst);
324 size = g_slist_length(lst);
325 462
326 if(query_data->cb.srv && size > 0) 463 if(query_data->cb.srv && size > 0)
327 srvres_tmp = srvres = g_new0(PurpleSrvResponse, size); 464 srvres_tmp = srvres = g_new0(PurpleSrvResponse, size);
328 while (lst) { 465 while (lst) {
466 PurpleSrvResponse *lstdata = lst->data;
467 lst = g_list_delete_link(lst, lst);
468
329 if(query_data->cb.srv) 469 if(query_data->cb.srv)
330 memcpy(srvres_tmp++, lst->data, sizeof(PurpleSrvResponse)); 470 memcpy(srvres_tmp++, lstdata, sizeof(PurpleSrvResponse));
331 g_free(lst->data); 471 g_free(lstdata);
332 lst = g_slist_remove(lst, lst->data);
333 } 472 }
334 473
335 query_data->results = NULL; 474 query_data->results = NULL;
336 475
337 purple_debug_info("dnssrv", "found %d SRV entries\n", size); 476 purple_debug_info("dnssrv", "found %d SRV entries\n", size);
338 477
339 if(query_data->cb.srv) query_data->cb.srv(srvres, size, query_data->extradata); 478 if(query_data->cb.srv) query_data->cb.srv(srvres, size, query_data->extradata);
340 } else if (query_data->type == DNS_TYPE_TXT) { 479 } else if (query_data->type == DNS_TYPE_TXT) {
341 GSList *lst = query_data->results; 480 GList *lst = query_data->results;
342 481
343 purple_debug_info("dnssrv", "found %d TXT entries\n", g_slist_length(lst)); 482 purple_debug_info("dnssrv", "found %d TXT entries\n", g_list_length(lst));
344 483
345 if (query_data->cb.txt) { 484 if (query_data->cb.txt) {
346 query_data->results = NULL; 485 query_data->results = NULL;
347 query_data->cb.txt(lst, query_data->extradata); 486 query_data->cb.txt(lst, query_data->extradata);
348 } 487 }
349 } else { 488 } else {
377 } 516 }
378 g_free(msg); 517 g_free(msg);
379 } else { 518 } else {
380 if (type == DNS_TYPE_SRV) { 519 if (type == DNS_TYPE_SRV) {
381 PDNS_RECORD dr_tmp; 520 PDNS_RECORD dr_tmp;
382 GSList *lst = NULL; 521 GList *lst = NULL;
383 DNS_SRV_DATA *srv_data; 522 DNS_SRV_DATA *srv_data;
384 PurpleSrvResponse *srvres; 523 PurpleSrvResponse *srvres;
385 524
386 for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) { 525 for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) {
387 /* Discard any incorrect entries. I'm not sure if this is necessary */ 526 /* Discard any incorrect entries. I'm not sure if this is necessary */
388 if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) { 527 if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) {
389 continue; 528 continue;
390 } 529 }
394 strncpy(srvres->hostname, srv_data->pNameTarget, 255); 533 strncpy(srvres->hostname, srv_data->pNameTarget, 255);
395 srvres->hostname[255] = '\0'; 534 srvres->hostname[255] = '\0';
396 srvres->pref = srv_data->wPriority; 535 srvres->pref = srv_data->wPriority;
397 srvres->port = srv_data->wPort; 536 srvres->port = srv_data->wPort;
398 srvres->weight = srv_data->wWeight; 537 srvres->weight = srv_data->wWeight;
399 538
400 lst = g_slist_insert_sorted(lst, srvres, responsecompare); 539 lst = g_list_prepend(lst, srvres);
401 } 540 }
402 541
403 MyDnsRecordListFree(dr, DnsFreeRecordList); 542 MyDnsRecordListFree(dr, DnsFreeRecordList);
404 query_data->results = lst; 543 query_data->results = purple_srv_sort(lst);
405 } else if (type == DNS_TYPE_TXT) { 544 } else if (type == DNS_TYPE_TXT) {
406 PDNS_RECORD dr_tmp; 545 PDNS_RECORD dr_tmp;
407 GSList *lst = NULL; 546 GList *lst = NULL;
408 DNS_TXT_DATA *txt_data; 547 DNS_TXT_DATA *txt_data;
409 PurpleTxtResponse *txtres; 548 PurpleTxtResponse *txtres;
410 549
411 for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) { 550 for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) {
412 GString *s; 551 GString *s;
423 s = g_string_new(""); 562 s = g_string_new("");
424 for (i = 0; i < txt_data->dwStringCount; ++i) 563 for (i = 0; i < txt_data->dwStringCount; ++i)
425 s = g_string_append(s, txt_data->pStringArray[i]); 564 s = g_string_append(s, txt_data->pStringArray[i]);
426 txtres->content = g_string_free(s, FALSE); 565 txtres->content = g_string_free(s, FALSE);
427 566
428 lst = g_slist_append(lst, txtres); 567 lst = g_list_append(lst, txtres);
429 } 568 }
430 569
431 MyDnsRecordListFree(dr, DnsFreeRecordList); 570 MyDnsRecordListFree(dr, DnsFreeRecordList);
432 query_data->results = lst; 571 query_data->results = lst;
433 } else { 572 } else {
434 573
435 } 574 }
436 } 575 }
437 576
438 /* back to main thread */ 577 /* back to main thread */
439 /* Note: this should *not* be attached to query_data->handle - it will cause leakage */ 578 /* Note: this should *not* be attached to query_data->handle - it will cause leakage */
498 close(out[1]); 637 close(out[1]);
499 close(in[0]); 638 close(in[0]);
500 639
501 internal_query.type = T_SRV; 640 internal_query.type = T_SRV;
502 strncpy(internal_query.query, query, 255); 641 strncpy(internal_query.query, query, 255);
503 642
504 if (write(in[1], &internal_query, sizeof(internal_query)) < 0) 643 if (write(in[1], &internal_query, sizeof(internal_query)) < 0)
505 purple_debug_error("dnssrv", "Could not write to SRV resolver\n"); 644 purple_debug_error("dnssrv", "Could not write to SRV resolver\n");
506
507 645
508 query_data = g_new0(PurpleSrvQueryData, 1); 646 query_data = g_new0(PurpleSrvQueryData, 1);
509 query_data->type = T_SRV; 647 query_data->type = T_SRV;
510 query_data->cb.srv = cb; 648 query_data->cb.srv = cb;
511 query_data->extradata = extradata; 649 query_data->extradata = extradata;
594 /* resolve() does not return */ 732 /* resolve() does not return */
595 } 733 }
596 734
597 close(out[1]); 735 close(out[1]);
598 close(in[0]); 736 close(in[0]);
599 737
600 internal_query.type = T_TXT; 738 internal_query.type = T_TXT;
601 strncpy(internal_query.query, query, 255); 739 strncpy(internal_query.query, query, 255);
602 740
603 if (write(in[1], &internal_query, sizeof(internal_query)) < 0) 741 if (write(in[1], &internal_query, sizeof(internal_query)) < 0)
604 purple_debug_error("dnssrv", "Could not write to TXT resolver\n"); 742 purple_debug_error("dnssrv", "Could not write to TXT resolver\n");
605 743
606 query_data = g_new0(PurpleSrvQueryData, 1); 744 query_data = g_new0(PurpleSrvQueryData, 1);
607 query_data->type = T_TXT; 745 query_data->type = T_TXT;