blob: 122ae9b7df4d88f8759b4f168be96c095c211d25 [file] [log] [blame]
Benny Prijonob681a2f2007-03-25 18:44:51 +00001/* $Id$ */
2/*
Benny Prijono32177c02008-06-20 22:44:47 +00003 * Copyright (C)2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijonob681a2f2007-03-25 18:44:51 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijonoa6bd7582007-03-28 15:49:48 +000019#include <pj/config.h>
Benny Prijonob681a2f2007-03-25 18:44:51 +000020
21#define WIN32_LEAN_AND_MEAN
22#include <windows.h>
23
Benny Prijonoa6bd7582007-03-28 15:49:48 +000024/* PMIB_ICMP_EX is not declared in VC6, causing error.
25 * But EVC4, which also claims to be VC6, does have it!
26 */
27#if defined(_MSC_VER) && _MSC_VER==1200 && !defined(PJ_WIN32_WINCE)
Benny Prijonob681a2f2007-03-25 18:44:51 +000028# define PMIB_ICMP_EX void*
29#endif
Benny Prijono62b86eb2007-12-01 08:52:57 +000030#include <winsock2.h>
31
32/* If you encounter error "Cannot open include file: 'Iphlpapi.h' here,
33 * you need to install newer Platform SDK. Presumably you're using
34 * Microsoft Visual Studio 6?
35 */
Benny Prijonob681a2f2007-03-25 18:44:51 +000036#include <Iphlpapi.h>
37
38#include <pj/ip_helper.h>
39#include <pj/assert.h>
40#include <pj/errno.h>
41#include <pj/string.h>
42
Benny Prijono48ef5a32007-07-28 01:58:36 +000043typedef DWORD (WINAPI *PFN_GetIpAddrTable)(PMIB_IPADDRTABLE pIpAddrTable,
44 PULONG pdwSize,
45 BOOL bOrder);
Benny Prijono62b86eb2007-12-01 08:52:57 +000046#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
47typedef DWORD (WINAPI *PFN_GetAdapterAddresses)(ULONG Family,
48 ULONG Flags,
49 PVOID Reserved,
50 PIP_ADAPTER_ADDRESSES AdapterAddresses,
51 PULONG SizePointer);
52#endif /* PJ_HAS_IPV6 */
Benny Prijono48ef5a32007-07-28 01:58:36 +000053typedef DWORD (WINAPI *PFN_GetIpForwardTable)(PMIB_IPFORWARDTABLE pIpForwardTable,
54 PULONG pdwSize,
55 BOOL bOrder);
Benny Prijono2501e132007-09-24 19:46:41 +000056typedef DWORD (WINAPI *PFN_GetIfEntry)(PMIB_IFROW pIfRow);
Benny Prijono48ef5a32007-07-28 01:58:36 +000057
58static HANDLE s_hDLL;
59static PFN_GetIpAddrTable s_pfnGetIpAddrTable;
Benny Prijono62b86eb2007-12-01 08:52:57 +000060#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
61 static PFN_GetAdapterAddresses s_pfnGetAdapterAddresses;
62#endif /* PJ_HAS_IPV6 */
Benny Prijono48ef5a32007-07-28 01:58:36 +000063static PFN_GetIpForwardTable s_pfnGetIpForwardTable;
Benny Prijono2501e132007-09-24 19:46:41 +000064static PFN_GetIfEntry s_pfnGetIfEntry;
Benny Prijono48ef5a32007-07-28 01:58:36 +000065
Benny Prijono62b86eb2007-12-01 08:52:57 +000066
Benny Prijono16f64792007-08-16 14:13:28 +000067static void unload_iphlp_module(void)
Benny Prijono48ef5a32007-07-28 01:58:36 +000068{
69 FreeLibrary(s_hDLL);
70 s_hDLL = NULL;
71 s_pfnGetIpAddrTable = NULL;
72 s_pfnGetIpForwardTable = NULL;
Benny Prijono62b86eb2007-12-01 08:52:57 +000073 s_pfnGetIfEntry = NULL;
74#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
75 s_pfnGetAdapterAddresses = NULL;
76#endif
Benny Prijono48ef5a32007-07-28 01:58:36 +000077}
78
79static FARPROC GetIpHlpApiProc(pj_char_t *lpProcName)
80{
81 if(NULL == s_hDLL) {
82 s_hDLL = LoadLibrary(PJ_T("IpHlpApi"));
83 if(NULL != s_hDLL) {
84 pj_atexit(&unload_iphlp_module);
85 }
86 }
87
88 if(NULL != s_hDLL)
89 return GetProcAddress(s_hDLL, lpProcName);
90
91 return NULL;
92}
93
94static DWORD MyGetIpAddrTable(PMIB_IPADDRTABLE pIpAddrTable,
95 PULONG pdwSize,
96 BOOL bOrder)
97{
98 if(NULL == s_pfnGetIpAddrTable) {
99 s_pfnGetIpAddrTable = (PFN_GetIpAddrTable)
100 GetIpHlpApiProc(PJ_T("GetIpAddrTable"));
101 }
102
103 if(NULL != s_pfnGetIpAddrTable) {
104 return s_pfnGetIpAddrTable(pIpAddrTable, pdwSize, bOrder);
105 }
106
107 return ERROR_NOT_SUPPORTED;
108}
109
Benny Prijono62b86eb2007-12-01 08:52:57 +0000110#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
111static DWORD MyGetAdapterAddresses(ULONG Family,
112 ULONG Flags,
113 PVOID Reserved,
114 PIP_ADAPTER_ADDRESSES AdapterAddresses,
115 PULONG SizePointer)
116{
117 if(NULL == s_pfnGetAdapterAddresses) {
118 s_pfnGetAdapterAddresses = (PFN_GetAdapterAddresses)
119 GetIpHlpApiProc(PJ_T("GetAdapterAddresses"));
120 }
121
122 if(NULL != s_pfnGetAdapterAddresses) {
123 return s_pfnGetAdapterAddresses(Family, Flags, Reserved,
124 AdapterAddresses, SizePointer);
125 }
126
127 return ERROR_NOT_SUPPORTED;
128}
129#endif /* PJ_HAS_IPV6 */
Benny Prijono48ef5a32007-07-28 01:58:36 +0000130
Benny Prijono2501e132007-09-24 19:46:41 +0000131#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
132static DWORD MyGetIfEntry(MIB_IFROW *pIfRow)
133{
134 if(NULL == s_pfnGetIfEntry) {
135 s_pfnGetIfEntry = (PFN_GetIfEntry)
136 GetIpHlpApiProc(PJ_T("GetIfEntry"));
137 }
138
139 if(NULL != s_pfnGetIfEntry) {
140 return s_pfnGetIfEntry(pIfRow);
141 }
142
143 return ERROR_NOT_SUPPORTED;
144}
145#endif
146
147
Benny Prijono48ef5a32007-07-28 01:58:36 +0000148static DWORD MyGetIpForwardTable(PMIB_IPFORWARDTABLE pIpForwardTable,
149 PULONG pdwSize,
150 BOOL bOrder)
151{
152 if(NULL == s_pfnGetIpForwardTable) {
153 s_pfnGetIpForwardTable = (PFN_GetIpForwardTable)
154 GetIpHlpApiProc(PJ_T("GetIpForwardTable"));
155 }
156
157 if(NULL != s_pfnGetIpForwardTable) {
158 return s_pfnGetIpForwardTable(pIpForwardTable, pdwSize, bOrder);
159 }
160
161 return ERROR_NOT_SUPPORTED;
162}
Benny Prijonob681a2f2007-03-25 18:44:51 +0000163
Benny Prijono62b86eb2007-12-01 08:52:57 +0000164/* Enumerate local IP interface using GetIpAddrTable()
165 * for IPv4 addresses only.
Benny Prijonob681a2f2007-03-25 18:44:51 +0000166 */
Benny Prijono62b86eb2007-12-01 08:52:57 +0000167static pj_status_t enum_ipv4_interface(unsigned *p_cnt,
168 pj_sockaddr ifs[])
Benny Prijonob681a2f2007-03-25 18:44:51 +0000169{
170 /* Provide enough buffer or otherwise it will fail with
171 * error 22 ("Not Enough Buffer") error.
172 */
Benny Prijono6b5b6602007-03-30 18:54:46 +0000173 char ipTabBuff[1024];
Benny Prijonob681a2f2007-03-25 18:44:51 +0000174 MIB_IPADDRTABLE *pTab;
175 ULONG tabSize;
176 unsigned i, count;
Benny Prijono48ef5a32007-07-28 01:58:36 +0000177 DWORD rc = NO_ERROR;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000178
179 PJ_ASSERT_RETURN(p_cnt && ifs, PJ_EINVAL);
180
Benny Prijono6b5b6602007-03-30 18:54:46 +0000181 pTab = (MIB_IPADDRTABLE*)ipTabBuff;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000182
183 /* Get IP address table */
184 tabSize = sizeof(ipTabBuff);
Benny Prijono48ef5a32007-07-28 01:58:36 +0000185
186 rc = MyGetIpAddrTable(pTab, &tabSize, FALSE);
Benny Prijonob681a2f2007-03-25 18:44:51 +0000187 if (rc != NO_ERROR)
188 return PJ_RETURN_OS_ERROR(rc);
189
190 /* Reset result */
191 pj_bzero(ifs, sizeof(ifs[0]) * (*p_cnt));
192
193 /* Now fill out the entries */
194 count = (pTab->dwNumEntries < *p_cnt) ? pTab->dwNumEntries : *p_cnt;
Benny Prijonofed1af92007-03-27 23:29:27 +0000195 *p_cnt = 0;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000196 for (i=0; i<count; ++i) {
Benny Prijono2501e132007-09-24 19:46:41 +0000197 MIB_IFROW ifRow;
198
Benny Prijono62b86eb2007-12-01 08:52:57 +0000199 /* Ignore 0.0.0.0 address (interface is down?) */
Benny Prijonofed1af92007-03-27 23:29:27 +0000200 if (pTab->table[i].dwAddr == 0)
201 continue;
Benny Prijono2501e132007-09-24 19:46:41 +0000202
Benny Prijonodd538ed2008-06-07 11:14:32 +0000203 /* Ignore 0.0.0.0/8 address. This is a special address
204 * which doesn't seem to have practical use.
205 */
206 if ((pj_ntohl(pTab->table[i].dwAddr) >> 24) == 0)
207 continue;
208
Benny Prijono2501e132007-09-24 19:46:41 +0000209#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
210 /* Investigate the type of this interface */
211 pj_bzero(&ifRow, sizeof(ifRow));
212 ifRow.dwIndex = pTab->table[i].dwIndex;
213 if (MyGetIfEntry(&ifRow) != 0)
214 continue;
215
216 if (ifRow.dwType == MIB_IF_TYPE_LOOPBACK)
217 continue;
218#endif
219
Benny Prijono62b86eb2007-12-01 08:52:57 +0000220 ifs[*p_cnt].ipv4.sin_family = PJ_AF_INET;
221 ifs[*p_cnt].ipv4.sin_addr.s_addr = pTab->table[i].dwAddr;
Benny Prijonofed1af92007-03-27 23:29:27 +0000222 (*p_cnt)++;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000223 }
224
Benny Prijono93993562008-01-24 19:54:51 +0000225 return (*p_cnt) ? PJ_SUCCESS : PJ_ENOTFOUND;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000226}
227
228
Benny Prijono62b86eb2007-12-01 08:52:57 +0000229/* Enumerate local IP interface using GetAdapterAddresses(),
230 * which works for both IPv4 and IPv6.
231 */
232#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
233static pj_status_t enum_ipv4_ipv6_interface(int af,
234 unsigned *p_cnt,
235 pj_sockaddr ifs[])
236{
237 pj_uint8_t buffer[1024];
238 IP_ADAPTER_ADDRESSES *adapter = (IP_ADAPTER_ADDRESSES*)buffer;
239 ULONG size = sizeof(buffer);
240 unsigned i;
241 DWORD rc;
242
243 rc = MyGetAdapterAddresses(af, 0, NULL, adapter, &size);
244 if (rc != ERROR_SUCCESS)
245 return PJ_RETURN_OS_ERROR(rc);
246
247 for (i=0; i<*p_cnt && adapter; ++i, adapter = adapter->Next) {
248 SOCKET_ADDRESS *pAddr = &adapter->FirstUnicastAddress->Address;
249 ifs[i].addr.sa_family = pAddr->lpSockaddr->sa_family;
250 pj_memcpy(&ifs[i], pAddr->lpSockaddr, pAddr->iSockaddrLength);
251 }
252
253 return PJ_SUCCESS;
254}
255#endif
256
257
258/*
259 * Enumerate the local IP interface currently active in the host.
260 */
261PJ_DEF(pj_status_t) pj_enum_ip_interface(int af,
262 unsigned *p_cnt,
263 pj_sockaddr ifs[])
264{
265 pj_status_t status = -1;
266
267 PJ_ASSERT_RETURN(p_cnt && ifs, PJ_EINVAL);
268 PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC || af==PJ_AF_INET || af==PJ_AF_INET6,
269 PJ_EAFNOTSUP);
270
271#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
272 status = enum_ipv4_ipv6_interface(af, p_cnt, ifs);
273 if (status != PJ_SUCCESS && (af==PJ_AF_INET || af==PJ_AF_UNSPEC))
274 status = enum_ipv4_interface(p_cnt, ifs);
275 return status;
276#else
277 if (af==PJ_AF_INET6)
278 return PJ_EIPV6NOTSUP;
279 else if (af != PJ_AF_INET && af != PJ_AF_UNSPEC)
280 return PJ_EAFNOTSUP;
281
282 status = enum_ipv4_interface(p_cnt, ifs);
283 return status;
284#endif
285}
286
Benny Prijonob681a2f2007-03-25 18:44:51 +0000287/*
288 * Enumerate the IP routing table for this host.
289 */
290PJ_DEF(pj_status_t) pj_enum_ip_route(unsigned *p_cnt,
291 pj_ip_route_entry routes[])
292{
Benny Prijono6b5b6602007-03-30 18:54:46 +0000293 char ipTabBuff[1024];
Benny Prijonob681a2f2007-03-25 18:44:51 +0000294 MIB_IPADDRTABLE *pIpTab;
Benny Prijono6b5b6602007-03-30 18:54:46 +0000295 char rtabBuff[1024];
Benny Prijonob681a2f2007-03-25 18:44:51 +0000296 MIB_IPFORWARDTABLE *prTab;
297 ULONG tabSize;
298 unsigned i, count;
Benny Prijono48ef5a32007-07-28 01:58:36 +0000299 DWORD rc = NO_ERROR;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000300
301 PJ_ASSERT_RETURN(p_cnt && routes, PJ_EINVAL);
302
Benny Prijono6b5b6602007-03-30 18:54:46 +0000303 pIpTab = (MIB_IPADDRTABLE *)ipTabBuff;
304 prTab = (MIB_IPFORWARDTABLE *)rtabBuff;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000305
306 /* First get IP address table */
307 tabSize = sizeof(ipTabBuff);
Benny Prijono48ef5a32007-07-28 01:58:36 +0000308 rc = MyGetIpAddrTable(pIpTab, &tabSize, FALSE);
Benny Prijonob681a2f2007-03-25 18:44:51 +0000309 if (rc != NO_ERROR)
310 return PJ_RETURN_OS_ERROR(rc);
311
312 /* Next get IP route table */
313 tabSize = sizeof(rtabBuff);
Benny Prijono48ef5a32007-07-28 01:58:36 +0000314
315 rc = MyGetIpForwardTable(prTab, &tabSize, 1);
Benny Prijonob681a2f2007-03-25 18:44:51 +0000316 if (rc != NO_ERROR)
317 return PJ_RETURN_OS_ERROR(rc);
318
319 /* Reset routes */
320 pj_bzero(routes, sizeof(routes[0]) * (*p_cnt));
321
322 /* Now fill out the route entries */
323 count = (prTab->dwNumEntries < *p_cnt) ? prTab->dwNumEntries : *p_cnt;
324 *p_cnt = 0;
325 for (i=0; i<count; ++i) {
326 unsigned j;
327
328 /* Find interface entry */
329 for (j=0; j<pIpTab->dwNumEntries; ++j) {
330 if (pIpTab->table[j].dwIndex == prTab->table[i].dwForwardIfIndex)
331 break;
332 }
333
334 if (j==pIpTab->dwNumEntries)
335 continue; /* Interface not found */
336
337 routes[*p_cnt].ipv4.if_addr.s_addr = pIpTab->table[j].dwAddr;
338 routes[*p_cnt].ipv4.dst_addr.s_addr = prTab->table[i].dwForwardDest;
339 routes[*p_cnt].ipv4.mask.s_addr = prTab->table[i].dwForwardMask;
340
341 (*p_cnt)++;
342 }
343
344 return PJ_SUCCESS;
345}
346