comparison src/proxy.c @ 10711:00483ba950bf

[gaim-migrate @ 12301] Add some comments to the proxy code, rename some functions, and shuffle things around in a way that I think makes it much more readable. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Mon, 21 Mar 2005 02:14:46 +0000
parents cc9922ce3a52
children dcbc56eb3f37
comparison
equal deleted inserted replaced
10710:a2c0ce632bec 10711:00483ba950bf
189 typedef void (*dns_callback_t)(GSList *hosts, gpointer data, 189 typedef void (*dns_callback_t)(GSList *hosts, gpointer data,
190 const char *error_message); 190 const char *error_message);
191 191
192 #ifdef __unix__ 192 #ifdef __unix__
193 193
194 /* This structure represents both a pending DNS request and 194 /*
195 * a free child process. 195 * This structure represents both a pending DNS request and
196 * a free child process.
196 */ 197 */
197 typedef struct { 198 typedef struct {
198 char *host; 199 char *host;
199 int port; 200 int port;
200 dns_callback_t callback; 201 dns_callback_t callback;
220 dns_params_t params; 221 dns_params_t params;
221 dns_callback_t callback; 222 dns_callback_t callback;
222 gpointer data; 223 gpointer data;
223 } queued_dns_request_t; 224 } queued_dns_request_t;
224 225
225 static void req_free(pending_dns_request_t *req) 226 /*
227 * Begin the DNS resolver child process functions.
228 */
229 #ifdef HAVE_SIGNAL_H
230 static void
231 trap_gdb_bug()
232 {
233 const char *message =
234 "Gaim's DNS child got a SIGTRAP signal. \n"
235 "This can be caused by trying to run gaim inside gdb.\n"
236 "There is a known gdb bug which prevents this. Supposedly gaim\n"
237 "should have detected you were using gdb and used an ugly hack,\n"
238 "check cope_with_gdb_brokenness() in proxy.c.\n\n"
239 "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n";
240 fputs("\n* * *\n",stderr);
241 fputs(message,stderr);
242 fputs("* * *\n\n",stderr);
243 execlp("xmessage","xmessage","-center", message, NULL);
244 _exit(1);
245 }
246 #endif
247
248 static void
249 cope_with_gdb_brokenness()
250 {
251 #ifdef __linux__
252 static gboolean already_done = FALSE;
253 char s[256], e[512];
254 int n;
255 pid_t ppid;
256
257 if(already_done)
258 return;
259 already_done = TRUE;
260 ppid = getppid();
261 snprintf(s, sizeof(s), "/proc/%d/exe", ppid);
262 n = readlink(s, e, sizeof(e));
263 if(n < 0)
264 return;
265
266 e[MIN(n,sizeof(e)-1)] = '\0';
267
268 if(strstr(e,"gdb")) {
269 gaim_debug_info("dns",
270 "Debugger detected, performing useless query...\n");
271 gethostbyname("x.x.x.x.x");
272 }
273 #endif
274 }
275
276 static void
277 gaim_dns_resolverthread(int child_out, int child_in, gboolean show_debug)
278 {
279 dns_params_t dns_params;
280 const size_t zero = 0;
281 int rc;
282 #if HAVE_GETADDRINFO
283 struct addrinfo hints, *res, *tmp;
284 char servname[20];
285 #else
286 struct sockaddr_in sin;
287 const size_t addrlen = sizeof(sin);
288 #endif
289
290 #ifdef HAVE_SIGNAL_H
291 signal(SIGHUP, SIG_DFL);
292 signal(SIGINT, SIG_DFL);
293 signal(SIGQUIT, SIG_DFL);
294 signal(SIGCHLD, SIG_DFL);
295 signal(SIGTERM, SIG_DFL);
296 signal(SIGTRAP, trap_gdb_bug);
297 #endif
298
299 /*
300 * We resolve 1 host name for each iteration of this
301 * while loop.
302 *
303 * The top half of this reads in the hostname and port
304 * number from the socket with our parent. The bottom
305 * half of this resolves the IP (blocking) and sends
306 * the result back to our parent, when finished.
307 */
308 while (1) {
309 const char ch = 'Y';
310 fd_set fds;
311 struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 };
312 FD_ZERO(&fds);
313 FD_SET(child_in, &fds);
314 rc = select(child_in + 1, &fds, NULL, NULL, &tv);
315 if (!rc) {
316 if (show_debug)
317 fprintf(stderr,"dns[%d]: nobody needs me... =(\n", getpid());
318 break;
319 }
320 rc = read(child_in, &dns_params, sizeof(dns_params_t));
321 if (rc < 0) {
322 perror("read()");
323 break;
324 }
325 if (rc == 0) {
326 if (show_debug)
327 fprintf(stderr,"dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid());
328 _exit(0);
329 }
330 if (dns_params.hostname[0] == '\0') {
331 fprintf(stderr, "dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port);
332 _exit(1);
333 }
334 /* Tell our parent that we read the data successfully */
335 write(child_out, &ch, sizeof(ch));
336
337 /* We have the hostname and port, now resolve the IP */
338
339 #if HAVE_GETADDRINFO
340 g_snprintf(servname, sizeof(servname), "%d", dns_params.port);
341 memset(&hints, 0, sizeof(hints));
342
343 /* This is only used to convert a service
344 * name to a port number. As we know we are
345 * passing a number already, we know this
346 * value will not be really used by the C
347 * library.
348 */
349 hints.ai_socktype = SOCK_STREAM;
350 rc = getaddrinfo(dns_params.hostname, servname, &hints, &res);
351 write(child_out, &rc, sizeof(rc));
352 if (rc != 0) {
353 close(child_out);
354 if (show_debug)
355 fprintf(stderr,"dns[%d] Error: getaddrinfo returned %d\n",
356 getpid(), rc);
357 dns_params.hostname[0] = '\0';
358 continue;
359 }
360 tmp = res;
361 while (res) {
362 size_t ai_addrlen = res->ai_addrlen;
363 write(child_out, &ai_addrlen, sizeof(ai_addrlen));
364 write(child_out, res->ai_addr, res->ai_addrlen);
365 res = res->ai_next;
366 }
367 freeaddrinfo(tmp);
368 write(child_out, &zero, sizeof(zero));
369 #else
370 if (!inet_aton(dns_params.hostname, &sin.sin_addr)) {
371 struct hostent *hp;
372 if (!(hp = gethostbyname(dns_params.hostname))) {
373 write(child_out, &h_errno, sizeof(int));
374 close(child_out);
375 if (show_debug)
376 fprintf(stderr,"DNS Error: %d\n", h_errno);
377 _exit(0);
378 }
379 memset(&sin, 0, sizeof(struct sockaddr_in));
380 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
381 sin.sin_family = hp->h_addrtype;
382 } else
383 sin.sin_family = AF_INET;
384
385 sin.sin_port = htons(dns_params.port);
386 write(child_out, &addrlen, sizeof(addrlen));
387 write(child_out, &sin, addrlen);
388 write(child_out, &zero, sizeof(zero));
389 #endif
390 dns_params.hostname[0] = '\0';
391 }
392
393 close(child_out);
394 close(child_in);
395
396 _exit(0);
397 }
398
399 static pending_dns_request_t *
400 gaim_dns_new_resolverthread(gboolean show_debug)
401 {
402 pending_dns_request_t *req;
403 int child_out[2], child_in[2];
404
405 /* Create pipes for communicating with the child process */
406 if (pipe(child_out) || pipe(child_in)) {
407 gaim_debug_error("dns",
408 "Could not create pipes: %s\n", strerror(errno));
409 return NULL;
410 }
411
412 req = g_new(pending_dns_request_t, 1);
413
414 cope_with_gdb_brokenness();
415
416 /* Fork! */
417 req->dns_pid = fork();
418
419 /* If we are the child process... */
420 if (req->dns_pid == 0) {
421 /* We should not access the parent's side of the pipes, so close them */
422 close(child_out[0]);
423 close(child_in[1]);
424
425 gaim_dns_resolverthread(child_out[1], child_in[0], show_debug);
426 /* The thread calls _exit() rather than returning, so we never get here */
427 }
428
429 /* We should not access the child's side of the pipes, so close them */
430 close(child_out[1]);
431 close(child_in[0]);
432 if (req->dns_pid == -1) {
433 gaim_debug_error("dns",
434 "Could not create child process for DNS: %s\n",
435 strerror(errno));
436 g_free(req);
437 return NULL;
438 }
439
440 req->fd_out = child_out[0];
441 req->fd_in = child_in[1];
442 number_of_dns_children++;
443 gaim_debug_info("dns",
444 "Created new DNS child %d, there are now %d children.\n",
445 req->dns_pid, number_of_dns_children);
446
447 return req;
448 }
449 /*
450 * End the DNS resolver child process functions.
451 */
452
453 /*
454 * Begin the functions for dealing with the DNS child processes.
455 */
456 static void
457 req_free(pending_dns_request_t *req)
226 { 458 {
227 g_return_if_fail(req != NULL); 459 g_return_if_fail(req != NULL);
228 460
229 close(req->fd_in); 461 close(req->fd_in);
230 close(req->fd_out); 462 close(req->fd_out);
233 g_free(req); 465 g_free(req);
234 466
235 number_of_dns_children--; 467 number_of_dns_children--;
236 } 468 }
237 469
238 static int send_dns_request_to_child(pending_dns_request_t *req, dns_params_t *dns_params) 470 static int
471 send_dns_request_to_child(pending_dns_request_t *req, dns_params_t *dns_params)
239 { 472 {
240 char ch; 473 char ch;
241 int rc; 474 int rc;
242 475
243 /* Are you alive? */ 476 /* Are you alive? */
274 "Successfully sent DNS request to child %d\n", req->dns_pid); 507 "Successfully sent DNS request to child %d\n", req->dns_pid);
275 508
276 return 0; 509 return 0;
277 } 510 }
278 511
279 static void host_resolved(gpointer data, gint source, GaimInputCondition cond); 512 static void
280 513 host_resolved(gpointer data, gint source, GaimInputCondition cond);
281 static void release_dns_child(pending_dns_request_t *req) 514
515 static void
516 release_dns_child(pending_dns_request_t *req)
282 { 517 {
283 g_free(req->host); 518 g_free(req->host);
284 req->host = NULL; 519 req->host = NULL;
285 520
286 if (queued_requests && !g_queue_is_empty(queued_requests)) { 521 if (queued_requests && !g_queue_is_empty(queued_requests)) {
313 req->data = NULL; 548 req->data = NULL;
314 free_dns_children = g_slist_append(free_dns_children, req); 549 free_dns_children = g_slist_append(free_dns_children, req);
315 } 550 }
316 } 551 }
317 552
318 static void host_resolved(gpointer data, gint source, GaimInputCondition cond) 553 static void
554 host_resolved(gpointer data, gint source, GaimInputCondition cond)
319 { 555 {
320 pending_dns_request_t *req = (pending_dns_request_t*)data; 556 pending_dns_request_t *req = (pending_dns_request_t*)data;
321 int rc, err; 557 int rc, err;
322 GSList *hosts = NULL; 558 GSList *hosts = NULL;
323 struct sockaddr *addr = NULL; 559 struct sockaddr *addr = NULL;
376 612
377 req->callback(hosts, req->data, NULL); 613 req->callback(hosts, req->data, NULL);
378 614
379 release_dns_child(req); 615 release_dns_child(req);
380 } 616 }
381 617 /*
382 static void trap_gdb_bug() 618 * End the functions for dealing with the DNS child processes.
383 { 619 */
384 const char *message = 620
385 "Gaim's DNS child got a SIGTRAP signal. \n" 621 int
386 "This can be caused by trying to run gaim inside gdb.\n" 622 gaim_gethostbyname_async(const char *hostname, int port, dns_callback_t callback, gpointer data)
387 "There is a known gdb bug which prevents this. Supposedly gaim\n"
388 "should have detected you were using gdb and used an ugly hack,\n"
389 "check cope_with_gdb_brokenness() in proxy.c.\n\n"
390 "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n";
391 fputs("\n* * *\n",stderr);
392 fputs(message,stderr);
393 fputs("* * *\n\n",stderr);
394 execlp("xmessage","xmessage","-center", message, NULL);
395 _exit(1);
396 }
397
398 static void cope_with_gdb_brokenness()
399 {
400 #ifdef __linux__
401 static gboolean already_done = FALSE;
402 char s[256], e[512];
403 int n;
404 pid_t ppid;
405
406 if(already_done)
407 return;
408 already_done = TRUE;
409 ppid = getppid();
410 snprintf(s, sizeof(s), "/proc/%d/exe", ppid);
411 n = readlink(s, e, sizeof(e));
412 if(n < 0)
413 return;
414
415 e[MIN(n,sizeof(e)-1)] = '\0';
416
417 if(strstr(e,"gdb")) {
418 gaim_debug_info("dns",
419 "Debugger detected, performing useless query...\n");
420 gethostbyname("x.x.x.x.x");
421 }
422 #endif
423 }
424
425 static void
426 gaim_dns_childthread(int child_out, int child_in, dns_params_t *dns_params, gboolean show_debug)
427 {
428 const size_t zero = 0;
429 int rc;
430 #if HAVE_GETADDRINFO
431 struct addrinfo hints, *res, *tmp;
432 char servname[20];
433 #else
434 struct sockaddr_in sin;
435 const size_t addrlen = sizeof(sin);
436 #endif
437
438 #ifdef HAVE_SIGNAL_H
439 signal(SIGHUP, SIG_DFL);
440 signal(SIGINT, SIG_DFL);
441 signal(SIGQUIT, SIG_DFL);
442 signal(SIGCHLD, SIG_DFL);
443 signal(SIGTERM, SIG_DFL);
444 signal(SIGTRAP, trap_gdb_bug);
445 #endif
446
447 while (1) {
448 if (dns_params->hostname[0] == '\0') {
449 const char ch = 'Y';
450 fd_set fds;
451 struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 };
452 FD_ZERO(&fds);
453 FD_SET(child_in, &fds);
454 rc = select(child_in + 1, &fds, NULL, NULL, &tv);
455 if (!rc) {
456 if (show_debug)
457 fprintf(stderr,"dns[%d]: nobody needs me... =(\n", getpid());
458 break;
459 }
460 rc = read(child_in, dns_params, sizeof(dns_params_t));
461 if (rc < 0) {
462 perror("read()");
463 break;
464 }
465 if (rc == 0) {
466 if (show_debug)
467 fprintf(stderr,"dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid());
468 _exit(0);
469 }
470 if (dns_params->hostname[0] == '\0') {
471 fprintf(stderr, "dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params->port);
472 _exit(1);
473 }
474 write(child_out, &ch, sizeof(ch));
475 }
476
477 #if HAVE_GETADDRINFO
478 g_snprintf(servname, sizeof(servname), "%d", dns_params->port);
479 memset(&hints, 0, sizeof(hints));
480
481 /* This is only used to convert a service
482 * name to a port number. As we know we are
483 * passing a number already, we know this
484 * value will not be really used by the C
485 * library.
486 */
487 hints.ai_socktype = SOCK_STREAM;
488 rc = getaddrinfo(dns_params->hostname, servname, &hints, &res);
489 write(child_out, &rc, sizeof(rc));
490 if (rc != 0) {
491 close(child_out);
492 if (show_debug)
493 fprintf(stderr,"dns[%d] Error: getaddrinfo returned %d\n",
494 getpid(), rc);
495 dns_params->hostname[0] = '\0';
496 continue;
497 }
498 tmp = res;
499 while (res) {
500 size_t ai_addrlen = res->ai_addrlen;
501 write(child_out, &ai_addrlen, sizeof(ai_addrlen));
502 write(child_out, res->ai_addr, res->ai_addrlen);
503 res = res->ai_next;
504 }
505 freeaddrinfo(tmp);
506 write(child_out, &zero, sizeof(zero));
507 #else
508 if (!inet_aton(dns_params->hostname, &sin.sin_addr)) {
509 struct hostent *hp;
510 if (!(hp = gethostbyname(dns_params->hostname))) {
511 write(child_out, &h_errno, sizeof(int));
512 close(child_out);
513 if (show_debug)
514 fprintf(stderr,"DNS Error: %d\n", h_errno);
515 _exit(0);
516 }
517 memset(&sin, 0, sizeof(struct sockaddr_in));
518 memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
519 sin.sin_family = hp->h_addrtype;
520 } else
521 sin.sin_family = AF_INET;
522
523 sin.sin_port = htons(dns_params->port);
524 write(child_out, &addrlen, sizeof(addrlen));
525 write(child_out, &sin, addrlen);
526 write(child_out, &zero, sizeof(zero));
527 #endif
528 dns_params->hostname[0] = '\0';
529 }
530
531 close(child_out);
532 close(child_in);
533
534 _exit(0);
535 }
536
537 int gaim_gethostbyname_async(const char *hostname, int port, dns_callback_t callback, gpointer data)
538 { 623 {
539 pending_dns_request_t *req = NULL; 624 pending_dns_request_t *req = NULL;
540 dns_params_t dns_params; 625 dns_params_t dns_params;
541 gchar *host_temp; 626 gchar *host_temp;
542 gboolean show_debug; 627 gboolean show_debug;
567 req = NULL; 652 req = NULL;
568 } 653 }
569 654
570 /* We need to create a new DNS request child */ 655 /* We need to create a new DNS request child */
571 if (req == NULL) { 656 if (req == NULL) {
572 int child_out[2], child_in[2];
573
574 if (number_of_dns_children >= MAX_DNS_CHILDREN) { 657 if (number_of_dns_children >= MAX_DNS_CHILDREN) {
575 queued_dns_request_t *r = g_new(queued_dns_request_t, 1); 658 queued_dns_request_t *r = g_new(queued_dns_request_t, 1);
576 memcpy(&(r->params), &dns_params, sizeof(dns_params)); 659 memcpy(&(r->params), &dns_params, sizeof(dns_params));
577 r->callback = callback; 660 r->callback = callback;
578 r->data = data; 661 r->data = data;
584 "DNS query for '%s' queued\n", dns_params.hostname); 667 "DNS query for '%s' queued\n", dns_params.hostname);
585 668
586 return 0; 669 return 0;
587 } 670 }
588 671
589 /* Create pipes for communicating with the child process */ 672 req = gaim_dns_new_resolverthread(show_debug);
590 if (pipe(child_out) || pipe(child_in)) { 673 send_dns_request_to_child(req, &dns_params);
591 gaim_debug_error("dns",
592 "Could not create pipes: %s\n", strerror(errno));
593 return -1;
594 }
595
596 req = g_new(pending_dns_request_t, 1);
597
598 cope_with_gdb_brokenness();
599
600 /* Fork! */
601 req->dns_pid = fork();
602
603 /* If we are the child process... */
604 if (req->dns_pid == 0) {
605 /* We should not access the parent's side of the pipe, so close them... */
606 close(child_out[0]);
607 close(child_in[1]);
608
609 gaim_dns_childthread(child_out[1], child_in[0], &dns_params, show_debug);
610 /* The thread calls _exit() rather than returning, so we never get here */
611 }
612
613 /* We should not access the child's side of the pipe, so close them... */
614 close(child_out[1]);
615 close(child_in[0]);
616 if (req->dns_pid == -1) {
617 gaim_debug_error("dns",
618 "Could not create child process for DNS: %s\n",
619 strerror(errno));
620 g_free(req);
621 return -1;
622 }
623
624 req->fd_out = child_out[0];
625 req->fd_in = child_in[1];
626 number_of_dns_children++;
627 gaim_debug_info("dns",
628 "Created new DNS child %d, there are now %d children.\n",
629 req->dns_pid, number_of_dns_children);
630 } 674 }
631 675
632 req->host = g_strdup(hostname); 676 req->host = g_strdup(hostname);
633 req->port = port; 677 req->port = port;
634 req->callback = callback; 678 req->callback = callback;
715 /* back to main thread */ 759 /* back to main thread */
716 g_idle_add(dns_main_thread_cb, td); 760 g_idle_add(dns_main_thread_cb, td);
717 return 0; 761 return 0;
718 } 762 }
719 763
720 int gaim_gethostbyname_async(const char *hostname, int port, 764 int
721 dns_callback_t callback, gpointer data) { 765 gaim_gethostbyname_async(const char *hostname, int port,
766 dns_callback_t callback, gpointer data)
767 {
722 dns_tdata *td; 768 dns_tdata *td;
723 struct sockaddr_in sin; 769 struct sockaddr_in sin;
724 GError* err = NULL; 770 GError* err = NULL;
725 771
726 if(inet_aton(hostname, &sin.sin_addr)) { 772 if(inet_aton(hostname, &sin.sin_addr)) {