1 /* Copyright (C) 2003,2006 Juan Lang
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 * Implementation notes
19 * - I don't support IPv6 addresses here, since SIOCGIFCONF can't return them
21 * There are three implemented methods for determining the MAC address of an
23 * - a specific IOCTL (Linux)
24 * - looking in the ARP cache (at least Solaris)
25 * - using the sysctl interface (FreeBSD and Mac OS X)
26 * Solaris and some others have SIOCGENADDR, but I haven't gotten that to work
27 * on the Solaris boxes at SourceForge's compile farm, whereas SIOCGARP does.
41 #include <sys/types.h>
42 #ifdef HAVE_SYS_PARAM_H
43 #include <sys/param.h>
46 #ifdef HAVE_SYS_SOCKET_H
47 #include <sys/socket.h>
50 #ifdef HAVE_NETINET_IN_H
51 #include <netinet/in.h>
54 #ifdef HAVE_ARPA_INET_H
55 #include <arpa/inet.h>
62 #ifdef HAVE_NET_IF_ARP_H
63 #include <net/if_arp.h>
66 #ifdef HAVE_NET_ROUTE_H
67 #include <net/route.h>
70 #ifdef HAVE_SYS_IOCTL_H
71 #include <sys/ioctl.h>
74 #ifdef HAVE_SYS_SYSCTL_H
75 #include <sys/sysctl.h>
78 #ifdef HAVE_SYS_SOCKIO_H
79 #include <sys/sockio.h>
82 #ifdef HAVE_NET_IF_DL_H
83 #include <net/if_dl.h>
86 #ifdef HAVE_NET_IF_TYPES_H
87 #include <net/if_types.h>
97 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
98 #define ifreq_len(ifr) \
99 max(sizeof(struct ifreq), sizeof((ifr)->ifr_name)+(ifr)->ifr_addr.sa_len)
101 #define ifreq_len(ifr) sizeof(struct ifreq)
109 #define IF_NAMESIZE 16
113 #define INADDR_NONE (~0U)
116 #define INITIAL_INTERFACES_ASSUMED 4
120 static int isLoopbackInterface(int fd
, const char *name
)
127 lstrcpynA(ifr
.ifr_name
, name
, IFNAMSIZ
);
128 if (ioctl(fd
, SIOCGIFFLAGS
, &ifr
) == 0)
129 ret
= ifr
.ifr_flags
& IFF_LOOPBACK
;
134 /* The comments say MAX_ADAPTER_NAME is required, but really only IF_NAMESIZE
135 * bytes are necessary.
137 char *getInterfaceNameByIndex(DWORD index
, char *name
)
139 return if_indextoname(index
, name
);
142 DWORD
getInterfaceIndexByName(const char *name
, PDWORD index
)
148 return ERROR_INVALID_PARAMETER
;
150 return ERROR_INVALID_PARAMETER
;
151 idx
= if_nametoindex(name
);
157 ret
= ERROR_INVALID_DATA
;
161 BOOL
isIfIndexLoopback(ULONG idx
)
167 getInterfaceNameByIndex(idx
, name
);
168 fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
170 ret
= isLoopbackInterface(fd
, name
);
176 DWORD
getNumNonLoopbackInterfaces(void)
179 int fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
182 struct if_nameindex
*indexes
= if_nameindex();
185 struct if_nameindex
*p
;
187 for (p
= indexes
, numInterfaces
= 0; p
&& p
->if_name
; p
++)
188 if (!isLoopbackInterface(fd
, p
->if_name
))
190 if_freenameindex(indexes
);
198 return numInterfaces
;
201 DWORD
getNumInterfaces(void)
204 struct if_nameindex
*indexes
= if_nameindex();
207 struct if_nameindex
*p
;
209 for (p
= indexes
, numInterfaces
= 0; p
&& p
->if_name
; p
++)
211 if_freenameindex(indexes
);
215 return numInterfaces
;
218 InterfaceIndexTable
*getInterfaceIndexTable(void)
221 InterfaceIndexTable
*ret
;
222 struct if_nameindex
*indexes
= if_nameindex();
225 struct if_nameindex
*p
;
226 DWORD size
= sizeof(InterfaceIndexTable
);
228 for (p
= indexes
, numInterfaces
= 0; p
&& p
->if_name
; p
++)
230 if (numInterfaces
> 1)
231 size
+= (numInterfaces
- 1) * sizeof(DWORD
);
232 ret
= HeapAlloc(GetProcessHeap(), 0, size
);
235 for (p
= indexes
; p
&& p
->if_name
; p
++)
236 ret
->indexes
[ret
->numIndexes
++] = p
->if_index
;
238 if_freenameindex(indexes
);
245 InterfaceIndexTable
*getNonLoopbackInterfaceIndexTable(void)
248 InterfaceIndexTable
*ret
;
249 int fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
252 struct if_nameindex
*indexes
= if_nameindex();
255 struct if_nameindex
*p
;
256 DWORD size
= sizeof(InterfaceIndexTable
);
258 for (p
= indexes
, numInterfaces
= 0; p
&& p
->if_name
; p
++)
259 if (!isLoopbackInterface(fd
, p
->if_name
))
261 if (numInterfaces
> 1)
262 size
+= (numInterfaces
- 1) * sizeof(DWORD
);
263 ret
= HeapAlloc(GetProcessHeap(), 0, size
);
266 for (p
= indexes
; p
&& p
->if_name
; p
++)
267 if (!isLoopbackInterface(fd
, p
->if_name
))
268 ret
->indexes
[ret
->numIndexes
++] = p
->if_index
;
270 if_freenameindex(indexes
);
281 static DWORD
getInterfaceBCastAddrByName(const char *name
)
283 DWORD ret
= INADDR_ANY
;
286 int fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
291 lstrcpynA(ifr
.ifr_name
, name
, IFNAMSIZ
);
292 if (ioctl(fd
, SIOCGIFBRDADDR
, &ifr
) == 0)
293 memcpy(&ret
, ifr
.ifr_addr
.sa_data
+ 2, sizeof(DWORD
));
300 static DWORD
getInterfaceMaskByName(const char *name
)
302 DWORD ret
= INADDR_NONE
;
305 int fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
310 lstrcpynA(ifr
.ifr_name
, name
, IFNAMSIZ
);
311 if (ioctl(fd
, SIOCGIFNETMASK
, &ifr
) == 0)
312 memcpy(&ret
, ifr
.ifr_addr
.sa_data
+ 2, sizeof(DWORD
));
319 #if defined (SIOCGIFHWADDR) && defined (HAVE_STRUCT_IFREQ_IFR_HWADDR)
320 static DWORD
getInterfacePhysicalByName(const char *name
, PDWORD len
, PBYTE addr
,
326 if (!name
|| !len
|| !addr
|| !type
)
327 return ERROR_INVALID_PARAMETER
;
329 fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
333 memset(&ifr
, 0, sizeof(struct ifreq
));
334 lstrcpynA(ifr
.ifr_name
, name
, IFNAMSIZ
);
335 if ((ioctl(fd
, SIOCGIFHWADDR
, &ifr
)))
336 ret
= ERROR_INVALID_DATA
;
338 unsigned int addrLen
;
340 switch (ifr
.ifr_hwaddr
.sa_family
)
342 #ifdef ARPHRD_LOOPBACK
343 case ARPHRD_LOOPBACK
:
345 *type
= MIB_IF_TYPE_LOOPBACK
;
351 *type
= MIB_IF_TYPE_ETHERNET
;
357 *type
= MIB_IF_TYPE_FDDI
;
360 #ifdef ARPHRD_IEEE802
361 case ARPHRD_IEEE802
: /* 802.2 Ethernet && Token Ring, guess TR? */
363 *type
= MIB_IF_TYPE_TOKENRING
;
366 #ifdef ARPHRD_IEEE802_TR
367 case ARPHRD_IEEE802_TR
: /* also Token Ring? */
369 *type
= MIB_IF_TYPE_TOKENRING
;
375 *type
= MIB_IF_TYPE_SLIP
;
381 *type
= MIB_IF_TYPE_PPP
;
385 addrLen
= min(MAX_INTERFACE_PHYSADDR
, sizeof(ifr
.ifr_hwaddr
.sa_data
));
386 *type
= MIB_IF_TYPE_OTHER
;
388 if (addrLen
> *len
) {
389 ret
= ERROR_INSUFFICIENT_BUFFER
;
394 memcpy(addr
, ifr
.ifr_hwaddr
.sa_data
, addrLen
);
395 /* zero out remaining bytes for broken implementations */
396 memset(addr
+ addrLen
, 0, *len
- addrLen
);
404 ret
= ERROR_NO_MORE_FILES
;
407 #elif defined (SIOCGARP)
408 static DWORD
getInterfacePhysicalByName(const char *name
, PDWORD len
, PBYTE addr
,
414 if (!name
|| !len
|| !addr
|| !type
)
415 return ERROR_INVALID_PARAMETER
;
417 fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
419 if (isLoopbackInterface(fd
, name
)) {
420 *type
= MIB_IF_TYPE_LOOPBACK
;
421 memset(addr
, 0, *len
);
427 struct sockaddr_in
*saddr
;
431 lstrcpynA(ifr
.ifr_name
, name
, IFNAMSIZ
);
432 ioctl(fd
, SIOCGIFADDR
, &ifr
);
433 memset(&arp
, 0, sizeof(struct arpreq
));
434 arp
.arp_pa
.sa_family
= AF_INET
;
435 saddr
= (struct sockaddr_in
*)&arp
; /* proto addr is first member */
436 saddr
->sin_family
= AF_INET
;
437 memcpy(&saddr
->sin_addr
.s_addr
, ifr
.ifr_addr
.sa_data
+ 2, sizeof(DWORD
));
438 if ((ioctl(fd
, SIOCGARP
, &arp
)))
439 ret
= ERROR_INVALID_DATA
;
441 /* FIXME: heh: who said it was ethernet? */
442 int addrLen
= ETH_ALEN
;
444 if (addrLen
> *len
) {
445 ret
= ERROR_INSUFFICIENT_BUFFER
;
450 memcpy(addr
, &arp
.arp_ha
.sa_data
[0], addrLen
);
451 /* zero out remaining bytes for broken implementations */
452 memset(addr
+ addrLen
, 0, *len
- addrLen
);
454 *type
= MIB_IF_TYPE_ETHERNET
;
462 ret
= ERROR_NO_MORE_FILES
;
466 #elif defined (HAVE_SYS_SYSCTL_H) && defined (HAVE_NET_IF_DL_H)
467 static DWORD
getInterfacePhysicalByName(const char *name
, PDWORD len
, PBYTE addr
,
471 struct if_msghdr
*ifm
;
472 struct sockaddr_dl
*sdl
;
475 int mib
[] = { CTL_NET
, AF_ROUTE
, 0, AF_LINK
, NET_RT_IFLIST
, 0 };
479 if (!name
|| !len
|| !addr
|| !type
)
480 return ERROR_INVALID_PARAMETER
;
482 if (sysctl(mib
, 6, NULL
, &mibLen
, NULL
, 0) < 0)
483 return ERROR_NO_MORE_FILES
;
485 buf
= HeapAlloc(GetProcessHeap(), 0, mibLen
);
487 return ERROR_NOT_ENOUGH_MEMORY
;
489 if (sysctl(mib
, 6, buf
, &mibLen
, NULL
, 0) < 0) {
490 HeapFree(GetProcessHeap(), 0, buf
);
491 return ERROR_NO_MORE_FILES
;
494 ret
= ERROR_INVALID_DATA
;
495 for (p
= buf
; !found
&& p
< buf
+ mibLen
; p
+= ifm
->ifm_msglen
) {
496 ifm
= (struct if_msghdr
*)p
;
497 sdl
= (struct sockaddr_dl
*)(ifm
+ 1);
499 if (ifm
->ifm_type
!= RTM_IFINFO
|| (ifm
->ifm_addrs
& RTA_IFP
) == 0)
502 if (sdl
->sdl_family
!= AF_LINK
|| sdl
->sdl_nlen
== 0 ||
503 memcmp(sdl
->sdl_data
, name
, max(sdl
->sdl_nlen
, strlen(name
))) != 0)
507 addrLen
= min(MAX_INTERFACE_PHYSADDR
, sdl
->sdl_alen
);
508 if (addrLen
> *len
) {
509 ret
= ERROR_INSUFFICIENT_BUFFER
;
514 memcpy(addr
, LLADDR(sdl
), addrLen
);
515 /* zero out remaining bytes for broken implementations */
516 memset(addr
+ addrLen
, 0, *len
- addrLen
);
518 #if defined(HAVE_NET_IF_TYPES_H)
519 switch (sdl
->sdl_type
)
522 *type
= MIB_IF_TYPE_ETHERNET
;
525 *type
= MIB_IF_TYPE_FDDI
;
527 case IFT_ISO88024
: /* Token Bus */
528 *type
= MIB_IF_TYPE_TOKENRING
;
530 case IFT_ISO88025
: /* Token Ring */
531 *type
= MIB_IF_TYPE_TOKENRING
;
534 *type
= MIB_IF_TYPE_PPP
;
537 *type
= MIB_IF_TYPE_SLIP
;
540 *type
= MIB_IF_TYPE_LOOPBACK
;
543 *type
= MIB_IF_TYPE_OTHER
;
546 /* default if we don't know */
547 *type
= MIB_IF_TYPE_ETHERNET
;
552 HeapFree(GetProcessHeap(), 0, buf
);
557 DWORD
getInterfacePhysicalByIndex(DWORD index
, PDWORD len
, PBYTE addr
,
560 char nameBuf
[IF_NAMESIZE
];
561 char *name
= getInterfaceNameByIndex(index
, nameBuf
);
564 return getInterfacePhysicalByName(name
, len
, addr
, type
);
566 return ERROR_INVALID_DATA
;
569 DWORD
getInterfaceMtuByName(const char *name
, PDWORD mtu
)
575 return ERROR_INVALID_PARAMETER
;
577 return ERROR_INVALID_PARAMETER
;
579 fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
583 lstrcpynA(ifr
.ifr_name
, name
, IFNAMSIZ
);
584 if ((ioctl(fd
, SIOCGIFMTU
, &ifr
)))
585 ret
= ERROR_INVALID_DATA
;
590 *mtu
= ifr
.ifr_metric
;
597 ret
= ERROR_NO_MORE_FILES
;
601 DWORD
getInterfaceStatusByName(const char *name
, PDWORD status
)
607 return ERROR_INVALID_PARAMETER
;
609 return ERROR_INVALID_PARAMETER
;
611 fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
615 lstrcpynA(ifr
.ifr_name
, name
, IFNAMSIZ
);
616 if ((ioctl(fd
, SIOCGIFFLAGS
, &ifr
)))
617 ret
= ERROR_INVALID_DATA
;
619 if (ifr
.ifr_flags
& IFF_UP
)
620 *status
= MIB_IF_OPER_STATUS_OPERATIONAL
;
622 *status
= MIB_IF_OPER_STATUS_NON_OPERATIONAL
;
628 ret
= ERROR_NO_MORE_FILES
;
632 DWORD
getInterfaceEntryByName(const char *name
, PMIB_IFROW entry
)
634 BYTE addr
[MAX_INTERFACE_PHYSADDR
];
635 DWORD ret
, len
= sizeof(addr
), type
;
638 return ERROR_INVALID_PARAMETER
;
640 return ERROR_INVALID_PARAMETER
;
642 if (getInterfacePhysicalByName(name
, &len
, addr
, &type
) == NO_ERROR
) {
646 memset(entry
, 0, sizeof(MIB_IFROW
));
647 for (assigner
= entry
->wszName
, walker
= name
; *walker
;
648 walker
++, assigner
++)
651 getInterfaceIndexByName(name
, &entry
->dwIndex
);
652 entry
->dwPhysAddrLen
= len
;
653 memcpy(entry
->bPhysAddr
, addr
, len
);
654 memset(entry
->bPhysAddr
+ len
, 0, sizeof(entry
->bPhysAddr
) - len
);
655 entry
->dwType
= type
;
656 /* FIXME: how to calculate real speed? */
657 getInterfaceMtuByName(name
, &entry
->dwMtu
);
658 /* lie, there's no "administratively down" here */
659 entry
->dwAdminStatus
= MIB_IF_ADMIN_STATUS_UP
;
660 getInterfaceStatusByName(name
, &entry
->dwOperStatus
);
661 /* punt on dwLastChange? */
662 entry
->dwDescrLen
= min(strlen(name
), MAX_INTERFACE_DESCRIPTION
- 1);
663 memcpy(entry
->bDescr
, name
, entry
->dwDescrLen
);
664 entry
->bDescr
[entry
->dwDescrLen
] = '\0';
669 ret
= ERROR_INVALID_DATA
;
673 /* Enumerates the IP addresses in the system using SIOCGIFCONF, returning
674 * the count to you in *pcAddresses. It also returns to you the struct ifconf
675 * used by the call to ioctl, so that you may process the addresses further.
676 * Free ifc->ifc_buf using HeapFree.
677 * Returns NO_ERROR on success, something else on failure.
679 static DWORD
enumIPAddresses(PDWORD pcAddresses
, struct ifconf
*ifc
)
684 fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
687 DWORD guessedNumAddresses
= 0, numAddresses
= 0;
694 /* there is no way to know the interface count beforehand,
695 so we need to loop again and again upping our max each time
696 until returned is constant across 2 calls */
698 lastlen
= ifc
->ifc_len
;
699 HeapFree(GetProcessHeap(), 0, ifc
->ifc_buf
);
700 if (guessedNumAddresses
== 0)
701 guessedNumAddresses
= INITIAL_INTERFACES_ASSUMED
;
703 guessedNumAddresses
*= 2;
704 ifc
->ifc_len
= sizeof(struct ifreq
) * guessedNumAddresses
;
705 ifc
->ifc_buf
= HeapAlloc(GetProcessHeap(), 0, ifc
->ifc_len
);
706 ioctlRet
= ioctl(fd
, SIOCGIFCONF
, ifc
);
707 } while ((ioctlRet
== 0) && (ifc
->ifc_len
!= lastlen
));
710 ifPtr
= ifc
->ifc_buf
;
711 while (ifPtr
&& ifPtr
< ifc
->ifc_buf
+ ifc
->ifc_len
) {
712 struct ifreq
*ifr
= (struct ifreq
*)ifPtr
;
714 if (ifr
->ifr_addr
.sa_family
== AF_INET
)
717 ifPtr
+= ifreq_len((struct ifreq
*)ifPtr
);
721 ret
= ERROR_INVALID_PARAMETER
; /* FIXME: map from errno to Win32 */
723 *pcAddresses
= numAddresses
;
726 HeapFree(GetProcessHeap(), 0, ifc
->ifc_buf
);
732 ret
= ERROR_NO_SYSTEM_RESOURCES
;
736 DWORD
getNumIPAddresses(void)
738 DWORD numAddresses
= 0;
741 if (!enumIPAddresses(&numAddresses
, &ifc
))
742 HeapFree(GetProcessHeap(), 0, ifc
.ifc_buf
);
746 DWORD
getIPAddrTable(PMIB_IPADDRTABLE
*ppIpAddrTable
, HANDLE heap
, DWORD flags
)
751 ret
= ERROR_INVALID_PARAMETER
;
754 DWORD numAddresses
= 0;
757 ret
= enumIPAddresses(&numAddresses
, &ifc
);
760 DWORD size
= sizeof(MIB_IPADDRTABLE
);
762 if (numAddresses
> 1)
763 size
+= (numAddresses
- 1) * sizeof(MIB_IPADDRROW
);
764 *ppIpAddrTable
= HeapAlloc(heap
, flags
, size
);
765 if (*ppIpAddrTable
) {
770 (*ppIpAddrTable
)->dwNumEntries
= numAddresses
;
772 while (!ret
&& ifPtr
&& ifPtr
< ifc
.ifc_buf
+ ifc
.ifc_len
) {
773 struct ifreq
*ifr
= (struct ifreq
*)ifPtr
;
775 ifPtr
+= ifreq_len(ifr
);
777 if (ifr
->ifr_addr
.sa_family
!= AF_INET
)
780 ret
= getInterfaceIndexByName(ifr
->ifr_name
,
781 &(*ppIpAddrTable
)->table
[i
].dwIndex
);
782 memcpy(&(*ppIpAddrTable
)->table
[i
].dwAddr
, ifr
->ifr_addr
.sa_data
+ 2,
784 (*ppIpAddrTable
)->table
[i
].dwMask
=
785 getInterfaceMaskByName(ifr
->ifr_name
);
786 /* the dwBCastAddr member isn't the broadcast address, it indicates
787 * whether the interface uses the 1's broadcast address (1) or the
788 * 0's broadcast address (0).
790 bcast
= getInterfaceBCastAddrByName(ifr
->ifr_name
);
791 (*ppIpAddrTable
)->table
[i
].dwBCastAddr
=
792 (bcast
& (*ppIpAddrTable
)->table
[i
].dwMask
) ? 1 : 0;
793 /* FIXME: hardcoded reasm size, not sure where to get it */
794 (*ppIpAddrTable
)->table
[i
].dwReasmSize
= 65535;
796 (*ppIpAddrTable
)->table
[i
].unused1
= 0;
797 (*ppIpAddrTable
)->table
[i
].wType
= 0;
802 ret
= ERROR_OUTOFMEMORY
;
803 HeapFree(GetProcessHeap(), 0, ifc
.ifc_buf
);
809 #ifdef HAVE_IFADDRS_H
810 ULONG
v6addressesFromIndex(DWORD index
, SOCKET_ADDRESS
**addrs
, ULONG
*num_addrs
)
815 if (!getifaddrs(&ifa
))
821 getInterfaceNameByIndex(index
, name
);
822 for (p
= ifa
, n
= 0; p
; p
= p
->ifa_next
)
823 if (p
->ifa_addr
&& p
->ifa_addr
->sa_family
== AF_INET6
&&
824 !strcmp(name
, p
->ifa_name
))
828 *addrs
= HeapAlloc(GetProcessHeap(), 0, n
* (sizeof(SOCKET_ADDRESS
) +
829 sizeof(struct WS_sockaddr_in6
)));
832 struct WS_sockaddr_in6
*next_addr
= (struct WS_sockaddr_in6
*)(
833 (BYTE
*)*addrs
+ n
* sizeof(SOCKET_ADDRESS
));
835 for (p
= ifa
, n
= 0; p
; p
= p
->ifa_next
)
837 if (p
->ifa_addr
&& p
->ifa_addr
->sa_family
== AF_INET6
&&
838 !strcmp(name
, p
->ifa_name
))
840 struct sockaddr_in6
*addr
= (struct sockaddr_in6
*)p
->ifa_addr
;
842 next_addr
->sin6_family
= WS_AF_INET6
;
843 next_addr
->sin6_port
= addr
->sin6_port
;
844 next_addr
->sin6_flowinfo
= addr
->sin6_flowinfo
;
845 memcpy(&next_addr
->sin6_addr
, &addr
->sin6_addr
,
846 sizeof(next_addr
->sin6_addr
));
847 next_addr
->sin6_scope_id
= addr
->sin6_scope_id
;
848 (*addrs
)[n
].lpSockaddr
= (LPSOCKADDR
)next_addr
;
849 (*addrs
)[n
].iSockaddrLength
= sizeof(struct WS_sockaddr_in6
);
858 ret
= ERROR_OUTOFMEMORY
;
873 ULONG
v6addressesFromIndex(DWORD index
, SOCKET_ADDRESS
**addrs
, ULONG
*num_addrs
)
877 return ERROR_SUCCESS
;
881 char *toIPAddressString(unsigned int addr
, char string
[16])
884 struct in_addr iAddr
;
887 /* extra-anal, just to make auditors happy */
888 lstrcpynA(string
, inet_ntoa(iAddr
), 16);