Coverage Report

Created: 2025-11-16 06:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pidgin/libpurple/network.c
Line
Count
Source
1
/**
2
 * @file network.c Network Implementation
3
 * @ingroup core
4
 */
5
6
/* purple
7
 *
8
 * Purple is the legal property of its developers, whose names are too numerous
9
 * to list here.  Please refer to the COPYRIGHT file distributed with this
10
 * source distribution.
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
25
 */
26
27
#include "internal.h"
28
29
#ifndef _WIN32
30
#include <arpa/nameser.h>
31
#include <resolv.h>
32
#include <netinet/in.h>
33
#include <net/if.h>
34
#include <sys/ioctl.h>
35
#ifdef HAVE_GETIFADDRS
36
#include <ifaddrs.h>
37
#endif
38
#else
39
#include <nspapi.h>
40
#endif
41
42
/* Solaris */
43
#if defined (__SVR4) && defined (__sun)
44
#include <sys/sockio.h>
45
#endif
46
47
#include "debug.h"
48
#include "account.h"
49
#include "nat-pmp.h"
50
#include "network.h"
51
#include "prefs.h"
52
#include "stun.h"
53
#include "upnp.h"
54
#include "dnsquery.h"
55
56
#ifdef USE_IDN
57
#include <idna.h>
58
#endif
59
60
#ifdef __HAIKU__
61
#  ifndef SIOCGIFCONF
62
#    include <sys/sockio.h>
63
#  endif
64
#endif
65
66
/*
67
 * Calling sizeof(struct ifreq) isn't always correct on
68
 * Mac OS X (and maybe others).
69
 */
70
#ifdef _SIZEOF_ADDR_IFREQ
71
#  define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
72
#else
73
0
#  define HX_SIZE_OF_IFREQ(a) sizeof(a)
74
#endif
75
76
#ifdef HAVE_NETWORKMANAGER
77
#include <dbus/dbus-glib.h>
78
#include <NetworkManager.h>
79
80
#if !defined(NM_CHECK_VERSION)
81
#define NM_CHECK_VERSION(x,y,z) 0
82
#endif
83
84
static DBusGConnection *nm_conn = NULL;
85
static DBusGProxy *nm_proxy = NULL;
86
static DBusGProxy *dbus_proxy = NULL;
87
static NMState nm_state = NM_STATE_UNKNOWN;
88
static gboolean have_nm_state = FALSE;
89
90
#elif defined _WIN32
91
static int current_network_count;
92
93
/* Mutex for the other global vars */
94
static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
95
static gboolean network_initialized = FALSE;
96
static HANDLE network_change_handle = NULL;
97
static int (WSAAPI *MyWSANSPIoctl) (
98
    HANDLE hLookup, DWORD dwControlCode, LPVOID lpvInBuffer,
99
    DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer,
100
    LPDWORD lpcbBytesReturned, LPWSACOMPLETION lpCompletion) = NULL;
101
#endif
102
103
struct _PurpleNetworkListenData {
104
  int listenfd;
105
  int socket_type;
106
  gboolean retry;
107
  gboolean adding;
108
  PurpleNetworkListenCallback cb;
109
  gpointer cb_data;
110
  UPnPMappingAddRemove *mapping_data;
111
  int timer;
112
};
113
114
#ifdef HAVE_NETWORKMANAGER
115
static NMState nm_get_network_state(void);
116
#endif
117
118
#if defined(HAVE_NETWORKMANAGER) || defined(_WIN32)
119
static gboolean force_online;
120
#endif
121
122
/* Cached IP addresses for STUN and TURN servers (set globally in prefs) */
123
static gchar *stun_ip = NULL;
124
static gchar *turn_ip = NULL;
125
126
/* Keep track of port mappings done with UPnP and NAT-PMP */
127
static GHashTable *upnp_port_mappings = NULL;
128
static GHashTable *nat_pmp_port_mappings = NULL;
129
130
const unsigned char *
131
purple_network_ip_atoi(const char *ip)
132
0
{
133
0
  static unsigned char ret[4];
134
0
  gchar *delimiter = ".";
135
0
  gchar **split;
136
0
  int i;
137
138
0
  g_return_val_if_fail(ip != NULL, NULL);
139
140
0
  split = g_strsplit(ip, delimiter, 4);
141
0
  for (i = 0; split[i] != NULL; i++)
142
0
    ret[i] = atoi(split[i]);
143
0
  g_strfreev(split);
144
145
  /* i should always be 4 */
146
0
  if (i != 4)
147
0
    return NULL;
148
149
0
  return ret;
150
0
}
151
152
void
153
purple_network_set_public_ip(const char *ip)
154
0
{
155
0
  g_return_if_fail(ip != NULL);
156
157
  /* XXX - Ensure the IP address is valid */
158
159
0
  purple_prefs_set_string("/purple/network/public_ip", ip);
160
0
}
161
162
const char *
163
purple_network_get_public_ip(void)
164
0
{
165
0
  return purple_prefs_get_string("/purple/network/public_ip");
166
0
}
167
168
const char *
169
purple_network_get_local_system_ip(int fd)
170
0
{
171
0
  char buffer[1024];
172
0
  static char ip[16];
173
0
  char *tmp;
174
0
  struct ifconf ifc;
175
0
  struct ifreq *ifr;
176
0
  struct sockaddr_in *sinptr;
177
0
  guint32 lhost = htonl((127 << 24) + 1); /* 127.0.0.1 */
178
0
  long unsigned int add;
179
0
  int source = fd;
180
181
0
  if (fd < 0)
182
0
    source = socket(PF_INET,SOCK_STREAM, 0);
183
184
0
  ifc.ifc_len = sizeof(buffer);
185
0
  ifc.ifc_req = (struct ifreq *)buffer;
186
0
  ioctl(source, SIOCGIFCONF, &ifc);
187
188
0
  if (fd < 0 && source >= 0)
189
0
    close(source);
190
191
0
  tmp = buffer;
192
0
  while (tmp < buffer + ifc.ifc_len)
193
0
  {
194
0
    ifr = (struct ifreq *)tmp;
195
0
    tmp += HX_SIZE_OF_IFREQ(*ifr);
196
197
0
    if (ifr->ifr_addr.sa_family == AF_INET)
198
0
    {
199
0
      sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
200
0
      if (sinptr->sin_addr.s_addr != lhost)
201
0
      {
202
0
        add = ntohl(sinptr->sin_addr.s_addr);
203
0
        g_snprintf(ip, 16, "%lu.%lu.%lu.%lu",
204
0
          ((add >> 24) & 255),
205
0
          ((add >> 16) & 255),
206
0
          ((add >> 8) & 255),
207
0
          add & 255);
208
209
0
        return ip;
210
0
      }
211
0
    }
212
0
  }
213
214
0
  return "0.0.0.0";
215
0
}
216
217
GList *
218
purple_network_get_all_local_system_ips(void)
219
0
{
220
0
#if defined(HAVE_GETIFADDRS) && defined(HAVE_INET_NTOP)
221
0
  GList *result = NULL;
222
0
  struct ifaddrs *start, *ifa;
223
0
  int ret;
224
225
0
  ret = getifaddrs(&start);
226
0
  if (ret < 0) {
227
0
    purple_debug_warning("network",
228
0
        "getifaddrs() failed: %s\n", g_strerror(errno));
229
0
    return NULL;
230
0
  }
231
232
0
  for (ifa = start; ifa; ifa = ifa->ifa_next) {
233
0
    int family = ifa->ifa_addr ? ifa->ifa_addr->sa_family : AF_UNSPEC;
234
0
    char host[INET6_ADDRSTRLEN];
235
0
    const char *tmp = NULL;
236
237
0
    if ((family != AF_INET && family != AF_INET6) || ifa->ifa_flags & IFF_LOOPBACK)
238
0
      continue;
239
240
0
    if (family == AF_INET)
241
0
      tmp = inet_ntop(family, &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, host, sizeof(host));
242
0
    else {
243
0
      struct sockaddr_in6 *sockaddr = (struct sockaddr_in6 *)ifa->ifa_addr;
244
      /* Peer-peer link-local communication is a big TODO.  I am not sure
245
       * how communicating link-local addresses is supposed to work, and
246
       * it seems like it would require attempting the cartesian product
247
       * of the local and remote interfaces to see if any match (eww).
248
       */
249
0
      if (!IN6_IS_ADDR_LINKLOCAL(&sockaddr->sin6_addr))
250
0
        tmp = inet_ntop(family, &sockaddr->sin6_addr, host, sizeof(host));
251
0
    }
252
0
    if (tmp != NULL)
253
0
      result = g_list_prepend(result, g_strdup(tmp));
254
0
  }
255
256
0
  freeifaddrs(start);
257
258
0
  return g_list_reverse(result);
259
#else /* HAVE_GETIFADDRS && HAVE_INET_NTOP */
260
  GList *result = NULL;
261
  int source = socket(PF_INET,SOCK_STREAM, 0);
262
  char buffer[1024];
263
  char *tmp;
264
  struct ifconf ifc;
265
  struct ifreq *ifr;
266
267
  ifc.ifc_len = sizeof(buffer);
268
  ifc.ifc_req = (struct ifreq *)buffer;
269
  ioctl(source, SIOCGIFCONF, &ifc);
270
  close(source);
271
272
  tmp = buffer;
273
  while (tmp < buffer + ifc.ifc_len) {
274
    char dst[INET_ADDRSTRLEN];
275
276
    ifr = (struct ifreq *)tmp;
277
    tmp += HX_SIZE_OF_IFREQ(*ifr);
278
279
    if (ifr->ifr_addr.sa_family == AF_INET) {
280
      struct sockaddr_in *sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
281
282
      inet_ntop(AF_INET, &sinptr->sin_addr, dst,
283
        sizeof(dst));
284
      purple_debug_info("network",
285
        "found local i/f with address %s on IPv4\n", dst);
286
      if (!purple_strequal(dst, "127.0.0.1")) {
287
        result = g_list_append(result, g_strdup(dst));
288
      }
289
    }
290
  }
291
292
  return result;
293
#endif /* HAVE_GETIFADDRS && HAVE_INET_NTOP */
294
0
}
295
296
const char *
297
purple_network_get_my_ip(int fd)
298
0
{
299
0
  const char *ip = NULL;
300
0
  PurpleStunNatDiscovery *stun;
301
302
  /* Check if the user specified an IP manually */
303
0
  if (!purple_prefs_get_bool("/purple/network/auto_ip")) {
304
0
    ip = purple_network_get_public_ip();
305
    /* Make sure the IP address entered by the user is valid */
306
0
    if ((ip != NULL) && (purple_network_ip_atoi(ip) != NULL))
307
0
      return ip;
308
0
  } else {
309
    /* Check if STUN discovery was already done */
310
0
    stun = purple_stun_discover(NULL);
311
0
    if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED))
312
0
      return stun->publicip;
313
314
    /* Attempt to get the IP from a NAT device using UPnP */
315
0
    ip = purple_upnp_get_public_ip();
316
0
    if (ip != NULL)
317
0
      return ip;
318
319
    /* Attempt to get the IP from a NAT device using NAT-PMP */
320
0
    ip = purple_pmp_get_public_ip();
321
0
    if (ip != NULL)
322
0
      return ip;
323
0
  }
324
325
  /* Just fetch the IP of the local system */
326
0
  return purple_network_get_local_system_ip(fd);
327
0
}
328
329
330
static void
331
purple_network_set_upnp_port_mapping_cb(gboolean success, gpointer data)
332
0
{
333
0
  PurpleNetworkListenData *listen_data;
334
335
0
  listen_data = data;
336
  /* TODO: Once we're keeping track of upnp requests... */
337
  /* listen_data->pnp_data = NULL; */
338
339
0
  if (!success) {
340
0
    purple_debug_warning("network", "Couldn't create UPnP mapping\n");
341
0
    if (listen_data->retry) {
342
0
      listen_data->retry = FALSE;
343
0
      listen_data->adding = FALSE;
344
0
      listen_data->mapping_data = purple_upnp_remove_port_mapping(
345
0
            purple_network_get_port_from_fd(listen_data->listenfd),
346
0
            (listen_data->socket_type == SOCK_STREAM) ? "TCP" : "UDP",
347
0
            purple_network_set_upnp_port_mapping_cb, listen_data);
348
0
      return;
349
0
    }
350
0
  } else if (!listen_data->adding) {
351
    /* We've tried successfully to remove the port mapping.
352
     * Try to add it again */
353
0
    listen_data->adding = TRUE;
354
0
    listen_data->mapping_data = purple_upnp_set_port_mapping(
355
0
          purple_network_get_port_from_fd(listen_data->listenfd),
356
0
          (listen_data->socket_type == SOCK_STREAM) ? "TCP" : "UDP",
357
0
          purple_network_set_upnp_port_mapping_cb, listen_data);
358
0
    return;
359
0
  }
360
361
0
  if (success) {
362
    /* add port mapping to hash table */
363
0
    gint key = purple_network_get_port_from_fd(listen_data->listenfd);
364
0
    gint value = listen_data->socket_type;
365
0
    g_hash_table_insert(upnp_port_mappings, GINT_TO_POINTER(key), GINT_TO_POINTER(value));
366
0
  }
367
368
0
  if (listen_data->cb)
369
0
    listen_data->cb(listen_data->listenfd, listen_data->cb_data);
370
371
  /* Clear the UPnP mapping data, since it's complete and purple_network_listen_cancel() will try to cancel
372
   * it otherwise. */
373
0
  listen_data->mapping_data = NULL;
374
0
  purple_network_listen_cancel(listen_data);
375
0
}
376
377
static gboolean
378
purple_network_finish_pmp_map_cb(gpointer data)
379
0
{
380
0
  PurpleNetworkListenData *listen_data;
381
0
  gint key;
382
0
  gint value;
383
384
0
  listen_data = data;
385
0
  listen_data->timer = 0;
386
387
  /* add port mapping to hash table */
388
0
  key = purple_network_get_port_from_fd(listen_data->listenfd);
389
0
  value = listen_data->socket_type;
390
0
  g_hash_table_insert(nat_pmp_port_mappings, GINT_TO_POINTER(key), GINT_TO_POINTER(value));
391
392
0
  if (listen_data->cb)
393
0
    listen_data->cb(listen_data->listenfd, listen_data->cb_data);
394
395
0
  purple_network_listen_cancel(listen_data);
396
397
0
  return FALSE;
398
0
}
399
400
static gboolean listen_map_external = TRUE;
401
void purple_network_listen_map_external(gboolean map_external)
402
0
{
403
0
  listen_map_external = map_external;
404
0
}
405
406
static PurpleNetworkListenData *
407
purple_network_do_listen(unsigned short port, int socket_family, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
408
0
{
409
0
  int listenfd = -1;
410
0
  const int on = 1;
411
0
  PurpleNetworkListenData *listen_data;
412
0
  unsigned short actual_port;
413
0
#ifdef HAVE_GETADDRINFO
414
0
  int errnum;
415
0
  struct addrinfo hints, *res, *next;
416
0
  char serv[6];
417
418
  /*
419
   * Get a list of addresses on this machine.
420
   */
421
0
  g_snprintf(serv, sizeof(serv), "%hu", port);
422
0
  memset(&hints, 0, sizeof(struct addrinfo));
423
0
  hints.ai_flags = AI_PASSIVE;
424
0
  hints.ai_family = socket_family;
425
0
  hints.ai_socktype = socket_type;
426
0
  errnum = getaddrinfo(NULL /* any IP */, serv, &hints, &res);
427
0
  if (errnum != 0) {
428
0
#ifndef _WIN32
429
0
    purple_debug_warning("network", "getaddrinfo: %s\n", purple_gai_strerror(errnum));
430
0
    if (errnum == EAI_SYSTEM)
431
0
      purple_debug_warning("network", "getaddrinfo: system error: %s\n", g_strerror(errno));
432
#else
433
    purple_debug_warning("network", "getaddrinfo: Error Code = %d\n", errnum);
434
#endif
435
0
    return NULL;
436
0
  }
437
438
  /*
439
   * Go through the list of addresses and attempt to listen on
440
   * one of them.
441
   * XXX - Try IPv6 addresses first?
442
   */
443
0
  for (next = res; next != NULL; next = next->ai_next) {
444
#if _WIN32
445
    /*
446
     * On Windows, the address family for the transport
447
     * address should always be set to AF_INET.
448
     */
449
    if(next->ai_family != AF_INET)
450
      continue;
451
#endif
452
0
    listenfd = socket(next->ai_family, next->ai_socktype, next->ai_protocol);
453
0
    if (listenfd < 0)
454
0
      continue;
455
0
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
456
0
      purple_debug_warning("network", "setsockopt(SO_REUSEADDR): %s\n", g_strerror(errno));
457
0
    if (bind(listenfd, next->ai_addr, next->ai_addrlen) == 0)
458
0
      break; /* success */
459
    /* XXX - It is unclear to me (datallah) whether we need to be
460
       using a new socket each time */
461
0
    close(listenfd);
462
0
  }
463
464
0
  freeaddrinfo(res);
465
466
0
  if (next == NULL)
467
0
    return NULL;
468
#else
469
  struct sockaddr_in sockin;
470
471
  if (socket_family != AF_INET && socket_family != AF_UNSPEC) {
472
    purple_debug_warning("network", "Address family %d only "
473
                         "supported when built with getaddrinfo() "
474
                         "support\n", socket_family);
475
    return NULL;
476
  }
477
478
  if ((listenfd = socket(AF_INET, socket_type, 0)) < 0) {
479
    purple_debug_warning("network", "socket: %s\n", g_strerror(errno));
480
    return NULL;
481
  }
482
483
  if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
484
    purple_debug_warning("network", "setsockopt: %s\n", g_strerror(errno));
485
486
  memset(&sockin, 0, sizeof(struct sockaddr_in));
487
  sockin.sin_family = PF_INET;
488
  sockin.sin_port = htons(port);
489
490
  if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
491
    purple_debug_warning("network", "bind: %s\n", g_strerror(errno));
492
    close(listenfd);
493
    return NULL;
494
  }
495
#endif
496
497
0
  if (socket_type == SOCK_STREAM && listen(listenfd, 4) != 0) {
498
0
    purple_debug_warning("network", "listen: %s\n", g_strerror(errno));
499
0
    close(listenfd);
500
0
    return NULL;
501
0
  }
502
0
  _purple_network_set_common_socket_flags(listenfd);
503
0
  actual_port = purple_network_get_port_from_fd(listenfd);
504
505
0
  purple_debug_info("network", "Listening on port: %hu\n", actual_port);
506
507
0
  listen_data = g_new0(PurpleNetworkListenData, 1);
508
0
  listen_data->listenfd = listenfd;
509
0
  listen_data->adding = TRUE;
510
0
  listen_data->retry = TRUE;
511
0
  listen_data->cb = cb;
512
0
  listen_data->cb_data = cb_data;
513
0
  listen_data->socket_type = socket_type;
514
515
0
  if (!purple_socket_speaks_ipv4(listenfd) || !listen_map_external ||
516
0
      !purple_prefs_get_bool("/purple/network/map_ports"))
517
0
  {
518
0
    purple_debug_info("network", "Skipping external port mapping.\n");
519
    /* The pmp_map_cb does what we want to do */
520
0
    listen_data->timer = purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
521
0
  }
522
  /* Attempt a NAT-PMP Mapping, which will return immediately */
523
0
  else if (purple_pmp_create_map(((socket_type == SOCK_STREAM) ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP),
524
0
                actual_port, actual_port, PURPLE_PMP_LIFETIME))
525
0
  {
526
0
    purple_debug_info("network", "Created NAT-PMP mapping on port %i\n", actual_port);
527
    /* We want to return listen_data now, and on the next run loop trigger the cb and destroy listen_data */
528
0
    listen_data->timer = purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
529
0
  }
530
0
  else
531
0
  {
532
    /* Attempt a UPnP Mapping */
533
0
    listen_data->mapping_data = purple_upnp_set_port_mapping(
534
0
             actual_port,
535
0
             (socket_type == SOCK_STREAM) ? "TCP" : "UDP",
536
0
             purple_network_set_upnp_port_mapping_cb, listen_data);
537
0
  }
538
539
0
  return listen_data;
540
0
}
541
542
PurpleNetworkListenData *
543
purple_network_listen_family(unsigned short port, int socket_family,
544
                             int socket_type, PurpleNetworkListenCallback cb,
545
                             gpointer cb_data)
546
0
{
547
0
  g_return_val_if_fail(port != 0, NULL);
548
549
0
  return purple_network_do_listen(port, socket_family, socket_type,
550
0
                                  cb, cb_data);
551
0
}
552
553
PurpleNetworkListenData *
554
purple_network_listen(unsigned short port, int socket_type,
555
    PurpleNetworkListenCallback cb, gpointer cb_data)
556
0
{
557
0
  return purple_network_listen_family(port, AF_UNSPEC, socket_type,
558
0
                                      cb, cb_data);
559
0
}
560
561
PurpleNetworkListenData *
562
purple_network_listen_range_family(unsigned short start, unsigned short end,
563
                                   int socket_family, int socket_type,
564
                                   PurpleNetworkListenCallback cb,
565
                                   gpointer cb_data)
566
0
{
567
0
  PurpleNetworkListenData *ret = NULL;
568
569
0
  if (purple_prefs_get_bool("/purple/network/ports_range_use")) {
570
0
    start = purple_prefs_get_int("/purple/network/ports_range_start");
571
0
    end = purple_prefs_get_int("/purple/network/ports_range_end");
572
0
  } else {
573
0
    if (end < start)
574
0
      end = start;
575
0
  }
576
577
0
  for (; start <= end; start++) {
578
0
    ret = purple_network_do_listen(start, AF_UNSPEC, socket_type, cb, cb_data);
579
0
    if (ret != NULL)
580
0
      break;
581
0
  }
582
583
0
  return ret;
584
0
}
585
586
PurpleNetworkListenData *
587
purple_network_listen_range(unsigned short start, unsigned short end,
588
                            int socket_type, PurpleNetworkListenCallback cb,
589
                            gpointer cb_data)
590
0
{
591
0
  return purple_network_listen_range_family(start, end, AF_UNSPEC,
592
0
                                            socket_type, cb, cb_data);
593
0
}
594
595
void purple_network_listen_cancel(PurpleNetworkListenData *listen_data)
596
0
{
597
0
  if (listen_data->mapping_data != NULL)
598
0
    purple_upnp_cancel_port_mapping(listen_data->mapping_data);
599
600
0
  if (listen_data->timer > 0)
601
0
    purple_timeout_remove(listen_data->timer);
602
603
0
  g_free(listen_data);
604
0
}
605
606
unsigned short
607
purple_network_get_port_from_fd(int fd)
608
0
{
609
0
  common_sockaddr_t addr;
610
0
  socklen_t len;
611
612
0
  g_return_val_if_fail(fd >= 0, 0);
613
614
0
  len = sizeof(addr);
615
0
  if (getsockname(fd, (struct sockaddr *) &addr, &len) == -1) {
616
0
    purple_debug_warning("network", "getsockname: %s\n", g_strerror(errno));
617
0
    return 0;
618
0
  }
619
620
0
  return ntohs(addr.in.sin_port);
621
0
}
622
623
#ifdef _WIN32
624
#ifndef NS_NLA
625
#define NS_NLA 15
626
#endif
627
static gint
628
wpurple_get_connected_network_count(void)
629
{
630
  gint net_cnt = 0;
631
632
  WSAQUERYSET qs;
633
  HANDLE h;
634
  gint retval;
635
  int errorid;
636
637
  memset(&qs, 0, sizeof(WSAQUERYSET));
638
  qs.dwSize = sizeof(WSAQUERYSET);
639
  qs.dwNameSpace = NS_NLA;
640
641
  retval = WSALookupServiceBeginA(&qs, LUP_RETURN_ALL, &h);
642
  if (retval != ERROR_SUCCESS) {
643
    gchar *msg;
644
    errorid = WSAGetLastError();
645
    msg = g_win32_error_message(errorid);
646
    purple_debug_warning("network", "Couldn't retrieve NLA SP lookup handle. "
647
            "NLA service is probably not running. Message: %s (%d).\n",
648
            msg, errorid);
649
    g_free(msg);
650
651
    return -1;
652
  } else {
653
    gchar *buf = NULL;
654
    WSAQUERYSET *res = (LPWSAQUERYSET) buf;
655
    DWORD current_size = 0;
656
    int iteration_count = 0;
657
    while (iteration_count++ < 100) {
658
      DWORD size = current_size;
659
      retval = WSALookupServiceNextA(h, 0, &size, res);
660
      if (retval == ERROR_SUCCESS) {
661
        net_cnt++;
662
        purple_debug_info("network", "found network '%s'\n",
663
            res->lpszServiceInstanceName ? res->lpszServiceInstanceName : "(NULL)");
664
      } else {
665
        errorid = WSAGetLastError();
666
        if (errorid == WSAEFAULT) {
667
          if (size == 0 || size > 102400) {
668
            purple_debug_warning("network", "Got unexpected NLA buffer size %" G_GUINT32_FORMAT ".\n", (guint32) size);
669
            break;
670
          }
671
          buf = g_realloc(buf, size);
672
          res = (LPWSAQUERYSET) buf;
673
          current_size = size;
674
        } else {
675
          break;
676
        }
677
      }
678
    }
679
    g_free(buf);
680
681
    if (!(errorid == WSA_E_NO_MORE || errorid == WSAENOMORE)) {
682
      gchar *msg = g_win32_error_message(errorid);
683
      purple_debug_error("network", "got unexpected NLA response %s (%d)\n", msg, errorid);
684
      g_free(msg);
685
686
      net_cnt = -1;
687
    }
688
689
    retval = WSALookupServiceEnd(h);
690
  }
691
692
  return net_cnt;
693
694
}
695
696
static gboolean wpurple_network_change_thread_cb(gpointer data)
697
{
698
  gint new_count;
699
  PurpleConnectionUiOps *ui_ops = purple_connections_get_ui_ops();
700
701
  new_count = wpurple_get_connected_network_count();
702
703
  if (new_count < 0)
704
    return FALSE;
705
706
  purple_debug_info("network", "Received Network Change Notification. Current network count is %d, previous count was %d.\n", new_count, current_network_count);
707
708
  purple_signal_emit(purple_network_get_handle(), "network-configuration-changed", NULL);
709
710
  if (new_count > 0 && ui_ops != NULL && ui_ops->network_connected != NULL) {
711
    ui_ops->network_connected();
712
  } else if (new_count == 0 && current_network_count > 0 &&
713
         ui_ops != NULL && ui_ops->network_disconnected != NULL) {
714
    ui_ops->network_disconnected();
715
  }
716
717
  current_network_count = new_count;
718
719
  return FALSE;
720
}
721
722
static gboolean _print_debug_msg(gpointer data) {
723
  gchar *msg = data;
724
  purple_debug_warning("network", "%s", msg);
725
  g_free(msg);
726
  return FALSE;
727
}
728
729
static gpointer wpurple_network_change_thread(gpointer data)
730
{
731
  WSAQUERYSET qs;
732
  WSAEVENT *nla_event;
733
  time_t last_trigger = time(NULL) - 31;
734
  gchar *buf = NULL;
735
  WSAQUERYSET *res = (LPWSAQUERYSET) buf;
736
  DWORD current_size = 0;
737
738
  if ((nla_event = WSACreateEvent()) == WSA_INVALID_EVENT) {
739
    int errorid = WSAGetLastError();
740
    gchar *msg = g_win32_error_message(errorid);
741
    purple_timeout_add(0, _print_debug_msg,
742
               g_strdup_printf("Couldn't create WSA event. "
743
                       "Message: %s (%d).\n", msg, errorid));
744
    g_free(msg);
745
    g_thread_exit(NULL);
746
    return NULL;
747
  }
748
749
  while (TRUE) {
750
    int retval;
751
    int iteration_count;
752
    DWORD retLen = 0;
753
    WSACOMPLETION completion;
754
    WSAOVERLAPPED overlapped;
755
756
    g_static_mutex_lock(&mutex);
757
    if (network_initialized == FALSE) {
758
      /* purple_network_uninit has been called */
759
      WSACloseEvent(nla_event);
760
      g_static_mutex_unlock(&mutex);
761
      g_thread_exit(NULL);
762
      return NULL;
763
    }
764
765
    if (network_change_handle == NULL) {
766
      memset(&qs, 0, sizeof(WSAQUERYSET));
767
      qs.dwSize = sizeof(WSAQUERYSET);
768
      qs.dwNameSpace = NS_NLA;
769
      if (WSALookupServiceBeginA(&qs, 0, &network_change_handle) == SOCKET_ERROR) {
770
        int errorid = WSAGetLastError();
771
        gchar *msg = g_win32_error_message(errorid);
772
        purple_timeout_add(0, _print_debug_msg,
773
                   g_strdup_printf("Couldn't retrieve NLA SP lookup handle. "
774
                           "NLA service is probably not running. Message: %s (%d).\n",
775
                          msg, errorid));
776
        g_free(msg);
777
        WSACloseEvent(nla_event);
778
        g_static_mutex_unlock(&mutex);
779
        g_thread_exit(NULL);
780
        return NULL;
781
      }
782
    }
783
    g_static_mutex_unlock(&mutex);
784
785
    memset(&completion, 0, sizeof(WSACOMPLETION));
786
    completion.Type = NSP_NOTIFY_EVENT;
787
    overlapped.hEvent = nla_event;
788
    completion.Parameters.Event.lpOverlapped = &overlapped;
789
790
    if (MyWSANSPIoctl(network_change_handle, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &retLen, &completion) == SOCKET_ERROR) {
791
      int errorid = WSAGetLastError();
792
      if (errorid == WSA_INVALID_HANDLE) {
793
        purple_timeout_add(0, _print_debug_msg,
794
                   g_strdup("Invalid NLA handle; resetting.\n"));
795
        g_static_mutex_lock(&mutex);
796
        retval = WSALookupServiceEnd(network_change_handle);
797
        network_change_handle = NULL;
798
        g_static_mutex_unlock(&mutex);
799
        continue;
800
      /* WSA_IO_PENDING indicates successful async notification will happen */
801
      } else if (errorid != WSA_IO_PENDING) {
802
        gchar *msg = g_win32_error_message(errorid);
803
        purple_timeout_add(0, _print_debug_msg,
804
                   g_strdup_printf("Unable to wait for changes. Message: %s (%d).\n",
805
                           msg, errorid));
806
        g_free(msg);
807
      }
808
    }
809
810
    /* Make sure at least 30 seconds have elapsed since the last
811
     * notification so we don't peg the cpu if this keeps changing. */
812
    if ((time(NULL) - last_trigger) < 30)
813
      Sleep(30000);
814
815
    /* This will block until NLA notifies us */
816
    retval = WaitForSingleObjectEx(nla_event, WSA_INFINITE, TRUE);
817
818
    last_trigger = time(NULL);
819
820
    g_static_mutex_lock(&mutex);
821
    if (network_initialized == FALSE) {
822
      /* Time to die */
823
      WSACloseEvent(nla_event);
824
      g_static_mutex_unlock(&mutex);
825
      g_thread_exit(NULL);
826
      return NULL;
827
    }
828
829
    iteration_count = 0;
830
    while (iteration_count++ < 100) {
831
      DWORD size = current_size;
832
      retval = WSALookupServiceNextA(network_change_handle, 0, &size, res);
833
      if (retval == ERROR_SUCCESS) {
834
        /*purple_timeout_add(0, _print_debug_msg,
835
                 g_strdup_printf("thread found network '%s'\n",
836
                         res->lpszServiceInstanceName ? res->lpszServiceInstanceName : "(NULL)"));*/
837
      } else {
838
        int errorid = WSAGetLastError();
839
        if (errorid == WSAEFAULT) {
840
          if (size == 0 || size > 102400) {
841
            purple_timeout_add(0, _print_debug_msg,
842
                 g_strdup_printf("Thread got unexpected NLA buffer size %" G_GUINT32_FORMAT ".\n", (guint32) size));
843
            break;
844
          }
845
          buf = g_realloc(buf, size);
846
          res = (LPWSAQUERYSET) buf;
847
          current_size = size;
848
        } else {
849
          break;
850
        }
851
      }
852
853
    }
854
    g_free(buf);
855
    buf = NULL;
856
    current_size = 0;
857
858
    WSAResetEvent(nla_event);
859
    g_static_mutex_unlock(&mutex);
860
861
    purple_timeout_add(0, wpurple_network_change_thread_cb, NULL);
862
  }
863
864
  g_thread_exit(NULL);
865
  return NULL;
866
}
867
#endif
868
869
gboolean
870
purple_network_is_available(void)
871
0
{
872
#ifdef HAVE_NETWORKMANAGER
873
  if (force_online)
874
    return TRUE;
875
876
  if (!have_nm_state)
877
  {
878
    have_nm_state = TRUE;
879
    nm_state = nm_get_network_state();
880
    if (nm_state == NM_STATE_UNKNOWN)
881
      purple_debug_warning("network", "NetworkManager not active. Assuming connection exists.\n");
882
  }
883
884
  switch (nm_state)
885
  {
886
    case NM_STATE_UNKNOWN:
887
#if NM_CHECK_VERSION(0,8,992)
888
    case NM_STATE_CONNECTED_LOCAL:
889
    case NM_STATE_CONNECTED_SITE:
890
    case NM_STATE_CONNECTED_GLOBAL:
891
#else
892
    case NM_STATE_CONNECTED:
893
#endif
894
      return TRUE;
895
    default:
896
      break;
897
  }
898
899
  return FALSE;
900
901
#elif defined _WIN32
902
  return (current_network_count > 0 || force_online);
903
#else
904
0
  return TRUE;
905
0
#endif
906
0
}
907
908
void
909
purple_network_force_online()
910
0
{
911
#if defined(HAVE_NETWORKMANAGER) || defined(_WIN32)
912
  force_online = TRUE;
913
#endif
914
0
}
915
916
#ifdef HAVE_NETWORKMANAGER
917
static void
918
nm_update_state(NMState state)
919
{
920
  NMState prev = nm_state;
921
  PurpleConnectionUiOps *ui_ops = purple_connections_get_ui_ops();
922
923
  have_nm_state = TRUE;
924
  nm_state = state;
925
926
  purple_signal_emit(purple_network_get_handle(), "network-configuration-changed", NULL);
927
928
  switch(state)
929
  {
930
#if NM_CHECK_VERSION(0,8,992)
931
    case NM_STATE_CONNECTED_LOCAL:
932
    case NM_STATE_CONNECTED_SITE:
933
    case NM_STATE_CONNECTED_GLOBAL:
934
#else
935
    case NM_STATE_CONNECTED:
936
#endif
937
      /* Call res_init in case DNS servers have changed */
938
      res_init();
939
      /* update STUN IP in case we it changed (theoretically we could
940
         have gone from IPv4 to IPv6, f.ex. or we were previously
941
         offline */
942
      purple_network_set_stun_server(
943
        purple_prefs_get_string("/purple/network/stun_server"));
944
      purple_network_set_turn_server(
945
        purple_prefs_get_string("/purple/network/turn_server"));
946
947
      if (ui_ops != NULL && ui_ops->network_connected != NULL)
948
        ui_ops->network_connected();
949
      break;
950
    case NM_STATE_ASLEEP:
951
    case NM_STATE_CONNECTING:
952
    case NM_STATE_DISCONNECTED:
953
#if NM_CHECK_VERSION(0,8,992)
954
    case NM_STATE_DISCONNECTING:
955
#endif
956
#if NM_CHECK_VERSION(1,0,0)
957
      if (prev != NM_STATE_CONNECTED_GLOBAL && prev != NM_STATE_UNKNOWN)
958
        break;
959
#else
960
      if (prev != NM_STATE_CONNECTED && prev != NM_STATE_UNKNOWN)
961
        break;
962
#endif
963
      if (ui_ops != NULL && ui_ops->network_disconnected != NULL)
964
        ui_ops->network_disconnected();
965
      break;
966
    case NM_STATE_UNKNOWN:
967
    default:
968
      break;
969
  }
970
}
971
972
static void
973
nm_state_change_cb(DBusGProxy *proxy, NMState state, gpointer user_data)
974
{
975
  purple_debug_info("network", "Got StateChange from NetworkManager: %d.\n", state);
976
  nm_update_state(state);
977
}
978
979
static NMState
980
nm_get_network_state(void)
981
{
982
  GError *err = NULL;
983
  NMState state = NM_STATE_UNKNOWN;
984
985
  if (!nm_proxy)
986
    return NM_STATE_UNKNOWN;
987
988
  if (!dbus_g_proxy_call(nm_proxy, "state", &err, G_TYPE_INVALID, G_TYPE_UINT, &state, G_TYPE_INVALID)) {
989
    g_error_free(err);
990
    return NM_STATE_UNKNOWN;
991
  }
992
993
  return state;
994
}
995
996
static void
997
nm_dbus_name_owner_changed_cb(DBusGProxy *proxy, char *service, char *old_owner, char *new_owner, gpointer user_data)
998
{
999
  if (purple_strequal(service, NM_DBUS_SERVICE)) {
1000
    gboolean old_owner_good = old_owner && (old_owner[0] != '\0');
1001
    gboolean new_owner_good = new_owner && (new_owner[0] != '\0');
1002
1003
    purple_debug_info("network", "Got NameOwnerChanged signal, service = '%s', old_owner = '%s', new_owner = '%s'\n", service, old_owner, new_owner);
1004
    if (!old_owner_good && new_owner_good) {  /* Equivalent to old ServiceCreated signal */
1005
      purple_debug_info("network", "NetworkManager has started.\n");
1006
      nm_update_state(nm_get_network_state());
1007
    } else if (old_owner_good && !new_owner_good) { /* Equivalent to old ServiceDeleted signal */
1008
      purple_debug_info("network", "NetworkManager has gone away.\n");
1009
      nm_update_state(NM_STATE_UNKNOWN);
1010
    }
1011
  }
1012
}
1013
1014
#endif
1015
1016
static void
1017
purple_network_ip_lookup_cb(GSList *hosts, gpointer data,
1018
  const char *error_message)
1019
0
{
1020
0
  const gchar **ip = (const gchar **) data;
1021
1022
0
  if (error_message) {
1023
0
    purple_debug_error("network", "lookup of IP address failed: %s\n",
1024
0
      error_message);
1025
0
    g_slist_free(hosts);
1026
0
    return;
1027
0
  }
1028
1029
0
  if (hosts && g_slist_next(hosts)) {
1030
0
    struct sockaddr *addr = g_slist_next(hosts)->data;
1031
0
    char dst[INET6_ADDRSTRLEN];
1032
1033
0
    if (addr->sa_family == AF_INET6) {
1034
0
      inet_ntop(addr->sa_family, &((struct sockaddr_in6 *) addr)->sin6_addr,
1035
0
        dst, sizeof(dst));
1036
0
    } else {
1037
0
      inet_ntop(addr->sa_family, &((struct sockaddr_in *) addr)->sin_addr,
1038
0
        dst, sizeof(dst));
1039
0
    }
1040
1041
0
    *ip = g_strdup(dst);
1042
0
    purple_debug_info("network", "set IP address: %s\n", *ip);
1043
0
  }
1044
1045
0
  while (hosts != NULL) {
1046
0
    hosts = g_slist_delete_link(hosts, hosts);
1047
    /* Free the address */
1048
0
    g_free(hosts->data);
1049
0
    hosts = g_slist_delete_link(hosts, hosts);
1050
0
  }
1051
0
}
1052
1053
void
1054
purple_network_set_stun_server(const gchar *stun_server)
1055
0
{
1056
0
  if (stun_server && stun_server[0] != '\0') {
1057
0
    if (purple_network_is_available()) {
1058
0
      purple_debug_info("network", "running DNS query for STUN server\n");
1059
0
      purple_dnsquery_a_account(NULL, stun_server, 3478, purple_network_ip_lookup_cb,
1060
0
        &stun_ip);
1061
0
    } else {
1062
0
      purple_debug_info("network",
1063
0
        "network is unavailable, don't try to update STUN IP");
1064
0
    }
1065
0
  } else if (stun_ip) {
1066
0
    g_free(stun_ip);
1067
0
    stun_ip = NULL;
1068
0
  }
1069
0
}
1070
1071
void
1072
purple_network_set_turn_server(const gchar *turn_server)
1073
0
{
1074
0
  if (turn_server && turn_server[0] != '\0') {
1075
0
    if (purple_network_is_available()) {
1076
0
      purple_debug_info("network", "running DNS query for TURN server\n");
1077
0
      purple_dnsquery_a_account(NULL, turn_server,
1078
0
        purple_prefs_get_int("/purple/network/turn_port"),
1079
0
        purple_network_ip_lookup_cb, &turn_ip);
1080
0
    } else {
1081
0
      purple_debug_info("network",
1082
0
        "network is unavailable, don't try to update TURN IP");
1083
0
    }
1084
0
  } else if (turn_ip) {
1085
0
    g_free(turn_ip);
1086
0
    turn_ip = NULL;
1087
0
  }
1088
0
}
1089
1090
1091
const gchar *
1092
purple_network_get_stun_ip(void)
1093
0
{
1094
0
  return stun_ip;
1095
0
}
1096
1097
const gchar *
1098
purple_network_get_turn_ip(void)
1099
0
{
1100
0
  return turn_ip;
1101
0
}
1102
1103
void *
1104
purple_network_get_handle(void)
1105
0
{
1106
0
  static int handle;
1107
1108
0
  return &handle;
1109
0
}
1110
1111
static void
1112
purple_network_upnp_mapping_remove_cb(gboolean sucess, gpointer data)
1113
0
{
1114
0
  purple_debug_info("network", "done removing UPnP port mapping\n");
1115
0
}
1116
1117
/* the reason for these functions to have these signatures is to be able to
1118
 use them for g_hash_table_foreach to clean remaining port mappings, which is
1119
 not yet done */
1120
static void
1121
purple_network_upnp_mapping_remove(gpointer key, gpointer value,
1122
  gpointer user_data)
1123
0
{
1124
0
  gint port = GPOINTER_TO_INT(key);
1125
0
  gint protocol = GPOINTER_TO_INT(value);
1126
0
  purple_debug_info("network", "removing UPnP port mapping for port %d\n",
1127
0
    port);
1128
0
  purple_upnp_remove_port_mapping(port,
1129
0
    protocol == SOCK_STREAM ? "TCP" : "UDP",
1130
0
    purple_network_upnp_mapping_remove_cb, NULL);
1131
0
  g_hash_table_remove(upnp_port_mappings, GINT_TO_POINTER(port));
1132
0
}
1133
1134
static void
1135
purple_network_nat_pmp_mapping_remove(gpointer key, gpointer value,
1136
  gpointer user_data)
1137
0
{
1138
0
  gint port = GPOINTER_TO_INT(key);
1139
0
  gint protocol = GPOINTER_TO_INT(value);
1140
0
  purple_debug_info("network", "removing NAT-PMP port mapping for port %d\n",
1141
0
    port);
1142
0
  purple_pmp_destroy_map(
1143
0
    protocol == SOCK_STREAM ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP,
1144
0
    port);
1145
0
  g_hash_table_remove(nat_pmp_port_mappings, GINT_TO_POINTER(port));
1146
0
}
1147
1148
void
1149
purple_network_remove_port_mapping(gint fd)
1150
0
{
1151
0
  int port = purple_network_get_port_from_fd(fd);
1152
0
  gint protocol = GPOINTER_TO_INT(g_hash_table_lookup(upnp_port_mappings, GINT_TO_POINTER(port)));
1153
1154
0
  if (protocol) {
1155
0
    purple_network_upnp_mapping_remove(GINT_TO_POINTER(port), GINT_TO_POINTER(protocol), NULL);
1156
0
  } else {
1157
0
    protocol = GPOINTER_TO_INT(g_hash_table_lookup(nat_pmp_port_mappings, GINT_TO_POINTER(port)));
1158
0
    if (protocol) {
1159
0
      purple_network_nat_pmp_mapping_remove(GINT_TO_POINTER(port), GINT_TO_POINTER(protocol), NULL);
1160
0
    }
1161
0
  }
1162
0
}
1163
1164
int purple_network_convert_idn_to_ascii(const gchar *in, gchar **out)
1165
0
{
1166
#ifdef USE_IDN
1167
  char *tmp;
1168
  int ret;
1169
1170
  g_return_val_if_fail(out != NULL, -1);
1171
1172
  ret = idna_to_ascii_8z(in, &tmp, IDNA_USE_STD3_ASCII_RULES);
1173
  if (ret != IDNA_SUCCESS) {
1174
    *out = NULL;
1175
    return ret;
1176
  }
1177
1178
  *out = g_strdup(tmp);
1179
  /* This *MUST* be freed with free, not g_free */
1180
  free(tmp);
1181
  return 0;
1182
#else
1183
0
  g_return_val_if_fail(out != NULL, -1);
1184
1185
0
  *out = g_strdup(in);
1186
0
  return 0;
1187
0
#endif
1188
0
}
1189
1190
gboolean
1191
_purple_network_set_common_socket_flags(int fd)
1192
0
{
1193
0
  int flags;
1194
0
  gboolean succ = TRUE;
1195
1196
0
  g_return_val_if_fail(fd >= 0, FALSE);
1197
1198
0
  flags = fcntl(fd, F_GETFL);
1199
1200
0
  if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
1201
0
    purple_debug_warning("network",
1202
0
      "Couldn't set O_NONBLOCK flag\n");
1203
0
    succ = FALSE;
1204
0
  }
1205
1206
0
#ifndef _WIN32
1207
0
  if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
1208
0
    purple_debug_warning("network",
1209
0
      "Couldn't set FD_CLOEXEC flag\n");
1210
0
    succ = FALSE;
1211
0
  }
1212
0
#endif
1213
1214
0
  return succ;
1215
0
}
1216
1217
void
1218
purple_network_init(void)
1219
0
{
1220
#ifdef HAVE_NETWORKMANAGER
1221
  GError *error = NULL;
1222
#endif
1223
#ifdef _WIN32
1224
  GError *err = NULL;
1225
  gint cnt = wpurple_get_connected_network_count();
1226
1227
  network_initialized = TRUE;
1228
  if (cnt < 0) /* Assume there is a network */
1229
    current_network_count = 1;
1230
  /* Don't listen for network changes if we can't tell anyway */
1231
  else {
1232
    current_network_count = cnt;
1233
    if ((MyWSANSPIoctl = (void*) wpurple_find_and_loadproc("ws2_32.dll", "WSANSPIoctl"))) {
1234
      if (!g_thread_create(wpurple_network_change_thread, NULL, FALSE, &err))
1235
        purple_debug_error("network", "Couldn't create Network Monitor thread: %s\n", err ? err->message : "");
1236
    }
1237
  }
1238
#endif
1239
1240
0
  purple_prefs_add_none  ("/purple/network");
1241
0
  purple_prefs_add_string("/purple/network/stun_server", "");
1242
0
  purple_prefs_add_string("/purple/network/turn_server", "");
1243
0
  purple_prefs_add_int   ("/purple/network/turn_port", 3478);
1244
0
  purple_prefs_add_int   ("/purple/network/turn_port_tcp", 3478);
1245
0
  purple_prefs_add_string("/purple/network/turn_username", "");
1246
0
  purple_prefs_add_string("/purple/network/turn_password", "");
1247
0
  purple_prefs_add_bool  ("/purple/network/auto_ip", FALSE);
1248
0
  purple_prefs_add_string("/purple/network/public_ip", "");
1249
0
  purple_prefs_add_bool  ("/purple/network/map_ports", FALSE);
1250
0
  purple_prefs_add_bool  ("/purple/network/ports_range_use", FALSE);
1251
0
  purple_prefs_add_int   ("/purple/network/ports_range_start", 1024);
1252
0
  purple_prefs_add_int   ("/purple/network/ports_range_end", 2048);
1253
1254
0
  if(purple_prefs_get_bool("/purple/network/map_ports") || purple_prefs_get_bool("/purple/network/auto_ip"))
1255
0
    purple_upnp_discover(NULL, NULL);
1256
1257
#ifdef HAVE_NETWORKMANAGER
1258
  nm_conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
1259
  if (!nm_conn) {
1260
    purple_debug_warning("network", "Error connecting to DBus System service: %s.\n", error->message);
1261
  } else {
1262
    nm_proxy = dbus_g_proxy_new_for_name(nm_conn,
1263
                                         NM_DBUS_SERVICE,
1264
                                         NM_DBUS_PATH,
1265
                                         NM_DBUS_INTERFACE);
1266
    /* NM 0.6 signal */
1267
    dbus_g_proxy_add_signal(nm_proxy, "StateChange", G_TYPE_UINT, G_TYPE_INVALID);
1268
    dbus_g_proxy_connect_signal(nm_proxy, "StateChange",
1269
                                G_CALLBACK(nm_state_change_cb), NULL, NULL);
1270
    /* NM 0.7 and later signal */
1271
    dbus_g_proxy_add_signal(nm_proxy, "StateChanged", G_TYPE_UINT, G_TYPE_INVALID);
1272
    dbus_g_proxy_connect_signal(nm_proxy, "StateChanged",
1273
                                G_CALLBACK(nm_state_change_cb), NULL, NULL);
1274
1275
    dbus_proxy = dbus_g_proxy_new_for_name(nm_conn,
1276
                                           DBUS_SERVICE_DBUS,
1277
                                           DBUS_PATH_DBUS,
1278
                                           DBUS_INTERFACE_DBUS);
1279
    dbus_g_proxy_add_signal(dbus_proxy, "NameOwnerChanged", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
1280
    dbus_g_proxy_connect_signal(dbus_proxy, "NameOwnerChanged",
1281
                                G_CALLBACK(nm_dbus_name_owner_changed_cb), NULL, NULL);
1282
  }
1283
#endif
1284
1285
0
  purple_signal_register(purple_network_get_handle(), "network-configuration-changed",
1286
0
               purple_marshal_VOID, NULL, 0);
1287
1288
0
  purple_pmp_init();
1289
0
  purple_upnp_init();
1290
1291
0
  purple_network_set_stun_server(
1292
0
    purple_prefs_get_string("/purple/network/stun_server"));
1293
0
  purple_network_set_turn_server(
1294
0
    purple_prefs_get_string("/purple/network/turn_server"));
1295
1296
0
  upnp_port_mappings = g_hash_table_new(g_direct_hash, g_direct_equal);
1297
0
  nat_pmp_port_mappings = g_hash_table_new(g_direct_hash, g_direct_equal);
1298
0
}
1299
1300
1301
1302
void
1303
purple_network_uninit(void)
1304
0
{
1305
#ifdef HAVE_NETWORKMANAGER
1306
  if (nm_proxy) {
1307
    dbus_g_proxy_disconnect_signal(nm_proxy, "StateChange", G_CALLBACK(nm_state_change_cb), NULL);
1308
    dbus_g_proxy_disconnect_signal(nm_proxy, "StateChanged", G_CALLBACK(nm_state_change_cb), NULL);
1309
    g_object_unref(G_OBJECT(nm_proxy));
1310
  }
1311
  if (dbus_proxy) {
1312
    dbus_g_proxy_disconnect_signal(dbus_proxy, "NameOwnerChanged", G_CALLBACK(nm_dbus_name_owner_changed_cb), NULL);
1313
    g_object_unref(G_OBJECT(dbus_proxy));
1314
  }
1315
  if (nm_conn)
1316
    dbus_g_connection_unref(nm_conn);
1317
#endif
1318
1319
#ifdef _WIN32
1320
  g_static_mutex_lock(&mutex);
1321
  network_initialized = FALSE;
1322
  if (network_change_handle != NULL) {
1323
    int retval;
1324
    /* Trigger the NLA thread to stop waiting for network changes. Not
1325
     * doing this can cause hangs on WSACleanup. */
1326
    purple_debug_warning("network", "Terminating the NLA thread\n");
1327
    if ((retval = WSALookupServiceEnd(network_change_handle)) == SOCKET_ERROR) {
1328
      int errorid = WSAGetLastError();
1329
      gchar *msg = g_win32_error_message(errorid);
1330
      purple_debug_warning("network", "Unable to kill NLA thread. Message: %s (%d).\n",
1331
        msg, errorid);
1332
      g_free(msg);
1333
    }
1334
    network_change_handle = NULL;
1335
1336
  }
1337
  g_static_mutex_unlock(&mutex);
1338
1339
#endif
1340
0
  purple_signal_unregister(purple_network_get_handle(),
1341
0
               "network-configuration-changed");
1342
1343
0
  if (stun_ip)
1344
0
    g_free(stun_ip);
1345
1346
0
  g_hash_table_destroy(upnp_port_mappings);
1347
0
  g_hash_table_destroy(nat_pmp_port_mappings);
1348
1349
  /* TODO: clean up remaining port mappings, note calling
1350
   purple_upnp_remove_port_mapping from here doesn't quite work... */
1351
0
}