95e64a8b76feb24a6d1aa95a6979da0cdb56886e
[platform/upstream/libnice.git] / agent / interfaces.c
1 /*
2  * interfaces.c - Source for interface discovery code
3  *
4  * Copyright (C) 2006 Youness Alaoui <kakaroto@kakaroto.homelinux.net>
5  * Copyright (C) 2007 Collabora, Nokia
6  *  Contact: Youness Alaoui
7  * Copyright (C) 2008 Haakon Sporsheim <haakon.sporsheim@tandberg.com>
8  *
9  * The contents of this file are subject to the Mozilla Public License Version
10  * 1.1 (the "License"); you may not use this file except in compliance with
11  * the License. You may obtain a copy of the License at
12  * http://www.mozilla.org/MPL/
13  *
14  * Software distributed under the License is distributed on an "AS IS" basis,
15  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16  * for the specific language governing rights and limitations under the
17  * License.
18  *
19  * The Original Code is the Nice GLib ICE library.
20  *
21  * The Initial Developers of the Original Code are Collabora Ltd and Nokia
22  * Corporation. All Rights Reserved.
23  *
24  * Contributors:
25  *   Dafydd Harries, Collabora Ltd.
26  *   Youness Alaoui, Collabora Ltd.
27  *   Kai Vehmanen, Nokia
28  *   Philip Withnall, Collabora Ltd.
29  *   Haakon Sporsheim
30  *
31  * Alternatively, the contents of this file may be used under the terms of the
32  * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
33  * case the provisions of LGPL are applicable instead of those above. If you
34  * wish to allow use of your version of this file only under the terms of the
35  * LGPL and not to allow others to use your version of this file under the
36  * MPL, indicate your decision by deleting the provisions above and replace
37  * them with the notice and other provisions required by the LGPL. If you do
38  * not delete the provisions above, a recipient may use your version of this
39  * file under either the MPL or the LGPL.
40  */
41
42 #ifdef HAVE_CONFIG_H
43 # include "config.h"
44 #endif
45
46 #include "interfaces.h"
47 #include "agent-priv.h"
48
49 #ifdef G_OS_UNIX
50
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <unistd.h>
54 #include <string.h>
55 #include <errno.h>
56 #include <sys/ioctl.h>
57 #include <sys/types.h>
58
59 #include <sys/socket.h>
60 #include <netinet/in.h>
61 #include <netdb.h>
62
63 #ifdef __sun
64 #include <sys/sockio.h>
65 #endif
66
67 #ifdef HAVE_GETIFADDRS
68  #include <ifaddrs.h>
69 #endif
70
71 #include <net/if.h>
72 #include <arpa/inet.h>
73
74 #endif /* G_OS_UNIX */
75
76 #ifdef IGNORED_IFACE_PREFIX
77 static const gchar *ignored_iface_prefix_list[] = {
78   IGNORED_IFACE_PREFIX,
79   NULL
80 };
81 #endif
82
83 #if (defined(G_OS_UNIX) && defined(HAVE_GETIFADDRS)) || defined(G_OS_WIN32)
84 /* Works on both UNIX and Windows. Magic! */
85 static gchar *
86 sockaddr_to_string (const struct sockaddr *addr)
87 {
88   char addr_as_string[INET6_ADDRSTRLEN+1];
89   size_t addr_len;
90
91   switch (addr->sa_family) {
92     case AF_INET: addr_len = sizeof (struct sockaddr_in); break;
93     case AF_INET6: addr_len = sizeof (struct sockaddr_in6); break;
94     default: return NULL;
95   }
96
97   if (getnameinfo (addr, addr_len,
98           addr_as_string, sizeof (addr_as_string), NULL, 0,
99           NI_NUMERICHOST) != 0) {
100     return NULL;
101   }
102
103   return g_strdup (addr_as_string);
104 }
105 #endif
106
107 #ifdef G_OS_UNIX
108
109 static GList *
110 get_local_interfaces_ioctl (void)
111 {
112   GList *interfaces = NULL;
113   gint sockfd;
114   gint size = 0;
115   struct ifreq *ifr;
116   struct ifconf ifc;
117
118   if ((sockfd = socket (AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
119     nice_debug ("error : Cannot open socket to retrieve interface list");
120     return NULL;
121   }
122
123   ifc.ifc_len = 0;
124   ifc.ifc_req = NULL;
125
126   /* Loop and get each interface the system has, one by one... */
127   do {
128     size += sizeof (struct ifreq);
129     /* realloc buffer size until no overflow occurs  */
130     if (NULL == (ifc.ifc_req = realloc (ifc.ifc_req, size))) {
131       nice_debug ("Error : Out of memory while allocation interface"
132           "configuration structure");
133       close (sockfd);
134       return NULL;
135     }
136     ifc.ifc_len = size;
137
138     if (ioctl (sockfd, SIOCGIFCONF, &ifc)) {
139       perror ("ioctl SIOCFIFCONF");
140       close (sockfd);
141       free (ifc.ifc_req);
142       return NULL;
143     }
144   } while (size <= ifc.ifc_len);
145
146
147   /* Loop throught the interface list and get the IP address of each IF */
148   for (ifr = ifc.ifc_req;
149        (gchar *) ifr < (gchar *) ifc.ifc_req + ifc.ifc_len;
150        ++ifr) {
151     nice_debug ("Found interface : %s", ifr->ifr_name);
152     interfaces = g_list_prepend (interfaces, g_strdup (ifr->ifr_name));
153   }
154
155   free (ifc.ifc_req);
156   close (sockfd);
157
158   return interfaces;
159 }
160
161 #ifdef HAVE_GETIFADDRS
162
163 GList *
164 nice_interfaces_get_local_interfaces (void)
165 {
166   GList *interfaces = NULL;
167   struct ifaddrs *ifa, *results;
168
169   if (getifaddrs (&results) < 0) {
170     nice_debug ("Failed to retrieve list of network interfaces with \"getifaddrs\": %s."
171       "Trying to use fallback ...", strerror (errno));
172     return get_local_interfaces_ioctl ();
173   }
174
175   /* Loop and get each interface the system has, one by one... */
176   for (ifa = results; ifa; ifa = ifa->ifa_next) {
177     /* no ip address from interface that is down */
178     if ((ifa->ifa_flags & IFF_UP) == 0)
179       continue;
180
181     if (ifa->ifa_addr == NULL)
182       continue;
183
184     if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) {
185       nice_debug ("Found interface : %s", ifa->ifa_name);
186       interfaces = g_list_prepend (interfaces, g_strdup (ifa->ifa_name));
187     }
188   }
189
190   freeifaddrs (results);
191
192   return interfaces;
193 }
194
195 #else /* ! HAVE_GETIFADDRS */
196
197 GList *
198 nice_interfaces_get_local_interfaces (void)
199 {
200   return get_local_interfaces_ioctl ();
201 }
202
203 #endif /* HAVE_GETIFADDRS */
204
205
206 static gboolean
207 nice_interfaces_is_private_ip (const struct sockaddr *sa)
208 {
209   NiceAddress niceaddr;
210
211   nice_address_init (&niceaddr);
212   nice_address_set_from_sockaddr (&niceaddr, sa);
213   return nice_address_is_private (&niceaddr);
214 }
215
216 static GList *
217 add_ip_to_list (GList *list, gchar *ip, gboolean append)
218 {
219   GList *i;
220
221   for (i = list; i; i = i->next) {
222     gchar *addr = (gchar *) i->data;
223
224     if (g_strcmp0 (addr, ip) == 0)
225       return list;
226   }
227   if (append)
228     return g_list_append (list, ip);
229   else
230     return g_list_prepend (list, ip);
231 }
232
233 static GList *
234 get_local_ips_ioctl (gboolean include_loopback)
235 {
236   GList *ips = NULL;
237   gint sockfd;
238   gint size = 0;
239   struct ifreq *ifr;
240   struct ifconf ifc;
241   union {
242     struct sockaddr_in *sin;
243     struct sockaddr *sa;
244   } sa;
245   
246   GList *loopbacks = NULL;
247 #ifdef IGNORED_IFACE_PREFIX
248   const gchar **prefix;
249   gboolean ignored;
250 #endif
251
252   if ((sockfd = socket (AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
253     nice_debug ("Error : Cannot open socket to retrieve interface list");
254     return NULL;
255   }
256
257   ifc.ifc_len = 0;
258   ifc.ifc_req = NULL;
259
260   /* Loop and get each interface the system has, one by one... */
261   do {
262     size += sizeof (struct ifreq);
263     /* realloc buffer size until no overflow occurs  */
264     if (NULL == (ifc.ifc_req = realloc (ifc.ifc_req, size))) {
265       nice_debug ("Error : Out of memory while allocation interface"
266           " configuration structure");
267       close (sockfd);
268       return NULL;
269     }
270     ifc.ifc_len = size;
271
272     if (ioctl (sockfd, SIOCGIFCONF, &ifc)) {
273       perror ("ioctl SIOCFIFCONF");
274       close (sockfd);
275       free (ifc.ifc_req);
276       return NULL;
277     }
278   } while  (size <= ifc.ifc_len);
279
280
281   /* Loop throught the interface list and get the IP address of each IF */
282   for (ifr = ifc.ifc_req;
283        (gchar *) ifr < (gchar *) ifc.ifc_req + ifc.ifc_len;
284        ++ifr) {
285
286     if (ioctl (sockfd, SIOCGIFFLAGS, ifr)) {
287       nice_debug ("Error : Unable to get IP information for interface %s."
288           " Skipping...", ifr->ifr_name);
289       continue;  /* failed to get flags, skip it */
290     }
291
292     /* no ip address from interface that is down */
293     if ((ifr->ifr_flags & IFF_UP) == 0)
294       continue;
295
296     /* no ip address from interface that isn't running */
297     if ((ifr->ifr_flags & IFF_RUNNING) == 0)
298       continue;
299
300     sa.sa = &ifr->ifr_addr;
301     nice_debug ("Interface:  %s", ifr->ifr_name);
302     nice_debug ("IP Address: %s", inet_ntoa (sa.sin->sin_addr));
303     if ((ifr->ifr_flags & IFF_LOOPBACK) == IFF_LOOPBACK){
304       if (include_loopback)
305         loopbacks = add_ip_to_list (loopbacks, g_strdup (inet_ntoa (sa.sin->sin_addr)), TRUE);
306       else
307         nice_debug ("Ignoring loopback interface");
308       continue;
309     }
310
311 #ifdef IGNORED_IFACE_PREFIX
312     ignored = FALSE;
313     for (prefix = ignored_iface_prefix_list; *prefix; prefix++) {
314       if (g_str_has_prefix (ifr->ifr_name, *prefix)) {
315         nice_debug ("Ignoring interface %s as it matches prefix %s",
316             ifr->ifr_name, *prefix);
317         ignored = TRUE;
318         break;
319       }
320     }
321
322     if (ignored)
323       continue;
324 #endif
325
326     if (nice_interfaces_is_private_ip (sa.sa)) {
327       ips = add_ip_to_list (ips, g_strdup (inet_ntoa (sa.sin->sin_addr)), TRUE);
328     } else {
329       ips = add_ip_to_list (ips, g_strdup (inet_ntoa (sa.sin->sin_addr)), FALSE);
330     }
331   }
332
333   close (sockfd);
334   free (ifc.ifc_req);
335
336   if (loopbacks)
337     ips = g_list_concat (ips, loopbacks);
338
339   return ips;
340 }
341
342 #ifdef HAVE_GETIFADDRS
343
344 GList *
345 nice_interfaces_get_local_ips (gboolean include_loopback)
346 {
347   GList *ips = NULL;
348   struct ifaddrs *ifa, *results;
349   GList *loopbacks = NULL;
350 #ifdef IGNORED_IFACE_PREFIX
351   const gchar **prefix;
352   gboolean ignored;
353 #endif
354
355   if (getifaddrs (&results) < 0) {
356     nice_debug ("Failed to retrieve list of network interfaces with \"getifaddrs\": %s."
357       "Trying to use fallback ...", strerror (errno));
358     return get_local_ips_ioctl (include_loopback);
359   }
360
361   /* Loop through the interface list and get the IP address of each IF */
362   for (ifa = results; ifa; ifa = ifa->ifa_next) {
363     gchar *addr_string;
364
365     /* no ip address from interface that is down */
366     if ((ifa->ifa_flags & IFF_UP) == 0)
367       continue;
368
369     /* no ip address from interface that isn't running */
370     if ((ifa->ifa_flags & IFF_RUNNING) == 0)
371       continue;
372
373     if (ifa->ifa_addr == NULL)
374       continue;
375
376     /* Convert to a string. */
377     addr_string = sockaddr_to_string (ifa->ifa_addr);
378     if (addr_string == NULL) {
379       nice_debug ("Failed to convert address to string for interface ‘%s’.",
380           ifa->ifa_name);
381       continue;
382     }
383
384     nice_debug ("Interface:  %s", ifa->ifa_name);
385     nice_debug ("IP Address: %s", addr_string);
386     if ((ifa->ifa_flags & IFF_LOOPBACK) == IFF_LOOPBACK) {
387       if (include_loopback) {
388         loopbacks = add_ip_to_list (loopbacks, addr_string, TRUE);
389       } else {
390         nice_debug ("Ignoring loopback interface");
391         g_free (addr_string);
392       }
393       continue;
394     }
395
396 #ifdef IGNORED_IFACE_PREFIX
397     ignored = FALSE;
398     for (prefix = ignored_iface_prefix_list; *prefix; prefix++) {
399       if (g_str_has_prefix (ifa->ifa_name, *prefix)) {
400         nice_debug ("Ignoring interface %s as it matches prefix %s",
401             ifa->ifa_name, *prefix);
402         g_free (addr_string);
403         ignored = TRUE;
404         break;
405       }
406     }
407
408     if (ignored)
409       continue;
410 #endif
411
412     if (nice_interfaces_is_private_ip (ifa->ifa_addr))
413       ips = add_ip_to_list (ips, addr_string, TRUE);
414     else
415       ips = add_ip_to_list (ips, addr_string, FALSE);
416   }
417
418   freeifaddrs (results);
419
420   if (loopbacks)
421     ips = g_list_concat (ips, loopbacks);
422
423   return ips;
424 }
425
426 #else /* ! HAVE_GETIFADDRS */
427
428 GList *
429 nice_interfaces_get_local_ips (gboolean include_loopback)
430 {
431   return get_local_ips_ioctl (include_loopback);
432 }
433
434 #endif /* HAVE_GETIFADDRS */
435
436 gchar *
437 nice_interfaces_get_ip_for_interface (gchar *interface_name)
438 {
439   struct ifreq ifr;
440   union {
441     struct sockaddr *addr;
442     struct sockaddr_in *in;
443   } sa;
444   gint sockfd;
445
446   g_return_val_if_fail (interface_name != NULL, NULL);
447
448   ifr.ifr_addr.sa_family = AF_INET;
449   memset (ifr.ifr_name, 0, sizeof (ifr.ifr_name));
450   g_strlcpy (ifr.ifr_name, interface_name, sizeof (ifr.ifr_name));
451
452   if ((sockfd = socket (AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
453     nice_debug ("Error : Cannot open socket to retrieve interface list");
454     return NULL;
455   }
456
457   if (ioctl (sockfd, SIOCGIFADDR, &ifr) < 0) {
458     nice_debug ("Error : Unable to get IP information for interface %s",
459       interface_name);
460     close (sockfd);
461     return NULL;
462   }
463
464   close (sockfd);
465   sa.addr = &ifr.ifr_addr;
466   nice_debug ("Address for %s: %s", interface_name, inet_ntoa (sa.in->sin_addr));
467   return g_strdup (inet_ntoa (sa.in->sin_addr));
468 }
469
470 #else /* G_OS_UNIX */
471 #ifdef G_OS_WIN32
472
473 #include <winsock2.h>
474 #include <iphlpapi.h>
475
476 // Should be in Iphlpapi.h, but mingw doesn't seem to have these
477 // Values copied directly from:
478 // http://msdn.microsoft.com/en-us/library/aa366845(v=vs.85).aspx
479 // (Title: MIB_IPADDRROW structure)
480
481 #ifndef MIB_IPADDR_DISCONNECTED
482 #define MIB_IPADDR_DISCONNECTED 0x0008
483 #endif
484
485 #ifndef MIB_IPADDR_DELETED
486 #define MIB_IPADDR_DELETED 0x0040
487 #endif
488
489 #if 0
490 static gboolean started_wsa_engine = FALSE;
491
492 /*
493  * private function that initializes the WinSock engine and
494  *  returns a prebuilt socket
495  */
496 SOCKET nice_interfaces_get_WSA_socket ()
497 {
498   WORD wVersionRequested;
499   WSADATA wsaData;
500   int err;
501   SOCKET sock;
502
503   if (started_wsa_engine == FALSE) {
504     wVersionRequested = MAKEWORD ( 2, 0 );
505
506     err = WSAStartup ( wVersionRequested, &wsaData );
507     if ( err != 0 ) {
508       nice_debug ("Error : Could not start the winsocket engine");
509       return INVALID_SOCKET;
510     }
511     started_wsa_engine = TRUE;
512   }
513
514
515   if ((sock = socket (AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
516     nice_debug ("Error : Could not open socket to retrieve interface list,"
517         " error no : %d", WSAGetLastError ());
518     return INVALID_SOCKET;
519   }
520
521   return sock;
522 }
523 #endif
524
525 GList * nice_interfaces_get_local_interfaces (void)
526 {
527   ULONG size = 0;
528   PMIB_IFTABLE if_table;
529   GList * ret = NULL;
530
531   GetIfTable(NULL, &size, TRUE);
532
533   if (!size)
534     return NULL;
535
536   if_table = (PMIB_IFTABLE)g_malloc0(size);
537
538   if (GetIfTable(if_table, &size, TRUE) == ERROR_SUCCESS) {
539     DWORD i;
540     for (i = 0; i < if_table->dwNumEntries; i++) {
541       ret = g_list_prepend (ret, g_strdup ((gchar*)if_table->table[i].bDescr));
542     }
543   }
544
545   g_free(if_table);
546
547   return ret;
548 }
549
550 GList * nice_interfaces_get_local_ips (gboolean include_loopback)
551 {
552   IP_ADAPTER_ADDRESSES *addresses = NULL, *a;
553   ULONG status;
554   guint iterations;
555   ULONG addresses_size;
556   DWORD pref = 0;
557   GList *ret = NULL;
558
559   /* As suggested on
560    * http://msdn.microsoft.com/en-gb/library/windows/desktop/aa365915%28v=vs.85%29.aspx */
561   #define MAX_TRIES 3
562   #define INITIAL_BUFFER_SIZE 15000
563
564   addresses_size = INITIAL_BUFFER_SIZE;
565   iterations = 0;
566
567   do {
568     g_free (addresses);
569     addresses = g_malloc0 (addresses_size);
570
571     status = GetAdaptersAddresses (AF_UNSPEC,
572         GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
573         GAA_FLAG_SKIP_DNS_SERVER, NULL, addresses, &addresses_size);
574   } while ((status == ERROR_BUFFER_OVERFLOW) && (iterations++ < MAX_TRIES));
575
576   nice_debug ("Queried addresses with status %lu.", status);
577
578   #undef INITIAL_BUFFER_SIZE
579   #undef MAX_TRIES
580
581   /* Error? */
582   if (status != NO_ERROR) {
583     nice_debug ("Error retrieving local addresses (error code %lu).", status);
584     g_free (addresses);
585     return NULL;
586   }
587
588   /*
589    * Get the best interface for transport to 0.0.0.0.
590    * This interface should be first in list!
591    */
592   if (GetBestInterface (0, &pref) != NO_ERROR)
593     pref = 0;
594
595   /* Loop over the adapters. */
596   for (a = addresses; a != NULL; a = a->Next) {
597     IP_ADAPTER_UNICAST_ADDRESS *unicast;
598
599     nice_debug ("Interface ‘%S’:", a->FriendlyName);
600
601     /* Various conditions for ignoring the interface. */
602     if (a->Flags & IP_ADAPTER_RECEIVE_ONLY ||
603         a->OperStatus == IfOperStatusDown ||
604         a->OperStatus == IfOperStatusNotPresent ||
605         a->OperStatus == IfOperStatusLowerLayerDown) {
606       nice_debug ("Rejecting interface due to being down or read-only.");
607       continue;
608     }
609
610     if (!include_loopback &&
611         a->IfType == IF_TYPE_SOFTWARE_LOOPBACK) {
612       nice_debug ("Rejecting loopback interface ‘%S’.", a->FriendlyName);
613       continue;
614     }
615
616     /* Grab the interface’s unicast addresses. */
617     for (unicast = a->FirstUnicastAddress;
618          unicast != NULL; unicast = unicast->Next) {
619       gchar *addr_string;
620
621       addr_string = sockaddr_to_string (unicast->Address.lpSockaddr);
622       if (addr_string == NULL) {
623         nice_debug ("Failed to convert address to string for interface ‘%S’.",
624             a->FriendlyName);
625         continue;
626       }
627
628       nice_debug ("IP address: %s", addr_string);
629
630       if (a->IfIndex == pref || a->Ipv6IfIndex == pref)
631         ret = g_list_prepend (ret, addr_string);
632       else
633         ret = g_list_append (ret, addr_string);
634     }
635   }
636
637   g_free (addresses);
638
639   return ret;
640 }
641
642 /*
643  * returns ip address as an utf8 string
644  */
645 // Source for idx's type (Was IF_INDEX):
646 // http://msdn.microsoft.com/en-us/library/aa366836(v=VS.85).aspx
647 // (Title: MIB_IFROW structure)
648 static gchar *
649 win32_get_ip_for_interface (DWORD idx)
650 {
651   ULONG size = 0;
652   PMIB_IPADDRTABLE ip_table;
653   gchar * ret = NULL;
654
655   GetIpAddrTable (NULL, &size, TRUE);
656
657   if (!size)
658     return NULL;
659
660   ip_table = (PMIB_IPADDRTABLE)g_malloc0 (size);
661
662   if (GetIpAddrTable (ip_table, &size, TRUE) == ERROR_SUCCESS) {
663     DWORD i;
664     for (i = 0; i < ip_table->dwNumEntries; i++) {
665       PMIB_IPADDRROW ipaddr = &ip_table->table[i];
666       if (ipaddr->dwIndex == idx &&
667           !(ipaddr->wType & (MIB_IPADDR_DISCONNECTED | MIB_IPADDR_DELETED))) {
668         ret = g_strdup_printf ("%lu.%lu.%lu.%lu",
669             (ipaddr->dwAddr      ) & 0xFF,
670             (ipaddr->dwAddr >>  8) & 0xFF,
671             (ipaddr->dwAddr >> 16) & 0xFF,
672             (ipaddr->dwAddr >> 24) & 0xFF);
673         break;
674       }
675     }
676   }
677
678   g_free (ip_table);
679   return ret;
680 }
681
682 gchar * nice_interfaces_get_ip_for_interface (gchar *interface_name)
683 {
684   ULONG size = 0;
685   PMIB_IFTABLE if_table;
686   gchar * ret = NULL;
687
688   GetIfTable (NULL, &size, TRUE);
689
690   if (!size)
691     return NULL;
692
693   if_table = (PMIB_IFTABLE)g_malloc0 (size);
694
695   if (GetIfTable (if_table, &size, TRUE) == ERROR_SUCCESS) {
696     DWORD i;
697     gchar * tmp_str;
698     for (i = 0; i < if_table->dwNumEntries; i++) {
699       tmp_str = g_utf16_to_utf8 (
700           if_table->table[i].wszName, MAX_INTERFACE_NAME_LEN,
701           NULL, NULL, NULL);
702
703       if (strlen (interface_name) == strlen (tmp_str) &&
704           g_ascii_strncasecmp (interface_name, tmp_str, strlen (interface_name)) == 0) {
705         ret = win32_get_ip_for_interface (if_table->table[i].dwIndex);
706         g_free (tmp_str);
707         break;
708       }
709
710       g_free (tmp_str);
711     }
712   }
713
714   g_free (if_table);
715
716   return ret;
717 }
718
719
720 #else /* G_OS_WIN32 */
721 #error Can not use this method for retreiving ip list from OS other than unix or windows
722 #endif /* G_OS_WIN32 */
723 #endif /* G_OS_UNIX */