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