comparison libpurple/network.c @ 29741:35a1cf247168

Add network listen functions that accept a family argument (AF_INET(6?)). These allow code to portably support IPv6 for listeners (mostly file transfers and Bonjour). Callers should use the purple_socket_speaks_ipv4 to determine whether they need two sockets or just an IPv6 one. I used GIO's g_socket_speaks_ipv4 as the inspiration for that.
author Paul Aurich <paul@darkrain42.org>
date Sat, 17 Apr 2010 01:27:04 +0000
parents ce3f4bd8939b
children c91976cf319c
comparison
equal deleted inserted replaced
29740:5be4137c4a68 29741:35a1cf247168
392 { 392 {
393 listen_map_external = map_external; 393 listen_map_external = map_external;
394 } 394 }
395 395
396 static PurpleNetworkListenData * 396 static PurpleNetworkListenData *
397 purple_network_do_listen(unsigned short port, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data) 397 purple_network_do_listen(unsigned short port, int socket_family, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
398 { 398 {
399 int listenfd = -1; 399 int listenfd = -1;
400 int flags; 400 int flags;
401 const int on = 1; 401 const int on = 1;
402 PurpleNetworkListenData *listen_data; 402 PurpleNetworkListenData *listen_data;
410 * Get a list of addresses on this machine. 410 * Get a list of addresses on this machine.
411 */ 411 */
412 g_snprintf(serv, sizeof(serv), "%hu", port); 412 g_snprintf(serv, sizeof(serv), "%hu", port);
413 memset(&hints, 0, sizeof(struct addrinfo)); 413 memset(&hints, 0, sizeof(struct addrinfo));
414 hints.ai_flags = AI_PASSIVE; 414 hints.ai_flags = AI_PASSIVE;
415 hints.ai_family = AF_UNSPEC; 415 hints.ai_family = socket_family;
416 hints.ai_socktype = socket_type; 416 hints.ai_socktype = socket_type;
417 errnum = getaddrinfo(NULL /* any IP */, serv, &hints, &res); 417 errnum = getaddrinfo(NULL /* any IP */, serv, &hints, &res);
418 if (errnum != 0) { 418 if (errnum != 0) {
419 #ifndef _WIN32 419 #ifndef _WIN32
420 purple_debug_warning("network", "getaddrinfo: %s\n", purple_gai_strerror(errnum)); 420 purple_debug_warning("network", "getaddrinfo: %s\n", purple_gai_strerror(errnum));
434 for (next = res; next != NULL; next = next->ai_next) { 434 for (next = res; next != NULL; next = next->ai_next) {
435 listenfd = socket(next->ai_family, next->ai_socktype, next->ai_protocol); 435 listenfd = socket(next->ai_family, next->ai_socktype, next->ai_protocol);
436 if (listenfd < 0) 436 if (listenfd < 0)
437 continue; 437 continue;
438 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) 438 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
439 purple_debug_warning("network", "setsockopt: %s\n", g_strerror(errno)); 439 purple_debug_warning("network", "setsockopt(SO_REUSEADDR): %s\n", g_strerror(errno));
440 if (bind(listenfd, next->ai_addr, next->ai_addrlen) == 0) 440 if (bind(listenfd, next->ai_addr, next->ai_addrlen) == 0)
441 break; /* success */ 441 break; /* success */
442 /* XXX - It is unclear to me (datallah) whether we need to be 442 /* XXX - It is unclear to me (datallah) whether we need to be
443 using a new socket each time */ 443 using a new socket each time */
444 close(listenfd); 444 close(listenfd);
448 448
449 if (next == NULL) 449 if (next == NULL)
450 return NULL; 450 return NULL;
451 #else 451 #else
452 struct sockaddr_in sockin; 452 struct sockaddr_in sockin;
453
454 if (socket_family != AF_INET && socket_family != AF_UNSPEC) {
455 purple_debug_warning("network", "Address family %d only "
456 "supported when built with getaddrinfo() "
457 "support\n", socket_family);
458 return NULL;
459 }
453 460
454 if ((listenfd = socket(AF_INET, socket_type, 0)) < 0) { 461 if ((listenfd = socket(AF_INET, socket_type, 0)) < 0) {
455 purple_debug_warning("network", "socket: %s\n", g_strerror(errno)); 462 purple_debug_warning("network", "socket: %s\n", g_strerror(errno));
456 return NULL; 463 return NULL;
457 } 464 }
490 listen_data->retry = TRUE; 497 listen_data->retry = TRUE;
491 listen_data->cb = cb; 498 listen_data->cb = cb;
492 listen_data->cb_data = cb_data; 499 listen_data->cb_data = cb_data;
493 listen_data->socket_type = socket_type; 500 listen_data->socket_type = socket_type;
494 501
495 if (!listen_map_external || !purple_prefs_get_bool("/purple/network/map_ports")) 502 if (!purple_socket_speaks_ipv4(listenfd) || !listen_map_external ||
503 !purple_prefs_get_bool("/purple/network/map_ports"))
496 { 504 {
497 purple_debug_info("network", "Skipping external port mapping.\n"); 505 purple_debug_info("network", "Skipping external port mapping.\n");
498 /* The pmp_map_cb does what we want to do */ 506 /* The pmp_map_cb does what we want to do */
499 purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data); 507 purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
500 } 508 }
517 525
518 return listen_data; 526 return listen_data;
519 } 527 }
520 528
521 PurpleNetworkListenData * 529 PurpleNetworkListenData *
530 purple_network_listen_family(unsigned short port, int socket_family,
531 int socket_type, PurpleNetworkListenCallback cb,
532 gpointer cb_data)
533 {
534 g_return_val_if_fail(port != 0, NULL);
535
536 return purple_network_do_listen(port, socket_family, socket_type,
537 cb, cb_data);
538 }
539
540 PurpleNetworkListenData *
522 purple_network_listen(unsigned short port, int socket_type, 541 purple_network_listen(unsigned short port, int socket_type,
523 PurpleNetworkListenCallback cb, gpointer cb_data) 542 PurpleNetworkListenCallback cb, gpointer cb_data)
524 { 543 {
525 g_return_val_if_fail(port != 0, NULL); 544 return purple_network_listen_family(port, AF_UNSPEC, socket_type,
526 545 cb, cb_data);
527 return purple_network_do_listen(port, socket_type, cb, cb_data);
528 } 546 }
529 547
530 PurpleNetworkListenData * 548 PurpleNetworkListenData *
531 purple_network_listen_range(unsigned short start, unsigned short end, 549 purple_network_listen_range_family(unsigned short start, unsigned short end,
532 int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data) 550 int socket_family, int socket_type,
551 PurpleNetworkListenCallback cb,
552 gpointer cb_data)
533 { 553 {
534 PurpleNetworkListenData *ret = NULL; 554 PurpleNetworkListenData *ret = NULL;
535 555
536 if (purple_prefs_get_bool("/purple/network/ports_range_use")) { 556 if (purple_prefs_get_bool("/purple/network/ports_range_use")) {
537 start = purple_prefs_get_int("/purple/network/ports_range_start"); 557 start = purple_prefs_get_int("/purple/network/ports_range_start");
540 if (end < start) 560 if (end < start)
541 end = start; 561 end = start;
542 } 562 }
543 563
544 for (; start <= end; start++) { 564 for (; start <= end; start++) {
545 ret = purple_network_do_listen(start, socket_type, cb, cb_data); 565 ret = purple_network_do_listen(start, AF_UNSPEC, socket_type, cb, cb_data);
546 if (ret != NULL) 566 if (ret != NULL)
547 break; 567 break;
548 } 568 }
549 569
550 return ret; 570 return ret;
571 }
572
573 PurpleNetworkListenData *
574 purple_network_listen_range(unsigned short start, unsigned short end,
575 int socket_type, PurpleNetworkListenCallback cb,
576 gpointer cb_data)
577 {
578 return purple_network_listen_range_family(start, end, AF_UNSPEC,
579 socket_type, cb, cb_data);
551 } 580 }
552 581
553 void purple_network_listen_cancel(PurpleNetworkListenData *listen_data) 582 void purple_network_listen_cancel(PurpleNetworkListenData *listen_data)
554 { 583 {
555 if (listen_data->mapping_data != NULL) 584 if (listen_data->mapping_data != NULL)