blob: ed55c981a121086d6b3e58fbc90894484efbc610 [file] [log] [blame]
Benny Prijonob681a2f2007-03-25 18:44:51 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijonob681a2f2007-03-25 18:44:51 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
Benny Prijonoa6bd7582007-03-28 15:49:48 +000020#include <pj/config.h>
Benny Prijonob681a2f2007-03-25 18:44:51 +000021
22#define WIN32_LEAN_AND_MEAN
23#include <windows.h>
24
Benny Prijonoa6bd7582007-03-28 15:49:48 +000025/* PMIB_ICMP_EX is not declared in VC6, causing error.
26 * But EVC4, which also claims to be VC6, does have it!
27 */
28#if defined(_MSC_VER) && _MSC_VER==1200 && !defined(PJ_WIN32_WINCE)
Benny Prijonob681a2f2007-03-25 18:44:51 +000029# define PMIB_ICMP_EX void*
30#endif
Benny Prijono62b86eb2007-12-01 08:52:57 +000031#include <winsock2.h>
32
33/* If you encounter error "Cannot open include file: 'Iphlpapi.h' here,
34 * you need to install newer Platform SDK. Presumably you're using
35 * Microsoft Visual Studio 6?
36 */
Benny Prijonob681a2f2007-03-25 18:44:51 +000037#include <Iphlpapi.h>
38
39#include <pj/ip_helper.h>
40#include <pj/assert.h>
41#include <pj/errno.h>
42#include <pj/string.h>
43
Benny Prijono54d642d2009-12-14 14:24:26 +000044/* Dealing with Unicode quirks:
45
46 There seems to be a difference with GetProcAddress() API signature between
47 Windows (i.e. Win32) and Windows CE (e.g. Windows Mobile). On Windows, the
48 API is declared as:
49
50 FARPROC GetProcAddress(
51 HMODULE hModule,
52 LPCSTR lpProcName);
53
54 while on Windows CE:
55
56 FARPROC GetProcAddress(
57 HMODULE hModule,
58 LPCWSTR lpProcName);
59
60 Notice the difference with lpProcName argument type. This means that on
61 Windows, even on Unicode Windows, the lpProcName always takes ANSI format,
62 while on Windows CE, the argument follows the UNICODE setting.
63
64 Because of this, we use a different Unicode treatment here than the usual
65 PJ_NATIVE_STRING_IS_UNICODE PJLIB setting (<pj/unicode.h>):
66 - GPA_TEXT macro: convert literal string to platform's native literal
67 string
68 - gpa_char: the platform native character type
69
70 Note that "GPA" and "gpa" are abbreviations for GetProcAddress.
71*/
72#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
73 /* on CE, follow the PJLIB Unicode setting */
74# define GPA_TEXT(x) PJ_T(x)
75# define gpa_char pj_char_t
76#else
77 /* on non-CE, always use ANSI format */
78# define GPA_TEXT(x) x
79# define gpa_char char
80#endif
81
82
Benny Prijono48ef5a32007-07-28 01:58:36 +000083typedef DWORD (WINAPI *PFN_GetIpAddrTable)(PMIB_IPADDRTABLE pIpAddrTable,
84 PULONG pdwSize,
85 BOOL bOrder);
Benny Prijono62b86eb2007-12-01 08:52:57 +000086typedef DWORD (WINAPI *PFN_GetAdapterAddresses)(ULONG Family,
87 ULONG Flags,
88 PVOID Reserved,
89 PIP_ADAPTER_ADDRESSES AdapterAddresses,
90 PULONG SizePointer);
Benny Prijono48ef5a32007-07-28 01:58:36 +000091typedef DWORD (WINAPI *PFN_GetIpForwardTable)(PMIB_IPFORWARDTABLE pIpForwardTable,
92 PULONG pdwSize,
93 BOOL bOrder);
Benny Prijono2501e132007-09-24 19:46:41 +000094typedef DWORD (WINAPI *PFN_GetIfEntry)(PMIB_IFROW pIfRow);
Benny Prijono48ef5a32007-07-28 01:58:36 +000095
96static HANDLE s_hDLL;
97static PFN_GetIpAddrTable s_pfnGetIpAddrTable;
Benny Prijono49e78b42009-12-08 16:53:29 +000098static PFN_GetAdapterAddresses s_pfnGetAdapterAddresses;
Benny Prijono48ef5a32007-07-28 01:58:36 +000099static PFN_GetIpForwardTable s_pfnGetIpForwardTable;
Benny Prijono2501e132007-09-24 19:46:41 +0000100static PFN_GetIfEntry s_pfnGetIfEntry;
Benny Prijono48ef5a32007-07-28 01:58:36 +0000101
Benny Prijono62b86eb2007-12-01 08:52:57 +0000102
Benny Prijono16f64792007-08-16 14:13:28 +0000103static void unload_iphlp_module(void)
Benny Prijono48ef5a32007-07-28 01:58:36 +0000104{
105 FreeLibrary(s_hDLL);
106 s_hDLL = NULL;
107 s_pfnGetIpAddrTable = NULL;
108 s_pfnGetIpForwardTable = NULL;
Benny Prijono62b86eb2007-12-01 08:52:57 +0000109 s_pfnGetIfEntry = NULL;
Benny Prijono62b86eb2007-12-01 08:52:57 +0000110 s_pfnGetAdapterAddresses = NULL;
Benny Prijono48ef5a32007-07-28 01:58:36 +0000111}
112
Benny Prijono54d642d2009-12-14 14:24:26 +0000113static FARPROC GetIpHlpApiProc(gpa_char *lpProcName)
Benny Prijono48ef5a32007-07-28 01:58:36 +0000114{
115 if(NULL == s_hDLL) {
116 s_hDLL = LoadLibrary(PJ_T("IpHlpApi"));
117 if(NULL != s_hDLL) {
118 pj_atexit(&unload_iphlp_module);
119 }
120 }
121
122 if(NULL != s_hDLL)
123 return GetProcAddress(s_hDLL, lpProcName);
124
125 return NULL;
126}
127
128static DWORD MyGetIpAddrTable(PMIB_IPADDRTABLE pIpAddrTable,
129 PULONG pdwSize,
130 BOOL bOrder)
131{
132 if(NULL == s_pfnGetIpAddrTable) {
133 s_pfnGetIpAddrTable = (PFN_GetIpAddrTable)
Benny Prijono54d642d2009-12-14 14:24:26 +0000134 GetIpHlpApiProc(GPA_TEXT("GetIpAddrTable"));
Benny Prijono48ef5a32007-07-28 01:58:36 +0000135 }
136
137 if(NULL != s_pfnGetIpAddrTable) {
138 return s_pfnGetIpAddrTable(pIpAddrTable, pdwSize, bOrder);
139 }
140
141 return ERROR_NOT_SUPPORTED;
142}
143
Benny Prijono62b86eb2007-12-01 08:52:57 +0000144static DWORD MyGetAdapterAddresses(ULONG Family,
145 ULONG Flags,
146 PVOID Reserved,
147 PIP_ADAPTER_ADDRESSES AdapterAddresses,
148 PULONG SizePointer)
149{
150 if(NULL == s_pfnGetAdapterAddresses) {
151 s_pfnGetAdapterAddresses = (PFN_GetAdapterAddresses)
Benny Prijono54d642d2009-12-14 14:24:26 +0000152 GetIpHlpApiProc(GPA_TEXT("GetAdaptersAddresses"));
Benny Prijono62b86eb2007-12-01 08:52:57 +0000153 }
154
155 if(NULL != s_pfnGetAdapterAddresses) {
156 return s_pfnGetAdapterAddresses(Family, Flags, Reserved,
157 AdapterAddresses, SizePointer);
158 }
159
160 return ERROR_NOT_SUPPORTED;
161}
Benny Prijono48ef5a32007-07-28 01:58:36 +0000162
Benny Prijono2501e132007-09-24 19:46:41 +0000163#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
164static DWORD MyGetIfEntry(MIB_IFROW *pIfRow)
165{
166 if(NULL == s_pfnGetIfEntry) {
167 s_pfnGetIfEntry = (PFN_GetIfEntry)
Benny Prijono54d642d2009-12-14 14:24:26 +0000168 GetIpHlpApiProc(GPA_TEXT("GetIfEntry"));
Benny Prijono2501e132007-09-24 19:46:41 +0000169 }
170
171 if(NULL != s_pfnGetIfEntry) {
172 return s_pfnGetIfEntry(pIfRow);
173 }
174
175 return ERROR_NOT_SUPPORTED;
176}
177#endif
178
179
Benny Prijono48ef5a32007-07-28 01:58:36 +0000180static DWORD MyGetIpForwardTable(PMIB_IPFORWARDTABLE pIpForwardTable,
181 PULONG pdwSize,
182 BOOL bOrder)
183{
184 if(NULL == s_pfnGetIpForwardTable) {
185 s_pfnGetIpForwardTable = (PFN_GetIpForwardTable)
Benny Prijono54d642d2009-12-14 14:24:26 +0000186 GetIpHlpApiProc(GPA_TEXT("GetIpForwardTable"));
Benny Prijono48ef5a32007-07-28 01:58:36 +0000187 }
188
189 if(NULL != s_pfnGetIpForwardTable) {
190 return s_pfnGetIpForwardTable(pIpForwardTable, pdwSize, bOrder);
191 }
192
193 return ERROR_NOT_SUPPORTED;
194}
Benny Prijonob681a2f2007-03-25 18:44:51 +0000195
Benny Prijono62b86eb2007-12-01 08:52:57 +0000196/* Enumerate local IP interface using GetIpAddrTable()
197 * for IPv4 addresses only.
Benny Prijonob681a2f2007-03-25 18:44:51 +0000198 */
Benny Prijono62b86eb2007-12-01 08:52:57 +0000199static pj_status_t enum_ipv4_interface(unsigned *p_cnt,
200 pj_sockaddr ifs[])
Benny Prijonob681a2f2007-03-25 18:44:51 +0000201{
Benny Prijono18217d62009-12-30 08:39:14 +0000202 char ipTabBuff[512];
203 MIB_IPADDRTABLE *pTab = (MIB_IPADDRTABLE*)ipTabBuff;
204 ULONG tabSize = sizeof(ipTabBuff);
Benny Prijonob681a2f2007-03-25 18:44:51 +0000205 unsigned i, count;
Benny Prijono48ef5a32007-07-28 01:58:36 +0000206 DWORD rc = NO_ERROR;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000207
208 PJ_ASSERT_RETURN(p_cnt && ifs, PJ_EINVAL);
209
Benny Prijonob681a2f2007-03-25 18:44:51 +0000210 /* Get IP address table */
Benny Prijono48ef5a32007-07-28 01:58:36 +0000211 rc = MyGetIpAddrTable(pTab, &tabSize, FALSE);
Benny Prijono18217d62009-12-30 08:39:14 +0000212 if (rc != NO_ERROR) {
213 if (rc == ERROR_INSUFFICIENT_BUFFER) {
214 /* Retry with larger buffer */
215 pTab = (MIB_IPADDRTABLE*)malloc(tabSize);
216 if (pTab)
217 rc = MyGetIpAddrTable(pTab, &tabSize, FALSE);
218 }
219
220 if (rc != NO_ERROR) {
221 if (pTab != (MIB_IPADDRTABLE*)ipTabBuff)
222 free(pTab);
223 return PJ_RETURN_OS_ERROR(rc);
224 }
225 }
Benny Prijonob681a2f2007-03-25 18:44:51 +0000226
227 /* Reset result */
228 pj_bzero(ifs, sizeof(ifs[0]) * (*p_cnt));
229
230 /* Now fill out the entries */
231 count = (pTab->dwNumEntries < *p_cnt) ? pTab->dwNumEntries : *p_cnt;
Benny Prijonofed1af92007-03-27 23:29:27 +0000232 *p_cnt = 0;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000233 for (i=0; i<count; ++i) {
Benny Prijono2501e132007-09-24 19:46:41 +0000234 MIB_IFROW ifRow;
235
Benny Prijono62b86eb2007-12-01 08:52:57 +0000236 /* Ignore 0.0.0.0 address (interface is down?) */
Benny Prijonofed1af92007-03-27 23:29:27 +0000237 if (pTab->table[i].dwAddr == 0)
238 continue;
Benny Prijono2501e132007-09-24 19:46:41 +0000239
Benny Prijonodd538ed2008-06-07 11:14:32 +0000240 /* Ignore 0.0.0.0/8 address. This is a special address
241 * which doesn't seem to have practical use.
242 */
243 if ((pj_ntohl(pTab->table[i].dwAddr) >> 24) == 0)
244 continue;
245
Benny Prijono2501e132007-09-24 19:46:41 +0000246#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
247 /* Investigate the type of this interface */
248 pj_bzero(&ifRow, sizeof(ifRow));
249 ifRow.dwIndex = pTab->table[i].dwIndex;
250 if (MyGetIfEntry(&ifRow) != 0)
251 continue;
252
253 if (ifRow.dwType == MIB_IF_TYPE_LOOPBACK)
254 continue;
255#endif
256
Benny Prijono62b86eb2007-12-01 08:52:57 +0000257 ifs[*p_cnt].ipv4.sin_family = PJ_AF_INET;
258 ifs[*p_cnt].ipv4.sin_addr.s_addr = pTab->table[i].dwAddr;
Benny Prijonofed1af92007-03-27 23:29:27 +0000259 (*p_cnt)++;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000260 }
261
Benny Prijono18217d62009-12-30 08:39:14 +0000262 if (pTab != (MIB_IPADDRTABLE*)ipTabBuff)
263 free(pTab);
264
Benny Prijono93993562008-01-24 19:54:51 +0000265 return (*p_cnt) ? PJ_SUCCESS : PJ_ENOTFOUND;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000266}
267
Benny Prijono62b86eb2007-12-01 08:52:57 +0000268/* Enumerate local IP interface using GetAdapterAddresses(),
269 * which works for both IPv4 and IPv6.
270 */
Benny Prijono62b86eb2007-12-01 08:52:57 +0000271static pj_status_t enum_ipv4_ipv6_interface(int af,
272 unsigned *p_cnt,
273 pj_sockaddr ifs[])
274{
Benny Prijono18217d62009-12-30 08:39:14 +0000275 pj_uint8_t buffer[600];
Benny Prijono62b86eb2007-12-01 08:52:57 +0000276 IP_ADAPTER_ADDRESSES *adapter = (IP_ADAPTER_ADDRESSES*)buffer;
Benny Prijono9bde8732010-03-27 03:08:08 +0000277 void *adapterBuf = NULL;
Benny Prijono62b86eb2007-12-01 08:52:57 +0000278 ULONG size = sizeof(buffer);
Benny Prijono18217d62009-12-30 08:39:14 +0000279 ULONG flags;
Benny Prijono62b86eb2007-12-01 08:52:57 +0000280 unsigned i;
281 DWORD rc;
282
Benny Prijono18217d62009-12-30 08:39:14 +0000283 flags = GAA_FLAG_SKIP_FRIENDLY_NAME |
284 GAA_FLAG_SKIP_DNS_SERVER |
285 GAA_FLAG_SKIP_MULTICAST;
Benny Prijono62b86eb2007-12-01 08:52:57 +0000286
Benny Prijono18217d62009-12-30 08:39:14 +0000287 rc = MyGetAdapterAddresses(af, flags, NULL, adapter, &size);
288 if (rc != ERROR_SUCCESS) {
289 if (rc == ERROR_BUFFER_OVERFLOW) {
290 /* Retry with larger memory size */
Benny Prijono9bde8732010-03-27 03:08:08 +0000291 adapterBuf = malloc(size);
292 adapter = (IP_ADAPTER_ADDRESSES*) adapterBuf;
Benny Prijono18217d62009-12-30 08:39:14 +0000293 if (adapter != NULL)
294 rc = MyGetAdapterAddresses(af, flags, NULL, adapter, &size);
295 }
296
297 if (rc != ERROR_SUCCESS) {
Benny Prijono9bde8732010-03-27 03:08:08 +0000298 if (adapterBuf)
299 free(adapterBuf);
Benny Prijono18217d62009-12-30 08:39:14 +0000300 return PJ_RETURN_OS_ERROR(rc);
Benny Prijonofcaf9cf2009-12-10 04:56:26 +0000301 }
Benny Prijono62b86eb2007-12-01 08:52:57 +0000302 }
303
Benny Prijono18217d62009-12-30 08:39:14 +0000304 /* Reset result */
305 pj_bzero(ifs, sizeof(ifs[0]) * (*p_cnt));
306
307 /* Enumerate interface */
308 for (i=0; i<*p_cnt && adapter; adapter = adapter->Next) {
309 if (adapter->FirstUnicastAddress) {
310 SOCKET_ADDRESS *pAddr = &adapter->FirstUnicastAddress->Address;
311
312 /* Ignore address family which we didn't request, just in case */
313 if (pAddr->lpSockaddr->sa_family != PJ_AF_INET &&
314 pAddr->lpSockaddr->sa_family != PJ_AF_INET6)
315 {
316 continue;
317 }
318
319 /* Apply some filtering to known IPv4 unusable addresses */
320 if (pAddr->lpSockaddr->sa_family == PJ_AF_INET) {
321 const pj_sockaddr_in *addr_in =
322 (const pj_sockaddr_in*)pAddr->lpSockaddr;
323
324 /* Ignore 0.0.0.0 address (interface is down?) */
325 if (addr_in->sin_addr.s_addr == 0)
326 continue;
327
328 /* Ignore 0.0.0.0/8 address. This is a special address
329 * which doesn't seem to have practical use.
330 */
331 if ((pj_ntohl(addr_in->sin_addr.s_addr) >> 24) == 0)
332 continue;
333 }
334
335#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
336 /* Ignore loopback interfaces */
337 /* This should have been IF_TYPE_SOFTWARE_LOOPBACK according to
338 * MSDN, and this macro should have been declared in Ipifcons.h,
339 * but some SDK versions don't have it.
340 */
341 if (adapter->IfType == MIB_IF_TYPE_LOOPBACK)
342 continue;
343#endif
344
345 /* Ignore down interface */
346 if (adapter->OperStatus != IfOperStatusUp)
347 continue;
348
349 ifs[i].addr.sa_family = pAddr->lpSockaddr->sa_family;
350 pj_memcpy(&ifs[i], pAddr->lpSockaddr, pAddr->iSockaddrLength);
351 ++i;
352 }
353 }
354
Benny Prijono9bde8732010-03-27 03:08:08 +0000355 if (adapterBuf)
356 free(adapterBuf);
Benny Prijono18217d62009-12-30 08:39:14 +0000357
Benny Prijono49e78b42009-12-08 16:53:29 +0000358 *p_cnt = i;
Benny Prijono18217d62009-12-30 08:39:14 +0000359 return (*p_cnt) ? PJ_SUCCESS : PJ_ENOTFOUND;
Benny Prijono62b86eb2007-12-01 08:52:57 +0000360}
Benny Prijono62b86eb2007-12-01 08:52:57 +0000361
362
363/*
364 * Enumerate the local IP interface currently active in the host.
365 */
366PJ_DEF(pj_status_t) pj_enum_ip_interface(int af,
367 unsigned *p_cnt,
368 pj_sockaddr ifs[])
369{
370 pj_status_t status = -1;
371
372 PJ_ASSERT_RETURN(p_cnt && ifs, PJ_EINVAL);
373 PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC || af==PJ_AF_INET || af==PJ_AF_INET6,
374 PJ_EAFNOTSUP);
375
Benny Prijono62b86eb2007-12-01 08:52:57 +0000376 status = enum_ipv4_ipv6_interface(af, p_cnt, ifs);
377 if (status != PJ_SUCCESS && (af==PJ_AF_INET || af==PJ_AF_UNSPEC))
378 status = enum_ipv4_interface(p_cnt, ifs);
379 return status;
Benny Prijono62b86eb2007-12-01 08:52:57 +0000380}
381
Benny Prijonob681a2f2007-03-25 18:44:51 +0000382/*
383 * Enumerate the IP routing table for this host.
384 */
385PJ_DEF(pj_status_t) pj_enum_ip_route(unsigned *p_cnt,
386 pj_ip_route_entry routes[])
387{
Benny Prijono6b5b6602007-03-30 18:54:46 +0000388 char ipTabBuff[1024];
Benny Prijonob681a2f2007-03-25 18:44:51 +0000389 MIB_IPADDRTABLE *pIpTab;
Benny Prijono6b5b6602007-03-30 18:54:46 +0000390 char rtabBuff[1024];
Benny Prijonob681a2f2007-03-25 18:44:51 +0000391 MIB_IPFORWARDTABLE *prTab;
392 ULONG tabSize;
393 unsigned i, count;
Benny Prijono48ef5a32007-07-28 01:58:36 +0000394 DWORD rc = NO_ERROR;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000395
396 PJ_ASSERT_RETURN(p_cnt && routes, PJ_EINVAL);
397
Benny Prijono6b5b6602007-03-30 18:54:46 +0000398 pIpTab = (MIB_IPADDRTABLE *)ipTabBuff;
399 prTab = (MIB_IPFORWARDTABLE *)rtabBuff;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000400
401 /* First get IP address table */
402 tabSize = sizeof(ipTabBuff);
Benny Prijono48ef5a32007-07-28 01:58:36 +0000403 rc = MyGetIpAddrTable(pIpTab, &tabSize, FALSE);
Benny Prijonob681a2f2007-03-25 18:44:51 +0000404 if (rc != NO_ERROR)
405 return PJ_RETURN_OS_ERROR(rc);
406
407 /* Next get IP route table */
408 tabSize = sizeof(rtabBuff);
Benny Prijono48ef5a32007-07-28 01:58:36 +0000409
410 rc = MyGetIpForwardTable(prTab, &tabSize, 1);
Benny Prijonob681a2f2007-03-25 18:44:51 +0000411 if (rc != NO_ERROR)
412 return PJ_RETURN_OS_ERROR(rc);
413
414 /* Reset routes */
415 pj_bzero(routes, sizeof(routes[0]) * (*p_cnt));
416
417 /* Now fill out the route entries */
418 count = (prTab->dwNumEntries < *p_cnt) ? prTab->dwNumEntries : *p_cnt;
419 *p_cnt = 0;
420 for (i=0; i<count; ++i) {
421 unsigned j;
422
423 /* Find interface entry */
424 for (j=0; j<pIpTab->dwNumEntries; ++j) {
425 if (pIpTab->table[j].dwIndex == prTab->table[i].dwForwardIfIndex)
426 break;
427 }
428
429 if (j==pIpTab->dwNumEntries)
430 continue; /* Interface not found */
431
432 routes[*p_cnt].ipv4.if_addr.s_addr = pIpTab->table[j].dwAddr;
433 routes[*p_cnt].ipv4.dst_addr.s_addr = prTab->table[i].dwForwardDest;
434 routes[*p_cnt].ipv4.mask.s_addr = prTab->table[i].dwForwardMask;
435
436 (*p_cnt)++;
437 }
438
439 return PJ_SUCCESS;
440}
441