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