source: trunk/third-party/libnatpmp/getgateway.c @ 6303

Last change on this file since 6303 was 6303, checked in by charles, 13 years ago

Update to the 20080707 version of libnatpmp and the 20080703 version of miniupnpc

File size: 12.4 KB
Line 
1/* $Id: getgateway.c,v 1.11 2008/07/02 23:56:11 nanard Exp $ */
2/* libnatpmp
3 * Copyright (c) 2007-2008, Thomas BERNARD <miniupnp@free.fr>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
16#include <stdio.h>
17#include <ctype.h>
18#ifndef WIN32
19#include <netinet/in.h>
20#endif
21#include <sys/param.h>
22/* There is no portable method to get the default route gateway.
23 * So below are three differents functions implementing this.
24 * Parsing /proc/net/route is for linux.
25 * sysctl is the way to access such informations on BSD systems.
26 * Many systems should provide route information through raw PF_ROUTE
27 * sockets. */
28#ifdef __linux__
29#define USE_PROC_NET_ROUTE
30#undef USE_SOCKET_ROUTE
31#undef USE_SYSCTL_NET_ROUTE
32#endif
33
34#ifdef BSD
35#undef USE_PROC_NET_ROUTE
36#define USE_SOCKET_ROUTE
37#undef USE_SYSCTL_NET_ROUTE
38#endif
39
40#ifdef __APPLE__
41#undef USE_PROC_NET_ROUTE
42#undef USE_SOCKET_ROUTE
43#define USE_SYSCTL_NET_ROUTE
44#endif
45
46#if (defined(sun) && defined(__SVR4))
47#undef USE_PROC_NET_ROUTE
48#define USE_SOCKET_ROUTE
49#undef USE_SYSCTL_NET_ROUTE
50#endif
51
52#ifdef WIN32
53#undef USE_PROC_NET_ROUTE
54#undef USE_SOCKET_ROUTE
55#undef USE_SYSCTL_NET_ROUTE
56#define USE_WIN32_CODE
57#endif
58
59#ifdef USE_SYSCTL_NET_ROUTE
60#include <stdlib.h>
61#include <sys/sysctl.h>
62#include <sys/socket.h>
63#include <net/route.h>
64#endif
65#ifdef USE_SOCKET_ROUTE
66#include <unistd.h>
67#include <string.h>
68#include <sys/socket.h>
69#include <net/if.h>
70#include <net/route.h>
71#endif
72#ifdef WIN32
73#include <unknwn.h>
74#include <winreg.h>
75#define MAX_KEY_LENGTH 255
76#define MAX_VALUE_LENGTH 16383
77#endif
78#include "getgateway.h"
79
80#ifndef WIN32
81#define SUCCESS (0)
82#define FAILED  (-1)
83#endif
84
85#ifdef USE_PROC_NET_ROUTE
86int getdefaultgateway(in_addr_t * addr)
87{
88        long d, g;
89        char buf[256];
90        int line = 0;
91        FILE * f;
92        char * p;
93        f = fopen("/proc/net/route", "r");
94        if(!f)
95                return FAILED;
96        while(fgets(buf, sizeof(buf), f)) {
97                if(line > 0) {
98                        p = buf;
99                        while(*p && !isspace(*p))
100                                p++;
101                        while(*p && isspace(*p))
102                                p++;
103                        if(sscanf(p, "%lx%lx", &d, &g)==2) {
104                                if(d == 0) { /* default */
105                                        *addr = g;
106                                        fclose(f);
107                                        return SUCCESS;
108                                }
109                        }
110                }
111                line++;
112        }
113        /* default route not found ! */
114        if(f)
115                fclose(f);
116        return FAILED;
117}
118#endif /* #ifdef USE_PROC_NET_ROUTE */
119
120
121#ifdef USE_SYSCTL_NET_ROUTE
122
123#define ROUNDUP(a) \
124        ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
125
126int getdefaultgateway(in_addr_t * addr)
127{
128#if 0
129        /* net.route.0.inet.dump.0.0 ? */
130        int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET,
131                     NET_RT_DUMP, 0, 0/*tableid*/};
132#endif
133        /* net.route.0.inet.flags.gateway */
134        int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET,
135                     NET_RT_FLAGS, RTF_GATEWAY};
136        size_t l;
137        char * buf, * p;
138        struct rt_msghdr * rt;
139        struct sockaddr * sa;
140        struct sockaddr * sa_tab[RTAX_MAX];
141        int i;
142        int r = FAILED;
143        if(sysctl(mib, sizeof(mib)/sizeof(int), 0, &l, 0, 0) < 0) {
144                return FAILED;
145        }
146        if(l>0) {
147                buf = malloc(l);
148                if(sysctl(mib, sizeof(mib)/sizeof(int), buf, &l, 0, 0) < 0) {
149                        return FAILED;
150                }
151                for(p=buf; p<buf+l; p+=rt->rtm_msglen) {
152                        rt = (struct rt_msghdr *)p;
153                        sa = (struct sockaddr *)(rt + 1);
154                        for(i=0; i<RTAX_MAX; i++) {
155                                if(rt->rtm_addrs & (1 << i)) {
156                                        sa_tab[i] = sa;
157                                        sa = (struct sockaddr *)((char *)sa + ROUNDUP(sa->sa_len));
158                                } else {
159                                        sa_tab[i] = NULL;
160                                }
161                        }
162                        if( ((rt->rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY))
163              && sa_tab[RTAX_DST]->sa_family == AF_INET
164              && sa_tab[RTAX_GATEWAY]->sa_family == AF_INET) {
165                                if(((struct sockaddr_in *)sa_tab[RTAX_DST])->sin_addr.s_addr == 0) {
166                                        *addr = ((struct sockaddr_in *)(sa_tab[RTAX_GATEWAY]))->sin_addr.s_addr;
167                                        r = SUCCESS;
168                                }
169                        }
170                }
171                free(buf);
172        }
173        return r;
174}
175#endif /* #ifdef USE_SYSCTL_NET_ROUTE */
176
177
178#ifdef USE_SOCKET_ROUTE
179/* Thanks to Darren Kenny for this code */
180#define NEXTADDR(w, u) \
181        if (rtm_addrs & (w)) {\
182            l = sizeof(struct sockaddr); memmove(cp, &(u), l); cp += l;\
183        }
184
185#define rtm m_rtmsg.m_rtm
186
187struct {
188  struct rt_msghdr m_rtm;
189  char       m_space[512];
190} m_rtmsg;
191
192int getdefaultgateway(in_addr_t *addr)
193{
194  int s, seq, l, rtm_addrs, i;
195  pid_t pid;
196  struct sockaddr so_dst, so_mask;
197  char *cp = m_rtmsg.m_space; 
198  struct sockaddr *gate = NULL, *sa;
199  struct rt_msghdr *msg_hdr;
200
201  pid = getpid();
202  seq = 0;
203  rtm_addrs = RTA_DST | RTA_NETMASK;
204
205  memset(&so_dst, 0, sizeof(so_dst));
206  memset(&so_mask, 0, sizeof(so_mask));
207  memset(&rtm, 0, sizeof(struct rt_msghdr));
208
209  rtm.rtm_type = RTM_GET;
210  rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
211  rtm.rtm_version = RTM_VERSION;
212  rtm.rtm_seq = ++seq;
213  rtm.rtm_addrs = rtm_addrs; 
214
215  so_dst.sa_family = AF_INET;
216  so_mask.sa_family = AF_INET;
217
218  NEXTADDR(RTA_DST, so_dst);
219  NEXTADDR(RTA_NETMASK, so_mask);
220
221  rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
222
223  s = socket(PF_ROUTE, SOCK_RAW, 0);
224
225  if (write(s, (char *)&m_rtmsg, l) < 0) {
226      close(s);
227      return FAILED;
228  }
229
230  do {
231    l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
232  } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
233                       
234  close(s);
235
236  msg_hdr = &rtm;
237
238  cp = ((char *)(msg_hdr + 1));
239  if (msg_hdr->rtm_addrs) {
240    for (i = 1; i; i <<= 1)
241      if (i & msg_hdr->rtm_addrs) {
242        sa = (struct sockaddr *)cp;
243        if (i == RTA_GATEWAY )
244          gate = sa;
245
246        cp += sizeof(struct sockaddr);
247      }
248  } else {
249      return FAILED;
250  }
251
252
253  if (gate != NULL ) {
254      *addr = ((struct sockaddr_in *)gate)->sin_addr.s_addr;
255      return SUCCESS;
256  } else {
257      return FAILED;
258  }
259}
260#endif /* #ifdef USE_SOCKET_ROUTE */
261
262#ifdef USE_WIN32_CODE
263int getdefaultgateway(in_addr_t * addr)
264{
265        HKEY networkCardsKey;
266        HKEY networkCardKey;
267        HKEY interfacesKey;
268        HKEY interfaceKey;
269        DWORD i = 0;
270        DWORD numSubKeys = 0;
271        TCHAR keyName[MAX_KEY_LENGTH];
272        DWORD keyNameLength = MAX_KEY_LENGTH;
273        TCHAR keyValue[MAX_VALUE_LENGTH];
274        DWORD keyValueLength = MAX_VALUE_LENGTH;
275        DWORD keyValueType = REG_SZ;
276        TCHAR gatewayValue[MAX_VALUE_LENGTH];
277        DWORD gatewayValueLength = MAX_VALUE_LENGTH;
278        DWORD gatewayValueType = REG_MULTI_SZ;
279        int done = 0;
280       
281        char networkCardsPath[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards";
282        char interfacesPath[] = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
283       
284        // The windows registry lists its primary network devices in the following location:
285        // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards
286        //
287        // Each network device has its own subfolder, named with an index, with various properties:
288        // -NetworkCards
289        //   -5
290        //     -Description = Broadcom 802.11n Network Adapter
291        //     -ServiceName = {E35A72F8-5065-4097-8DFE-C7790774EE4D}
292        //   -8
293        //     -Description = Marvell Yukon 88E8058 PCI-E Gigabit Ethernet Controller
294        //     -ServiceName = {86226414-5545-4335-A9D1-5BD7120119AD}
295        //
296        // The above service name is the name of a subfolder within:
297        // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces
298        //
299        // There may be more subfolders in this interfaces path than listed in the network cards path above:
300        // -Interfaces
301        //   -{3a539854-6a70-11db-887c-806e6f6e6963}
302        //     -DhcpIPAddress = 0.0.0.0
303        //     -[more]
304        //   -{E35A72F8-5065-4097-8DFE-C7790774EE4D}
305        //     -DhcpIPAddress = 10.0.1.4
306        //     -DhcpDefaultGateway = 10.0.1.1
307        //     -[more]
308        //   -{86226414-5545-4335-A9D1-5BD7120119AD}
309        //     -DhcpIpAddress = 10.0.1.5
310        //     -DhcpDefaultGateay = 10.0.1.1
311        //     -[more]
312        //
313        // In order to extract this information, we enumerate each network card, and extract the ServiceName value.
314        // This is then used to open the interface subfolder, and attempt to extract a DhcpDefaultGateway value.
315        // Once one is found, we're done.
316        //
317        // It may be possible to simply enumerate the interface folders until we find one with a DhcpDefaultGateway value.
318        // However, the technique used is the technique most cited on the web, and we assume it to be more correct.
319       
320        if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, // Open registry key or predifined key
321                                         networkCardsPath,   // Name of registry subkey to open
322                                         0,                  // Reserved - must be zero
323                                         KEY_READ,           // Mask - desired access rights
324                                         &networkCardsKey))  // Pointer to output key
325        {
326                // Unable to open network cards keys
327                return -1;
328        }
329       
330        if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, // Open registry key or predefined key
331                                         interfacesPath,     // Name of registry subkey to open
332                                         0,                  // Reserved - must be zero
333                                         KEY_READ,           // Mask - desired access rights
334                                         &interfacesKey))    // Pointer to output key
335        {
336                // Unable to open interfaces key
337                RegCloseKey(networkCardsKey);
338                return -1;
339        }
340       
341        // Figure out how many subfolders are within the NetworkCards folder
342        RegQueryInfoKey(networkCardsKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
343       
344        //printf( "Number of subkeys: %u\n", (unsigned int)numSubKeys);
345       
346        // Enumrate through each subfolder within the NetworkCards folder
347        for(i = 0; i < numSubKeys && !done; i++)
348        {
349                keyNameLength = MAX_KEY_LENGTH;
350                if(ERROR_SUCCESS == RegEnumKeyEx(networkCardsKey, // Open registry key
351                                                 i,               // Index of subkey to retrieve
352                                                 keyName,         // Buffer that receives the name of the subkey
353                                                 &keyNameLength,  // Variable that receives the size of the above buffer
354                                                 NULL,            // Reserved - must be NULL
355                                                 NULL,            // Buffer that receives the class string
356                                                 NULL,            // Variable that receives the size of the above buffer
357                                                 NULL))           // Variable that receives the last write time of subkey
358                {
359                        if(RegOpenKeyEx(networkCardsKey,  keyName, 0, KEY_READ, &networkCardKey) == ERROR_SUCCESS)
360                        {
361                                keyValueLength = MAX_VALUE_LENGTH;
362                                if(ERROR_SUCCESS == RegQueryValueEx(networkCardKey,   // Open registry key
363                                                                    "ServiceName",    // Name of key to query
364                                                                    NULL,             // Reserved - must be NULL
365                                                                    &keyValueType,    // Receives value type
366                                                                    keyValue,         // Receives value
367                                                                    &keyValueLength)) // Receives value length in bytes
368                                {
369                                        //printf("keyValue: %s\n", keyValue);
370                                       
371                                        if(RegOpenKeyEx(interfacesKey, keyValue, 0, KEY_READ, &interfaceKey) == ERROR_SUCCESS)
372                                        {
373                                                gatewayValueLength = MAX_VALUE_LENGTH;
374                                                if(ERROR_SUCCESS == RegQueryValueEx(interfaceKey,         // Open registry key
375                                                                                    "DhcpDefaultGateway", // Name of key to query
376                                                                                    NULL,                 // Reserved - must be NULL
377                                                                                    &gatewayValueType,    // Receives value type
378                                                                                    gatewayValue,         // Receives value
379                                                                                    &gatewayValueLength)) // Receives value length in bytes
380                                                {
381                                                        // Check to make sure it's a string
382                                                        if(gatewayValueType == REG_MULTI_SZ || gatewayValueType == REG_SZ)
383                                                        {
384                                                                //printf("gatewayValue: %s\n", gatewayValue);
385                                                                done = 1;
386                                                        }
387                                                }
388                                                RegCloseKey(interfaceKey);
389                                        }
390                                }
391                                RegCloseKey(networkCardKey);
392                        }
393                }
394        }
395       
396        RegCloseKey(interfacesKey);
397        RegCloseKey(networkCardsKey);
398       
399        if(done)
400        {
401                *addr = inet_addr(gatewayValue);
402                return 0;
403        }
404       
405        return -1;
406}
407#endif /* #ifdef USE_WIN32_CODE */
408
Note: See TracBrowser for help on using the repository browser.