comparison libgaim/dnsquery.c @ 14244:935f8b258d1b

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