* @file network.c Network Implementation * 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 * 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 <arpa/nameser.h> #if defined (__SVR4) && defined (__sun) * 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) # define HX_SIZE_OF_IFREQ(a) sizeof(a) #ifdef HAVE_NETWORKMANAGER #include <dbus/dbus-glib.h> #include <NetworkManager.h> #if !defined(NM_CHECK_VERSION) #define NM_CHECK_VERSION(x,y,z) 0 static DBusGConnection * nm_conn = NULL ; static DBusGProxy * nm_proxy = NULL ; static DBusGProxy * dbus_proxy = NULL ; static NMState nm_state = NM_STATE_UNKNOWN ; static gboolean have_nm_state = FALSE ; static int current_network_count ; /* Mutex for the other global vars */ static GStaticMutex mutex = G_STATIC_MUTEX_INIT ; static gboolean network_initialized = FALSE ; static HANDLE network_change_handle = NULL ; static int ( WSAAPI * MyWSANSPIoctl ) ( HANDLE hLookup , DWORD dwControlCode , LPVOID lpvInBuffer , DWORD cbInBuffer , LPVOID lpvOutBuffer , DWORD cbOutBuffer , LPDWORD lpcbBytesReturned , LPWSACOMPLETION lpCompletion ) = NULL ; struct _PurpleNetworkListenData { PurpleNetworkListenCallback cb ; UPnPMappingAddRemove * mapping_data ; #ifdef HAVE_NETWORKMANAGER static NMState nm_get_network_state ( void ); #if defined(HAVE_NETWORKMANAGER) || defined(_WIN32) static gboolean force_online ; /* 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 ; purple_network_ip_atoi ( const char * ip ) static unsigned char ret [ 4 ]; g_return_val_if_fail ( ip != NULL , NULL ); split = g_strsplit ( ip , delimiter , 4 ); for ( i = 0 ; split [ i ] != NULL ; i ++ ) /* i should always be 4 */ 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 ); purple_network_get_public_ip ( void ) return purple_prefs_get_string ( "/purple/network/public_ip" ); purple_network_get_local_system_ip ( int fd ) struct sockaddr_in * sinptr ; guint32 lhost = htonl (( 127 << 24 ) + 1 ); /* 127.0.0.1 */ source = socket ( PF_INET , SOCK_STREAM , 0 ); ifc . ifc_len = sizeof ( buffer ); ifc . ifc_req = ( struct ifreq * ) buffer ; ioctl ( source , SIOCGIFCONF , & ifc ); if ( fd < 0 && source >= 0 ) while ( tmp < buffer + ifc . ifc_len ) ifr = ( struct ifreq * ) tmp ; tmp += HX_SIZE_OF_IFREQ ( * ifr ); if ( ifr -> ifr_addr . sa_family == AF_INET ) sinptr = ( struct sockaddr_in * ) & 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" , purple_network_get_all_local_system_ips ( void ) #if defined(HAVE_GETIFADDRS) && defined(HAVE_INET_NTOP) struct ifaddrs * start , * ifa ; ret = getifaddrs ( & start ); purple_debug_warning ( "network" , "getifaddrs() failed: %s \n " , g_strerror ( errno )); for ( ifa = start ; ifa ; ifa = ifa -> ifa_next ) { int family = ifa -> ifa_addr ? ifa -> ifa_addr -> sa_family : AF_UNSPEC ; char host [ INET6_ADDRSTRLEN ]; if (( family != AF_INET && family != AF_INET6 ) || ifa -> ifa_flags & IFF_LOOPBACK ) tmp = inet_ntop ( family , & (( struct sockaddr_in * ) ifa -> ifa_addr ) -> sin_addr , host , sizeof ( host )); struct sockaddr_in6 * sockaddr = ( struct sockaddr_in6 * ) ifa -> ifa_addr ; /* 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 ( & sockaddr -> sin6_addr )) tmp = inet_ntop ( family , & sockaddr -> sin6_addr , host , sizeof ( host )); result = g_list_prepend ( result , g_strdup ( tmp )); return g_list_reverse ( result ); #else /* HAVE_GETIFADDRS && HAVE_INET_NTOP */ int source = socket ( PF_INET , SOCK_STREAM , 0 ); ifc . ifc_len = sizeof ( buffer ); ifc . ifc_req = ( struct ifreq * ) buffer ; ioctl ( source , SIOCGIFCONF , & ifc ); while ( tmp < buffer + ifc . ifc_len ) { char dst [ INET_ADDRSTRLEN ]; ifr = ( struct ifreq * ) tmp ; tmp += HX_SIZE_OF_IFREQ ( * ifr ); if ( ifr -> ifr_addr . sa_family == AF_INET ) { struct sockaddr_in * sinptr = ( struct sockaddr_in * ) & ifr -> ifr_addr ; inet_ntop ( AF_INET , & sinptr -> sin_addr , 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 )); #endif /* HAVE_GETIFADDRS && HAVE_INET_NTOP */ purple_network_get_my_ip ( int fd ) 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_ip_atoi ( ip ) != NULL )) /* Check if STUN discovery was already done */ stun = purple_stun_discover ( NULL ); if (( stun != NULL ) && ( stun -> status == PURPLE_STUN_STATUS_DISCOVERED )) /* Attempt to get the IP from a NAT device using UPnP */ ip = purple_upnp_get_public_ip (); /* Attempt to get the IP from a NAT device using NAT-PMP */ ip = purple_pmp_get_public_ip (); /* Just fetch the IP of the local system */ return purple_network_get_local_system_ip ( fd ); purple_network_set_upnp_port_mapping_cb ( gboolean success , gpointer data ) PurpleNetworkListenData * listen_data ; /* TODO: Once we're keeping track of upnp requests... */ /* listen_data->pnp_data = NULL; */ 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 ); } else if ( ! listen_data -> adding ) { /* We've tried successfully to remove the port mapping. 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 ); /* 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 )); 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 listen_data -> mapping_data = NULL ; purple_network_listen_cancel ( listen_data ); purple_network_finish_pmp_map_cb ( gpointer data ) PurpleNetworkListenData * listen_data ; /* 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 )); listen_data -> cb ( listen_data -> listenfd , listen_data -> cb_data ); purple_network_listen_cancel ( listen_data ); static gboolean listen_map_external = TRUE ; void purple_network_listen_map_external ( gboolean map_external ) listen_map_external = map_external ; static PurpleNetworkListenData * purple_network_do_listen ( unsigned short port , int socket_family , int socket_type , PurpleNetworkListenCallback cb , gpointer cb_data ) PurpleNetworkListenData * listen_data ; unsigned short actual_port ; struct addrinfo hints , * res , * next ; * 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 ); 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 )); purple_debug_warning ( "network" , "getaddrinfo: Error Code = %d \n " , errnum ); * Go through the list of addresses and attempt to listen on * 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 ( 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 ) /* XXX - It is unclear to me (datallah) whether we need to be using a new socket each time */ 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 ); if (( listenfd = socket ( AF_INET , socket_type , 0 )) < 0 ) { purple_debug_warning ( "network" , "socket: %s \n " , g_strerror ( errno )); 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 )); if ( socket_type == SOCK_STREAM && listen ( listenfd , 4 ) != 0 ) { purple_debug_warning ( "network" , "listen: %s \n " , g_strerror ( errno )); _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_data = cb_data ; listen_data -> socket_type = socket_type ; if ( ! purple_socket_speaks_ipv4 ( listenfd ) || ! listen_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 = purple_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 = purple_timeout_add ( 0 , purple_network_finish_pmp_map_cb , listen_data ); /* Attempt a UPnP Mapping */ listen_data -> mapping_data = purple_upnp_set_port_mapping ( ( socket_type == SOCK_STREAM ) ? "TCP" : "UDP" , purple_network_set_upnp_port_mapping_cb , listen_data ); PurpleNetworkListenData * purple_network_listen_family ( unsigned short port , int socket_family , int socket_type , PurpleNetworkListenCallback cb , g_return_val_if_fail ( port != 0 , NULL ); return purple_network_do_listen ( port , socket_family , socket_type , PurpleNetworkListenData * purple_network_listen ( unsigned short port , int socket_type , PurpleNetworkListenCallback cb , gpointer cb_data ) return purple_network_listen_family ( port , AF_UNSPEC , socket_type , PurpleNetworkListenData * purple_network_listen_range_family ( unsigned short start , unsigned short end , int socket_family , int socket_type , PurpleNetworkListenCallback cb , 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" ); for (; start <= end ; start ++ ) { ret = purple_network_do_listen ( start , AF_UNSPEC , socket_type , cb , cb_data ); PurpleNetworkListenData * purple_network_listen_range ( unsigned short start , unsigned short end , int socket_type , PurpleNetworkListenCallback cb , return purple_network_listen_range_family ( start , end , AF_UNSPEC , socket_type , cb , cb_data ); 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 ) purple_timeout_remove ( listen_data -> timer ); purple_network_get_port_from_fd ( int fd ) g_return_val_if_fail ( fd >= 0 , 0 ); if ( getsockname ( fd , ( struct sockaddr * ) & addr , & len ) == -1 ) { purple_debug_warning ( "network" , "getsockname: %s \n " , g_strerror ( errno )); return ntohs ( addr . sin_port ); wpurple_get_connected_network_count ( void ) memset ( & qs , 0 , sizeof ( WSAQUERYSET )); qs . dwSize = sizeof ( WSAQUERYSET ); retval = WSALookupServiceBegin ( & qs , LUP_RETURN_ALL , & h ); if ( retval != ERROR_SUCCESS ) { errorid = WSAGetLastError (); msg = g_win32_error_message ( errorid ); purple_debug_warning ( "network" , "Couldn't retrieve NLA SP lookup handle. " "NLA service is probably not running. Message: %s (%d). \n " , WSAQUERYSET * res = ( LPWSAQUERYSET ) buf ; DWORD size = sizeof ( buf ); while (( retval = WSALookupServiceNext ( h , 0 , & size , res )) == ERROR_SUCCESS ) { purple_debug_info ( "network" , "found network '%s' \n " , res -> lpszServiceInstanceName ? res -> lpszServiceInstanceName : "(NULL)" ); errorid = WSAGetLastError (); if ( ! ( errorid == WSA_E_NO_MORE || errorid == WSAENOMORE )) { gchar * msg = g_win32_error_message ( errorid ); purple_debug_error ( "network" , "got unexpected NLA response %s (%d) \n " , msg , errorid ); retval = WSALookupServiceEnd ( h ); static gboolean wpurple_network_change_thread_cb ( gpointer data ) PurpleConnectionUiOps * ui_ops = purple_connections_get_ui_ops (); new_count = wpurple_get_connected_network_count (); purple_debug_info ( "network" , "Received Network Change Notification. Current network count is %d, previous count was %d. \n " , new_count , current_network_count ); purple_signal_emit ( purple_network_get_handle (), "network-configuration-changed" , NULL ); if ( new_count > 0 && ui_ops != NULL && ui_ops -> network_connected != NULL ) { ui_ops -> network_connected (); } else if ( new_count == 0 && current_network_count > 0 && ui_ops != NULL && ui_ops -> network_disconnected != NULL ) { ui_ops -> network_disconnected (); current_network_count = new_count ; static gboolean _print_debug_msg ( gpointer data ) { purple_debug_warning ( "network" , "%s" , msg ); static gpointer wpurple_network_change_thread ( gpointer data ) time_t last_trigger = time ( NULL ) - 31 ; WSAQUERYSET * res = ( LPWSAQUERYSET ) buf ; if (( nla_event = WSACreateEvent ()) == WSA_INVALID_EVENT ) { int errorid = WSAGetLastError (); gchar * msg = g_win32_error_message ( errorid ); purple_timeout_add ( 0 , _print_debug_msg , g_strdup_printf ( "Couldn't create WSA event. " "Message: %s (%d). \n " , msg , errorid )); WSACOMPLETION completion ; WSAOVERLAPPED overlapped ; g_static_mutex_lock ( & mutex ); if ( network_initialized == FALSE ) { /* purple_network_uninit has been called */ WSACloseEvent ( nla_event ); g_static_mutex_unlock ( & mutex ); if ( network_change_handle == NULL ) { memset ( & qs , 0 , sizeof ( WSAQUERYSET )); qs . dwSize = sizeof ( WSAQUERYSET ); if ( WSALookupServiceBegin ( & qs , 0 , & network_change_handle ) == SOCKET_ERROR ) { int errorid = WSAGetLastError (); gchar * msg = g_win32_error_message ( errorid ); purple_timeout_add ( 0 , _print_debug_msg , g_strdup_printf ( "Couldn't retrieve NLA SP lookup handle. " "NLA service is probably not running. Message: %s (%d). \n " , WSACloseEvent ( nla_event ); g_static_mutex_unlock ( & mutex ); g_static_mutex_unlock ( & mutex ); memset ( & completion , 0 , sizeof ( WSACOMPLETION )); completion . Type = NSP_NOTIFY_EVENT ; overlapped . hEvent = nla_event ; completion . Parameters . Event . lpOverlapped = & overlapped ; if ( MyWSANSPIoctl ( network_change_handle , SIO_NSP_NOTIFY_CHANGE , NULL , 0 , NULL , 0 , & retLen , & completion ) == SOCKET_ERROR ) { int errorid = WSAGetLastError (); if ( errorid == WSA_INVALID_HANDLE ) { purple_timeout_add ( 0 , _print_debug_msg , g_strdup ( "Invalid NLA handle; resetting. \n " )); g_static_mutex_lock ( & mutex ); retval = WSALookupServiceEnd ( network_change_handle ); network_change_handle = NULL ; g_static_mutex_unlock ( & mutex ); /* WSA_IO_PENDING indicates successful async notification will happen */ } else if ( errorid != WSA_IO_PENDING ) { gchar * msg = g_win32_error_message ( errorid ); purple_timeout_add ( 0 , _print_debug_msg , g_strdup_printf ( "Unable to wait for changes. Message: %s (%d). \n " , /* Make sure at least 30 seconds have elapsed since the last * notification so we don't peg the cpu if this keeps changing. */ if (( time ( NULL ) - last_trigger ) < 30 ) /* This will block until NLA notifies us */ retval = WaitForSingleObjectEx ( nla_event , WSA_INFINITE , TRUE ); last_trigger = time ( NULL ); g_static_mutex_lock ( & mutex ); if ( network_initialized == FALSE ) { WSACloseEvent ( nla_event ); g_static_mutex_unlock ( & mutex ); while (( retval = WSALookupServiceNext ( network_change_handle , 0 , & size , res )) == ERROR_SUCCESS ) { /*purple_timeout_add(0, _print_debug_msg, g_strdup_printf("thread found network '%s'\n", res->lpszServiceInstanceName ? res->lpszServiceInstanceName : "(NULL)"));*/ WSAResetEvent ( nla_event ); g_static_mutex_unlock ( & mutex ); purple_timeout_add ( 0 , wpurple_network_change_thread_cb , NULL ); purple_network_is_available ( void ) #ifdef HAVE_NETWORKMANAGER nm_state = nm_get_network_state (); if ( nm_state == NM_STATE_UNKNOWN ) purple_debug_warning ( "network" , "NetworkManager not active. Assuming connection exists. \n " ); #if NM_CHECK_VERSION(0,8,992) case NM_STATE_CONNECTED_LOCAL : case NM_STATE_CONNECTED_SITE : case NM_STATE_CONNECTED_GLOBAL : return ( current_network_count > 0 || force_online ); purple_network_force_online () #if defined(HAVE_NETWORKMANAGER) || defined(_WIN32) #ifdef HAVE_NETWORKMANAGER nm_update_state ( NMState state ) PurpleConnectionUiOps * ui_ops = purple_connections_get_ui_ops (); purple_signal_emit ( purple_network_get_handle (), "network-configuration-changed" , NULL ); #if NM_CHECK_VERSION(0,8,992) case NM_STATE_CONNECTED_LOCAL : case NM_STATE_CONNECTED_SITE : case NM_STATE_CONNECTED_GLOBAL : /* Call res_init in case DNS servers have changed */ /* update STUN IP in case we it changed (theoretically we could have gone from IPv4 to IPv6, f.ex. or we were previously 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" )); if ( ui_ops != NULL && ui_ops -> network_connected != NULL ) ui_ops -> network_connected (); case NM_STATE_CONNECTING : case NM_STATE_DISCONNECTED : #if NM_CHECK_VERSION(0,8,992) case NM_STATE_DISCONNECTING : if ( prev != NM_STATE_CONNECTED && prev != NM_STATE_UNKNOWN ) if ( ui_ops != NULL && ui_ops -> network_disconnected != NULL ) ui_ops -> network_disconnected (); nm_state_change_cb ( DBusGProxy * proxy , NMState state , gpointer user_data ) purple_debug_info ( "network" , "Got StateChange from NetworkManager: %d. \n " , state ); nm_get_network_state ( void ) NMState state = NM_STATE_UNKNOWN ; if ( ! dbus_g_proxy_call ( nm_proxy , "state" , & err , G_TYPE_INVALID , G_TYPE_UINT , & state , G_TYPE_INVALID )) { nm_dbus_name_owner_changed_cb ( DBusGProxy * proxy , char * service , char * old_owner , char * new_owner , gpointer user_data ) if ( g_str_equal ( service , NM_DBUS_SERVICE )) { gboolean old_owner_good = old_owner && ( old_owner [ 0 ] != '\0' ); gboolean new_owner_good = new_owner && ( new_owner [ 0 ] != '\0' ); purple_debug_info ( "network" , "Got NameOwnerChanged signal, service = '%s', old_owner = '%s', new_owner = '%s' \n " , service , old_owner , new_owner ); if ( ! old_owner_good && new_owner_good ) { /* Equivalent to old ServiceCreated signal */ purple_debug_info ( "network" , "NetworkManager has started. \n " ); nm_update_state ( nm_get_network_state ()); } else if ( old_owner_good && ! new_owner_good ) { /* Equivalent to old ServiceDeleted signal */ purple_debug_info ( "network" , "NetworkManager has gone away. \n " ); nm_update_state ( NM_STATE_UNKNOWN ); purple_network_ip_lookup_cb ( GSList * hosts , gpointer data , const char * error_message ) const gchar ** ip = ( const gchar ** ) data ; purple_debug_error ( "network" , "lookup of IP address failed: %s \n " , if ( hosts && g_slist_next ( hosts )) { struct sockaddr * addr = g_slist_next ( hosts ) -> data ; char dst [ INET6_ADDRSTRLEN ]; if ( addr -> sa_family == AF_INET6 ) { inet_ntop ( addr -> sa_family , & (( struct sockaddr_in6 * ) addr ) -> sin6_addr , inet_ntop ( addr -> sa_family , & (( struct sockaddr_in * ) addr ) -> sin_addr , purple_debug_info ( "network" , "set IP address: %s \n " , * ip ); hosts = g_slist_delete_link ( hosts , hosts ); hosts = g_slist_delete_link ( hosts , hosts ); purple_network_set_stun_server ( const gchar * stun_server ) if ( stun_server && stun_server [ 0 ] != '\0' ) { if ( purple_network_is_available ()) { purple_debug_info ( "network" , "running DNS query for STUN server \n " ); purple_dnsquery_a_account ( NULL , stun_server , 3478 , purple_network_ip_lookup_cb , purple_debug_info ( "network" , "network is unavailable, don't try to update STUN IP" ); purple_network_set_turn_server ( const gchar * turn_server ) if ( turn_server && turn_server [ 0 ] != '\0' ) { if ( purple_network_is_available ()) { purple_debug_info ( "network" , "running DNS query for TURN server \n " ); purple_dnsquery_a_account ( NULL , turn_server , purple_prefs_get_int ( "/purple/network/turn_port" ), purple_network_ip_lookup_cb , & turn_ip ); purple_debug_info ( "network" , "network is unavailable, don't try to update TURN IP" ); purple_network_get_stun_ip ( void ) purple_network_get_turn_ip ( void ) purple_network_get_handle ( 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 purple_network_upnp_mapping_remove ( gpointer key , gpointer value , gint port = GPOINTER_TO_INT ( key ); gint protocol = GPOINTER_TO_INT ( value ); purple_debug_info ( "network" , "removing UPnP port mapping for port %d \n " , 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 )); purple_network_nat_pmp_mapping_remove ( gpointer key , gpointer value , 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 " , protocol == SOCK_STREAM ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP , g_hash_table_remove ( nat_pmp_port_mappings , GINT_TO_POINTER ( port )); 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 ))); purple_network_upnp_mapping_remove ( GINT_TO_POINTER ( port ), GINT_TO_POINTER ( protocol ), NULL ); protocol = GPOINTER_TO_INT ( g_hash_table_lookup ( nat_pmp_port_mappings , GINT_TO_POINTER ( port ))); purple_network_nat_pmp_mapping_remove ( GINT_TO_POINTER ( port ), GINT_TO_POINTER ( protocol ), NULL ); int purple_network_convert_idn_to_ascii ( const gchar * in , gchar ** out ) g_return_val_if_fail ( out != NULL , -1 ); ret = idna_to_ascii_8z ( in , & tmp , IDNA_USE_STD3_ASCII_RULES ); if ( ret != IDNA_SUCCESS ) { /* This *MUST* be freed with free, not g_free */ g_return_val_if_fail ( out != NULL , -1 ); _purple_network_set_common_socket_flags ( int fd ) 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 " ); if ( fcntl ( fd , F_SETFD , FD_CLOEXEC ) != 0 ) { purple_debug_warning ( "network" , "Couldn't set FD_CLOEXEC flag \n " ); purple_network_init ( void ) #ifdef HAVE_NETWORKMANAGER gint cnt = wpurple_get_connected_network_count (); network_initialized = TRUE ; if ( cnt < 0 ) /* Assume there is a network */ current_network_count = 1 ; /* Don't listen for network changes if we can't tell anyway */ current_network_count = cnt ; if (( MyWSANSPIoctl = ( void * ) wpurple_find_and_loadproc ( "ws2_32.dll" , "WSANSPIoctl" ))) { if ( ! g_thread_create ( wpurple_network_change_thread , NULL , FALSE , & err )) purple_debug_error ( "network" , "Couldn't create Network Monitor thread: %s \n " , err ? err -> message : "" ); 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 ); #ifdef HAVE_NETWORKMANAGER nm_conn = dbus_g_bus_get ( DBUS_BUS_SYSTEM , & error ); purple_debug_warning ( "network" , "Error connecting to DBus System service: %s. \n " , error -> message ); nm_proxy = dbus_g_proxy_new_for_name ( nm_conn , dbus_g_proxy_add_signal ( nm_proxy , "StateChange" , G_TYPE_UINT , G_TYPE_INVALID ); dbus_g_proxy_connect_signal ( nm_proxy , "StateChange" , G_CALLBACK ( nm_state_change_cb ), NULL , NULL ); /* NM 0.7 and later signal */ dbus_g_proxy_add_signal ( nm_proxy , "StateChanged" , G_TYPE_UINT , G_TYPE_INVALID ); dbus_g_proxy_connect_signal ( nm_proxy , "StateChanged" , G_CALLBACK ( nm_state_change_cb ), NULL , NULL ); dbus_proxy = dbus_g_proxy_new_for_name ( nm_conn , dbus_g_proxy_add_signal ( dbus_proxy , "NameOwnerChanged" , G_TYPE_STRING , G_TYPE_STRING , G_TYPE_STRING , G_TYPE_INVALID ); dbus_g_proxy_connect_signal ( dbus_proxy , "NameOwnerChanged" , G_CALLBACK ( nm_dbus_name_owner_changed_cb ), NULL , NULL ); purple_signal_register ( purple_network_get_handle (), "network-configuration-changed" , purple_marshal_VOID , NULL , 0 ); 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 ); purple_network_uninit ( void ) #ifdef HAVE_NETWORKMANAGER dbus_g_proxy_disconnect_signal ( nm_proxy , "StateChange" , G_CALLBACK ( nm_state_change_cb ), NULL ); dbus_g_proxy_disconnect_signal ( nm_proxy , "StateChanged" , G_CALLBACK ( nm_state_change_cb ), NULL ); g_object_unref ( G_OBJECT ( nm_proxy )); dbus_g_proxy_disconnect_signal ( dbus_proxy , "NameOwnerChanged" , G_CALLBACK ( nm_dbus_name_owner_changed_cb ), NULL ); g_object_unref ( G_OBJECT ( dbus_proxy )); dbus_g_connection_unref ( nm_conn ); g_static_mutex_lock ( & mutex ); network_initialized = FALSE ; if ( network_change_handle != NULL ) { /* Trigger the NLA thread to stop waiting for network changes. Not * doing this can cause hangs on WSACleanup. */ purple_debug_warning ( "network" , "Terminating the NLA thread \n " ); if (( retval = WSALookupServiceEnd ( network_change_handle )) == SOCKET_ERROR ) { int errorid = WSAGetLastError (); gchar * msg = g_win32_error_message ( errorid ); purple_debug_warning ( "network" , "Unable to kill NLA thread. Message: %s (%d). \n " , network_change_handle = NULL ; g_static_mutex_unlock ( & mutex ); purple_signal_unregister ( purple_network_get_handle (), "network-configuration-changed" ); 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... */