Merged pidgin/main into default
[pidgin-git.git] / libpurple / network.c
bloba5c77d038b2edb3c9b1e8f6678a0e3a6e5dc65ea
1 /* purple
3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "internal.h"
24 #include <gio/gio.h>
26 #ifndef _WIN32
27 #include <arpa/nameser.h>
28 #include <resolv.h>
29 #include <netinet/in.h>
30 #include <net/if.h>
31 #include <sys/ioctl.h>
32 #ifdef HAVE_GETIFADDRS
33 #include <ifaddrs.h>
34 #endif
35 #else
36 #include <nspapi.h>
37 #endif
39 /* Solaris */
40 #if defined (__SVR4) && defined (__sun)
41 #include <sys/sockio.h>
42 #endif
44 #include "debug.h"
45 #include "account.h"
46 #include "nat-pmp.h"
47 #include "network.h"
48 #include "prefs.h"
49 #include "stun.h"
50 #include "upnp.h"
52 #ifdef USE_IDN
53 #include <idna.h>
54 #endif
57 * Calling sizeof(struct ifreq) isn't always correct on
58 * Mac OS X (and maybe others).
60 #ifdef _SIZEOF_ADDR_IFREQ
61 # define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
62 #else
63 # define HX_SIZE_OF_IFREQ(a) sizeof(a)
64 #endif
66 struct _PurpleNetworkListenData {
67 int listenfd;
68 int socket_type;
69 gboolean retry;
70 gboolean adding;
71 PurpleNetworkListenCallback cb;
72 gpointer cb_data;
73 PurpleUPnPMappingAddRemove *mapping_data;
74 int timer;
77 static gboolean force_online = FALSE;
79 /* Cached IP addresses for STUN and TURN servers (set globally in prefs) */
80 static gchar *stun_ip = NULL;
81 static gchar *turn_ip = NULL;
83 /* Keep track of port mappings done with UPnP and NAT-PMP */
84 static GHashTable *upnp_port_mappings = NULL;
85 static GHashTable *nat_pmp_port_mappings = NULL;
87 void
88 purple_network_set_public_ip(const char *ip)
90 g_return_if_fail(ip != NULL);
92 /* XXX - Ensure the IP address is valid */
94 purple_prefs_set_string("/purple/network/public_ip", ip);
97 const char *
98 purple_network_get_public_ip(void)
100 return purple_prefs_get_string("/purple/network/public_ip");
103 const char *
104 purple_network_get_local_system_ip(int fd)
106 struct ifreq buffer[100];
107 guchar *it, *it_end;
108 static char ip[16];
109 struct ifconf ifc;
110 struct ifreq *ifr;
111 struct sockaddr_in *sinptr;
112 guint32 lhost = htonl((127 << 24) + 1); /* 127.0.0.1 */
113 long unsigned int add;
114 int source = fd;
116 if (fd < 0)
117 source = socket(PF_INET,SOCK_STREAM, 0);
119 ifc.ifc_len = sizeof(buffer);
120 ifc.ifc_req = buffer;
121 ioctl(source, SIOCGIFCONF, &ifc);
123 if (fd < 0 && source >= 0)
124 close(source);
126 it = (guchar*)buffer;
127 it_end = it + ifc.ifc_len;
128 while (it < it_end) {
129 /* in this case "it" is:
130 * a) (struct ifreq)-aligned
131 * b) not aligned, because of OS quirks (see
132 * _SIZEOF_ADDR_IFREQ), so the OS should deal with it.
134 ifr = (struct ifreq *)(gpointer)it;
135 it += HX_SIZE_OF_IFREQ(*ifr);
137 if (ifr->ifr_addr.sa_family == AF_INET)
139 sinptr = (struct sockaddr_in *)(gpointer)&ifr->ifr_addr;
140 if (sinptr->sin_addr.s_addr != lhost)
142 add = ntohl(sinptr->sin_addr.s_addr);
143 g_snprintf(ip, 16, "%lu.%lu.%lu.%lu",
144 ((add >> 24) & 255),
145 ((add >> 16) & 255),
146 ((add >> 8) & 255),
147 add & 255);
149 return ip;
154 return "0.0.0.0";
157 GList *
158 purple_network_get_all_local_system_ips(void)
160 #if defined(HAVE_GETIFADDRS) && defined(HAVE_INET_NTOP)
161 GList *result = NULL;
162 struct ifaddrs *start, *ifa;
163 int ret;
165 ret = getifaddrs(&start);
166 if (ret < 0) {
167 purple_debug_warning("network",
168 "getifaddrs() failed: %s\n", g_strerror(errno));
169 return NULL;
172 for (ifa = start; ifa; ifa = ifa->ifa_next) {
173 int family = ifa->ifa_addr ? ifa->ifa_addr->sa_family : AF_UNSPEC;
174 char host[INET6_ADDRSTRLEN];
175 const char *tmp = NULL;
176 common_sockaddr_t *addr =
177 (common_sockaddr_t *)(gpointer)ifa->ifa_addr;
179 if ((family != AF_INET && family != AF_INET6) || ifa->ifa_flags & IFF_LOOPBACK)
180 continue;
182 if (family == AF_INET)
183 tmp = inet_ntop(family, &addr->in.sin_addr, host, sizeof(host));
184 else {
185 /* Peer-peer link-local communication is a big TODO. I am not sure
186 * how communicating link-local addresses is supposed to work, and
187 * it seems like it would require attempting the cartesian product
188 * of the local and remote interfaces to see if any match (eww).
190 if (!IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr))
191 tmp = inet_ntop(family, &addr->in6.sin6_addr, host, sizeof(host));
193 if (tmp != NULL)
194 result = g_list_prepend(result, g_strdup(tmp));
197 freeifaddrs(start);
199 return g_list_reverse(result);
200 #else /* HAVE_GETIFADDRS && HAVE_INET_NTOP */
201 GList *result = NULL;
202 int source = socket(PF_INET,SOCK_STREAM, 0);
203 struct ifreq buffer[100];
204 guchar *it, *it_end;
205 struct ifconf ifc;
206 struct ifreq *ifr;
208 ifc.ifc_len = sizeof(buffer);
209 ifc.ifc_req = buffer;
210 ioctl(source, SIOCGIFCONF, &ifc);
211 close(source);
213 it = (guchar*)buffer;
214 it_end = it + ifc.ifc_len;
215 while (it < it_end) {
216 char dst[INET_ADDRSTRLEN];
218 /* alignment: see purple_network_get_local_system_ip */
219 ifr = (struct ifreq *)(gpointer)it;
220 it += HX_SIZE_OF_IFREQ(*ifr);
222 if (ifr->ifr_addr.sa_family == AF_INET) {
223 struct sockaddr_in *sinptr =
224 (struct sockaddr_in *)(gpointer)&ifr->ifr_addr;
226 inet_ntop(AF_INET, &sinptr->sin_addr, dst,
227 sizeof(dst));
228 purple_debug_info("network",
229 "found local i/f with address %s on IPv4\n", dst);
230 if (!purple_strequal(dst, "127.0.0.1")) {
231 result = g_list_append(result, g_strdup(dst));
236 return result;
237 #endif /* HAVE_GETIFADDRS && HAVE_INET_NTOP */
241 * purple_network_is_ipv4:
242 * @hostname: The hostname to be verified.
244 * Checks, if specified hostname is valid ipv4 address.
246 * Returns: TRUE, if the hostname is valid.
248 static gboolean
249 purple_network_is_ipv4(const gchar *hostname)
251 g_return_val_if_fail(hostname != NULL, FALSE);
253 /* We don't accept ipv6 here. */
254 if (strchr(hostname, ':') != NULL)
255 return FALSE;
257 return g_hostname_is_ip_address(hostname);
260 const char *
261 purple_network_get_my_ip(int fd)
263 const char *ip = NULL;
264 PurpleStunNatDiscovery *stun;
266 /* Check if the user specified an IP manually */
267 if (!purple_prefs_get_bool("/purple/network/auto_ip")) {
268 ip = purple_network_get_public_ip();
269 /* Make sure the IP address entered by the user is valid */
270 if ((ip != NULL) && (purple_network_is_ipv4(ip)))
271 return ip;
272 } else {
273 /* Check if STUN discovery was already done */
274 stun = purple_stun_discover(NULL);
275 if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED))
276 return stun->publicip;
278 /* Attempt to get the IP from a NAT device using UPnP */
279 ip = purple_upnp_get_public_ip();
280 if (ip != NULL)
281 return ip;
283 /* Attempt to get the IP from a NAT device using NAT-PMP */
284 ip = purple_pmp_get_public_ip();
285 if (ip != NULL)
286 return ip;
289 /* Just fetch the IP of the local system */
290 return purple_network_get_local_system_ip(fd);
294 static void
295 purple_network_set_upnp_port_mapping_cb(gboolean success, gpointer data)
297 PurpleNetworkListenData *listen_data;
299 listen_data = data;
300 /* TODO: Once we're keeping track of upnp requests... */
301 /* listen_data->pnp_data = NULL; */
303 if (!success) {
304 purple_debug_warning("network", "Couldn't create UPnP mapping\n");
305 if (listen_data->retry) {
306 listen_data->retry = FALSE;
307 listen_data->adding = FALSE;
308 listen_data->mapping_data = purple_upnp_remove_port_mapping(
309 purple_network_get_port_from_fd(listen_data->listenfd),
310 (listen_data->socket_type == SOCK_STREAM) ? "TCP" : "UDP",
311 purple_network_set_upnp_port_mapping_cb, listen_data);
312 return;
314 } else if (!listen_data->adding) {
315 /* We've tried successfully to remove the port mapping.
316 * Try to add it again */
317 listen_data->adding = TRUE;
318 listen_data->mapping_data = purple_upnp_set_port_mapping(
319 purple_network_get_port_from_fd(listen_data->listenfd),
320 (listen_data->socket_type == SOCK_STREAM) ? "TCP" : "UDP",
321 purple_network_set_upnp_port_mapping_cb, listen_data);
322 return;
325 if (success) {
326 /* add port mapping to hash table */
327 gint key = purple_network_get_port_from_fd(listen_data->listenfd);
328 gint value = listen_data->socket_type;
329 g_hash_table_insert(upnp_port_mappings, GINT_TO_POINTER(key), GINT_TO_POINTER(value));
332 if (listen_data->cb)
333 listen_data->cb(listen_data->listenfd, listen_data->cb_data);
335 /* Clear the UPnP mapping data, since it's complete and purple_network_listen_cancel() will try to cancel
336 * it otherwise. */
337 listen_data->mapping_data = NULL;
338 purple_network_listen_cancel(listen_data);
341 static gboolean
342 purple_network_finish_pmp_map_cb(gpointer data)
344 PurpleNetworkListenData *listen_data;
345 gint key;
346 gint value;
348 listen_data = data;
349 listen_data->timer = 0;
351 /* add port mapping to hash table */
352 key = purple_network_get_port_from_fd(listen_data->listenfd);
353 value = listen_data->socket_type;
354 g_hash_table_insert(nat_pmp_port_mappings, GINT_TO_POINTER(key), GINT_TO_POINTER(value));
356 if (listen_data->cb)
357 listen_data->cb(listen_data->listenfd, listen_data->cb_data);
359 purple_network_listen_cancel(listen_data);
361 return FALSE;
364 static PurpleNetworkListenData *
365 purple_network_do_listen(unsigned short port, int socket_family, int socket_type, gboolean map_external,
366 PurpleNetworkListenCallback cb, gpointer cb_data)
368 int listenfd = -1;
369 const int on = 1;
370 PurpleNetworkListenData *listen_data;
371 unsigned short actual_port;
372 #ifdef HAVE_GETADDRINFO
373 int errnum;
374 struct addrinfo hints, *res, *next;
375 char serv[6];
378 * Get a list of addresses on this machine.
380 g_snprintf(serv, sizeof(serv), "%hu", port);
381 memset(&hints, 0, sizeof(struct addrinfo));
382 hints.ai_flags = AI_PASSIVE;
383 hints.ai_family = socket_family;
384 hints.ai_socktype = socket_type;
385 errnum = getaddrinfo(NULL /* any IP */, serv, &hints, &res);
386 if (errnum != 0) {
387 #ifndef _WIN32
388 purple_debug_warning("network", "getaddrinfo: %s\n", purple_gai_strerror(errnum));
389 if (errnum == EAI_SYSTEM)
390 purple_debug_warning("network", "getaddrinfo: system error: %s\n", g_strerror(errno));
391 #else
392 purple_debug_warning("network", "getaddrinfo: Error Code = %d\n", errnum);
393 #endif
394 return NULL;
398 * Go through the list of addresses and attempt to listen on
399 * one of them.
400 * XXX - Try IPv6 addresses first?
402 for (next = res; next != NULL; next = next->ai_next) {
403 listenfd = socket(next->ai_family, next->ai_socktype, next->ai_protocol);
404 if (listenfd < 0)
405 continue;
406 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
407 purple_debug_warning("network", "setsockopt(SO_REUSEADDR): %s\n", g_strerror(errno));
408 if (bind(listenfd, next->ai_addr, next->ai_addrlen) == 0)
409 break; /* success */
410 /* XXX - It is unclear to me (datallah) whether we need to be
411 using a new socket each time */
412 close(listenfd);
415 freeaddrinfo(res);
417 if (next == NULL)
418 return NULL;
419 #else
420 struct sockaddr_in sockin;
422 if (socket_family != AF_INET && socket_family != AF_UNSPEC) {
423 purple_debug_warning("network", "Address family %d only "
424 "supported when built with getaddrinfo() "
425 "support\n", socket_family);
426 return NULL;
429 if ((listenfd = socket(AF_INET, socket_type, 0)) < 0) {
430 purple_debug_warning("network", "socket: %s\n", g_strerror(errno));
431 return NULL;
434 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
435 purple_debug_warning("network", "setsockopt: %s\n", g_strerror(errno));
437 memset(&sockin, 0, sizeof(struct sockaddr_in));
438 sockin.sin_family = PF_INET;
439 sockin.sin_port = htons(port);
441 if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
442 purple_debug_warning("network", "bind: %s\n", g_strerror(errno));
443 close(listenfd);
444 return NULL;
446 #endif
448 if (socket_type == SOCK_STREAM && listen(listenfd, 4) != 0) {
449 purple_debug_warning("network", "listen: %s\n", g_strerror(errno));
450 close(listenfd);
451 return NULL;
453 _purple_network_set_common_socket_flags(listenfd);
454 actual_port = purple_network_get_port_from_fd(listenfd);
456 purple_debug_info("network", "Listening on port: %hu\n", actual_port);
458 listen_data = g_new0(PurpleNetworkListenData, 1);
459 listen_data->listenfd = listenfd;
460 listen_data->adding = TRUE;
461 listen_data->retry = TRUE;
462 listen_data->cb = cb;
463 listen_data->cb_data = cb_data;
464 listen_data->socket_type = socket_type;
466 if (!purple_socket_speaks_ipv4(listenfd) || !map_external ||
467 !purple_prefs_get_bool("/purple/network/map_ports"))
469 purple_debug_info("network", "Skipping external port mapping.\n");
470 /* The pmp_map_cb does what we want to do */
471 listen_data->timer = g_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
473 /* Attempt a NAT-PMP Mapping, which will return immediately */
474 else if (purple_pmp_create_map(((socket_type == SOCK_STREAM) ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP),
475 actual_port, actual_port, PURPLE_PMP_LIFETIME))
477 purple_debug_info("network", "Created NAT-PMP mapping on port %i\n", actual_port);
478 /* We want to return listen_data now, and on the next run loop trigger the cb and destroy listen_data */
479 listen_data->timer = g_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
481 else
483 /* Attempt a UPnP Mapping */
484 listen_data->mapping_data = purple_upnp_set_port_mapping(
485 actual_port,
486 (socket_type == SOCK_STREAM) ? "TCP" : "UDP",
487 purple_network_set_upnp_port_mapping_cb, listen_data);
490 return listen_data;
493 PurpleNetworkListenData *
494 purple_network_listen(unsigned short port, int socket_family, int socket_type,
495 gboolean map_external, PurpleNetworkListenCallback cb,
496 gpointer cb_data)
498 g_return_val_if_fail(port != 0, NULL);
500 return purple_network_do_listen(port, socket_family, socket_type, map_external,
501 cb, cb_data);
504 PurpleNetworkListenData *
505 purple_network_listen_range(unsigned short start, unsigned short end,
506 int socket_family, int socket_type, gboolean map_external,
507 PurpleNetworkListenCallback cb,
508 gpointer cb_data)
510 PurpleNetworkListenData *ret = NULL;
512 if (purple_prefs_get_bool("/purple/network/ports_range_use")) {
513 start = purple_prefs_get_int("/purple/network/ports_range_start");
514 end = purple_prefs_get_int("/purple/network/ports_range_end");
515 } else {
516 if (end < start)
517 end = start;
520 for (; start <= end; start++) {
521 ret = purple_network_do_listen(start, AF_UNSPEC, socket_type, map_external, cb, cb_data);
522 if (ret != NULL)
523 break;
526 return ret;
529 void purple_network_listen_cancel(PurpleNetworkListenData *listen_data)
531 if (listen_data->mapping_data != NULL)
532 purple_upnp_cancel_port_mapping(listen_data->mapping_data);
534 if (listen_data->timer > 0)
535 g_source_remove(listen_data->timer);
537 g_free(listen_data);
540 unsigned short
541 purple_network_get_port_from_fd(int fd)
543 struct sockaddr_in addr;
544 socklen_t len;
546 g_return_val_if_fail(fd >= 0, 0);
548 len = sizeof(addr);
549 if (getsockname(fd, (struct sockaddr *) &addr, &len) == -1) {
550 purple_debug_warning("network", "getsockname: %s\n", g_strerror(errno));
551 return 0;
554 return ntohs(addr.sin_port);
557 gboolean
558 purple_network_is_available(void)
560 if(force_online) {
561 return TRUE;
564 return g_network_monitor_get_network_available(g_network_monitor_get_default());
567 void
568 purple_network_force_online()
570 force_online = TRUE;
573 static void
574 purple_network_ip_lookup_cb(GObject *sender, GAsyncResult *result, gpointer data) {
575 GError *error = NULL;
576 GList *addresses = NULL;
577 GInetAddress *address = NULL;
578 const gchar **ip_address = (const gchar **)data;
580 addresses = g_resolver_lookup_by_name_finish(G_RESOLVER(sender),
581 result, &error);
582 if(error) {
583 purple_debug_info("network", "lookup of IP address failed: %s\n", error->message);
585 g_error_free(error);
587 return;
590 address = G_INET_ADDRESS(addresses->data);
592 *ip_address = g_inet_address_to_string(address);
594 g_resolver_free_addresses(addresses);
597 void
598 purple_network_set_stun_server(const gchar *stun_server)
600 if (stun_server && stun_server[0] != '\0') {
601 if (purple_network_is_available()) {
602 GResolver *resolver = g_resolver_get_default();
603 g_resolver_lookup_by_name_async(resolver,
604 stun_server,
605 NULL,
606 purple_network_ip_lookup_cb,
607 &stun_ip);
608 g_object_unref(resolver);
609 } else {
610 purple_debug_info("network",
611 "network is unavailable, don't try to update STUN IP");
613 } else {
614 g_free(stun_ip);
615 stun_ip = NULL;
619 void
620 purple_network_set_turn_server(const gchar *turn_server)
622 if (turn_server && turn_server[0] != '\0') {
623 if (purple_network_is_available()) {
624 GResolver *resolver = g_resolver_get_default();
625 g_resolver_lookup_by_name_async(resolver,
626 turn_server,
627 NULL,
628 purple_network_ip_lookup_cb,
629 &turn_server);
630 g_object_unref(resolver);
631 } else {
632 purple_debug_info("network",
633 "network is unavailable, don't try to update TURN IP");
635 } else {
636 g_free(turn_ip);
637 turn_ip = NULL;
642 const gchar *
643 purple_network_get_stun_ip(void)
645 return stun_ip;
648 const gchar *
649 purple_network_get_turn_ip(void)
651 return turn_ip;
654 void *
655 purple_network_get_handle(void)
657 static int handle;
659 return &handle;
662 static void
663 purple_network_upnp_mapping_remove_cb(gboolean sucess, gpointer data)
665 purple_debug_info("network", "done removing UPnP port mapping\n");
668 /* the reason for these functions to have these signatures is to be able to
669 use them for g_hash_table_foreach to clean remaining port mappings, which is
670 not yet done */
671 static void
672 purple_network_upnp_mapping_remove(gpointer key, gpointer value,
673 gpointer user_data)
675 gint port = GPOINTER_TO_INT(key);
676 gint protocol = GPOINTER_TO_INT(value);
677 purple_debug_info("network", "removing UPnP port mapping for port %d\n",
678 port);
679 purple_upnp_remove_port_mapping(port,
680 protocol == SOCK_STREAM ? "TCP" : "UDP",
681 purple_network_upnp_mapping_remove_cb, NULL);
682 g_hash_table_remove(upnp_port_mappings, GINT_TO_POINTER(port));
685 static void
686 purple_network_nat_pmp_mapping_remove(gpointer key, gpointer value,
687 gpointer user_data)
689 gint port = GPOINTER_TO_INT(key);
690 gint protocol = GPOINTER_TO_INT(value);
691 purple_debug_info("network", "removing NAT-PMP port mapping for port %d\n",
692 port);
693 purple_pmp_destroy_map(
694 protocol == SOCK_STREAM ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP,
695 port);
696 g_hash_table_remove(nat_pmp_port_mappings, GINT_TO_POINTER(port));
699 void
700 purple_network_remove_port_mapping(gint fd)
702 int port = purple_network_get_port_from_fd(fd);
703 gint protocol = GPOINTER_TO_INT(g_hash_table_lookup(upnp_port_mappings, GINT_TO_POINTER(port)));
705 if (protocol) {
706 purple_network_upnp_mapping_remove(GINT_TO_POINTER(port), GINT_TO_POINTER(protocol), NULL);
707 } else {
708 protocol = GPOINTER_TO_INT(g_hash_table_lookup(nat_pmp_port_mappings, GINT_TO_POINTER(port)));
709 if (protocol) {
710 purple_network_nat_pmp_mapping_remove(GINT_TO_POINTER(port), GINT_TO_POINTER(protocol), NULL);
715 int purple_network_convert_idn_to_ascii(const gchar *in, gchar **out)
717 #ifdef USE_IDN
718 char *tmp;
719 int ret;
721 g_return_val_if_fail(out != NULL, -1);
723 ret = idna_to_ascii_8z(in, &tmp, IDNA_USE_STD3_ASCII_RULES);
724 if (ret != IDNA_SUCCESS) {
725 *out = NULL;
726 return ret;
729 *out = g_strdup(tmp);
730 /* This *MUST* be freed with free, not g_free */
731 free(tmp);
732 return 0;
733 #else
734 g_return_val_if_fail(out != NULL, -1);
736 *out = g_strdup(in);
737 return 0;
738 #endif
741 gboolean
742 _purple_network_set_common_socket_flags(int fd)
744 int flags;
745 gboolean succ = TRUE;
747 g_return_val_if_fail(fd >= 0, FALSE);
749 flags = fcntl(fd, F_GETFL);
751 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
752 purple_debug_warning("network",
753 "Couldn't set O_NONBLOCK flag\n");
754 succ = FALSE;
757 #ifndef _WIN32
758 if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
759 purple_debug_warning("network",
760 "Couldn't set FD_CLOEXEC flag\n");
761 succ = FALSE;
763 #endif
765 return succ;
768 void
769 purple_network_init(void)
771 purple_prefs_add_none ("/purple/network");
772 purple_prefs_add_string("/purple/network/stun_server", "");
773 purple_prefs_add_string("/purple/network/turn_server", "");
774 purple_prefs_add_int ("/purple/network/turn_port", 3478);
775 purple_prefs_add_int ("/purple/network/turn_port_tcp", 3478);
776 purple_prefs_add_string("/purple/network/turn_username", "");
777 purple_prefs_add_string("/purple/network/turn_password", "");
778 purple_prefs_add_bool ("/purple/network/auto_ip", TRUE);
779 purple_prefs_add_string("/purple/network/public_ip", "");
780 purple_prefs_add_bool ("/purple/network/map_ports", TRUE);
781 purple_prefs_add_bool ("/purple/network/ports_range_use", FALSE);
782 purple_prefs_add_int ("/purple/network/ports_range_start", 1024);
783 purple_prefs_add_int ("/purple/network/ports_range_end", 2048);
785 if(purple_prefs_get_bool("/purple/network/map_ports") || purple_prefs_get_bool("/purple/network/auto_ip"))
786 purple_upnp_discover(NULL, NULL);
788 purple_pmp_init();
789 purple_upnp_init();
791 purple_network_set_stun_server(
792 purple_prefs_get_string("/purple/network/stun_server"));
793 purple_network_set_turn_server(
794 purple_prefs_get_string("/purple/network/turn_server"));
796 upnp_port_mappings = g_hash_table_new(g_direct_hash, g_direct_equal);
797 nat_pmp_port_mappings = g_hash_table_new(g_direct_hash, g_direct_equal);
802 void
803 purple_network_uninit(void)
805 g_free(stun_ip);
807 g_hash_table_destroy(upnp_port_mappings);
808 g_hash_table_destroy(nat_pmp_port_mappings);
810 /* TODO: clean up remaining port mappings, note calling
811 purple_upnp_remove_port_mapping from here doesn't quite work... */