blob: abd10541b2fb44037c0cdd49d12d2e9725c3fd4b [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 Prijono48ef5a32007-07-28 01:58:36 +000044typedef DWORD (WINAPI *PFN_GetIpAddrTable)(PMIB_IPADDRTABLE pIpAddrTable,
45 PULONG pdwSize,
46 BOOL bOrder);
Benny Prijono62b86eb2007-12-01 08:52:57 +000047#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
48typedef DWORD (WINAPI *PFN_GetAdapterAddresses)(ULONG Family,
49 ULONG Flags,
50 PVOID Reserved,
51 PIP_ADAPTER_ADDRESSES AdapterAddresses,
52 PULONG SizePointer);
53#endif /* PJ_HAS_IPV6 */
Benny Prijono48ef5a32007-07-28 01:58:36 +000054typedef DWORD (WINAPI *PFN_GetIpForwardTable)(PMIB_IPFORWARDTABLE pIpForwardTable,
55 PULONG pdwSize,
56 BOOL bOrder);
Benny Prijono2501e132007-09-24 19:46:41 +000057typedef DWORD (WINAPI *PFN_GetIfEntry)(PMIB_IFROW pIfRow);
Benny Prijono48ef5a32007-07-28 01:58:36 +000058
59static HANDLE s_hDLL;
60static PFN_GetIpAddrTable s_pfnGetIpAddrTable;
Benny Prijono62b86eb2007-12-01 08:52:57 +000061#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
62 static PFN_GetAdapterAddresses s_pfnGetAdapterAddresses;
63#endif /* PJ_HAS_IPV6 */
Benny Prijono48ef5a32007-07-28 01:58:36 +000064static PFN_GetIpForwardTable s_pfnGetIpForwardTable;
Benny Prijono2501e132007-09-24 19:46:41 +000065static PFN_GetIfEntry s_pfnGetIfEntry;
Benny Prijono48ef5a32007-07-28 01:58:36 +000066
Benny Prijono62b86eb2007-12-01 08:52:57 +000067
Benny Prijono16f64792007-08-16 14:13:28 +000068static void unload_iphlp_module(void)
Benny Prijono48ef5a32007-07-28 01:58:36 +000069{
70 FreeLibrary(s_hDLL);
71 s_hDLL = NULL;
72 s_pfnGetIpAddrTable = NULL;
73 s_pfnGetIpForwardTable = NULL;
Benny Prijono62b86eb2007-12-01 08:52:57 +000074 s_pfnGetIfEntry = NULL;
75#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
76 s_pfnGetAdapterAddresses = NULL;
77#endif
Benny Prijono48ef5a32007-07-28 01:58:36 +000078}
79
80static FARPROC GetIpHlpApiProc(pj_char_t *lpProcName)
81{
82 if(NULL == s_hDLL) {
83 s_hDLL = LoadLibrary(PJ_T("IpHlpApi"));
84 if(NULL != s_hDLL) {
85 pj_atexit(&unload_iphlp_module);
86 }
87 }
88
89 if(NULL != s_hDLL)
90 return GetProcAddress(s_hDLL, lpProcName);
91
92 return NULL;
93}
94
95static DWORD MyGetIpAddrTable(PMIB_IPADDRTABLE pIpAddrTable,
96 PULONG pdwSize,
97 BOOL bOrder)
98{
99 if(NULL == s_pfnGetIpAddrTable) {
100 s_pfnGetIpAddrTable = (PFN_GetIpAddrTable)
101 GetIpHlpApiProc(PJ_T("GetIpAddrTable"));
102 }
103
104 if(NULL != s_pfnGetIpAddrTable) {
105 return s_pfnGetIpAddrTable(pIpAddrTable, pdwSize, bOrder);
106 }
107
108 return ERROR_NOT_SUPPORTED;
109}
110
Benny Prijono62b86eb2007-12-01 08:52:57 +0000111#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
112static DWORD MyGetAdapterAddresses(ULONG Family,
113 ULONG Flags,
114 PVOID Reserved,
115 PIP_ADAPTER_ADDRESSES AdapterAddresses,
116 PULONG SizePointer)
117{
118 if(NULL == s_pfnGetAdapterAddresses) {
119 s_pfnGetAdapterAddresses = (PFN_GetAdapterAddresses)
120 GetIpHlpApiProc(PJ_T("GetAdapterAddresses"));
121 }
122
123 if(NULL != s_pfnGetAdapterAddresses) {
124 return s_pfnGetAdapterAddresses(Family, Flags, Reserved,
125 AdapterAddresses, SizePointer);
126 }
127
128 return ERROR_NOT_SUPPORTED;
129}
130#endif /* PJ_HAS_IPV6 */
Benny Prijono48ef5a32007-07-28 01:58:36 +0000131
Benny Prijono2501e132007-09-24 19:46:41 +0000132#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
133static DWORD MyGetIfEntry(MIB_IFROW *pIfRow)
134{
135 if(NULL == s_pfnGetIfEntry) {
136 s_pfnGetIfEntry = (PFN_GetIfEntry)
137 GetIpHlpApiProc(PJ_T("GetIfEntry"));
138 }
139
140 if(NULL != s_pfnGetIfEntry) {
141 return s_pfnGetIfEntry(pIfRow);
142 }
143
144 return ERROR_NOT_SUPPORTED;
145}
146#endif
147
148
Benny Prijono48ef5a32007-07-28 01:58:36 +0000149static DWORD MyGetIpForwardTable(PMIB_IPFORWARDTABLE pIpForwardTable,
150 PULONG pdwSize,
151 BOOL bOrder)
152{
153 if(NULL == s_pfnGetIpForwardTable) {
154 s_pfnGetIpForwardTable = (PFN_GetIpForwardTable)
155 GetIpHlpApiProc(PJ_T("GetIpForwardTable"));
156 }
157
158 if(NULL != s_pfnGetIpForwardTable) {
159 return s_pfnGetIpForwardTable(pIpForwardTable, pdwSize, bOrder);
160 }
161
162 return ERROR_NOT_SUPPORTED;
163}
Benny Prijonob681a2f2007-03-25 18:44:51 +0000164
Benny Prijono62b86eb2007-12-01 08:52:57 +0000165/* Enumerate local IP interface using GetIpAddrTable()
166 * for IPv4 addresses only.
Benny Prijonob681a2f2007-03-25 18:44:51 +0000167 */
Benny Prijono62b86eb2007-12-01 08:52:57 +0000168static pj_status_t enum_ipv4_interface(unsigned *p_cnt,
169 pj_sockaddr ifs[])
Benny Prijonob681a2f2007-03-25 18:44:51 +0000170{
171 /* Provide enough buffer or otherwise it will fail with
172 * error 22 ("Not Enough Buffer") error.
173 */
Benny Prijono6b5b6602007-03-30 18:54:46 +0000174 char ipTabBuff[1024];
Benny Prijonob681a2f2007-03-25 18:44:51 +0000175 MIB_IPADDRTABLE *pTab;
176 ULONG tabSize;
177 unsigned i, count;
Benny Prijono48ef5a32007-07-28 01:58:36 +0000178 DWORD rc = NO_ERROR;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000179
180 PJ_ASSERT_RETURN(p_cnt && ifs, PJ_EINVAL);
181
Benny Prijono6b5b6602007-03-30 18:54:46 +0000182 pTab = (MIB_IPADDRTABLE*)ipTabBuff;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000183
184 /* Get IP address table */
185 tabSize = sizeof(ipTabBuff);
Benny Prijono48ef5a32007-07-28 01:58:36 +0000186
187 rc = MyGetIpAddrTable(pTab, &tabSize, FALSE);
Benny Prijonob681a2f2007-03-25 18:44:51 +0000188 if (rc != NO_ERROR)
189 return PJ_RETURN_OS_ERROR(rc);
190
191 /* Reset result */
192 pj_bzero(ifs, sizeof(ifs[0]) * (*p_cnt));
193
194 /* Now fill out the entries */
195 count = (pTab->dwNumEntries < *p_cnt) ? pTab->dwNumEntries : *p_cnt;
Benny Prijonofed1af92007-03-27 23:29:27 +0000196 *p_cnt = 0;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000197 for (i=0; i<count; ++i) {
Benny Prijono2501e132007-09-24 19:46:41 +0000198 MIB_IFROW ifRow;
199
Benny Prijono62b86eb2007-12-01 08:52:57 +0000200 /* Ignore 0.0.0.0 address (interface is down?) */
Benny Prijonofed1af92007-03-27 23:29:27 +0000201 if (pTab->table[i].dwAddr == 0)
202 continue;
Benny Prijono2501e132007-09-24 19:46:41 +0000203
Benny Prijonodd538ed2008-06-07 11:14:32 +0000204 /* Ignore 0.0.0.0/8 address. This is a special address
205 * which doesn't seem to have practical use.
206 */
207 if ((pj_ntohl(pTab->table[i].dwAddr) >> 24) == 0)
208 continue;
209
Benny Prijono2501e132007-09-24 19:46:41 +0000210#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
211 /* Investigate the type of this interface */
212 pj_bzero(&ifRow, sizeof(ifRow));
213 ifRow.dwIndex = pTab->table[i].dwIndex;
214 if (MyGetIfEntry(&ifRow) != 0)
215 continue;
216
217 if (ifRow.dwType == MIB_IF_TYPE_LOOPBACK)
218 continue;
219#endif
220
Benny Prijono62b86eb2007-12-01 08:52:57 +0000221 ifs[*p_cnt].ipv4.sin_family = PJ_AF_INET;
222 ifs[*p_cnt].ipv4.sin_addr.s_addr = pTab->table[i].dwAddr;
Benny Prijonofed1af92007-03-27 23:29:27 +0000223 (*p_cnt)++;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000224 }
225
Benny Prijono93993562008-01-24 19:54:51 +0000226 return (*p_cnt) ? PJ_SUCCESS : PJ_ENOTFOUND;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000227}
228
229
Benny Prijono62b86eb2007-12-01 08:52:57 +0000230/* Enumerate local IP interface using GetAdapterAddresses(),
231 * which works for both IPv4 and IPv6.
232 */
233#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
234static pj_status_t enum_ipv4_ipv6_interface(int af,
235 unsigned *p_cnt,
236 pj_sockaddr ifs[])
237{
238 pj_uint8_t buffer[1024];
239 IP_ADAPTER_ADDRESSES *adapter = (IP_ADAPTER_ADDRESSES*)buffer;
240 ULONG size = sizeof(buffer);
241 unsigned i;
242 DWORD rc;
243
244 rc = MyGetAdapterAddresses(af, 0, NULL, adapter, &size);
245 if (rc != ERROR_SUCCESS)
246 return PJ_RETURN_OS_ERROR(rc);
247
248 for (i=0; i<*p_cnt && adapter; ++i, adapter = adapter->Next) {
249 SOCKET_ADDRESS *pAddr = &adapter->FirstUnicastAddress->Address;
250 ifs[i].addr.sa_family = pAddr->lpSockaddr->sa_family;
251 pj_memcpy(&ifs[i], pAddr->lpSockaddr, pAddr->iSockaddrLength);
252 }
253
254 return PJ_SUCCESS;
255}
256#endif
257
258
259/*
260 * Enumerate the local IP interface currently active in the host.
261 */
262PJ_DEF(pj_status_t) pj_enum_ip_interface(int af,
263 unsigned *p_cnt,
264 pj_sockaddr ifs[])
265{
266 pj_status_t status = -1;
267
268 PJ_ASSERT_RETURN(p_cnt && ifs, PJ_EINVAL);
269 PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC || af==PJ_AF_INET || af==PJ_AF_INET6,
270 PJ_EAFNOTSUP);
271
272#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
273 status = enum_ipv4_ipv6_interface(af, p_cnt, ifs);
274 if (status != PJ_SUCCESS && (af==PJ_AF_INET || af==PJ_AF_UNSPEC))
275 status = enum_ipv4_interface(p_cnt, ifs);
276 return status;
277#else
278 if (af==PJ_AF_INET6)
279 return PJ_EIPV6NOTSUP;
280 else if (af != PJ_AF_INET && af != PJ_AF_UNSPEC)
281 return PJ_EAFNOTSUP;
282
283 status = enum_ipv4_interface(p_cnt, ifs);
284 return status;
285#endif
286}
287
Benny Prijonob681a2f2007-03-25 18:44:51 +0000288/*
289 * Enumerate the IP routing table for this host.
290 */
291PJ_DEF(pj_status_t) pj_enum_ip_route(unsigned *p_cnt,
292 pj_ip_route_entry routes[])
293{
Benny Prijono6b5b6602007-03-30 18:54:46 +0000294 char ipTabBuff[1024];
Benny Prijonob681a2f2007-03-25 18:44:51 +0000295 MIB_IPADDRTABLE *pIpTab;
Benny Prijono6b5b6602007-03-30 18:54:46 +0000296 char rtabBuff[1024];
Benny Prijonob681a2f2007-03-25 18:44:51 +0000297 MIB_IPFORWARDTABLE *prTab;
298 ULONG tabSize;
299 unsigned i, count;
Benny Prijono48ef5a32007-07-28 01:58:36 +0000300 DWORD rc = NO_ERROR;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000301
302 PJ_ASSERT_RETURN(p_cnt && routes, PJ_EINVAL);
303
Benny Prijono6b5b6602007-03-30 18:54:46 +0000304 pIpTab = (MIB_IPADDRTABLE *)ipTabBuff;
305 prTab = (MIB_IPFORWARDTABLE *)rtabBuff;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000306
307 /* First get IP address table */
308 tabSize = sizeof(ipTabBuff);
Benny Prijono48ef5a32007-07-28 01:58:36 +0000309 rc = MyGetIpAddrTable(pIpTab, &tabSize, FALSE);
Benny Prijonob681a2f2007-03-25 18:44:51 +0000310 if (rc != NO_ERROR)
311 return PJ_RETURN_OS_ERROR(rc);
312
313 /* Next get IP route table */
314 tabSize = sizeof(rtabBuff);
Benny Prijono48ef5a32007-07-28 01:58:36 +0000315
316 rc = MyGetIpForwardTable(prTab, &tabSize, 1);
Benny Prijonob681a2f2007-03-25 18:44:51 +0000317 if (rc != NO_ERROR)
318 return PJ_RETURN_OS_ERROR(rc);
319
320 /* Reset routes */
321 pj_bzero(routes, sizeof(routes[0]) * (*p_cnt));
322
323 /* Now fill out the route entries */
324 count = (prTab->dwNumEntries < *p_cnt) ? prTab->dwNumEntries : *p_cnt;
325 *p_cnt = 0;
326 for (i=0; i<count; ++i) {
327 unsigned j;
328
329 /* Find interface entry */
330 for (j=0; j<pIpTab->dwNumEntries; ++j) {
331 if (pIpTab->table[j].dwIndex == prTab->table[i].dwForwardIfIndex)
332 break;
333 }
334
335 if (j==pIpTab->dwNumEntries)
336 continue; /* Interface not found */
337
338 routes[*p_cnt].ipv4.if_addr.s_addr = pIpTab->table[j].dwAddr;
339 routes[*p_cnt].ipv4.dst_addr.s_addr = prTab->table[i].dwForwardDest;
340 routes[*p_cnt].ipv4.mask.s_addr = prTab->table[i].dwForwardMask;
341
342 (*p_cnt)++;
343 }
344
345 return PJ_SUCCESS;
346}
347