Mercurial > pidgin
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; |