pidgin/pidgin

Hide common_sockaddr_t as it needs to be removed but it's causing issues now

As mentioned in https://reviews.imfreedom.org/r/14/ this is my current fix for the `common_sockaddr_t` problem. And that is to just hide. By hiding it, we make the core network api and bonjour uses of it independent which means we can swap the out individually with smaller review requests.

Hide common_sockaddr_t as it needs to be removed but it's causing issues now

Reviewed at https://reviews.imfreedom.org/r/15/
/* purple
*
* Purple is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#include "internal.h"
#include "purpleprivate.h"
#include <gio/gio.h>
#ifndef _WIN32
#include <arpa/nameser.h>
#include <resolv.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#ifdef HAVE_GETIFADDRS
#include <ifaddrs.h>
#endif
#else
#include <nspapi.h>
#endif
/* Solaris */
#if defined (__SVR4) && defined (__sun)
#include <sys/sockio.h>
#endif
#include "debug.h"
#include "account.h"
#include "nat-pmp.h"
#include "network.h"
#include "prefs.h"
#include "stun.h"
#include "upnp.h"
/*
* Calling sizeof(struct ifreq) isn't always correct on
* Mac OS X (and maybe others).
*/
#ifdef _SIZEOF_ADDR_IFREQ
# define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
#else
# define HX_SIZE_OF_IFREQ(a) sizeof(a)
#endif
typedef union
{
struct sockaddr sa;
struct sockaddr_in in;
struct sockaddr_in6 in6;
struct sockaddr_storage storage;
} common_sockaddr_t;
struct _PurpleNetworkListenData {
int listenfd;
int socket_type;
gboolean retry;
gboolean adding;
PurpleNetworkListenCallback cb;
gpointer cb_data;
PurpleUPnPMappingAddRemove *mapping_data;
int timer;
};
static gboolean force_online = FALSE;
/* Cached IP addresses for STUN and TURN servers (set globally in prefs) */
static gchar *stun_ip = NULL;
static gchar *turn_ip = NULL;
/* Keep track of port mappings done with UPnP and NAT-PMP */
static GHashTable *upnp_port_mappings = NULL;
static GHashTable *nat_pmp_port_mappings = NULL;
void
purple_network_set_public_ip(const char *ip)
{
g_return_if_fail(ip != NULL);
/* XXX - Ensure the IP address is valid */
purple_prefs_set_string("/purple/network/public_ip", ip);
}
const char *
purple_network_get_public_ip(void)
{
return purple_prefs_get_string("/purple/network/public_ip");
}
const char *
purple_network_get_local_system_ip(int fd)
{
struct ifreq buffer[100];
guchar *it, *it_end;
static char ip[16];
struct ifconf ifc;
struct ifreq *ifr;
struct sockaddr_in *sinptr;
guint32 lhost = htonl((127 << 24) + 1); /* 127.0.0.1 */
long unsigned int add;
int source = fd;
if (fd < 0)
source = socket(PF_INET,SOCK_STREAM, 0);
ifc.ifc_len = sizeof(buffer);
ifc.ifc_req = buffer;
ioctl(source, SIOCGIFCONF, &ifc);
if (fd < 0 && source >= 0)
close(source);
it = (guchar*)buffer;
it_end = it + ifc.ifc_len;
while (it < it_end) {
/* in this case "it" is:
* a) (struct ifreq)-aligned
* b) not aligned, because of OS quirks (see
* _SIZEOF_ADDR_IFREQ), so the OS should deal with it.
*/
ifr = (struct ifreq *)(gpointer)it;
it += HX_SIZE_OF_IFREQ(*ifr);
if (ifr->ifr_addr.sa_family == AF_INET)
{
sinptr = (struct sockaddr_in *)(gpointer)&ifr->ifr_addr;
if (sinptr->sin_addr.s_addr != lhost)
{
add = ntohl(sinptr->sin_addr.s_addr);
g_snprintf(ip, 16, "%lu.%lu.%lu.%lu",
((add >> 24) & 255),
((add >> 16) & 255),
((add >> 8) & 255),
add & 255);
return ip;
}
}
}
return "0.0.0.0";
}
static gchar *
purple_network_get_local_system_ip_from_gio(GSocketConnection *sockconn)
{
GSocketAddress *addr;
GInetSocketAddress *inetsockaddr;
gchar *ip;
addr = g_socket_connection_get_local_address(sockconn, NULL);
if ((inetsockaddr = G_INET_SOCKET_ADDRESS(addr)) != NULL) {
GInetAddress *inetaddr =
g_inet_socket_address_get_address(inetsockaddr);
if (g_inet_address_get_family(inetaddr) == G_SOCKET_FAMILY_IPV4 &&
!g_inet_address_get_is_loopback(inetaddr)) {
ip = g_inet_address_to_string(inetaddr);
g_object_unref(addr);
return ip;
}
}
g_object_unref(addr);
return g_strdup("0.0.0.0");
}
GList *
purple_network_get_all_local_system_ips(void)
{
#if defined(HAVE_GETIFADDRS) && defined(HAVE_INET_NTOP)
GList *result = NULL;
struct ifaddrs *start, *ifa;
int ret;
ret = getifaddrs(&start);
if (ret < 0) {
purple_debug_warning("network",
"getifaddrs() failed: %s\n", g_strerror(errno));
return NULL;
}
for (ifa = start; ifa; ifa = ifa->ifa_next) {
int family = ifa->ifa_addr ? ifa->ifa_addr->sa_family : AF_UNSPEC;
char host[INET6_ADDRSTRLEN];
const char *tmp = NULL;
common_sockaddr_t *addr =
(common_sockaddr_t *)(gpointer)ifa->ifa_addr;
if ((family != AF_INET && family != AF_INET6) || ifa->ifa_flags & IFF_LOOPBACK)
continue;
if (family == AF_INET)
tmp = inet_ntop(family, &addr->in.sin_addr, host, sizeof(host));
else {
/* Peer-peer link-local communication is a big TODO. I am not sure
* how communicating link-local addresses is supposed to work, and
* it seems like it would require attempting the cartesian product
* of the local and remote interfaces to see if any match (eww).
*/
if (!IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr))
tmp = inet_ntop(family, &addr->in6.sin6_addr, host, sizeof(host));
}
if (tmp != NULL)
result = g_list_prepend(result, g_strdup(tmp));
}
freeifaddrs(start);
return g_list_reverse(result);
#else /* HAVE_GETIFADDRS && HAVE_INET_NTOP */
GList *result = NULL;
int source = socket(PF_INET,SOCK_STREAM, 0);
struct ifreq buffer[100];
guchar *it, *it_end;
struct ifconf ifc;
struct ifreq *ifr;
ifc.ifc_len = sizeof(buffer);
ifc.ifc_req = buffer;
ioctl(source, SIOCGIFCONF, &ifc);
close(source);
it = (guchar*)buffer;
it_end = it + ifc.ifc_len;
while (it < it_end) {
char dst[INET_ADDRSTRLEN];
/* alignment: see purple_network_get_local_system_ip */
ifr = (struct ifreq *)(gpointer)it;
it += HX_SIZE_OF_IFREQ(*ifr);
if (ifr->ifr_addr.sa_family == AF_INET) {
struct sockaddr_in *sinptr =
(struct sockaddr_in *)(gpointer)&ifr->ifr_addr;
inet_ntop(AF_INET, &sinptr->sin_addr, dst,
sizeof(dst));
purple_debug_info("network",
"found local i/f with address %s on IPv4\n", dst);
if (!purple_strequal(dst, "127.0.0.1")) {
result = g_list_append(result, g_strdup(dst));
}
}
}
return result;
#endif /* HAVE_GETIFADDRS && HAVE_INET_NTOP */
}
/*
* purple_network_is_ipv4:
* @hostname: The hostname to be verified.
*
* Checks, if specified hostname is valid ipv4 address.
*
* Returns: TRUE, if the hostname is valid.
*/
static gboolean
purple_network_is_ipv4(const gchar *hostname)
{
g_return_val_if_fail(hostname != NULL, FALSE);
/* We don't accept ipv6 here. */
if (strchr(hostname, ':') != NULL)
return FALSE;
return g_hostname_is_ip_address(hostname);
}
const char *
purple_network_get_my_ip(int fd)
{
const char *ip = NULL;
PurpleStunNatDiscovery *stun;
/* Check if the user specified an IP manually */
if (!purple_prefs_get_bool("/purple/network/auto_ip")) {
ip = purple_network_get_public_ip();
/* Make sure the IP address entered by the user is valid */
if ((ip != NULL) && (purple_network_is_ipv4(ip)))
return ip;
} else {
/* Check if STUN discovery was already done */
stun = purple_stun_discover(NULL);
if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED))
return stun->publicip;
/* Attempt to get the IP from a NAT device using UPnP */
ip = purple_upnp_get_public_ip();
if (ip != NULL)
return ip;
/* Attempt to get the IP from a NAT device using NAT-PMP */
ip = purple_pmp_get_public_ip();
if (ip != NULL)
return ip;
}
/* Just fetch the IP of the local system */
return purple_network_get_local_system_ip(fd);
}
gchar *
purple_network_get_my_ip_from_gio(GSocketConnection *sockconn)
{
const gchar *ip = NULL;
PurpleStunNatDiscovery *stun;
/* Check if the user specified an IP manually */
if (!purple_prefs_get_bool("/purple/network/auto_ip")) {
ip = purple_network_get_public_ip();
/* Make sure the IP address entered by the user is valid */
if ((ip != NULL) && (purple_network_is_ipv4(ip))) {
return g_strdup(ip);
}
} else {
/* Check if STUN discovery was already done */
stun = purple_stun_discover(NULL);
if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED)) {
return g_strdup(stun->publicip);
}
/* Attempt to get the IP from a NAT device using UPnP */
ip = purple_upnp_get_public_ip();
if (ip != NULL) {
return g_strdup(ip);
}
/* Attempt to get the IP from a NAT device using NAT-PMP */
ip = purple_pmp_get_public_ip();
if (ip != NULL) {
return g_strdup(ip);
}
}
/* Just fetch the IP of the local system */
return purple_network_get_local_system_ip_from_gio(sockconn);
}
static void
purple_network_set_upnp_port_mapping_cb(gboolean success, gpointer data)
{
PurpleNetworkListenData *listen_data;
listen_data = data;
/* TODO: Once we're keeping track of upnp requests... */
/* listen_data->pnp_data = NULL; */
if (!success) {
purple_debug_warning("network", "Couldn't create UPnP mapping\n");
if (listen_data->retry) {
listen_data->retry = FALSE;
listen_data->adding = FALSE;
listen_data->mapping_data = purple_upnp_remove_port_mapping(
purple_network_get_port_from_fd(listen_data->listenfd),
(listen_data->socket_type == SOCK_STREAM) ? "TCP" : "UDP",
purple_network_set_upnp_port_mapping_cb, listen_data);
return;
}
} else if (!listen_data->adding) {
/* We've tried successfully to remove the port mapping.
* Try to add it again */
listen_data->adding = TRUE;
listen_data->mapping_data = purple_upnp_set_port_mapping(
purple_network_get_port_from_fd(listen_data->listenfd),
(listen_data->socket_type == SOCK_STREAM) ? "TCP" : "UDP",
purple_network_set_upnp_port_mapping_cb, listen_data);
return;
}
if (success) {
/* add port mapping to hash table */
gint key = purple_network_get_port_from_fd(listen_data->listenfd);
gint value = listen_data->socket_type;
g_hash_table_insert(upnp_port_mappings, GINT_TO_POINTER(key), GINT_TO_POINTER(value));
}
if (listen_data->cb)
listen_data->cb(listen_data->listenfd, listen_data->cb_data);
/* Clear the UPnP mapping data, since it's complete and purple_network_listen_cancel() will try to cancel
* it otherwise. */
listen_data->mapping_data = NULL;
purple_network_listen_cancel(listen_data);
}
static gboolean
purple_network_finish_pmp_map_cb(gpointer data)
{
PurpleNetworkListenData *listen_data;
gint key;
gint value;
listen_data = data;
listen_data->timer = 0;
/* add port mapping to hash table */
key = purple_network_get_port_from_fd(listen_data->listenfd);
value = listen_data->socket_type;
g_hash_table_insert(nat_pmp_port_mappings, GINT_TO_POINTER(key), GINT_TO_POINTER(value));
if (listen_data->cb)
listen_data->cb(listen_data->listenfd, listen_data->cb_data);
purple_network_listen_cancel(listen_data);
return FALSE;
}
static PurpleNetworkListenData *
purple_network_do_listen(unsigned short port, int socket_family, int socket_type, gboolean map_external,
PurpleNetworkListenCallback cb, gpointer cb_data)
{
int listenfd = -1;
const int on = 1;
PurpleNetworkListenData *listen_data;
unsigned short actual_port;
#ifdef HAVE_GETADDRINFO
int errnum;
struct addrinfo hints, *res, *next;
char serv[6];
/*
* Get a list of addresses on this machine.
*/
g_snprintf(serv, sizeof(serv), "%hu", port);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = socket_family;
hints.ai_socktype = socket_type;
errnum = getaddrinfo(NULL /* any IP */, serv, &hints, &res);
if (errnum != 0) {
#ifndef _WIN32
purple_debug_warning("network", "getaddrinfo: %s\n", purple_gai_strerror(errnum));
if (errnum == EAI_SYSTEM)
purple_debug_warning("network", "getaddrinfo: system error: %s\n", g_strerror(errno));
#else
purple_debug_warning("network", "getaddrinfo: Error Code = %d\n", errnum);
#endif
return NULL;
}
/*
* Go through the list of addresses and attempt to listen on
* one of them.
* XXX - Try IPv6 addresses first?
*/
for (next = res; next != NULL; next = next->ai_next) {
listenfd = socket(next->ai_family, next->ai_socktype, next->ai_protocol);
if (listenfd < 0)
continue;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
purple_debug_warning("network", "setsockopt(SO_REUSEADDR): %s\n", g_strerror(errno));
if (bind(listenfd, next->ai_addr, next->ai_addrlen) == 0)
break; /* success */
/* XXX - It is unclear to me (datallah) whether we need to be
using a new socket each time */
close(listenfd);
}
freeaddrinfo(res);
if (next == NULL)
return NULL;
#else
struct sockaddr_in sockin;
if (socket_family != AF_INET && socket_family != AF_UNSPEC) {
purple_debug_warning("network", "Address family %d only "
"supported when built with getaddrinfo() "
"support\n", socket_family);
return NULL;
}
if ((listenfd = socket(AF_INET, socket_type, 0)) < 0) {
purple_debug_warning("network", "socket: %s\n", g_strerror(errno));
return NULL;
}
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
purple_debug_warning("network", "setsockopt: %s\n", g_strerror(errno));
memset(&sockin, 0, sizeof(struct sockaddr_in));
sockin.sin_family = PF_INET;
sockin.sin_port = htons(port);
if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
purple_debug_warning("network", "bind: %s\n", g_strerror(errno));
close(listenfd);
return NULL;
}
#endif
if (socket_type == SOCK_STREAM && listen(listenfd, 4) != 0) {
purple_debug_warning("network", "listen: %s\n", g_strerror(errno));
close(listenfd);
return NULL;
}
_purple_network_set_common_socket_flags(listenfd);
actual_port = purple_network_get_port_from_fd(listenfd);
purple_debug_info("network", "Listening on port: %hu\n", actual_port);
listen_data = g_new0(PurpleNetworkListenData, 1);
listen_data->listenfd = listenfd;
listen_data->adding = TRUE;
listen_data->retry = TRUE;
listen_data->cb = cb;
listen_data->cb_data = cb_data;
listen_data->socket_type = socket_type;
if (!purple_socket_speaks_ipv4(listenfd) || !map_external ||
!purple_prefs_get_bool("/purple/network/map_ports"))
{
purple_debug_info("network", "Skipping external port mapping.\n");
/* The pmp_map_cb does what we want to do */
listen_data->timer = g_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
}
/* Attempt a NAT-PMP Mapping, which will return immediately */
else if (purple_pmp_create_map(((socket_type == SOCK_STREAM) ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP),
actual_port, actual_port, PURPLE_PMP_LIFETIME))
{
purple_debug_info("network", "Created NAT-PMP mapping on port %i\n", actual_port);
/* We want to return listen_data now, and on the next run loop trigger the cb and destroy listen_data */
listen_data->timer = g_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
}
else
{
/* Attempt a UPnP Mapping */
listen_data->mapping_data = purple_upnp_set_port_mapping(
actual_port,
(socket_type == SOCK_STREAM) ? "TCP" : "UDP",
purple_network_set_upnp_port_mapping_cb, listen_data);
}
return listen_data;
}
PurpleNetworkListenData *
purple_network_listen(unsigned short port, int socket_family, int socket_type,
gboolean map_external, PurpleNetworkListenCallback cb,
gpointer cb_data)
{
g_return_val_if_fail(port != 0, NULL);
return purple_network_do_listen(port, socket_family, socket_type, map_external,
cb, cb_data);
}
PurpleNetworkListenData *
purple_network_listen_range(unsigned short start, unsigned short end,
int socket_family, int socket_type, gboolean map_external,
PurpleNetworkListenCallback cb,
gpointer cb_data)
{
PurpleNetworkListenData *ret = NULL;
if (purple_prefs_get_bool("/purple/network/ports_range_use")) {
start = purple_prefs_get_int("/purple/network/ports_range_start");
end = purple_prefs_get_int("/purple/network/ports_range_end");
} else {
if (end < start)
end = start;
}
for (; start <= end; start++) {
ret = purple_network_do_listen(start, AF_UNSPEC, socket_type, map_external, cb, cb_data);
if (ret != NULL)
break;
}
return ret;
}
void purple_network_listen_cancel(PurpleNetworkListenData *listen_data)
{
if (listen_data->mapping_data != NULL)
purple_upnp_cancel_port_mapping(listen_data->mapping_data);
if (listen_data->timer > 0)
g_source_remove(listen_data->timer);
g_free(listen_data);
}
unsigned short
purple_network_get_port_from_fd(int fd)
{
struct sockaddr_in addr;
socklen_t len;
g_return_val_if_fail(fd >= 0, 0);
len = sizeof(addr);
if (getsockname(fd, (struct sockaddr *) &addr, &len) == -1) {
purple_debug_warning("network", "getsockname: %s\n", g_strerror(errno));
return 0;
}
return ntohs(addr.sin_port);
}
gboolean
purple_network_is_available(void)
{
if(force_online) {
return TRUE;
}
return g_network_monitor_get_network_available(g_network_monitor_get_default());
}
void
purple_network_force_online()
{
force_online = TRUE;
}
static void
purple_network_ip_lookup_cb(GObject *sender, GAsyncResult *result, gpointer data) {
GError *error = NULL;
GList *addresses = NULL;
GInetAddress *address = NULL;
const gchar **ip_address = (const gchar **)data;
addresses = g_resolver_lookup_by_name_finish(G_RESOLVER(sender),
result, &error);
if(error) {
purple_debug_info("network", "lookup of IP address failed: %s\n", error->message);
g_error_free(error);
return;
}
address = G_INET_ADDRESS(addresses->data);
*ip_address = g_inet_address_to_string(address);
g_resolver_free_addresses(addresses);
}
void
purple_network_set_stun_server(const gchar *stun_server)
{
if (stun_server && stun_server[0] != '\0') {
if (purple_network_is_available()) {
GResolver *resolver = g_resolver_get_default();
g_resolver_lookup_by_name_async(resolver,
stun_server,
NULL,
purple_network_ip_lookup_cb,
&stun_ip);
g_object_unref(resolver);
} else {
purple_debug_info("network",
"network is unavailable, don't try to update STUN IP");
}
} else {
g_free(stun_ip);
stun_ip = NULL;
}
}
void
purple_network_set_turn_server(const gchar *turn_server)
{
if (turn_server && turn_server[0] != '\0') {
if (purple_network_is_available()) {
GResolver *resolver = g_resolver_get_default();
g_resolver_lookup_by_name_async(resolver,
turn_server,
NULL,
purple_network_ip_lookup_cb,
&turn_ip);
g_object_unref(resolver);
} else {
purple_debug_info("network",
"network is unavailable, don't try to update TURN IP");
}
} else {
g_free(turn_ip);
turn_ip = NULL;
}
}
const gchar *
purple_network_get_stun_ip(void)
{
return stun_ip;
}
const gchar *
purple_network_get_turn_ip(void)
{
return turn_ip;
}
void *
purple_network_get_handle(void)
{
static int handle;
return &handle;
}
static void
purple_network_upnp_mapping_remove_cb(gboolean sucess, gpointer data)
{
purple_debug_info("network", "done removing UPnP port mapping\n");
}
/* the reason for these functions to have these signatures is to be able to
use them for g_hash_table_foreach to clean remaining port mappings, which is
not yet done */
static void
purple_network_upnp_mapping_remove(gpointer key, gpointer value,
gpointer user_data)
{
gint port = GPOINTER_TO_INT(key);
gint protocol = GPOINTER_TO_INT(value);
purple_debug_info("network", "removing UPnP port mapping for port %d\n",
port);
purple_upnp_remove_port_mapping(port,
protocol == SOCK_STREAM ? "TCP" : "UDP",
purple_network_upnp_mapping_remove_cb, NULL);
g_hash_table_remove(upnp_port_mappings, GINT_TO_POINTER(port));
}
static void
purple_network_nat_pmp_mapping_remove(gpointer key, gpointer value,
gpointer user_data)
{
gint port = GPOINTER_TO_INT(key);
gint protocol = GPOINTER_TO_INT(value);
purple_debug_info("network", "removing NAT-PMP port mapping for port %d\n",
port);
purple_pmp_destroy_map(
protocol == SOCK_STREAM ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP,
port);
g_hash_table_remove(nat_pmp_port_mappings, GINT_TO_POINTER(port));
}
void
purple_network_remove_port_mapping(gint fd)
{
int port = purple_network_get_port_from_fd(fd);
gint protocol = GPOINTER_TO_INT(g_hash_table_lookup(upnp_port_mappings, GINT_TO_POINTER(port)));
if (protocol) {
purple_network_upnp_mapping_remove(GINT_TO_POINTER(port), GINT_TO_POINTER(protocol), NULL);
} else {
protocol = GPOINTER_TO_INT(g_hash_table_lookup(nat_pmp_port_mappings, GINT_TO_POINTER(port)));
if (protocol) {
purple_network_nat_pmp_mapping_remove(GINT_TO_POINTER(port), GINT_TO_POINTER(protocol), NULL);
}
}
}
gboolean
_purple_network_set_common_socket_flags(int fd)
{
int flags;
gboolean succ = TRUE;
g_return_val_if_fail(fd >= 0, FALSE);
flags = fcntl(fd, F_GETFL);
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
purple_debug_warning("network",
"Couldn't set O_NONBLOCK flag\n");
succ = FALSE;
}
#ifndef _WIN32
if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
purple_debug_warning("network",
"Couldn't set FD_CLOEXEC flag\n");
succ = FALSE;
}
#endif
return succ;
}
void
purple_network_init(void)
{
purple_prefs_add_none ("/purple/network");
purple_prefs_add_string("/purple/network/stun_server", "");
purple_prefs_add_string("/purple/network/turn_server", "");
purple_prefs_add_int ("/purple/network/turn_port", 3478);
purple_prefs_add_int ("/purple/network/turn_port_tcp", 3478);
purple_prefs_add_string("/purple/network/turn_username", "");
purple_prefs_add_string("/purple/network/turn_password", "");
purple_prefs_add_bool ("/purple/network/auto_ip", TRUE);
purple_prefs_add_string("/purple/network/public_ip", "");
purple_prefs_add_bool ("/purple/network/map_ports", TRUE);
purple_prefs_add_bool ("/purple/network/ports_range_use", FALSE);
purple_prefs_add_int ("/purple/network/ports_range_start", 1024);
purple_prefs_add_int ("/purple/network/ports_range_end", 2048);
if(purple_prefs_get_bool("/purple/network/map_ports") || purple_prefs_get_bool("/purple/network/auto_ip"))
purple_upnp_discover(NULL, NULL);
purple_pmp_init();
purple_upnp_init();
purple_network_set_stun_server(
purple_prefs_get_string("/purple/network/stun_server"));
purple_network_set_turn_server(
purple_prefs_get_string("/purple/network/turn_server"));
upnp_port_mappings = g_hash_table_new(g_direct_hash, g_direct_equal);
nat_pmp_port_mappings = g_hash_table_new(g_direct_hash, g_direct_equal);
}
void
purple_network_uninit(void)
{
g_free(stun_ip);
g_free(turn_ip);
g_hash_table_destroy(upnp_port_mappings);
g_hash_table_destroy(nat_pmp_port_mappings);
/* TODO: clean up remaining port mappings, note calling
purple_upnp_remove_port_mapping from here doesn't quite work... */
purple_upnp_uninit();
}