blob: 4a4b53a4895f72fa70efb03f014570cf0f6b3b98 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
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 */
20#include <pj/config.h>
21
22#define WIN32_LEAN_AND_MEAN
23#include <windows.h>
24
25/* 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)
29# define PMIB_ICMP_EX void*
30#endif
31#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 */
37#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
44/* 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
83typedef DWORD (WINAPI *PFN_GetIpAddrTable)(PMIB_IPADDRTABLE pIpAddrTable,
84 PULONG pdwSize,
85 BOOL bOrder);
86typedef DWORD (WINAPI *PFN_GetAdapterAddresses)(ULONG Family,
87 ULONG Flags,
88 PVOID Reserved,
89 PIP_ADAPTER_ADDRESSES AdapterAddresses,
90 PULONG SizePointer);
91typedef DWORD (WINAPI *PFN_GetIpForwardTable)(PMIB_IPFORWARDTABLE pIpForwardTable,
92 PULONG pdwSize,
93 BOOL bOrder);
94typedef DWORD (WINAPI *PFN_GetIfEntry)(PMIB_IFROW pIfRow);
95
96static HANDLE s_hDLL;
97static PFN_GetIpAddrTable s_pfnGetIpAddrTable;
98static PFN_GetAdapterAddresses s_pfnGetAdapterAddresses;
99static PFN_GetIpForwardTable s_pfnGetIpForwardTable;
100static PFN_GetIfEntry s_pfnGetIfEntry;
101
102
103static void unload_iphlp_module(void)
104{
105 FreeLibrary(s_hDLL);
106 s_hDLL = NULL;
107 s_pfnGetIpAddrTable = NULL;
108 s_pfnGetIpForwardTable = NULL;
109 s_pfnGetIfEntry = NULL;
110 s_pfnGetAdapterAddresses = NULL;
111}
112
113static FARPROC GetIpHlpApiProc(gpa_char *lpProcName)
114{
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)
134 GetIpHlpApiProc(GPA_TEXT("GetIpAddrTable"));
135 }
136
137 if(NULL != s_pfnGetIpAddrTable) {
138 return s_pfnGetIpAddrTable(pIpAddrTable, pdwSize, bOrder);
139 }
140
141 return ERROR_NOT_SUPPORTED;
142}
143
144static 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)
152 GetIpHlpApiProc(GPA_TEXT("GetAdaptersAddresses"));
153 }
154
155 if(NULL != s_pfnGetAdapterAddresses) {
156 return s_pfnGetAdapterAddresses(Family, Flags, Reserved,
157 AdapterAddresses, SizePointer);
158 }
159
160 return ERROR_NOT_SUPPORTED;
161}
162
163#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
164static DWORD MyGetIfEntry(MIB_IFROW *pIfRow)
165{
166 if(NULL == s_pfnGetIfEntry) {
167 s_pfnGetIfEntry = (PFN_GetIfEntry)
168 GetIpHlpApiProc(GPA_TEXT("GetIfEntry"));
169 }
170
171 if(NULL != s_pfnGetIfEntry) {
172 return s_pfnGetIfEntry(pIfRow);
173 }
174
175 return ERROR_NOT_SUPPORTED;
176}
177#endif
178
179
180static DWORD MyGetIpForwardTable(PMIB_IPFORWARDTABLE pIpForwardTable,
181 PULONG pdwSize,
182 BOOL bOrder)
183{
184 if(NULL == s_pfnGetIpForwardTable) {
185 s_pfnGetIpForwardTable = (PFN_GetIpForwardTable)
186 GetIpHlpApiProc(GPA_TEXT("GetIpForwardTable"));
187 }
188
189 if(NULL != s_pfnGetIpForwardTable) {
190 return s_pfnGetIpForwardTable(pIpForwardTable, pdwSize, bOrder);
191 }
192
193 return ERROR_NOT_SUPPORTED;
194}
195
196/* Enumerate local IP interface using GetIpAddrTable()
197 * for IPv4 addresses only.
198 */
199static pj_status_t enum_ipv4_interface(unsigned *p_cnt,
200 pj_sockaddr ifs[])
201{
202 char ipTabBuff[512];
203 MIB_IPADDRTABLE *pTab = (MIB_IPADDRTABLE*)ipTabBuff;
204 ULONG tabSize = sizeof(ipTabBuff);
205 unsigned i, count;
206 DWORD rc = NO_ERROR;
207
208 PJ_ASSERT_RETURN(p_cnt && ifs, PJ_EINVAL);
209
210 /* Get IP address table */
211 rc = MyGetIpAddrTable(pTab, &tabSize, FALSE);
212 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 }
226
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;
232 *p_cnt = 0;
233 for (i=0; i<count; ++i) {
234 MIB_IFROW ifRow;
235
236 /* Ignore 0.0.0.0 address (interface is down?) */
237 if (pTab->table[i].dwAddr == 0)
238 continue;
239
240 /* 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
246#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
257 ifs[*p_cnt].ipv4.sin_family = PJ_AF_INET;
258 ifs[*p_cnt].ipv4.sin_addr.s_addr = pTab->table[i].dwAddr;
259 (*p_cnt)++;
260 }
261
262 if (pTab != (MIB_IPADDRTABLE*)ipTabBuff)
263 free(pTab);
264
265 return (*p_cnt) ? PJ_SUCCESS : PJ_ENOTFOUND;
266}
267
268/* Enumerate local IP interface using GetAdapterAddresses(),
269 * which works for both IPv4 and IPv6.
270 */
271static pj_status_t enum_ipv4_ipv6_interface(int af,
272 unsigned *p_cnt,
273 pj_sockaddr ifs[])
274{
275 pj_uint8_t buffer[600];
276 IP_ADAPTER_ADDRESSES *adapter = (IP_ADAPTER_ADDRESSES*)buffer;
277 void *adapterBuf = NULL;
278 ULONG size = sizeof(buffer);
279 ULONG flags;
280 unsigned i;
281 DWORD rc;
282
283 flags = GAA_FLAG_SKIP_FRIENDLY_NAME |
284 GAA_FLAG_SKIP_DNS_SERVER |
285 GAA_FLAG_SKIP_MULTICAST;
286
287 rc = MyGetAdapterAddresses(af, flags, NULL, adapter, &size);
288 if (rc != ERROR_SUCCESS) {
289 if (rc == ERROR_BUFFER_OVERFLOW) {
290 /* Retry with larger memory size */
291 adapterBuf = malloc(size);
292 adapter = (IP_ADAPTER_ADDRESSES*) adapterBuf;
293 if (adapter != NULL)
294 rc = MyGetAdapterAddresses(af, flags, NULL, adapter, &size);
295 }
296
297 if (rc != ERROR_SUCCESS) {
298 if (adapterBuf)
299 free(adapterBuf);
300 return PJ_RETURN_OS_ERROR(rc);
301 }
302 }
303
304 /* 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
355 if (adapterBuf)
356 free(adapterBuf);
357
358 *p_cnt = i;
359 return (*p_cnt) ? PJ_SUCCESS : PJ_ENOTFOUND;
360}
361
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
376 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;
380}
381
382/*
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{
388 char ipTabBuff[1024];
389 MIB_IPADDRTABLE *pIpTab;
390 char rtabBuff[1024];
391 MIB_IPFORWARDTABLE *prTab;
392 ULONG tabSize;
393 unsigned i, count;
394 DWORD rc = NO_ERROR;
395
396 PJ_ASSERT_RETURN(p_cnt && routes, PJ_EINVAL);
397
398 pIpTab = (MIB_IPADDRTABLE *)ipTabBuff;
399 prTab = (MIB_IPFORWARDTABLE *)rtabBuff;
400
401 /* First get IP address table */
402 tabSize = sizeof(ipTabBuff);
403 rc = MyGetIpAddrTable(pIpTab, &tabSize, FALSE);
404 if (rc != NO_ERROR)
405 return PJ_RETURN_OS_ERROR(rc);
406
407 /* Next get IP route table */
408 tabSize = sizeof(rtabBuff);
409
410 rc = MyGetIpForwardTable(prTab, &tabSize, 1);
411 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