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 }