blob: 8ae282268e396d8f17dedbc977e9de6fd81f7b90 [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;
277 ULONG size = sizeof(buffer);
Benny Prijono18217d62009-12-30 08:39:14 +0000278 ULONG flags;
Benny Prijono62b86eb2007-12-01 08:52:57 +0000279 unsigned i;
280 DWORD rc;
281
Benny Prijono18217d62009-12-30 08:39:14 +0000282 flags = GAA_FLAG_SKIP_FRIENDLY_NAME |
283 GAA_FLAG_SKIP_DNS_SERVER |
284 GAA_FLAG_SKIP_MULTICAST;
Benny Prijono62b86eb2007-12-01 08:52:57 +0000285
Benny Prijono18217d62009-12-30 08:39:14 +0000286 rc = MyGetAdapterAddresses(af, flags, NULL, adapter, &size);
287 if (rc != ERROR_SUCCESS) {
288 if (rc == ERROR_BUFFER_OVERFLOW) {
289 /* Retry with larger memory size */
290 adapter = (IP_ADAPTER_ADDRESSES*) malloc(size);
291 if (adapter != NULL)
292 rc = MyGetAdapterAddresses(af, flags, NULL, adapter, &size);
293 }
294
295 if (rc != ERROR_SUCCESS) {
296 if (adapter != (IP_ADAPTER_ADDRESSES*)buffer)
297 free(adapter);
298 return PJ_RETURN_OS_ERROR(rc);
Benny Prijonofcaf9cf2009-12-10 04:56:26 +0000299 }
Benny Prijono62b86eb2007-12-01 08:52:57 +0000300 }
301
Benny Prijono18217d62009-12-30 08:39:14 +0000302 /* Reset result */
303 pj_bzero(ifs, sizeof(ifs[0]) * (*p_cnt));
304
305 /* Enumerate interface */
306 for (i=0; i<*p_cnt && adapter; adapter = adapter->Next) {
307 if (adapter->FirstUnicastAddress) {
308 SOCKET_ADDRESS *pAddr = &adapter->FirstUnicastAddress->Address;
309
310 /* Ignore address family which we didn't request, just in case */
311 if (pAddr->lpSockaddr->sa_family != PJ_AF_INET &&
312 pAddr->lpSockaddr->sa_family != PJ_AF_INET6)
313 {
314 continue;
315 }
316
317 /* Apply some filtering to known IPv4 unusable addresses */
318 if (pAddr->lpSockaddr->sa_family == PJ_AF_INET) {
319 const pj_sockaddr_in *addr_in =
320 (const pj_sockaddr_in*)pAddr->lpSockaddr;
321
322 /* Ignore 0.0.0.0 address (interface is down?) */
323 if (addr_in->sin_addr.s_addr == 0)
324 continue;
325
326 /* Ignore 0.0.0.0/8 address. This is a special address
327 * which doesn't seem to have practical use.
328 */
329 if ((pj_ntohl(addr_in->sin_addr.s_addr) >> 24) == 0)
330 continue;
331 }
332
333#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
334 /* Ignore loopback interfaces */
335 /* This should have been IF_TYPE_SOFTWARE_LOOPBACK according to
336 * MSDN, and this macro should have been declared in Ipifcons.h,
337 * but some SDK versions don't have it.
338 */
339 if (adapter->IfType == MIB_IF_TYPE_LOOPBACK)
340 continue;
341#endif
342
343 /* Ignore down interface */
344 if (adapter->OperStatus != IfOperStatusUp)
345 continue;
346
347 ifs[i].addr.sa_family = pAddr->lpSockaddr->sa_family;
348 pj_memcpy(&ifs[i], pAddr->lpSockaddr, pAddr->iSockaddrLength);
349 ++i;
350 }
351 }
352
353 if (adapter != (IP_ADAPTER_ADDRESSES*)buffer)
354 free(adapter);
355
Benny Prijono49e78b42009-12-08 16:53:29 +0000356 *p_cnt = i;
Benny Prijono18217d62009-12-30 08:39:14 +0000357 return (*p_cnt) ? PJ_SUCCESS : PJ_ENOTFOUND;
Benny Prijono62b86eb2007-12-01 08:52:57 +0000358}
Benny Prijono62b86eb2007-12-01 08:52:57 +0000359
360
361/*
362 * Enumerate the local IP interface currently active in the host.
363 */
364PJ_DEF(pj_status_t) pj_enum_ip_interface(int af,
365 unsigned *p_cnt,
366 pj_sockaddr ifs[])
367{
368 pj_status_t status = -1;
369
370 PJ_ASSERT_RETURN(p_cnt && ifs, PJ_EINVAL);
371 PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC || af==PJ_AF_INET || af==PJ_AF_INET6,
372 PJ_EAFNOTSUP);
373
Benny Prijono62b86eb2007-12-01 08:52:57 +0000374 status = enum_ipv4_ipv6_interface(af, p_cnt, ifs);
375 if (status != PJ_SUCCESS && (af==PJ_AF_INET || af==PJ_AF_UNSPEC))
376 status = enum_ipv4_interface(p_cnt, ifs);
377 return status;
Benny Prijono62b86eb2007-12-01 08:52:57 +0000378}
379
Benny Prijonob681a2f2007-03-25 18:44:51 +0000380/*
381 * Enumerate the IP routing table for this host.
382 */
383PJ_DEF(pj_status_t) pj_enum_ip_route(unsigned *p_cnt,
384 pj_ip_route_entry routes[])
385{
Benny Prijono6b5b6602007-03-30 18:54:46 +0000386 char ipTabBuff[1024];
Benny Prijonob681a2f2007-03-25 18:44:51 +0000387 MIB_IPADDRTABLE *pIpTab;
Benny Prijono6b5b6602007-03-30 18:54:46 +0000388 char rtabBuff[1024];
Benny Prijonob681a2f2007-03-25 18:44:51 +0000389 MIB_IPFORWARDTABLE *prTab;
390 ULONG tabSize;
391 unsigned i, count;
Benny Prijono48ef5a32007-07-28 01:58:36 +0000392 DWORD rc = NO_ERROR;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000393
394 PJ_ASSERT_RETURN(p_cnt && routes, PJ_EINVAL);
395
Benny Prijono6b5b6602007-03-30 18:54:46 +0000396 pIpTab = (MIB_IPADDRTABLE *)ipTabBuff;
397 prTab = (MIB_IPFORWARDTABLE *)rtabBuff;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000398
399 /* First get IP address table */
400 tabSize = sizeof(ipTabBuff);
Benny Prijono48ef5a32007-07-28 01:58:36 +0000401 rc = MyGetIpAddrTable(pIpTab, &tabSize, FALSE);
Benny Prijonob681a2f2007-03-25 18:44:51 +0000402 if (rc != NO_ERROR)
403 return PJ_RETURN_OS_ERROR(rc);
404
405 /* Next get IP route table */
406 tabSize = sizeof(rtabBuff);
Benny Prijono48ef5a32007-07-28 01:58:36 +0000407
408 rc = MyGetIpForwardTable(prTab, &tabSize, 1);
Benny Prijonob681a2f2007-03-25 18:44:51 +0000409 if (rc != NO_ERROR)
410 return PJ_RETURN_OS_ERROR(rc);
411
412 /* Reset routes */
413 pj_bzero(routes, sizeof(routes[0]) * (*p_cnt));
414
415 /* Now fill out the route entries */
416 count = (prTab->dwNumEntries < *p_cnt) ? prTab->dwNumEntries : *p_cnt;
417 *p_cnt = 0;
418 for (i=0; i<count; ++i) {
419 unsigned j;
420
421 /* Find interface entry */
422 for (j=0; j<pIpTab->dwNumEntries; ++j) {
423 if (pIpTab->table[j].dwIndex == prTab->table[i].dwForwardIfIndex)
424 break;
425 }
426
427 if (j==pIpTab->dwNumEntries)
428 continue; /* Interface not found */
429
430 routes[*p_cnt].ipv4.if_addr.s_addr = pIpTab->table[j].dwAddr;
431 routes[*p_cnt].ipv4.dst_addr.s_addr = prTab->table[i].dwForwardDest;
432 routes[*p_cnt].ipv4.mask.s_addr = prTab->table[i].dwForwardMask;
433
434 (*p_cnt)++;
435 }
436
437 return PJ_SUCCESS;
438}
439