| 1 /** |
|
| 2 * @file dnsquery.c DNS query API |
|
| 3 * @ingroup core |
|
| 4 * |
|
| 5 * gaim |
|
| 6 * |
|
| 7 * Gaim is the legal property of its developers, whose names are too numerous |
|
| 8 * to list here. Please refer to the COPYRIGHT file distributed with this |
|
| 9 * source distribution. |
|
| 10 * |
|
| 11 * This program is free software; you can redistribute it and/or modify |
|
| 12 * it under the terms of the GNU General Public License as published by |
|
| 13 * the Free Software Foundation; either version 2 of the License, or |
|
| 14 * (at your option) any later version. |
|
| 15 * |
|
| 16 * This program is distributed in the hope that it will be useful, |
|
| 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 19 * GNU General Public License for more details. |
|
| 20 * |
|
| 21 * You should have received a copy of the GNU General Public License |
|
| 22 * along with this program; if not, write to the Free Software |
|
| 23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
| 24 * |
|
| 25 */ |
|
| 26 |
|
| 27 #include "internal.h" |
|
| 28 #include "debug.h" |
|
| 29 #include "notify.h" |
|
| 30 #include "prefs.h" |
|
| 31 #include "dnsquery.h" |
|
| 32 #include "util.h" |
|
| 33 |
|
| 34 /************************************************************************** |
|
| 35 * DNS query API |
|
| 36 **************************************************************************/ |
|
| 37 |
|
| 38 struct _GaimDnsQueryData { |
|
| 39 }; |
|
| 40 |
|
| 41 #if defined(__unix__) || defined(__APPLE__) |
|
| 42 |
|
| 43 #define MAX_DNS_CHILDREN 4 |
|
| 44 |
|
| 45 /* |
|
| 46 * This structure represents both a pending DNS request and |
|
| 47 * a free child process. |
|
| 48 */ |
|
| 49 typedef struct { |
|
| 50 char *host; |
|
| 51 int port; |
|
| 52 GaimDnsQueryConnectFunction callback; |
|
| 53 gpointer data; |
|
| 54 guint inpa; |
|
| 55 int fd_in, fd_out; |
|
| 56 pid_t dns_pid; |
|
| 57 } pending_dns_request_t; |
|
| 58 |
|
| 59 static GSList *free_dns_children = NULL; |
|
| 60 static GQueue *queued_requests = NULL; |
|
| 61 |
|
| 62 static int number_of_dns_children = 0; |
|
| 63 |
|
| 64 typedef struct { |
|
| 65 char hostname[512]; |
|
| 66 int port; |
|
| 67 } dns_params_t; |
|
| 68 |
|
| 69 typedef struct { |
|
| 70 dns_params_t params; |
|
| 71 GaimDnsQueryConnectFunction callback; |
|
| 72 gpointer data; |
|
| 73 } queued_dns_request_t; |
|
| 74 |
|
| 75 /* |
|
| 76 * Begin the DNS resolver child process functions. |
|
| 77 */ |
|
| 78 #ifdef HAVE_SIGNAL_H |
|
| 79 static void |
|
| 80 trap_gdb_bug() |
|
| 81 { |
|
| 82 const char *message = |
|
| 83 "Gaim's DNS child got a SIGTRAP signal.\n" |
|
| 84 "This can be caused by trying to run gaim inside gdb.\n" |
|
| 85 "There is a known gdb bug which prevents this. Supposedly gaim\n" |
|
| 86 "should have detected you were using gdb and used an ugly hack,\n" |
|
| 87 "check cope_with_gdb_brokenness() in dnsquery.c.\n\n" |
|
| 88 "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n"; |
|
| 89 fputs("\n* * *\n",stderr); |
|
| 90 fputs(message,stderr); |
|
| 91 fputs("* * *\n\n",stderr); |
|
| 92 execlp("xmessage","xmessage","-center", message, NULL); |
|
| 93 _exit(1); |
|
| 94 } |
|
| 95 #endif |
|
| 96 |
|
| 97 static void |
|
| 98 cope_with_gdb_brokenness() |
|
| 99 { |
|
| 100 #ifdef __linux__ |
|
| 101 static gboolean already_done = FALSE; |
|
| 102 char s[256], e[512]; |
|
| 103 int n; |
|
| 104 pid_t ppid; |
|
| 105 |
|
| 106 if(already_done) |
|
| 107 return; |
|
| 108 already_done = TRUE; |
|
| 109 ppid = getppid(); |
|
| 110 snprintf(s, sizeof(s), "/proc/%d/exe", ppid); |
|
| 111 n = readlink(s, e, sizeof(e)); |
|
| 112 if(n < 0) |
|
| 113 return; |
|
| 114 |
|
| 115 e[MIN(n,sizeof(e)-1)] = '\0'; |
|
| 116 |
|
| 117 if(strstr(e,"gdb")) { |
|
| 118 gaim_debug_info("dns", |
|
| 119 "Debugger detected, performing useless query...\n"); |
|
| 120 gethostbyname("x.x.x.x.x"); |
|
| 121 } |
|
| 122 #endif |
|
| 123 } |
|
| 124 |
|
| 125 static void |
|
| 126 gaim_dns_resolverthread(int child_out, int child_in, gboolean show_debug) |
|
| 127 { |
|
| 128 dns_params_t dns_params; |
|
| 129 const size_t zero = 0; |
|
| 130 int rc; |
|
| 131 #ifdef HAVE_GETADDRINFO |
|
| 132 struct addrinfo hints, *res, *tmp; |
|
| 133 char servname[20]; |
|
| 134 #else |
|
| 135 struct sockaddr_in sin; |
|
| 136 const size_t addrlen = sizeof(sin); |
|
| 137 #endif |
|
| 138 |
|
| 139 #ifdef HAVE_SIGNAL_H |
|
| 140 signal(SIGHUP, SIG_DFL); |
|
| 141 signal(SIGINT, SIG_DFL); |
|
| 142 signal(SIGQUIT, SIG_DFL); |
|
| 143 signal(SIGCHLD, SIG_DFL); |
|
| 144 signal(SIGTERM, SIG_DFL); |
|
| 145 signal(SIGTRAP, trap_gdb_bug); |
|
| 146 #endif |
|
| 147 |
|
| 148 /* |
|
| 149 * We resolve 1 host name for each iteration of this |
|
| 150 * while loop. |
|
| 151 * |
|
| 152 * The top half of this reads in the hostname and port |
|
| 153 * number from the socket with our parent. The bottom |
|
| 154 * half of this resolves the IP (blocking) and sends |
|
| 155 * the result back to our parent, when finished. |
|
| 156 */ |
|
| 157 while (1) { |
|
| 158 const char ch = 'Y'; |
|
| 159 fd_set fds; |
|
| 160 struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 }; |
|
| 161 FD_ZERO(&fds); |
|
| 162 FD_SET(child_in, &fds); |
|
| 163 rc = select(child_in + 1, &fds, NULL, NULL, &tv); |
|
| 164 if (!rc) { |
|
| 165 if (show_debug) |
|
| 166 printf("dns[%d]: nobody needs me... =(\n", getpid()); |
|
| 167 break; |
|
| 168 } |
|
| 169 rc = read(child_in, &dns_params, sizeof(dns_params_t)); |
|
| 170 if (rc < 0) { |
|
| 171 perror("read()"); |
|
| 172 break; |
|
| 173 } |
|
| 174 if (rc == 0) { |
|
| 175 if (show_debug) |
|
| 176 printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid()); |
|
| 177 _exit(0); |
|
| 178 } |
|
| 179 if (dns_params.hostname[0] == '\0') { |
|
| 180 printf("dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port); |
|
| 181 _exit(1); |
|
| 182 } |
|
| 183 /* Tell our parent that we read the data successfully */ |
|
| 184 write(child_out, &ch, sizeof(ch)); |
|
| 185 |
|
| 186 /* We have the hostname and port, now resolve the IP */ |
|
| 187 |
|
| 188 #ifdef HAVE_GETADDRINFO |
|
| 189 g_snprintf(servname, sizeof(servname), "%d", dns_params.port); |
|
| 190 memset(&hints, 0, sizeof(hints)); |
|
| 191 |
|
| 192 /* This is only used to convert a service |
|
| 193 * name to a port number. As we know we are |
|
| 194 * passing a number already, we know this |
|
| 195 * value will not be really used by the C |
|
| 196 * library. |
|
| 197 */ |
|
| 198 hints.ai_socktype = SOCK_STREAM; |
|
| 199 rc = getaddrinfo(dns_params.hostname, servname, &hints, &res); |
|
| 200 write(child_out, &rc, sizeof(rc)); |
|
| 201 if (rc != 0) { |
|
| 202 close(child_out); |
|
| 203 if (show_debug) |
|
| 204 printf("dns[%d] Error: getaddrinfo returned %d\n", |
|
| 205 getpid(), rc); |
|
| 206 dns_params.hostname[0] = '\0'; |
|
| 207 continue; |
|
| 208 } |
|
| 209 tmp = res; |
|
| 210 while (res) { |
|
| 211 size_t ai_addrlen = res->ai_addrlen; |
|
| 212 write(child_out, &ai_addrlen, sizeof(ai_addrlen)); |
|
| 213 write(child_out, res->ai_addr, res->ai_addrlen); |
|
| 214 res = res->ai_next; |
|
| 215 } |
|
| 216 freeaddrinfo(tmp); |
|
| 217 write(child_out, &zero, sizeof(zero)); |
|
| 218 #else |
|
| 219 if (!inet_aton(dns_params.hostname, &sin.sin_addr)) { |
|
| 220 struct hostent *hp; |
|
| 221 if (!(hp = gethostbyname(dns_params.hostname))) { |
|
| 222 write(child_out, &h_errno, sizeof(int)); |
|
| 223 close(child_out); |
|
| 224 if (show_debug) |
|
| 225 printf("DNS Error: %d\n", h_errno); |
|
| 226 _exit(0); |
|
| 227 } |
|
| 228 memset(&sin, 0, sizeof(struct sockaddr_in)); |
|
| 229 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); |
|
| 230 sin.sin_family = hp->h_addrtype; |
|
| 231 } else |
|
| 232 sin.sin_family = AF_INET; |
|
| 233 |
|
| 234 sin.sin_port = htons(dns_params.port); |
|
| 235 write(child_out, &addrlen, sizeof(addrlen)); |
|
| 236 write(child_out, &sin, addrlen); |
|
| 237 write(child_out, &zero, sizeof(zero)); |
|
| 238 #endif |
|
| 239 dns_params.hostname[0] = '\0'; |
|
| 240 } |
|
| 241 |
|
| 242 close(child_out); |
|
| 243 close(child_in); |
|
| 244 |
|
| 245 _exit(0); |
|
| 246 } |
|
| 247 |
|
| 248 static pending_dns_request_t * |
|
| 249 gaim_dns_new_resolverthread(gboolean show_debug) |
|
| 250 { |
|
| 251 pending_dns_request_t *req; |
|
| 252 int child_out[2], child_in[2]; |
|
| 253 |
|
| 254 /* Create pipes for communicating with the child process */ |
|
| 255 if (pipe(child_out) || pipe(child_in)) { |
|
| 256 gaim_debug_error("dns", |
|
| 257 "Could not create pipes: %s\n", strerror(errno)); |
|
| 258 return NULL; |
|
| 259 } |
|
| 260 |
|
| 261 req = g_new(pending_dns_request_t, 1); |
|
| 262 |
|
| 263 cope_with_gdb_brokenness(); |
|
| 264 |
|
| 265 /* Fork! */ |
|
| 266 req->dns_pid = fork(); |
|
| 267 |
|
| 268 /* If we are the child process... */ |
|
| 269 if (req->dns_pid == 0) { |
|
| 270 /* We should not access the parent's side of the pipes, so close them */ |
|
| 271 close(child_out[0]); |
|
| 272 close(child_in[1]); |
|
| 273 |
|
| 274 gaim_dns_resolverthread(child_out[1], child_in[0], show_debug); |
|
| 275 /* The thread calls _exit() rather than returning, so we never get here */ |
|
| 276 } |
|
| 277 |
|
| 278 /* We should not access the child's side of the pipes, so close them */ |
|
| 279 close(child_out[1]); |
|
| 280 close(child_in[0]); |
|
| 281 if (req->dns_pid == -1) { |
|
| 282 gaim_debug_error("dns", |
|
| 283 "Could not create child process for DNS: %s\n", |
|
| 284 strerror(errno)); |
|
| 285 g_free(req); |
|
| 286 return NULL; |
|
| 287 } |
|
| 288 |
|
| 289 req->fd_out = child_out[0]; |
|
| 290 req->fd_in = child_in[1]; |
|
| 291 number_of_dns_children++; |
|
| 292 gaim_debug_info("dns", |
|
| 293 "Created new DNS child %d, there are now %d children.\n", |
|
| 294 req->dns_pid, number_of_dns_children); |
|
| 295 |
|
| 296 return req; |
|
| 297 } |
|
| 298 /* |
|
| 299 * End the DNS resolver child process functions. |
|
| 300 */ |
|
| 301 |
|
| 302 /* |
|
| 303 * Begin the functions for dealing with the DNS child processes. |
|
| 304 */ |
|
| 305 static void |
|
| 306 req_free(pending_dns_request_t *req) |
|
| 307 { |
|
| 308 g_return_if_fail(req != NULL); |
|
| 309 |
|
| 310 close(req->fd_in); |
|
| 311 close(req->fd_out); |
|
| 312 |
|
| 313 g_free(req->host); |
|
| 314 g_free(req); |
|
| 315 |
|
| 316 number_of_dns_children--; |
|
| 317 } |
|
| 318 |
|
| 319 static int |
|
| 320 send_dns_request_to_child(pending_dns_request_t *req, dns_params_t *dns_params) |
|
| 321 { |
|
| 322 char ch; |
|
| 323 int rc; |
|
| 324 pid_t pid; |
|
| 325 |
|
| 326 /* This waitpid might return the child's PID if it has recently |
|
| 327 * exited, or it might return an error if it exited "long |
|
| 328 * enough" ago that it has already been reaped; in either |
|
| 329 * instance, we can't use it. */ |
|
| 330 if ((pid = waitpid (req->dns_pid, NULL, WNOHANG)) > 0) { |
|
| 331 gaim_debug_warning("dns", |
|
| 332 "DNS child %d no longer exists\n", req->dns_pid); |
|
| 333 return -1; |
|
| 334 } else if (pid < 0) { |
|
| 335 gaim_debug_warning("dns", |
|
| 336 "Wait for DNS child %d failed: %s\n", |
|
| 337 req->dns_pid, strerror(errno)); |
|
| 338 return -1; |
|
| 339 } |
|
| 340 |
|
| 341 /* Let's contact this lost child! */ |
|
| 342 rc = write(req->fd_in, dns_params, sizeof(*dns_params)); |
|
| 343 if (rc < 0) { |
|
| 344 gaim_debug_error("dns", |
|
| 345 "Unable to write to DNS child %d: %d\n", |
|
| 346 req->dns_pid, strerror(errno)); |
|
| 347 close(req->fd_in); |
|
| 348 return -1; |
|
| 349 } |
|
| 350 |
|
| 351 g_return_val_if_fail(rc == sizeof(*dns_params), -1); |
|
| 352 |
|
| 353 /* Did you hear me? (This avoids some race conditions) */ |
|
| 354 rc = read(req->fd_out, &ch, sizeof(ch)); |
|
| 355 if (rc != 1 || ch != 'Y') |
|
| 356 { |
|
| 357 gaim_debug_warning("dns", |
|
| 358 "DNS child %d not responding. Killing it!\n", |
|
| 359 req->dns_pid); |
|
| 360 kill(req->dns_pid, SIGKILL); |
|
| 361 return -1; |
|
| 362 } |
|
| 363 |
|
| 364 gaim_debug_info("dns", |
|
| 365 "Successfully sent DNS request to child %d\n", req->dns_pid); |
|
| 366 |
|
| 367 return 0; |
|
| 368 } |
|
| 369 |
|
| 370 static void |
|
| 371 host_resolved(gpointer data, gint source, GaimInputCondition cond); |
|
| 372 |
|
| 373 static void |
|
| 374 release_dns_child(pending_dns_request_t *req) |
|
| 375 { |
|
| 376 g_free(req->host); |
|
| 377 req->host = NULL; |
|
| 378 |
|
| 379 if (queued_requests && !g_queue_is_empty(queued_requests)) { |
|
| 380 queued_dns_request_t *r = g_queue_pop_head(queued_requests); |
|
| 381 req->host = g_strdup(r->params.hostname); |
|
| 382 req->port = r->params.port; |
|
| 383 req->callback = r->callback; |
|
| 384 req->data = r->data; |
|
| 385 |
|
| 386 gaim_debug_info("dns", |
|
| 387 "Processing queued DNS query for '%s' with child %d\n", |
|
| 388 req->host, req->dns_pid); |
|
| 389 |
|
| 390 if (send_dns_request_to_child(req, &(r->params)) != 0) { |
|
| 391 req_free(req); |
|
| 392 req = NULL; |
|
| 393 |
|
| 394 gaim_debug_warning("dns", |
|
| 395 "Intent of process queued query of '%s' failed, " |
|
| 396 "requeueing...\n", r->params.hostname); |
|
| 397 g_queue_push_head(queued_requests, r); |
|
| 398 } else { |
|
| 399 req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); |
|
| 400 g_free(r); |
|
| 401 } |
|
| 402 |
|
| 403 } else { |
|
| 404 req->host = NULL; |
|
| 405 req->callback = NULL; |
|
| 406 req->data = NULL; |
|
| 407 free_dns_children = g_slist_append(free_dns_children, req); |
|
| 408 } |
|
| 409 } |
|
| 410 |
|
| 411 static void |
|
| 412 host_resolved(gpointer data, gint source, GaimInputCondition cond) |
|
| 413 { |
|
| 414 pending_dns_request_t *req = (pending_dns_request_t*)data; |
|
| 415 int rc, err; |
|
| 416 GSList *hosts = NULL; |
|
| 417 struct sockaddr *addr = NULL; |
|
| 418 size_t addrlen; |
|
| 419 |
|
| 420 gaim_debug_info("dns", "Got response for '%s'\n", req->host); |
|
| 421 gaim_input_remove(req->inpa); |
|
| 422 |
|
| 423 rc = read(req->fd_out, &err, sizeof(err)); |
|
| 424 if ((rc == 4) && (err != 0)) |
|
| 425 { |
|
| 426 char message[1024]; |
|
| 427 #ifdef HAVE_GETADDRINFO |
|
| 428 g_snprintf(message, sizeof(message), "DNS error: %s (pid=%d)", |
|
| 429 gai_strerror(err), req->dns_pid); |
|
| 430 #else |
|
| 431 g_snprintf(message, sizeof(message), "DNS error: %d (pid=%d)", |
|
| 432 err, req->dns_pid); |
|
| 433 #endif |
|
| 434 gaim_debug_error("dns", "%s\n", message); |
|
| 435 req->callback(NULL, req->data, message); |
|
| 436 release_dns_child(req); |
|
| 437 return; |
|
| 438 } |
|
| 439 if (rc > 0) |
|
| 440 { |
|
| 441 while (rc > 0) { |
|
| 442 rc = read(req->fd_out, &addrlen, sizeof(addrlen)); |
|
| 443 if (rc > 0 && addrlen > 0) { |
|
| 444 addr = g_malloc(addrlen); |
|
| 445 rc = read(req->fd_out, addr, addrlen); |
|
| 446 hosts = g_slist_append(hosts, GINT_TO_POINTER(addrlen)); |
|
| 447 hosts = g_slist_append(hosts, addr); |
|
| 448 } else { |
|
| 449 break; |
|
| 450 } |
|
| 451 } |
|
| 452 } else if (rc == -1) { |
|
| 453 char message[1024]; |
|
| 454 g_snprintf(message, sizeof(message), "Error reading from DNS child: %s",strerror(errno)); |
|
| 455 gaim_debug_error("dns", "%s\n", message); |
|
| 456 req->callback(NULL, req->data, message); |
|
| 457 req_free(req); |
|
| 458 return; |
|
| 459 } else if (rc == 0) { |
|
| 460 char message[1024]; |
|
| 461 g_snprintf(message, sizeof(message), "EOF reading from DNS child"); |
|
| 462 close(req->fd_out); |
|
| 463 gaim_debug_error("dns", "%s\n", message); |
|
| 464 req->callback(NULL, req->data, message); |
|
| 465 req_free(req); |
|
| 466 return; |
|
| 467 } |
|
| 468 |
|
| 469 /* wait4(req->dns_pid, NULL, WNOHANG, NULL); */ |
|
| 470 |
|
| 471 req->callback(hosts, req->data, NULL); |
|
| 472 |
|
| 473 release_dns_child(req); |
|
| 474 } |
|
| 475 /* |
|
| 476 * End the functions for dealing with the DNS child processes. |
|
| 477 */ |
|
| 478 |
|
| 479 GaimDnsQueryData * |
|
| 480 gaim_dnsquery_a(const char *hostname, int port, GaimDnsQueryConnectFunction callback, gpointer data) |
|
| 481 { |
|
| 482 pending_dns_request_t *req = NULL; |
|
| 483 dns_params_t dns_params; |
|
| 484 gchar *host_temp; |
|
| 485 gboolean show_debug; |
|
| 486 |
|
| 487 show_debug = gaim_debug_is_enabled(); |
|
| 488 |
|
| 489 host_temp = g_strstrip(g_strdup(hostname)); |
|
| 490 strncpy(dns_params.hostname, host_temp, sizeof(dns_params.hostname) - 1); |
|
| 491 g_free(host_temp); |
|
| 492 dns_params.hostname[sizeof(dns_params.hostname) - 1] = '\0'; |
|
| 493 dns_params.port = port; |
|
| 494 |
|
| 495 /* |
|
| 496 * If we have any children, attempt to have them perform the DNS |
|
| 497 * query. If we're able to send the query to a child, then req |
|
| 498 * will be set to the pending_dns_request_t. Otherwise, req will |
|
| 499 * be NULL and we'll need to create a new DNS request child. |
|
| 500 */ |
|
| 501 while (free_dns_children != NULL) { |
|
| 502 req = free_dns_children->data; |
|
| 503 free_dns_children = g_slist_remove(free_dns_children, req); |
|
| 504 |
|
| 505 if (send_dns_request_to_child(req, &dns_params) == 0) |
|
| 506 /* We found an acceptable child, yay */ |
|
| 507 break; |
|
| 508 |
|
| 509 req_free(req); |
|
| 510 req = NULL; |
|
| 511 } |
|
| 512 |
|
| 513 /* We need to create a new DNS request child */ |
|
| 514 if (req == NULL) { |
|
| 515 if (number_of_dns_children >= MAX_DNS_CHILDREN) { |
|
| 516 queued_dns_request_t *r = g_new(queued_dns_request_t, 1); |
|
| 517 memcpy(&(r->params), &dns_params, sizeof(dns_params)); |
|
| 518 r->callback = callback; |
|
| 519 r->data = data; |
|
| 520 if (!queued_requests) |
|
| 521 queued_requests = g_queue_new(); |
|
| 522 g_queue_push_tail(queued_requests, r); |
|
| 523 |
|
| 524 gaim_debug_info("dns", |
|
| 525 "DNS query for '%s' queued\n", dns_params.hostname); |
|
| 526 |
|
| 527 return (GaimDnsQueryData *)1; |
|
| 528 } |
|
| 529 |
|
| 530 req = gaim_dns_new_resolverthread(show_debug); |
|
| 531 if (req == NULL) |
|
| 532 { |
|
| 533 gaim_debug_error("proxy", "oh dear, this is going to explode, I give up\n"); |
|
| 534 return NULL; |
|
| 535 } |
|
| 536 send_dns_request_to_child(req, &dns_params); |
|
| 537 } |
|
| 538 |
|
| 539 req->host = g_strdup(hostname); |
|
| 540 req->port = port; |
|
| 541 req->callback = callback; |
|
| 542 req->data = data; |
|
| 543 req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); |
|
| 544 |
|
| 545 return (GaimDnsQueryData *)1; |
|
| 546 } |
|
| 547 |
|
| 548 #elif defined _WIN32 /* end __unix__ || __APPLE__ */ |
|
| 549 |
|
| 550 typedef struct _dns_tdata { |
|
| 551 char *hostname; |
|
| 552 int port; |
|
| 553 GaimDnsQueryConnectFunction callback; |
|
| 554 gpointer data; |
|
| 555 GSList *hosts; |
|
| 556 char *errmsg; |
|
| 557 } dns_tdata; |
|
| 558 |
|
| 559 static gboolean dns_main_thread_cb(gpointer data) { |
|
| 560 dns_tdata *td = (dns_tdata*)data; |
|
| 561 if (td->errmsg != NULL) { |
|
| 562 gaim_debug_info("dns", "%s\n", td->errmsg); |
|
| 563 } |
|
| 564 td->callback(td->hosts, td->data, td->errmsg); |
|
| 565 g_free(td->hostname); |
|
| 566 g_free(td->errmsg); |
|
| 567 g_free(td); |
|
| 568 return FALSE; |
|
| 569 } |
|
| 570 |
|
| 571 static gpointer dns_thread(gpointer data) { |
|
| 572 |
|
| 573 #ifdef HAVE_GETADDRINFO |
|
| 574 int rc; |
|
| 575 struct addrinfo hints, *res, *tmp; |
|
| 576 char servname[20]; |
|
| 577 #else |
|
| 578 struct sockaddr_in sin; |
|
| 579 struct hostent *hp; |
|
| 580 #endif |
|
| 581 dns_tdata *td = (dns_tdata*)data; |
|
| 582 |
|
| 583 #ifdef HAVE_GETADDRINFO |
|
| 584 g_snprintf(servname, sizeof(servname), "%d", td->port); |
|
| 585 memset(&hints,0,sizeof(hints)); |
|
| 586 |
|
| 587 /* This is only used to convert a service |
|
| 588 * name to a port number. As we know we are |
|
| 589 * passing a number already, we know this |
|
| 590 * value will not be really used by the C |
|
| 591 * library. |
|
| 592 */ |
|
| 593 hints.ai_socktype = SOCK_STREAM; |
|
| 594 if ((rc = getaddrinfo(td->hostname, servname, &hints, &res)) == 0) { |
|
| 595 tmp = res; |
|
| 596 while(res) { |
|
| 597 td->hosts = g_slist_append(td->hosts, |
|
| 598 GSIZE_TO_POINTER(res->ai_addrlen)); |
|
| 599 td->hosts = g_slist_append(td->hosts, |
|
| 600 g_memdup(res->ai_addr, res->ai_addrlen)); |
|
| 601 res = res->ai_next; |
|
| 602 } |
|
| 603 freeaddrinfo(tmp); |
|
| 604 } else { |
|
| 605 td->errmsg = g_strdup_printf("DNS getaddrinfo(\"%s\", \"%s\") error: %d", td->hostname, servname, rc); |
|
| 606 } |
|
| 607 #else |
|
| 608 if ((hp = gethostbyname(td->hostname))) { |
|
| 609 memset(&sin, 0, sizeof(struct sockaddr_in)); |
|
| 610 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); |
|
| 611 sin.sin_family = hp->h_addrtype; |
|
| 612 sin.sin_port = htons(td->port); |
|
| 613 |
|
| 614 td->hosts = g_slist_append(td->hosts, |
|
| 615 GSIZE_TO_POINTER(sizeof(sin))); |
|
| 616 td->hosts = g_slist_append(td->hosts, |
|
| 617 g_memdup(&sin, sizeof(sin))); |
|
| 618 } else { |
|
| 619 td->errmsg = g_strdup_printf("DNS gethostbyname(\"%s\") error: %d", td->hostname, h_errno); |
|
| 620 } |
|
| 621 #endif |
|
| 622 /* back to main thread */ |
|
| 623 g_idle_add(dns_main_thread_cb, td); |
|
| 624 return 0; |
|
| 625 } |
|
| 626 |
|
| 627 GaimDnsQueryData * |
|
| 628 gaim_dnsquery_a(const char *hostname, int port, |
|
| 629 GaimDnsQueryConnectFunction callback, gpointer data) |
|
| 630 { |
|
| 631 dns_tdata *td; |
|
| 632 struct sockaddr_in sin; |
|
| 633 GError* err = NULL; |
|
| 634 |
|
| 635 if(inet_aton(hostname, &sin.sin_addr)) { |
|
| 636 GSList *hosts = NULL; |
|
| 637 sin.sin_family = AF_INET; |
|
| 638 sin.sin_port = htons(port); |
|
| 639 hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin))); |
|
| 640 hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); |
|
| 641 callback(hosts, data, NULL); |
|
| 642 return (GaimDnsQueryData *)1; |
|
| 643 } |
|
| 644 |
|
| 645 gaim_debug_info("dns", "DNS Lookup for: %s\n", hostname); |
|
| 646 td = g_new0(dns_tdata, 1); |
|
| 647 td->hostname = g_strdup(hostname); |
|
| 648 td->port = port; |
|
| 649 td->callback = callback; |
|
| 650 td->data = data; |
|
| 651 |
|
| 652 if(!g_thread_create(dns_thread, td, FALSE, &err)) { |
|
| 653 gaim_debug_error("dns", "DNS thread create failure: %s\n", err?err->message:""); |
|
| 654 g_error_free(err); |
|
| 655 g_free(td->hostname); |
|
| 656 g_free(td); |
|
| 657 return NULL; |
|
| 658 } |
|
| 659 return (GaimDnsQueryData *)1; |
|
| 660 } |
|
| 661 |
|
| 662 #else /* not __unix__ or __APPLE__ or _WIN32 */ |
|
| 663 |
|
| 664 typedef struct { |
|
| 665 gpointer data; |
|
| 666 size_t addrlen; |
|
| 667 struct sockaddr *addr; |
|
| 668 GaimDnsQueryConnectFunction callback; |
|
| 669 } pending_dns_request_t; |
|
| 670 |
|
| 671 static gboolean host_resolved(gpointer data) |
|
| 672 { |
|
| 673 pending_dns_request_t *req = (pending_dns_request_t*)data; |
|
| 674 GSList *hosts = NULL; |
|
| 675 hosts = g_slist_append(hosts, GINT_TO_POINTER(req->addrlen)); |
|
| 676 hosts = g_slist_append(hosts, req->addr); |
|
| 677 req->callback(hosts, req->data, NULL); |
|
| 678 g_free(req); |
|
| 679 return FALSE; |
|
| 680 } |
|
| 681 |
|
| 682 GaimDnsQueryData * |
|
| 683 gaim_dnsquery_a(const char *hostname, int port, |
|
| 684 GaimDnsQueryConnectFunction callback, gpointer data) |
|
| 685 { |
|
| 686 struct sockaddr_in sin; |
|
| 687 pending_dns_request_t *req; |
|
| 688 |
|
| 689 if (!inet_aton(hostname, &sin.sin_addr)) { |
|
| 690 struct hostent *hp; |
|
| 691 if(!(hp = gethostbyname(hostname))) { |
|
| 692 gaim_debug_error("dns", |
|
| 693 "gaim_gethostbyname(\"%s\", %d) failed: %d\n", |
|
| 694 hostname, port, h_errno); |
|
| 695 return NULL; |
|
| 696 } |
|
| 697 memset(&sin, 0, sizeof(struct sockaddr_in)); |
|
| 698 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); |
|
| 699 sin.sin_family = hp->h_addrtype; |
|
| 700 } else |
|
| 701 sin.sin_family = AF_INET; |
|
| 702 sin.sin_port = htons(port); |
|
| 703 |
|
| 704 req = g_new(pending_dns_request_t, 1); |
|
| 705 req->addr = (struct sockaddr*) g_memdup(&sin, sizeof(sin)); |
|
| 706 req->addrlen = sizeof(sin); |
|
| 707 req->data = data; |
|
| 708 req->callback = callback; |
|
| 709 gaim_timeout_add(10, host_resolved, req); |
|
| 710 return (GaimDnsQueryData *)1; |
|
| 711 } |
|
| 712 |
|
| 713 #endif /* not __unix__ or __APPLE__ or _WIN32 */ |
|