comparison libpurple/dnssrv.c @ 29077:d83ee160ffb6

propagate from branch 'im.pidgin.pidgin' (head eb9385f349a20856b9d3f9911dbc8024caa44052) to branch 'im.pidgin.pidgin.next.minor' (head 439fb2dd7a285d9ca645f65f36ef0f037abe7311)
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Wed, 19 Aug 2009 00:46:04 +0000
parents 7fbf964c6c6c
children bbb9e0ea67fe
comparison
equal deleted inserted replaced
29076:7a3458436140 29077:d83ee160ffb6
31 #include <arpa/nameser_compat.h> 31 #include <arpa/nameser_compat.h>
32 #endif 32 #endif
33 #ifndef T_SRV 33 #ifndef T_SRV
34 #define T_SRV 33 34 #define T_SRV 33
35 #endif 35 #endif
36 #else 36 #ifndef T_TXT
37 #define T_TXT 16
38 #endif
39 #else /* WIN32 */
37 #include <windns.h> 40 #include <windns.h>
38 /* Missing from the mingw headers */ 41 /* Missing from the mingw headers */
39 #ifndef DNS_TYPE_SRV 42 #ifndef DNS_TYPE_SRV
40 # define DNS_TYPE_SRV 33 43 # define DNS_TYPE_SRV 33
41 #endif 44 #endif
42 #endif 45 #ifndef DNS_TYPE_TXT
43 46 # define DNS_TYPE_TXT 16
47 #endif
48 #endif
49
50 #include "debug.h"
44 #include "dnssrv.h" 51 #include "dnssrv.h"
45 #include "eventloop.h" 52 #include "eventloop.h"
46 #include "debug.h" 53 #include "network.h"
47 54
48 #ifndef _WIN32 55 #ifndef _WIN32
49 typedef union { 56 typedef union {
50 HEADER hdr; 57 HEADER hdr;
51 u_char buf[1024]; 58 u_char buf[1024];
57 PVOID* pReserved) = NULL; 64 PVOID* pReserved) = NULL;
58 static void (WINAPI *MyDnsRecordListFree) (PDNS_RECORD pRecordList, 65 static void (WINAPI *MyDnsRecordListFree) (PDNS_RECORD pRecordList,
59 DNS_FREE_TYPE FreeType) = NULL; 66 DNS_FREE_TYPE FreeType) = NULL;
60 #endif 67 #endif
61 68
69 struct _PurpleTxtResponse {
70 char *content;
71 };
72
62 struct _PurpleSrvQueryData { 73 struct _PurpleSrvQueryData {
63 PurpleSrvCallback cb; 74 union {
75 PurpleSrvCallback srv;
76 PurpleTxtCallback txt;
77 } cb;
78
64 gpointer extradata; 79 gpointer extradata;
65 guint handle; 80 guint handle;
81 int type;
66 #ifdef _WIN32 82 #ifdef _WIN32
67 GThread *resolver; 83 GThread *resolver;
68 char *query; 84 char *query;
69 char *error_message; 85 char *error_message;
70 GSList *results; 86 GList *results;
71 #else 87 #else
72 int fd_in, fd_out; 88 int fd_in, fd_out;
73 pid_t pid; 89 pid_t pid;
74 #endif 90 #endif
75 }; 91 };
76 92
93 typedef struct _PurpleSrvInternalQuery {
94 int type;
95 char query[256];
96 } PurpleSrvInternalQuery;
97
98 typedef struct _PurpleSrvResponseContainer {
99 PurpleSrvResponse *response;
100 int sum;
101 } PurpleSrvResponseContainer;
102
103 /**
104 * Sort by priority, then by weight. Strictly numerically--no
105 * randomness. Technically we only need to sort by pref and then
106 * make sure any records with weight 0 are at the beginning of
107 * their group, but it's just as easy to sort by weight.
108 */
77 static gint 109 static gint
78 responsecompare(gconstpointer ar, gconstpointer br) 110 responsecompare(gconstpointer ar, gconstpointer br)
79 { 111 {
80 PurpleSrvResponse *a = (PurpleSrvResponse*)ar; 112 PurpleSrvResponse *a = (PurpleSrvResponse*)ar;
81 PurpleSrvResponse *b = (PurpleSrvResponse*)br; 113 PurpleSrvResponse *b = (PurpleSrvResponse*)br;
90 if(a->pref < b->pref) 122 if(a->pref < b->pref)
91 return -1; 123 return -1;
92 return 1; 124 return 1;
93 } 125 }
94 126
127 /**
128 * Iterate over a list of PurpleSrvResponseContainer making the sum
129 * the running total of the sums. Select a random integer in the range
130 * (1, sum+1), then find the first element greater than or equal to the
131 * number selected. From RFC 2782.
132 *
133 * @param list The list of PurpleSrvResponseContainer. This function
134 * removes a node from this list and returns the new list.
135 * @param container_ptr The PurpleSrvResponseContainer that was chosen
136 * will be returned here.
137 */
138 static GList *
139 select_random_response(GList *list, PurpleSrvResponseContainer **container_ptr)
140 {
141 GList *cur;
142 size_t runningtotal;
143 int r;
144
145 runningtotal = 0;
146 cur = list;
147
148 while (cur) {
149 PurpleSrvResponseContainer *container = cur->data;
150 runningtotal += container->response->weight;
151 container->sum = runningtotal;
152 cur = cur->next;
153 }
154
155 /*
156 * If the running total is greater than 0, pick a number between
157 * 1 and the runningtotal inclusive. (This is not precisely what
158 * the RFC algorithm describes, but we wish to deal with integers
159 * and avoid floats. This is functionally equivalent.)
160 * If running total is 0, then choose r = 0.
161 */
162 r = runningtotal ? g_random_int_range(1, runningtotal + 1) : 0;
163 cur = list;
164 while (r > ((PurpleSrvResponseContainer *)cur->data)->sum) {
165 cur = cur->next;
166 }
167
168 /* Set the return parameter and remove cur from the list */
169 *container_ptr = cur->data;
170 return g_list_delete_link(list, cur);
171 }
172
173 /**
174 * Reorder a GList of PurpleSrvResponses that have the same priority
175 * (aka "pref").
176 */
177 static void
178 srv_reorder(GList *list, int num)
179 {
180 int i;
181 GList *cur, *container_list = NULL;
182 PurpleSrvResponseContainer *container;
183
184 if (num < 2)
185 /* Nothing to sort */
186 return;
187
188 /* First build a list of container structs */
189 for (i = 0, cur = list; i < num; i++, cur = cur->next) {
190 container = g_new(PurpleSrvResponseContainer, 1);
191 container->response = cur->data;
192 container_list = g_list_prepend(container_list, container);
193 }
194 container_list = g_list_reverse(container_list);
195
196 /*
197 * Re-order the list that was passed in as a parameter. We leave
198 * the list nodes in place, but replace their data pointers.
199 */
200 cur = list;
201 while (container_list) {
202 container_list = select_random_response(container_list, &container);
203 cur->data = container->response;
204 g_free(container);
205 cur = cur->next;
206 }
207 }
208
209 /**
210 * Sorts a GList of PurpleSrvResponses according to the
211 * algorithm described in RFC 2782.
212 *
213 * @param response GList of PurpleSrvResponse's
214 * @param The original list, resorted
215 */
216 static GList *
217 purple_srv_sort(GList *list)
218 {
219 int pref, count;
220 GList *cur, *start;
221
222 if (!list || !list->next) {
223 /* Nothing to sort */
224 return list;
225 }
226
227 list = g_list_sort(list, responsecompare);
228
229 start = cur = list;
230 count = 1;
231 while (cur) {
232 PurpleSrvResponse *next_response;
233 pref = ((PurpleSrvResponse *)cur->data)->pref;
234 next_response = cur->next ? cur->next->data : NULL;
235 if (!next_response || next_response->pref != pref) {
236 /*
237 * The 'count' records starting at 'start' all have the same
238 * priority. Sort them by weight.
239 */
240 srv_reorder(start, count);
241 start = cur->next;
242 count = 0;
243 }
244 count++;
245 cur = cur->next;
246 }
247
248 return list;
249 }
250
251 static gboolean
252 dns_str_is_ascii(const char *name)
253 {
254 guchar *c;
255 for (c = (guchar *)name; c && *c; ++c) {
256 if (*c > 0x7f)
257 return FALSE;
258 }
259
260 return TRUE;
261 }
262
95 #ifndef _WIN32 263 #ifndef _WIN32
96 264
97 G_GNUC_NORETURN static void 265 G_GNUC_NORETURN static void
98 resolve(int in, int out) 266 resolve(int in, int out)
99 { 267 {
100 GList *ret = NULL; 268 GList *ret = NULL;
101 PurpleSrvResponse *srvres; 269 PurpleSrvResponse *srvres;
270 PurpleTxtResponse *txtres;
102 queryans answer; 271 queryans answer;
103 int size; 272 int size, qdcount, ancount;
104 int qdcount; 273 guchar *end, *cp;
105 int ancount;
106 guchar *end;
107 guchar *cp;
108 gchar name[256]; 274 gchar name[256];
109 guint16 type, dlen, pref, weight, port; 275 guint16 type, dlen, pref, weight, port;
110 gchar query[256]; 276 PurpleSrvInternalQuery query;
111 277
112 #ifdef HAVE_SIGNAL_H 278 #ifdef HAVE_SIGNAL_H
113 purple_restore_default_signal_handlers(); 279 purple_restore_default_signal_handlers();
114 #endif 280 #endif
115 281
116 if (read(in, query, 256) <= 0) { 282 if (read(in, &query, sizeof(query)) <= 0) {
117 close(out); 283 close(out);
118 close(in); 284 close(in);
119 _exit(0); 285 _exit(0);
120 } 286 }
121 287
122 size = res_query( query, C_IN, T_SRV, (u_char*)&answer, sizeof( answer)); 288 size = res_query( query.query, C_IN, query.type, (u_char*)&answer, sizeof( answer));
289 if (size == -1) {
290 write(out, &(query.type), sizeof(query.type));
291 write(out, &size, sizeof(int));
292 close(out);
293 close(in);
294 _exit(0);
295 }
123 296
124 qdcount = ntohs(answer.hdr.qdcount); 297 qdcount = ntohs(answer.hdr.qdcount);
125 ancount = ntohs(answer.hdr.ancount); 298 ancount = ntohs(answer.hdr.ancount);
126
127 cp = (guchar*)&answer + sizeof(HEADER); 299 cp = (guchar*)&answer + sizeof(HEADER);
128 end = (guchar*)&answer + size; 300 end = (guchar*)&answer + size;
129 301
130 /* skip over unwanted stuff */ 302 /* skip over unwanted stuff */
131 while (qdcount-- > 0 && cp < end) { 303 while (qdcount-- > 0 && cp < end) {
136 308
137 while (ancount-- > 0 && cp < end) { 309 while (ancount-- > 0 && cp < end) {
138 size = dn_expand((unsigned char*)&answer, end, cp, name, 256); 310 size = dn_expand((unsigned char*)&answer, end, cp, name, 256);
139 if(size < 0) 311 if(size < 0)
140 goto end; 312 goto end;
141
142 cp += size; 313 cp += size;
143
144 GETSHORT(type,cp); 314 GETSHORT(type,cp);
145 315
146 /* skip ttl and class since we already know it */ 316 /* skip ttl and class since we already know it */
147 cp += 6; 317 cp += 6;
148 318
149 GETSHORT(dlen,cp); 319 GETSHORT(dlen,cp);
150 320 if (query.type == T_SRV) {
151 if (type == T_SRV) {
152 GETSHORT(pref,cp); 321 GETSHORT(pref,cp);
153 322
154 GETSHORT(weight,cp); 323 GETSHORT(weight,cp);
155 324
156 GETSHORT(port,cp); 325 GETSHORT(port,cp);
165 strcpy(srvres->hostname, name); 334 strcpy(srvres->hostname, name);
166 srvres->pref = pref; 335 srvres->pref = pref;
167 srvres->port = port; 336 srvres->port = port;
168 srvres->weight = weight; 337 srvres->weight = weight;
169 338
170 ret = g_list_insert_sorted(ret, srvres, responsecompare); 339 ret = g_list_prepend(ret, srvres);
340 } else if (query.type == T_TXT) {
341 txtres = g_new0(PurpleTxtResponse, 1);
342 txtres->content = g_strndup((gchar*)(++cp), dlen-1);
343 ret = g_list_append(ret, txtres);
344 cp += dlen - 1;
171 } else { 345 } else {
172 cp += dlen; 346 cp += dlen;
173 } 347 }
174 } 348 }
175 349
176 end: 350 end:
177 size = g_list_length(ret); 351 size = g_list_length(ret);
178 write(out, &size, sizeof(int)); 352
353 if (query.type == T_SRV)
354 ret = purple_srv_sort(ret);
355
356 /* TODO: Check return value */
357 write(out, &(query.type), sizeof(query.type));
358 write(out, &size, sizeof(size));
179 while (ret != NULL) 359 while (ret != NULL)
180 { 360 {
181 write(out, ret->data, sizeof(PurpleSrvResponse)); 361 /* TODO: Check return value */
362 if (query.type == T_SRV)
363 write(out, ret->data, sizeof(PurpleSrvResponse));
364 if (query.type == T_TXT)
365 write(out, ret->data, sizeof(PurpleTxtResponse));
366
182 g_free(ret->data); 367 g_free(ret->data);
183 ret = g_list_remove(ret, ret->data); 368 ret = g_list_remove(ret, ret->data);
184 } 369 }
185 370
186 close(out); 371 close(out);
191 376
192 static void 377 static void
193 resolved(gpointer data, gint source, PurpleInputCondition cond) 378 resolved(gpointer data, gint source, PurpleInputCondition cond)
194 { 379 {
195 int size; 380 int size;
381 int type;
196 PurpleSrvQueryData *query_data = (PurpleSrvQueryData*)data; 382 PurpleSrvQueryData *query_data = (PurpleSrvQueryData*)data;
197 PurpleSrvResponse *res;
198 PurpleSrvResponse *tmp;
199 int i; 383 int i;
200 PurpleSrvCallback cb = query_data->cb;
201 int status; 384 int status;
202 385
203 if (read(source, &size, sizeof(int)) == sizeof(int)) 386 if (read(source, &type, sizeof(type)) == sizeof(type)) {
204 { 387 if (read(source, &size, sizeof(size)) == sizeof(size)) {
205 ssize_t red; 388 if (size == -1 || size == 0) {
206 purple_debug_info("dnssrv","found %d SRV entries\n", size); 389 if (size == -1) {
207 tmp = res = g_new0(PurpleSrvResponse, size); 390 purple_debug_warning("dnssrv", "res_query returned an error\n");
208 for (i = 0; i < size; i++) { 391 /* Re-read resolv.conf and friends in case DNS servers have changed */
209 red = read(source, tmp++, sizeof(PurpleSrvResponse)); 392 res_init();
210 if (red != sizeof(PurpleSrvResponse)) { 393 } else
211 purple_debug_error("dnssrv","unable to read srv " 394 purple_debug_info("dnssrv", "Found 0 entries, errno is %i\n", errno);
212 "response: %s\n", g_strerror(errno)); 395
213 size = 0; 396 if (type == T_SRV) {
214 g_free(res); 397 PurpleSrvCallback cb = query_data->cb.srv;
215 res = NULL; 398 cb(NULL, 0, query_data->extradata);
399 } else if (type == T_TXT) {
400 PurpleTxtCallback cb = query_data->cb.txt;
401 cb(NULL, query_data->extradata);
402 } else {
403 purple_debug_error("dnssrv", "type unknown of DNS result entry; errno is %i\n", errno);
404 }
405
406 } else if (size) {
407 if (type == T_SRV) {
408 PurpleSrvResponse *res;
409 PurpleSrvResponse *tmp;
410 PurpleSrvCallback cb = query_data->cb.srv;
411 ssize_t red;
412 purple_debug_info("dnssrv","found %d SRV entries\n", size);
413 tmp = res = g_new0(PurpleSrvResponse, size);
414 for (i = 0; i < size; i++) {
415 red = read(source, tmp++, sizeof(PurpleSrvResponse));
416 if (red != sizeof(PurpleSrvResponse)) {
417 purple_debug_error("dnssrv","unable to read srv "
418 "response: %s\n", g_strerror(errno));
419 size = 0;
420 g_free(res);
421 res = NULL;
422 }
423 }
424
425 cb(res, size, query_data->extradata);
426 } else if (type == T_TXT) {
427 GList *responses = NULL;
428 PurpleTxtResponse *res;
429 PurpleTxtCallback cb = query_data->cb.txt;
430 ssize_t red;
431 purple_debug_info("dnssrv","found %d TXT entries\n", size);
432 res = g_new0(PurpleTxtResponse, 1);
433 for (i = 0; i < size; i++) {
434 red = read(source, res, sizeof(PurpleTxtResponse));
435 if (red != sizeof(PurpleTxtResponse)) {
436 purple_debug_error("dnssrv","unable to read txt "
437 "response: %s\n", g_strerror(errno));
438 size = 0;
439 g_free(res);
440 g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL);
441 g_list_free(responses);
442 responses = NULL;
443 break;
444 }
445 }
446
447 cb(responses, query_data->extradata);
448 } else {
449 purple_debug_error("dnssrv", "type unknown of DNS result entry; errno is %i\n", errno);
450 }
216 } 451 }
217 } 452 }
218 } 453 }
219 else 454
220 {
221 purple_debug_info("dnssrv","found 0 SRV entries; errno is %i\n", errno);
222 size = 0;
223 res = NULL;
224 }
225
226 cb(res, size, query_data->extradata);
227 waitpid(query_data->pid, &status, 0); 455 waitpid(query_data->pid, &status, 0);
228
229 purple_srv_cancel(query_data); 456 purple_srv_cancel(query_data);
230 } 457 }
231 458
232 #else /* _WIN32 */ 459 #else /* _WIN32 */
233 460
235 462
236 static gboolean 463 static gboolean
237 res_main_thread_cb(gpointer data) 464 res_main_thread_cb(gpointer data)
238 { 465 {
239 PurpleSrvResponse *srvres = NULL; 466 PurpleSrvResponse *srvres = NULL;
240 int size = 0;
241 PurpleSrvQueryData *query_data = data; 467 PurpleSrvQueryData *query_data = data;
242
243 if(query_data->error_message != NULL) 468 if(query_data->error_message != NULL)
244 purple_debug_error("dnssrv", query_data->error_message); 469 purple_debug_error("dnssrv", query_data->error_message);
245 else { 470 else {
246 PurpleSrvResponse *srvres_tmp = NULL; 471 if (query_data->type == DNS_TYPE_SRV) {
247 GSList *lst = query_data->results; 472 PurpleSrvResponse *srvres_tmp = NULL;
248 473 GList *lst = query_data->results;
249 size = g_slist_length(lst); 474 int size = g_list_length(lst);
250 475
251 if(query_data->cb && size > 0) 476 if(query_data->cb.srv && size > 0)
252 srvres_tmp = srvres = g_new0(PurpleSrvResponse, size); 477 srvres_tmp = srvres = g_new0(PurpleSrvResponse, size);
253 while (lst) { 478 while (lst) {
254 if(query_data->cb) 479 PurpleSrvResponse *lstdata = lst->data;
255 memcpy(srvres_tmp++, lst->data, sizeof(PurpleSrvResponse)); 480 lst = g_list_delete_link(lst, lst);
256 g_free(lst->data); 481
257 lst = g_slist_remove(lst, lst->data); 482 if(query_data->cb.srv)
258 } 483 memcpy(srvres_tmp++, lstdata, sizeof(PurpleSrvResponse));
259 484 g_free(lstdata);
260 query_data->results = NULL; 485 }
261 486
262 purple_debug_info("dnssrv", "found %d SRV entries\n", size); 487 query_data->results = NULL;
263 } 488
264 489 purple_debug_info("dnssrv", "found %d SRV entries\n", size);
265 if(query_data->cb) 490
266 query_data->cb(srvres, size, query_data->extradata); 491 if(query_data->cb.srv) query_data->cb.srv(srvres, size, query_data->extradata);
492 } else if (query_data->type == DNS_TYPE_TXT) {
493 GList *lst = query_data->results;
494
495 purple_debug_info("dnssrv", "found %d TXT entries\n", g_list_length(lst));
496
497 if (query_data->cb.txt) {
498 query_data->results = NULL;
499 query_data->cb.txt(lst, query_data->extradata);
500 }
501 } else {
502 purple_debug_error("dnssrv", "unknown query type");
503 }
504 }
267 505
268 query_data->resolver = NULL; 506 query_data->resolver = NULL;
269 query_data->handle = 0; 507 query_data->handle = 0;
270 508
271 purple_srv_cancel(query_data); 509 purple_srv_cancel(query_data);
275 513
276 static gpointer 514 static gpointer
277 res_thread(gpointer data) 515 res_thread(gpointer data)
278 { 516 {
279 PDNS_RECORD dr = NULL; 517 PDNS_RECORD dr = NULL;
280 int type = DNS_TYPE_SRV; 518 int type;
281 DNS_STATUS ds; 519 DNS_STATUS ds;
282 PurpleSrvQueryData *query_data = data; 520 PurpleSrvQueryData *query_data = data;
283 521 type = query_data->type;
284 ds = MyDnsQuery_UTF8(query_data->query, type, DNS_QUERY_STANDARD, NULL, &dr, NULL); 522 ds = MyDnsQuery_UTF8(query_data->query, type, DNS_QUERY_STANDARD, NULL, &dr, NULL);
285 if (ds != ERROR_SUCCESS) { 523 if (ds != ERROR_SUCCESS) {
286 gchar *msg = g_win32_error_message(ds); 524 gchar *msg = g_win32_error_message(ds);
287 query_data->error_message = g_strdup_printf("Couldn't look up SRV record. %s (%lu).\n", msg, ds); 525 if (type == DNS_TYPE_SRV) {
526 query_data->error_message = g_strdup_printf("Couldn't look up SRV record. %s (%lu).\n", msg, ds);
527 } else if (type == DNS_TYPE_TXT) {
528 query_data->error_message = g_strdup_printf("Couldn't look up TXT record. %s (%lu).\n", msg, ds);
529 }
288 g_free(msg); 530 g_free(msg);
289 } else { 531 } else {
290 PDNS_RECORD dr_tmp; 532 if (type == DNS_TYPE_SRV) {
291 GSList *lst = NULL; 533 PDNS_RECORD dr_tmp;
292 DNS_SRV_DATA *srv_data; 534 GList *lst = NULL;
293 PurpleSrvResponse *srvres; 535 DNS_SRV_DATA *srv_data;
294 536 PurpleSrvResponse *srvres;
295 for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) { 537
296 /* Discard any incorrect entries. I'm not sure if this is necessary */ 538 for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) {
297 if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) { 539 /* Discard any incorrect entries. I'm not sure if this is necessary */
298 continue; 540 if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) {
541 continue;
542 }
543
544 srv_data = &dr_tmp->Data.SRV;
545 srvres = g_new0(PurpleSrvResponse, 1);
546 strncpy(srvres->hostname, srv_data->pNameTarget, 255);
547 srvres->hostname[255] = '\0';
548 srvres->pref = srv_data->wPriority;
549 srvres->port = srv_data->wPort;
550 srvres->weight = srv_data->wWeight;
551
552 lst = g_list_prepend(lst, srvres);
299 } 553 }
300 554
301 srv_data = &dr_tmp->Data.SRV; 555 MyDnsRecordListFree(dr, DnsFreeRecordList);
302 srvres = g_new0(PurpleSrvResponse, 1); 556 query_data->results = purple_srv_sort(lst);
303 strncpy(srvres->hostname, srv_data->pNameTarget, 255); 557 } else if (type == DNS_TYPE_TXT) {
304 srvres->hostname[255] = '\0'; 558 PDNS_RECORD dr_tmp;
305 srvres->pref = srv_data->wPriority; 559 GList *lst = NULL;
306 srvres->port = srv_data->wPort; 560 DNS_TXT_DATA *txt_data;
307 srvres->weight = srv_data->wWeight; 561 PurpleTxtResponse *txtres;
308 562
309 lst = g_slist_insert_sorted(lst, srvres, responsecompare); 563 for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) {
310 } 564 GString *s;
311 565 int i;
312 MyDnsRecordListFree(dr, DnsFreeRecordList); 566
313 query_data->results = lst; 567 /* Discard any incorrect entries. I'm not sure if this is necessary */
568 if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) {
569 continue;
570 }
571
572 txt_data = &dr_tmp->Data.TXT;
573 txtres = g_new0(PurpleTxtResponse, 1);
574
575 s = g_string_new("");
576 for (i = 0; i < txt_data->dwStringCount; ++i)
577 s = g_string_append(s, txt_data->pStringArray[i]);
578 txtres->content = g_string_free(s, FALSE);
579
580 lst = g_list_append(lst, txtres);
581 }
582
583 MyDnsRecordListFree(dr, DnsFreeRecordList);
584 query_data->results = lst;
585 } else {
586
587 }
314 } 588 }
315 589
316 /* back to main thread */ 590 /* back to main thread */
317 /* Note: this should *not* be attached to query_data->handle - it will cause leakage */ 591 /* Note: this should *not* be attached to query_data->handle - it will cause leakage */
318 purple_timeout_add(0, res_main_thread_cb, query_data); 592 purple_timeout_add(0, res_main_thread_cb, query_data);
325 599
326 PurpleSrvQueryData * 600 PurpleSrvQueryData *
327 purple_srv_resolve(const char *protocol, const char *transport, const char *domain, PurpleSrvCallback cb, gpointer extradata) 601 purple_srv_resolve(const char *protocol, const char *transport, const char *domain, PurpleSrvCallback cb, gpointer extradata)
328 { 602 {
329 char *query; 603 char *query;
604 char *hostname;
330 PurpleSrvQueryData *query_data; 605 PurpleSrvQueryData *query_data;
331 #ifndef _WIN32 606 #ifndef _WIN32
607 PurpleSrvInternalQuery internal_query;
332 int in[2], out[2]; 608 int in[2], out[2];
333 int pid; 609 int pid;
334 #else 610 #else
335 GError* err = NULL; 611 GError* err = NULL;
336 static gboolean initialized = FALSE; 612 static gboolean initialized = FALSE;
340 purple_debug_error("dnssrv", "Wrong arguments\n"); 616 purple_debug_error("dnssrv", "Wrong arguments\n");
341 cb(NULL, 0, extradata); 617 cb(NULL, 0, extradata);
342 g_return_val_if_reached(NULL); 618 g_return_val_if_reached(NULL);
343 } 619 }
344 620
345 query = g_strdup_printf("_%s._%s.%s", protocol, transport, domain); 621 #ifdef USE_IDN
346 purple_debug_info("dnssrv","querying SRV record for %s\n", query); 622 if (!dns_str_is_ascii(domain)) {
623 int ret = purple_network_convert_idn_to_ascii(domain, &hostname);
624 if (ret != 0) {
625 purple_debug_error("dnssrv", "IDNA ToASCII failed\n");
626 cb(NULL, 0, extradata);
627 return NULL;
628 }
629 } else /* Fallthru is intentional */
630 #endif
631 hostname = g_strdup(domain);
632
633 query = g_strdup_printf("_%s._%s.%s", protocol, transport, hostname);
634 purple_debug_info("dnssrv","querying SRV record for %s: %s\n", domain,
635 query);
636 g_free(hostname);
347 637
348 #ifndef _WIN32 638 #ifndef _WIN32
349 if(pipe(in) || pipe(out)) { 639 if(pipe(in) || pipe(out)) {
350 purple_debug_error("dnssrv", "Could not create pipe\n"); 640 purple_debug_error("dnssrv", "Could not create pipe\n");
351 g_free(query); 641 g_free(query);
373 } 663 }
374 664
375 close(out[1]); 665 close(out[1]);
376 close(in[0]); 666 close(in[0]);
377 667
378 if (write(in[1], query, strlen(query)+1) < 0) 668 internal_query.type = T_SRV;
669 strncpy(internal_query.query, query, 255);
670
671 if (write(in[1], &internal_query, sizeof(internal_query)) < 0)
379 purple_debug_error("dnssrv", "Could not write to SRV resolver\n"); 672 purple_debug_error("dnssrv", "Could not write to SRV resolver\n");
380 673
381 query_data = g_new0(PurpleSrvQueryData, 1); 674 query_data = g_new0(PurpleSrvQueryData, 1);
382 query_data->cb = cb; 675 query_data->type = T_SRV;
676 query_data->cb.srv = cb;
383 query_data->extradata = extradata; 677 query_data->extradata = extradata;
384 query_data->pid = pid; 678 query_data->pid = pid;
385 query_data->fd_out = out[0]; 679 query_data->fd_out = out[0];
386 query_data->fd_in = in[1]; 680 query_data->fd_in = in[1];
387 query_data->handle = purple_input_add(out[0], PURPLE_INPUT_READ, resolved, query_data); 681 query_data->handle = purple_input_add(out[0], PURPLE_INPUT_READ, resolved, query_data);
396 "dnsapi.dll", "DnsRecordListFree"); 690 "dnsapi.dll", "DnsRecordListFree");
397 initialized = TRUE; 691 initialized = TRUE;
398 } 692 }
399 693
400 query_data = g_new0(PurpleSrvQueryData, 1); 694 query_data = g_new0(PurpleSrvQueryData, 1);
401 query_data->cb = cb; 695 query_data->type = DNS_TYPE_SRV;
696 query_data->cb.srv = cb;
402 query_data->query = query; 697 query_data->query = query;
403 query_data->extradata = extradata; 698 query_data->extradata = extradata;
404 699
405 if (!MyDnsQuery_UTF8 || !MyDnsRecordListFree) 700 if (!MyDnsQuery_UTF8 || !MyDnsRecordListFree)
406 query_data->error_message = g_strdup("System missing DNS API (Requires W2K+)\n"); 701 query_data->error_message = g_strdup("System missing DNS API (Requires W2K+)\n");
411 g_error_free(err); 706 g_error_free(err);
412 } 707 }
413 } 708 }
414 709
415 /* The query isn't going to happen, so finish the SRV lookup now. 710 /* The query isn't going to happen, so finish the SRV lookup now.
711 * Asynchronously call the callback since stuff may not expect
712 * the callback to be called before this returns */
713 if (query_data->error_message != NULL)
714 query_data->handle = purple_timeout_add(0, res_main_thread_cb, query_data);
715
716 return query_data;
717 #endif
718 }
719
720 PurpleSrvQueryData *purple_txt_resolve(const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata)
721 {
722 char *query;
723 char *hostname;
724 PurpleSrvQueryData *query_data;
725 #ifndef _WIN32
726 PurpleSrvInternalQuery internal_query;
727 int in[2], out[2];
728 int pid;
729 #else
730 GError* err = NULL;
731 static gboolean initialized = FALSE;
732 #endif
733
734 #ifdef USE_IDN
735 if (!dns_str_is_ascii(domain)) {
736 int ret = purple_network_convert_idn_to_ascii(domain, &hostname);
737 if (ret != 0) {
738 purple_debug_error("dnssrv", "IDNA ToASCII failed\n");
739 cb(NULL, extradata);
740 return NULL;
741 }
742 } else /* fallthru is intentional */
743 #endif
744 hostname = g_strdup(domain);
745
746 query = g_strdup_printf("%s.%s", owner, hostname);
747 purple_debug_info("dnssrv","querying TXT record for %s: %s\n", domain,
748 query);
749 g_free(hostname);
750
751 #ifndef _WIN32
752 if(pipe(in) || pipe(out)) {
753 purple_debug_error("dnssrv", "Could not create pipe\n");
754 g_free(query);
755 cb(NULL, extradata);
756 return NULL;
757 }
758
759 pid = fork();
760 if (pid == -1) {
761 purple_debug_error("dnssrv", "Could not create process!\n");
762 cb(NULL, extradata);
763 g_free(query);
764 return NULL;
765 }
766
767 /* Child */
768 if (pid == 0)
769 {
770 g_free(query);
771
772 close(out[0]);
773 close(in[1]);
774 resolve(in[0], out[1]);
775 /* resolve() does not return */
776 }
777
778 close(out[1]);
779 close(in[0]);
780
781 internal_query.type = T_TXT;
782 strncpy(internal_query.query, query, 255);
783
784 if (write(in[1], &internal_query, sizeof(internal_query)) < 0)
785 purple_debug_error("dnssrv", "Could not write to TXT resolver\n");
786
787 query_data = g_new0(PurpleSrvQueryData, 1);
788 query_data->type = T_TXT;
789 query_data->cb.txt = cb;
790 query_data->extradata = extradata;
791 query_data->pid = pid;
792 query_data->fd_out = out[0];
793 query_data->fd_in = in[1];
794 query_data->handle = purple_input_add(out[0], PURPLE_INPUT_READ, resolved, query_data);
795
796 g_free(query);
797
798 return query_data;
799 #else
800 if (!initialized) {
801 MyDnsQuery_UTF8 = (void*) wpurple_find_and_loadproc("dnsapi.dll", "DnsQuery_UTF8");
802 MyDnsRecordListFree = (void*) wpurple_find_and_loadproc(
803 "dnsapi.dll", "DnsRecordListFree");
804 initialized = TRUE;
805 }
806
807 query_data = g_new0(PurpleSrvQueryData, 1);
808 query_data->type = DNS_TYPE_TXT;
809 query_data->cb.txt = cb;
810 query_data->query = query;
811 query_data->extradata = extradata;
812
813 if (!MyDnsQuery_UTF8 || !MyDnsRecordListFree)
814 query_data->error_message = g_strdup("System missing DNS API (Requires W2K+)\n");
815 else {
816 query_data->resolver = g_thread_create(res_thread, query_data, FALSE, &err);
817 if (query_data->resolver == NULL) {
818 query_data->error_message = g_strdup_printf("TXT thread create failure: %s\n", (err && err->message) ? err->message : "");
819 g_error_free(err);
820 }
821 }
822
823 /* The query isn't going to happen, so finish the TXT lookup now.
416 * Asynchronously call the callback since stuff may not expect 824 * Asynchronously call the callback since stuff may not expect
417 * the callback to be called before this returns */ 825 * the callback to be called before this returns */
418 if (query_data->error_message != NULL) 826 if (query_data->error_message != NULL)
419 query_data->handle = purple_timeout_add(0, res_main_thread_cb, query_data); 827 query_data->handle = purple_timeout_add(0, res_main_thread_cb, query_data);
420 828
433 /* 841 /*
434 * It's not really possible to kill a thread. So instead we 842 * It's not really possible to kill a thread. So instead we
435 * just set the callback to NULL and let the DNS lookup 843 * just set the callback to NULL and let the DNS lookup
436 * finish. 844 * finish.
437 */ 845 */
438 query_data->cb = NULL; 846 query_data->cb.srv = NULL;
439 return; 847 return;
440 } 848 }
441 g_free(query_data->query); 849 g_free(query_data->query);
442 g_free(query_data->error_message); 850 g_free(query_data->error_message);
443 #else 851 #else
444 close(query_data->fd_out); 852 close(query_data->fd_out);
445 close(query_data->fd_in); 853 close(query_data->fd_in);
446 #endif 854 #endif
447 g_free(query_data); 855 g_free(query_data);
448 } 856 }
857
858 void
859 purple_txt_cancel(PurpleSrvQueryData *query_data)
860 {
861 purple_srv_cancel(query_data);
862 }
863
864 const gchar *
865 purple_txt_response_get_content(PurpleTxtResponse *resp)
866 {
867 g_return_val_if_fail(resp != NULL, NULL);
868
869 return resp->content;
870 }
871
872 void purple_txt_response_destroy(PurpleTxtResponse *resp)
873 {
874 g_return_if_fail(resp != NULL);
875
876 g_free(resp->content);
877 g_free(resp);
878 }