Mercurial > pidgin.yaz
comparison libgaim/dnsquery.c @ 14287:e01a8316b08b
[gaim-migrate @ 16974]
Revert SVN revisions 16945 and 16926. Go back to
having 3 DNS lookup methods. It seems like people
tend to prefer this. Sigh.
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Tue, 22 Aug 2006 06:17:43 +0000 |
parents | d7b40a686355 |
children | fdf65b672b87 |
comparison
equal
deleted
inserted
replaced
14286:a0394e417fab | 14287:e01a8316b08b |
---|---|
42 int port; | 42 int port; |
43 GaimDnsQueryConnectFunction callback; | 43 GaimDnsQueryConnectFunction callback; |
44 gpointer data; | 44 gpointer data; |
45 guint timeout; | 45 guint timeout; |
46 | 46 |
47 #if defined(__unix__) || defined(__APPLE__) | |
48 GaimDnsQueryResolverProcess *resolver; | |
49 #elif defined _WIN32 /* end __unix__ || __APPLE__ */ | |
47 GThread *resolver; | 50 GThread *resolver; |
48 GSList *hosts; | 51 GSList *hosts; |
49 gchar *error_message; | 52 gchar *error_message; |
53 #endif | |
50 }; | 54 }; |
55 | |
56 #if defined(__unix__) || defined(__APPLE__) | |
57 | |
58 #define MAX_DNS_CHILDREN 4 | |
59 | |
60 /* | |
61 * This structure keeps a reference to a child resolver process. | |
62 */ | |
63 struct _GaimDnsQueryResolverProcess { | |
64 guint inpa; | |
65 int fd_in, fd_out; | |
66 pid_t dns_pid; | |
67 }; | |
68 | |
69 static GSList *free_dns_children = NULL; | |
70 static GQueue *queued_requests = NULL; | |
71 | |
72 static int number_of_dns_children = 0; | |
73 | |
74 /* | |
75 * This is a convenience struct used to pass data to | |
76 * the child resolver process. | |
77 */ | |
78 typedef struct { | |
79 char hostname[512]; | |
80 int port; | |
81 } dns_params_t; | |
82 #endif | |
51 | 83 |
52 static void | 84 static void |
53 gaim_dnsquery_resolved(GaimDnsQueryData *query_data, GSList *hosts) | 85 gaim_dnsquery_resolved(GaimDnsQueryData *query_data, GSList *hosts) |
54 { | 86 { |
55 gaim_debug_info("dnsquery", "IP resolved for %s\n", query_data->hostname); | 87 gaim_debug_info("dnsquery", "IP resolved for %s\n", query_data->hostname); |
65 if (query_data->callback != NULL) | 97 if (query_data->callback != NULL) |
66 query_data->callback(NULL, query_data->data, error_message); | 98 query_data->callback(NULL, query_data->data, error_message); |
67 gaim_dnsquery_destroy(query_data); | 99 gaim_dnsquery_destroy(query_data); |
68 } | 100 } |
69 | 101 |
102 #if defined(__unix__) || defined(__APPLE__) | |
103 | |
104 /* | |
105 * Unix! | |
106 */ | |
107 | |
108 /* | |
109 * Begin the DNS resolver child process functions. | |
110 */ | |
111 #ifdef HAVE_SIGNAL_H | |
112 static void | |
113 trap_gdb_bug() | |
114 { | |
115 const char *message = | |
116 "Gaim's DNS child got a SIGTRAP signal.\n" | |
117 "This can be caused by trying to run gaim inside gdb.\n" | |
118 "There is a known gdb bug which prevents this. Supposedly gaim\n" | |
119 "should have detected you were using gdb and used an ugly hack,\n" | |
120 "check cope_with_gdb_brokenness() in dnsquery.c.\n\n" | |
121 "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n"; | |
122 fputs("\n* * *\n",stderr); | |
123 fputs(message,stderr); | |
124 fputs("* * *\n\n",stderr); | |
125 execlp("xmessage","xmessage","-center", message, NULL); | |
126 _exit(1); | |
127 } | |
128 #endif | |
129 | |
130 static void | |
131 gaim_dnsquery_resolver_run(int child_out, int child_in, gboolean show_debug) | |
132 { | |
133 dns_params_t dns_params; | |
134 const size_t zero = 0; | |
135 int rc; | |
136 #ifdef HAVE_GETADDRINFO | |
137 struct addrinfo hints, *res, *tmp; | |
138 char servname[20]; | |
139 #else | |
140 struct sockaddr_in sin; | |
141 const size_t addrlen = sizeof(sin); | |
142 #endif | |
143 | |
144 #ifdef HAVE_SIGNAL_H | |
145 signal(SIGHUP, SIG_DFL); | |
146 signal(SIGINT, SIG_DFL); | |
147 signal(SIGQUIT, SIG_DFL); | |
148 signal(SIGCHLD, SIG_DFL); | |
149 signal(SIGTERM, SIG_DFL); | |
150 signal(SIGTRAP, trap_gdb_bug); | |
151 #endif | |
152 | |
153 /* | |
154 * We resolve 1 host name for each iteration of this | |
155 * while loop. | |
156 * | |
157 * The top half of this reads in the hostname and port | |
158 * number from the socket with our parent. The bottom | |
159 * half of this resolves the IP (blocking) and sends | |
160 * the result back to our parent, when finished. | |
161 */ | |
162 while (1) { | |
163 const char ch = 'Y'; | |
164 fd_set fds; | |
165 struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 }; | |
166 FD_ZERO(&fds); | |
167 FD_SET(child_in, &fds); | |
168 rc = select(child_in + 1, &fds, NULL, NULL, &tv); | |
169 if (!rc) { | |
170 if (show_debug) | |
171 printf("dns[%d]: nobody needs me... =(\n", getpid()); | |
172 break; | |
173 } | |
174 rc = read(child_in, &dns_params, sizeof(dns_params_t)); | |
175 if (rc < 0) { | |
176 perror("read()"); | |
177 break; | |
178 } | |
179 if (rc == 0) { | |
180 if (show_debug) | |
181 printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid()); | |
182 _exit(0); | |
183 } | |
184 if (dns_params.hostname[0] == '\0') { | |
185 printf("dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port); | |
186 _exit(1); | |
187 } | |
188 /* Tell our parent that we read the data successfully */ | |
189 write(child_out, &ch, sizeof(ch)); | |
190 | |
191 /* We have the hostname and port, now resolve the IP */ | |
192 | |
193 #ifdef HAVE_GETADDRINFO | |
194 g_snprintf(servname, sizeof(servname), "%d", dns_params.port); | |
195 memset(&hints, 0, sizeof(hints)); | |
196 | |
197 /* This is only used to convert a service | |
198 * name to a port number. As we know we are | |
199 * passing a number already, we know this | |
200 * value will not be really used by the C | |
201 * library. | |
202 */ | |
203 hints.ai_socktype = SOCK_STREAM; | |
204 rc = getaddrinfo(dns_params.hostname, servname, &hints, &res); | |
205 write(child_out, &rc, sizeof(rc)); | |
206 if (rc != 0) { | |
207 close(child_out); | |
208 if (show_debug) | |
209 printf("dns[%d] Error: getaddrinfo returned %d\n", | |
210 getpid(), rc); | |
211 dns_params.hostname[0] = '\0'; | |
212 continue; | |
213 } | |
214 tmp = res; | |
215 while (res) { | |
216 size_t ai_addrlen = res->ai_addrlen; | |
217 write(child_out, &ai_addrlen, sizeof(ai_addrlen)); | |
218 write(child_out, res->ai_addr, res->ai_addrlen); | |
219 res = res->ai_next; | |
220 } | |
221 freeaddrinfo(tmp); | |
222 write(child_out, &zero, sizeof(zero)); | |
223 #else | |
224 if (!inet_aton(dns_params.hostname, &sin.sin_addr)) { | |
225 struct hostent *hp; | |
226 if (!(hp = gethostbyname(dns_params.hostname))) { | |
227 write(child_out, &h_errno, sizeof(int)); | |
228 close(child_out); | |
229 if (show_debug) | |
230 printf("DNS Error: %d\n", h_errno); | |
231 _exit(0); | |
232 } | |
233 memset(&sin, 0, sizeof(struct sockaddr_in)); | |
234 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); | |
235 sin.sin_family = hp->h_addrtype; | |
236 } else | |
237 sin.sin_family = AF_INET; | |
238 | |
239 sin.sin_port = htons(dns_params.port); | |
240 write(child_out, &addrlen, sizeof(addrlen)); | |
241 write(child_out, &sin, addrlen); | |
242 write(child_out, &zero, sizeof(zero)); | |
243 #endif | |
244 dns_params.hostname[0] = '\0'; | |
245 } | |
246 | |
247 close(child_out); | |
248 close(child_in); | |
249 | |
250 _exit(0); | |
251 } | |
252 /* | |
253 * End the DNS resolver child process functions. | |
254 */ | |
255 | |
256 /* | |
257 * Begin the functions for dealing with the DNS child processes. | |
258 */ | |
259 static void | |
260 cope_with_gdb_brokenness() | |
261 { | |
262 #ifdef __linux__ | |
263 static gboolean already_done = FALSE; | |
264 char s[256], e[512]; | |
265 int n; | |
266 pid_t ppid; | |
267 | |
268 if(already_done) | |
269 return; | |
270 already_done = TRUE; | |
271 ppid = getppid(); | |
272 snprintf(s, sizeof(s), "/proc/%d/exe", ppid); | |
273 n = readlink(s, e, sizeof(e)); | |
274 if(n < 0) | |
275 return; | |
276 | |
277 e[MIN(n,sizeof(e)-1)] = '\0'; | |
278 | |
279 if(strstr(e,"gdb")) { | |
280 gaim_debug_info("dns", | |
281 "Debugger detected, performing useless query...\n"); | |
282 gethostbyname("x.x.x.x.x"); | |
283 } | |
284 #endif | |
285 } | |
286 | |
287 static void | |
288 gaim_dnsquery_resolver_destroy(GaimDnsQueryResolverProcess *resolver) | |
289 { | |
290 g_return_if_fail(resolver != NULL); | |
291 | |
292 /* | |
293 * We might as well attempt to kill our child process. It really | |
294 * doesn't matter if this fails, because children will expire on | |
295 * their own after a few seconds. | |
296 */ | |
297 if (resolver->dns_pid > 0) | |
298 kill(resolver->dns_pid, SIGKILL); | |
299 | |
300 if (resolver->inpa != 0) | |
301 gaim_input_remove(resolver->inpa); | |
302 | |
303 close(resolver->fd_in); | |
304 close(resolver->fd_out); | |
305 | |
306 g_free(resolver); | |
307 | |
308 number_of_dns_children--; | |
309 } | |
310 | |
311 static GaimDnsQueryResolverProcess * | |
312 gaim_dnsquery_resolver_new(gboolean show_debug) | |
313 { | |
314 GaimDnsQueryResolverProcess *resolver; | |
315 int child_out[2], child_in[2]; | |
316 | |
317 /* Create pipes for communicating with the child process */ | |
318 if (pipe(child_out) || pipe(child_in)) { | |
319 gaim_debug_error("dns", | |
320 "Could not create pipes: %s\n", strerror(errno)); | |
321 return NULL; | |
322 } | |
323 | |
324 resolver = g_new(GaimDnsQueryResolverProcess, 1); | |
325 resolver->inpa = 0; | |
326 | |
327 cope_with_gdb_brokenness(); | |
328 | |
329 /* "Go fork and multiply." --Tommy Caldwell (Emily's dad, not the climber) */ | |
330 resolver->dns_pid = fork(); | |
331 | |
332 /* If we are the child process... */ | |
333 if (resolver->dns_pid == 0) { | |
334 /* We should not access the parent's side of the pipes, so close them */ | |
335 close(child_out[0]); | |
336 close(child_in[1]); | |
337 | |
338 gaim_dnsquery_resolver_run(child_out[1], child_in[0], show_debug); | |
339 /* The thread calls _exit() rather than returning, so we never get here */ | |
340 } | |
341 | |
342 /* We should not access the child's side of the pipes, so close them */ | |
343 close(child_out[1]); | |
344 close(child_in[0]); | |
345 if (resolver->dns_pid == -1) { | |
346 gaim_debug_error("dns", | |
347 "Could not create child process for DNS: %s\n", | |
348 strerror(errno)); | |
349 gaim_dnsquery_resolver_destroy(resolver); | |
350 return NULL; | |
351 } | |
352 | |
353 resolver->fd_out = child_out[0]; | |
354 resolver->fd_in = child_in[1]; | |
355 number_of_dns_children++; | |
356 gaim_debug_info("dns", | |
357 "Created new DNS child %d, there are now %d children.\n", | |
358 resolver->dns_pid, number_of_dns_children); | |
359 | |
360 return resolver; | |
361 } | |
362 | |
363 /** | |
364 * @return TRUE if the request was sent succesfully. FALSE | |
365 * if the request could not be sent. This isn't | |
366 * necessarily an error. If the child has expired, | |
367 * for example, we won't be able to send the message. | |
368 */ | |
369 static gboolean | |
370 send_dns_request_to_child(GaimDnsQueryData *query_data, | |
371 GaimDnsQueryResolverProcess *resolver) | |
372 { | |
373 pid_t pid; | |
374 dns_params_t dns_params; | |
375 int rc; | |
376 char ch; | |
377 | |
378 /* This waitpid might return the child's PID if it has recently | |
379 * exited, or it might return an error if it exited "long | |
380 * enough" ago that it has already been reaped; in either | |
381 * instance, we can't use it. */ | |
382 pid = waitpid(resolver->dns_pid, NULL, WNOHANG); | |
383 if (pid > 0) { | |
384 gaim_debug_warning("dns", "DNS child %d no longer exists\n", | |
385 resolver->dns_pid); | |
386 gaim_dnsquery_resolver_destroy(resolver); | |
387 return FALSE; | |
388 } else if (pid < 0) { | |
389 gaim_debug_warning("dns", "Wait for DNS child %d failed: %s\n", | |
390 resolver->dns_pid, strerror(errno)); | |
391 gaim_dnsquery_resolver_destroy(resolver); | |
392 return FALSE; | |
393 } | |
394 | |
395 /* Copy the hostname and port into a single data structure */ | |
396 strncpy(dns_params.hostname, query_data->hostname, sizeof(dns_params.hostname) - 1); | |
397 dns_params.hostname[sizeof(dns_params.hostname) - 1] = '\0'; | |
398 dns_params.port = query_data->port; | |
399 | |
400 /* Send the data structure to the child */ | |
401 rc = write(resolver->fd_in, &dns_params, sizeof(dns_params)); | |
402 if (rc < 0) { | |
403 gaim_debug_error("dns", "Unable to write to DNS child %d: %d\n", | |
404 resolver->dns_pid, strerror(errno)); | |
405 gaim_dnsquery_resolver_destroy(resolver); | |
406 return FALSE; | |
407 } | |
408 | |
409 g_return_val_if_fail(rc == sizeof(dns_params), -1); | |
410 | |
411 /* Did you hear me? (This avoids some race conditions) */ | |
412 rc = read(resolver->fd_out, &ch, sizeof(ch)); | |
413 if (rc != 1 || ch != 'Y') | |
414 { | |
415 gaim_debug_warning("dns", | |
416 "DNS child %d not responding. Killing it!\n", | |
417 resolver->dns_pid); | |
418 gaim_dnsquery_resolver_destroy(resolver); | |
419 return FALSE; | |
420 } | |
421 | |
422 gaim_debug_info("dns", | |
423 "Successfully sent DNS request to child %d\n", | |
424 resolver->dns_pid); | |
425 | |
426 query_data->resolver = resolver; | |
427 | |
428 return TRUE; | |
429 } | |
430 | |
431 static void host_resolved(gpointer data, gint source, GaimInputCondition cond); | |
432 | |
433 static void | |
434 handle_next_queued_request() | |
435 { | |
436 GaimDnsQueryData *query_data; | |
437 GaimDnsQueryResolverProcess *resolver; | |
438 | |
439 if ((queued_requests == NULL) || (g_queue_is_empty(queued_requests))) | |
440 /* No more DNS queries, yay! */ | |
441 return; | |
442 | |
443 query_data = g_queue_pop_head(queued_requests); | |
444 | |
445 /* | |
446 * If we have any children, attempt to have them perform the DNS | |
447 * query. If we're able to send the query then resolver will be | |
448 * set to the GaimDnsQueryResolverProcess. Otherwise, resolver | |
449 * will be NULL and we'll need to create a new DNS request child. | |
450 */ | |
451 while (free_dns_children != NULL) | |
452 { | |
453 resolver = free_dns_children->data; | |
454 free_dns_children = g_slist_remove(free_dns_children, resolver); | |
455 | |
456 if (send_dns_request_to_child(query_data, resolver)) | |
457 /* We found an acceptable child, yay */ | |
458 break; | |
459 } | |
460 | |
461 /* We need to create a new DNS request child */ | |
462 if (query_data->resolver == NULL) | |
463 { | |
464 if (number_of_dns_children >= MAX_DNS_CHILDREN) | |
465 { | |
466 /* Apparently all our children are busy */ | |
467 g_queue_push_head(queued_requests, query_data); | |
468 return; | |
469 } | |
470 | |
471 resolver = gaim_dnsquery_resolver_new(gaim_debug_is_enabled()); | |
472 if (resolver == NULL) | |
473 { | |
474 gaim_dnsquery_failed(query_data, _("Unable to create new resolver process\n")); | |
475 return; | |
476 } | |
477 if (!send_dns_request_to_child(query_data, resolver)) | |
478 { | |
479 gaim_dnsquery_failed(query_data, _("Unable to send request to resolver process\n")); | |
480 return; | |
481 } | |
482 } | |
483 | |
484 query_data->resolver->inpa = gaim_input_add(query_data->resolver->fd_out, | |
485 GAIM_INPUT_READ, host_resolved, query_data); | |
486 } | |
487 | |
488 /* | |
489 * End the functions for dealing with the DNS child processes. | |
490 */ | |
491 | |
492 static void | |
493 host_resolved(gpointer data, gint source, GaimInputCondition cond) | |
494 { | |
495 GaimDnsQueryData *query_data; | |
496 int rc, err; | |
497 GSList *hosts = NULL; | |
498 struct sockaddr *addr = NULL; | |
499 size_t addrlen; | |
500 char message[1024]; | |
501 | |
502 query_data = data; | |
503 | |
504 gaim_debug_info("dns", "Got response for '%s'\n", query_data->hostname); | |
505 gaim_input_remove(query_data->resolver->inpa); | |
506 query_data->resolver->inpa = 0; | |
507 | |
508 rc = read(query_data->resolver->fd_out, &err, sizeof(err)); | |
509 if ((rc == 4) && (err != 0)) | |
510 { | |
511 #ifdef HAVE_GETADDRINFO | |
512 g_snprintf(message, sizeof(message), _("Error resolving %s: %s"), | |
513 query_data->hostname, gai_strerror(err)); | |
514 #else | |
515 g_snprintf(message, sizeof(message), _("Error resolving %s: %d"), | |
516 query_data->hostname, err); | |
517 #endif | |
518 gaim_dnsquery_failed(query_data, message); | |
519 | |
520 } else if (rc > 0) { | |
521 /* Success! */ | |
522 while (rc > 0) { | |
523 rc = read(query_data->resolver->fd_out, &addrlen, sizeof(addrlen)); | |
524 if (rc > 0 && addrlen > 0) { | |
525 addr = g_malloc(addrlen); | |
526 rc = read(query_data->resolver->fd_out, addr, addrlen); | |
527 hosts = g_slist_append(hosts, GINT_TO_POINTER(addrlen)); | |
528 hosts = g_slist_append(hosts, addr); | |
529 } else { | |
530 break; | |
531 } | |
532 } | |
533 /* wait4(resolver->dns_pid, NULL, WNOHANG, NULL); */ | |
534 gaim_dnsquery_resolved(query_data, hosts); | |
535 | |
536 } else if (rc == -1) { | |
537 g_snprintf(message, sizeof(message), _("Error reading from resolver process: %s"), strerror(errno)); | |
538 gaim_dnsquery_failed(query_data, message); | |
539 | |
540 } else if (rc == 0) { | |
541 g_snprintf(message, sizeof(message), _("EOF while reading from resolver process")); | |
542 gaim_dnsquery_failed(query_data, message); | |
543 } | |
544 | |
545 handle_next_queued_request(); | |
546 } | |
547 | |
548 static gboolean | |
549 resolve_host(gpointer data) | |
550 { | |
551 GaimDnsQueryData *query_data; | |
552 | |
553 query_data = data; | |
554 query_data->timeout = 0; | |
555 | |
556 handle_next_queued_request(); | |
557 | |
558 return FALSE; | |
559 } | |
560 | |
561 GaimDnsQueryData * | |
562 gaim_dnsquery_a(const char *hostname, int port, | |
563 GaimDnsQueryConnectFunction callback, gpointer data) | |
564 { | |
565 GaimDnsQueryData *query_data; | |
566 | |
567 g_return_val_if_fail(hostname != NULL, NULL); | |
568 g_return_val_if_fail(port != 0, NULL); | |
569 | |
570 query_data = g_new(GaimDnsQueryData, 1); | |
571 query_data->hostname = g_strdup(hostname); | |
572 g_strstrip(query_data->hostname); | |
573 query_data->port = port; | |
574 query_data->callback = callback; | |
575 query_data->data = data; | |
576 query_data->resolver = NULL; | |
577 | |
578 if (!queued_requests) | |
579 queued_requests = g_queue_new(); | |
580 g_queue_push_tail(queued_requests, query_data); | |
581 | |
582 gaim_debug_info("dns", "DNS query for '%s' queued\n", query_data->hostname); | |
583 | |
584 query_data->timeout = gaim_timeout_add(0, resolve_host, query_data); | |
585 | |
586 return query_data; | |
587 } | |
588 | |
589 #elif defined _WIN32 /* end __unix__ || __APPLE__ */ | |
590 | |
591 /* | |
592 * Windows! | |
593 */ | |
594 | |
70 static gboolean | 595 static gboolean |
71 dns_main_thread_cb(gpointer data) | 596 dns_main_thread_cb(gpointer data) |
72 { | 597 { |
73 GaimDnsQueryData *query_data; | 598 GaimDnsQueryData *query_data; |
74 | 599 |
78 gaim_dnsquery_failed(query_data, query_data->error_message); | 603 gaim_dnsquery_failed(query_data, query_data->error_message); |
79 else | 604 else |
80 { | 605 { |
81 GSList *hosts; | 606 GSList *hosts; |
82 | 607 |
83 /* We don't want gaim_dns_query_resolved() to free hosts */ | 608 /* We don't want gaim_dns_query_resolved() to free(hosts) */ |
84 hosts = query_data->hosts; | 609 hosts = query_data->hosts; |
85 query_data->hosts = NULL; | 610 query_data->hosts = NULL; |
86 gaim_dnsquery_resolved(query_data, hosts); | 611 gaim_dnsquery_resolved(query_data, hosts); |
87 } | 612 } |
88 | 613 |
89 return FALSE; | 614 return FALSE; |
90 } | 615 } |
91 | 616 |
92 #ifdef HAVE_GETADDRINFO | |
93 static gpointer | 617 static gpointer |
94 dns_thread(gpointer data) | 618 dns_thread(gpointer data) |
95 { | 619 { |
96 GaimDnsQueryData *query_data; | 620 GaimDnsQueryData *query_data; |
621 #ifdef HAVE_GETADDRINFO | |
97 int rc; | 622 int rc; |
98 struct addrinfo hints, *res, *tmp; | 623 struct addrinfo hints, *res, *tmp; |
99 char servname[20]; | 624 char servname[20]; |
625 #else | |
626 struct sockaddr_in sin; | |
627 struct hostent *hp; | |
628 #endif | |
100 | 629 |
101 query_data = data; | 630 query_data = data; |
102 | 631 |
632 #ifdef HAVE_GETADDRINFO | |
103 g_snprintf(servname, sizeof(servname), "%d", query_data->port); | 633 g_snprintf(servname, sizeof(servname), "%d", query_data->port); |
104 memset(&hints,0,sizeof(hints)); | 634 memset(&hints,0,sizeof(hints)); |
105 | 635 |
106 /* | 636 /* |
107 * This is only used to convert a service | 637 * This is only used to convert a service |
122 } | 652 } |
123 freeaddrinfo(tmp); | 653 freeaddrinfo(tmp); |
124 } else { | 654 } else { |
125 query_data->error_message = g_strdup_printf(_("Error resolving %s: %s"), query_data->hostname, gai_strerror(rc)); | 655 query_data->error_message = g_strdup_printf(_("Error resolving %s: %s"), query_data->hostname, gai_strerror(rc)); |
126 } | 656 } |
127 | 657 #else |
128 /* We're done, tell the main thread to look at our results */ | 658 if ((hp = gethostbyname(query_data->hostname))) { |
659 memset(&sin, 0, sizeof(struct sockaddr_in)); | |
660 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); | |
661 sin.sin_family = hp->h_addrtype; | |
662 sin.sin_port = htons(query_data->port); | |
663 | |
664 query_data->hosts = g_slist_append(query_data->hosts, | |
665 GSIZE_TO_POINTER(sizeof(sin))); | |
666 query_data->hosts = g_slist_append(query_data->hosts, | |
667 g_memdup(&sin, sizeof(sin))); | |
668 } else { | |
669 query_data->error_message = g_strdup_printf(_("Error resolving %s: %d"), query_data->hostname, h_errno); | |
670 } | |
671 #endif | |
672 | |
673 /* back to main thread */ | |
129 g_idle_add(dns_main_thread_cb, query_data); | 674 g_idle_add(dns_main_thread_cb, query_data); |
130 | 675 |
131 return 0; | 676 return 0; |
132 } | 677 } |
133 #endif | |
134 | 678 |
135 static gboolean | 679 static gboolean |
136 resolve_host(gpointer data) | 680 resolve_host(gpointer data) |
137 { | 681 { |
138 GaimDnsQueryData *query_data; | 682 GaimDnsQueryData *query_data; |
155 hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); | 699 hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); |
156 gaim_dnsquery_resolved(query_data, hosts); | 700 gaim_dnsquery_resolved(query_data, hosts); |
157 } | 701 } |
158 else | 702 else |
159 { | 703 { |
160 #ifdef HAVE_GETADDRINFO | |
161 /* | 704 /* |
162 * Spin off a separate thread to perform the DNS lookup so | 705 * Spin off a separate thread to perform the DNS lookup so |
163 * that we don't block the UI. | 706 * that we don't block the UI. |
164 */ | 707 */ |
165 query_data->resolver = g_thread_create(dns_thread, | 708 query_data->resolver = g_thread_create(dns_thread, |
170 g_snprintf(message, sizeof(message), _("Thread creation failure: %s"), | 713 g_snprintf(message, sizeof(message), _("Thread creation failure: %s"), |
171 err ? err->message : _("Unknown reason")); | 714 err ? err->message : _("Unknown reason")); |
172 g_error_free(err); | 715 g_error_free(err); |
173 gaim_dnsquery_failed(query_data, message); | 716 gaim_dnsquery_failed(query_data, message); |
174 } | 717 } |
175 #else | |
176 struct sockaddr_in sin; | |
177 struct hostent *hp; | |
178 | |
179 /* | |
180 * gethostbyname() is not threadsafe, but gethostbyname_r() is a GNU | |
181 * extension. Unfortunately this means that we'll have to do a | |
182 * blocking DNS query for systems without GETADDRINFO. Fortunately | |
183 * this should be a very small number of systems. | |
184 */ | |
185 | |
186 if ((hp = gethostbyname(query_data->hostname))) { | |
187 memset(&sin, 0, sizeof(struct sockaddr_in)); | |
188 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); | |
189 sin.sin_family = hp->h_addrtype; | |
190 sin.sin_port = htons(query_data->port); | |
191 | |
192 query_data->hosts = g_slist_append(query_data->hosts, | |
193 GSIZE_TO_POINTER(sizeof(sin))); | |
194 query_data->hosts = g_slist_append(query_data->hosts, | |
195 g_memdup(&sin, sizeof(sin))); | |
196 } else { | |
197 query_data->error_message = g_strdup_printf(_("Error resolving %s: %d"), query_data->hostname, h_errno); | |
198 } | |
199 | |
200 /* We're done! */ | |
201 dns_main_thread(query_data); | |
202 #endif | |
203 } | 718 } |
204 | 719 |
205 return FALSE; | 720 return FALSE; |
206 } | 721 } |
207 | 722 |
229 query_data->timeout = gaim_timeout_add(0, resolve_host, query_data); | 744 query_data->timeout = gaim_timeout_add(0, resolve_host, query_data); |
230 | 745 |
231 return query_data; | 746 return query_data; |
232 } | 747 } |
233 | 748 |
749 #else /* not __unix__ or __APPLE__ or _WIN32 */ | |
750 | |
751 /* | |
752 * We weren't able to do anything fancier above, so use the | |
753 * fail-safe name resolution code, which is blocking. | |
754 */ | |
755 | |
756 static gboolean | |
757 resolve_host(gpointer data) | |
758 { | |
759 GaimDnsQueryData *query_data; | |
760 struct sockaddr_in sin; | |
761 GSList *hosts = NULL; | |
762 | |
763 query_data = data; | |
764 query_data->timeout = 0; | |
765 | |
766 if (!inet_aton(query_data->hostname, &sin.sin_addr)) { | |
767 struct hostent *hp; | |
768 if(!(hp = gethostbyname(query_data->hostname))) { | |
769 char message[1024]; | |
770 g_snprintf(message, sizeof(message), _("Error resolving %s: %d"), | |
771 query_data->hostname, h_errno); | |
772 gaim_dnsquery_failed(query_data, message); | |
773 return FALSE; | |
774 } | |
775 memset(&sin, 0, sizeof(struct sockaddr_in)); | |
776 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); | |
777 sin.sin_family = hp->h_addrtype; | |
778 } else | |
779 sin.sin_family = AF_INET; | |
780 sin.sin_port = htons(query_data->port); | |
781 | |
782 hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin))); | |
783 hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); | |
784 | |
785 gaim_dnsquery_resolved(query_data, hosts); | |
786 | |
787 return FALSE; | |
788 } | |
789 | |
790 GaimDnsQueryData * | |
791 gaim_dnsquery_a(const char *hostname, int port, | |
792 GaimDnsQueryConnectFunction callback, gpointer data) | |
793 { | |
794 GaimDnsQueryData *query_data; | |
795 | |
796 g_return_val_if_fail(hostname != NULL, NULL); | |
797 g_return_val_if_fail(port != 0, NULL); | |
798 | |
799 query_data = g_new(GaimDnsQueryData, 1); | |
800 query_data->hostname = g_strdup(hostname); | |
801 g_strstrip(query_data->hostname); | |
802 query_data->port = port; | |
803 query_data->callback = callback; | |
804 query_data->data = data; | |
805 | |
806 /* Don't call the callback before returning */ | |
807 query_data->timeout = gaim_timeout_add(0, resolve_host, query_data); | |
808 | |
809 return query_data; | |
810 } | |
811 | |
812 #endif /* not __unix__ or __APPLE__ or _WIN32 */ | |
813 | |
234 void | 814 void |
235 gaim_dnsquery_destroy(GaimDnsQueryData *query_data) | 815 gaim_dnsquery_destroy(GaimDnsQueryData *query_data) |
236 { | 816 { |
817 #if defined(__unix__) || defined(__APPLE__) | |
818 if (query_data->resolver != NULL) | |
819 /* | |
820 * Ideally we would tell our resolver child to stop resolving | |
821 * shit and then we would add it back to the free_dns_children | |
822 * linked list. However, it's hard to tell children stuff, | |
823 * they just don't listen. | |
824 */ | |
825 gaim_dnsquery_resolver_destroy(query_data->resolver); | |
826 #elif defined _WIN32 /* end __unix__ || __APPLE__ */ | |
237 if (query_data->resolver != NULL) | 827 if (query_data->resolver != NULL) |
238 { | 828 { |
239 /* | 829 /* |
240 * It's not really possible to kill a thread. So instead we | 830 * It's not really possible to kill a thread. So instead we |
241 * just set the callback to NULL and let the DNS lookup | 831 * just set the callback to NULL and let the DNS lookup |
252 /* Free the address... */ | 842 /* Free the address... */ |
253 g_free(query_data->hosts->data); | 843 g_free(query_data->hosts->data); |
254 query_data->hosts = g_slist_remove(query_data->hosts, query_data->hosts->data); | 844 query_data->hosts = g_slist_remove(query_data->hosts, query_data->hosts->data); |
255 } | 845 } |
256 g_free(query_data->error_message); | 846 g_free(query_data->error_message); |
847 #endif | |
257 | 848 |
258 if (query_data->timeout > 0) | 849 if (query_data->timeout > 0) |
259 gaim_timeout_remove(query_data->timeout); | 850 gaim_timeout_remove(query_data->timeout); |
260 | 851 |
261 g_free(query_data->hostname); | 852 g_free(query_data->hostname); |
263 } | 854 } |
264 | 855 |
265 void | 856 void |
266 gaim_dnsquery_init(void) | 857 gaim_dnsquery_init(void) |
267 { | 858 { |
859 #ifdef _WIN32 | |
268 if (!g_thread_supported()) | 860 if (!g_thread_supported()) |
269 g_thread_init(NULL); | 861 g_thread_init(NULL); |
862 #endif | |
270 } | 863 } |
271 | 864 |
272 void | 865 void |
273 gaim_dnsquery_uninit(void) | 866 gaim_dnsquery_uninit(void) |
274 { | 867 { |
275 } | 868 #if defined(__unix__) || defined(__APPLE__) |
869 while (free_dns_children != NULL) | |
870 { | |
871 gaim_dnsquery_resolver_destroy(free_dns_children->data); | |
872 free_dns_children = g_slist_remove(free_dns_children, free_dns_children->data); | |
873 } | |
874 #endif | |
875 } |