rx: Remove RX_CALL_BUSY
[openafs.git] / src / WINNT / afsreg / syscfg.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /* Functions for accessing NT system configuration information. */
11
12 #include <afsconfig.h>
13 #include <afs/param.h>
14 #include <afs/stds.h>
15
16 #include <roken.h>
17
18 #include <windows.h>
19 #include <winsock2.h>
20 #include <iphlpapi.h>
21 #include <iptypes.h>
22 #include <ipifcons.h>
23
24 #include "afsreg.h"
25 #include "syscfg.h"
26
27 #ifdef DEBUG
28 #include <strsafe.h>
29 #endif
30
31 static int IsLoopback(char * guid);
32 int syscfg_GetIFInfo_2000(int *count, int *addrs, int *masks, int *mtus, int *flags);
33
34 DWORD GetMTUForAddress(PIP_ADAPTER_ADDRESSES cAddress)
35 {
36     DWORD min_mtu = 0xFFFFFFFF;         /* Default */
37
38     HKEY hk_services = NULL;
39
40     HKEY hk_adapters = NULL;
41
42     HKEY hk_adapter = NULL;
43
44     char * ipconfig = NULL;
45
46     if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services", 0,
47                      KEY_READ, &hk_services) != ERROR_SUCCESS)
48         goto done;
49
50     if (RegOpenKeyEx(hk_services, "Tcpip\\Parameters\\Adapters", 0, KEY_READ,
51                      &hk_adapters)
52         != ERROR_SUCCESS)
53         goto done;
54
55     if (RegOpenKeyEx(hk_adapters, cAddress->AdapterName, 0, KEY_READ, &hk_adapter)
56         != ERROR_SUCCESS)
57         goto done;
58
59     {
60         DWORD cb_alloc = 0;
61         DWORD cb_ipc = 0;
62         DWORD type = 0;
63         char * this_ipc;
64
65         if (RegQueryValueEx(hk_adapter, "IpConfig", 0, NULL, NULL, &cb_ipc)
66             != ERROR_SUCCESS)
67             goto done;
68
69         cb_alloc = cb_ipc + sizeof(char) * 2;
70         ipconfig = malloc(cb_alloc);
71         if (ipconfig == NULL)
72             goto done;
73
74         if (RegQueryValueEx(hk_adapter, "IpConfig", 0, &type, ipconfig,
75                             &cb_ipc) != ERROR_SUCCESS) {
76             goto done;
77         }
78
79         if (cb_ipc < cb_alloc)
80             memset(ipconfig + cb_ipc / sizeof(char), 0,
81                    (cb_alloc - cb_ipc) / sizeof(char));
82
83         for (this_ipc = ipconfig; (this_ipc - ipconfig) < cb_alloc / sizeof(char) &&
84                  *this_ipc; this_ipc = this_ipc + (strlen(this_ipc) + 1)) {
85
86             HKEY hk_interface = NULL;
87             DWORD mtu = 0;
88             DWORD cb_mtu;
89
90             if (RegOpenKeyEx(hk_services, this_ipc, 0, KEY_READ,
91                              &hk_interface) != ERROR_SUCCESS)
92                 continue;
93
94             cb_mtu = sizeof(mtu);
95             if (RegQueryValueEx(hk_interface, "MTU", NULL, NULL, &mtu,
96                                 &cb_mtu) == ERROR_SUCCESS) {
97                 min_mtu = min(min_mtu, mtu);
98             }
99
100             RegCloseKey(hk_interface);
101         }
102     }
103
104  done:
105     if (ipconfig)
106         free(ipconfig);
107
108     if (hk_services != NULL)
109         RegCloseKey(hk_services);
110
111     if (hk_adapters != NULL)
112         RegCloseKey(hk_adapters);
113
114     if (hk_adapter != NULL)
115         RegCloseKey(hk_adapter);
116
117     return min_mtu;
118 }
119
120 /* syscfg_GetIFInfo
121  *
122  * Retrieve IP addresses, subnet masks, MTU sizes, and flags for all
123  * configured interfaces.
124  *
125  * Arguments:
126  * IN/OUT:
127  *      count - in is max size of arrays. out is number of elements used.
128  *
129  * OUT:
130  *      addrs - array of configured IP addresses, in host order.
131  *      masks - array of subnet masks, in host order.
132  *      mtus  - array of MTU sizes.
133  *      flags - array of flags.
134  *
135  * Return Value:
136  *      Total number of configured interfaces (>= count) or -1 on error.
137  */
138
139 int syscfg_GetIFInfo(int *count, int *addrs, int *masks, int *mtus, int *flags)
140 {
141     PMIB_IPADDRTABLE pIpAddrTable = NULL;
142     DWORD            validAddrs = 0;
143
144     int maxCount = *count;
145     int nConfig = 0;
146     PIP_ADAPTER_ADDRESSES pAddresses, cAddress;
147     PMIB_IPADDRTABLE pIpTbl;
148     ULONG outBufLen = 0;
149     DWORD dwRetVal = 0;
150     int n = 0;
151     DWORD i;
152
153     HMODULE hIpHlp;
154     DWORD (WINAPI *pGetAdaptersAddresses)(ULONG, DWORD, PVOID,
155                                           PIP_ADAPTER_ADDRESSES, PULONG) = 0;
156
157     hIpHlp = LoadLibrary("iphlpapi");
158     if (hIpHlp != NULL) {
159         (FARPROC) pGetAdaptersAddresses = GetProcAddress(hIpHlp, "GetAdaptersAddresses");
160         if (pGetAdaptersAddresses == NULL)
161             FreeLibrary(hIpHlp);
162     }
163
164     if (pGetAdaptersAddresses == NULL)
165         return syscfg_GetIFInfo_2000(count, addrs, masks, mtus, flags);
166
167     /* first pass to get the required size of the IP table */
168     pIpTbl = (PMIB_IPADDRTABLE) malloc(sizeof(MIB_IPADDRTABLE));
169     outBufLen = sizeof(MIB_IPADDRTABLE);
170
171     dwRetVal = GetIpAddrTable(pIpTbl, &outBufLen, FALSE);
172     if (dwRetVal != ERROR_INSUFFICIENT_BUFFER) {
173         /* this should have failed with an insufficient buffer because we
174            didn't give any space to place the IP addresses */
175         free(pIpTbl);
176         *count = 0;
177         nConfig = -1;
178         goto done;
179     }
180
181     /* second pass to get the actual data */
182     free(pIpTbl);
183     pIpTbl = (PMIB_IPADDRTABLE) malloc(outBufLen);
184
185     dwRetVal = GetIpAddrTable(pIpTbl, &outBufLen, FALSE);
186     if (dwRetVal != NO_ERROR) {
187         free(pIpTbl);
188         *count = 0;
189         nConfig = -1;
190         goto done;
191     }
192
193     pAddresses = (IP_ADAPTER_ADDRESSES*) malloc(sizeof(IP_ADAPTER_ADDRESSES));
194
195     /* first call gets required buffer size */
196     if (pGetAdaptersAddresses(AF_INET,
197                               0,
198                               NULL,
199                               pAddresses,
200                               &outBufLen) == ERROR_BUFFER_OVERFLOW)
201     {
202         free(pAddresses);
203         pAddresses = (IP_ADAPTER_ADDRESSES*) malloc(outBufLen);
204     } else {
205         free(pIpTbl);
206         *count = 0;
207         nConfig = -1;
208         goto done;
209     }
210
211     /* second call to get the actual data */
212     if ((dwRetVal = pGetAdaptersAddresses(AF_INET,
213                                           0,
214                                           NULL,
215                                           pAddresses,
216                                           &outBufLen)) == NO_ERROR)
217     {
218         /* we have a list of addresses.  go through them and figure out
219            the IP addresses */
220         for (cAddress = pAddresses; cAddress; cAddress = cAddress->Next) {
221
222             /* skip software loopback adapters */
223             if (cAddress->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
224                 continue;
225
226             /* also skip interfaces that are not up */
227             if (cAddress->OperStatus != 1)
228                 continue;
229
230             /* starting with the AdapterName, which is actually the adapter
231                instance GUID, check if this is a MS loopback device */
232             if (IsLoopback(cAddress->AdapterName))
233                 continue;
234
235             /* ok. looks good.  Now fish out all the addresses from the
236                address table corresponding to the interface, and add them
237                to the list */
238             for (i=0;i<pIpTbl->dwNumEntries;i++) {
239                 if (pIpTbl->table[i].dwIndex == cAddress->IfIndex)
240                 {
241                     DWORD if_mtu;
242
243                     if_mtu = GetMTUForAddress(cAddress);
244 #ifdef DEBUG
245                     if (if_mtu < cAddress->Mtu) {
246                         char s[1024];
247                         StringCbPrintf(s, sizeof(s), "MTU from registry is less than MTU from adapter address (%d vs %d)\n", if_mtu, cAddress->Mtu);
248                         OutputDebugString(s);
249                     }
250 #endif
251
252                     if (n < maxCount) {
253                         addrs[n] = ntohl(pIpTbl->table[i].dwAddr);
254                         masks[n] = ntohl(pIpTbl->table[i].dwMask);
255                         mtus[n] = min(cAddress->Mtu, if_mtu);
256                         flags[n] = 0;
257                         n++;
258                     }
259                     nConfig++;
260                 }
261             }
262         }
263
264         free(pAddresses);
265         free(pIpTbl);
266
267         *count = n;
268     } else {
269         /* again. this is bad */
270         free(pAddresses);
271         free(pIpTbl);
272         *count = 0;
273         nConfig = -1;
274     }
275
276   done:
277     FreeLibrary(hIpHlp);
278     return nConfig;
279 }
280
281 static int IsLoopback(char * guid)
282 {
283     int isloopback = FALSE;
284
285     HKEY hkNet = NULL;
286     HKEY hkDev = NULL;
287     HKEY hkDevConn = NULL;
288     HKEY hkEnum = NULL;
289     HKEY hkAdapter = NULL;
290
291     char pnpIns[MAX_PATH];
292     char hwId[MAX_PATH];
293     char service[MAX_PATH];
294
295     DWORD size;
296
297     /* Open the network adapters key */
298     if (FAILED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}", 0, KEY_READ, &hkNet)))
299         goto _exit;
300
301     /* open the guid key */
302     if (FAILED(RegOpenKeyEx(hkNet, guid, 0, KEY_READ, &hkDev)))
303         goto _exit;
304
305     /* then the connection */
306     if (FAILED(RegOpenKeyEx(hkDev, "Connection", 0, KEY_READ, &hkDevConn)))
307         goto _exit;
308
309     /* and find out the plug-n-play instance ID */
310     size = MAX_PATH;
311     if (FAILED(RegQueryValueEx(hkDevConn, "PnpInstanceID", NULL, NULL, pnpIns, &size)))
312         goto _exit;
313
314     /* now look in the device ENUM */
315     if (FAILED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Enum", 0, KEY_READ, &hkEnum)))
316         goto _exit;
317
318     /* for the instance that we found above */
319     if (FAILED(RegOpenKeyEx(hkEnum, pnpIns, 0, KEY_READ, &hkAdapter)))
320         goto _exit;
321
322     /* and fetch the harware ID */
323     size = MAX_PATH;
324     if (FAILED(RegQueryValueEx(hkAdapter, "HardwareID", NULL, NULL, hwId, &size)))
325         goto _exit;
326
327     size = MAX_PATH;
328     if (FAILED(RegQueryValueEx(hkAdapter, "Service", NULL, NULL, service, &size)))
329         goto _exit;
330
331     /* and see if it is the loopback adapter */
332     if (!stricmp(hwId, "*msloop") || !stricmp(service, "msloop"))
333         isloopback = TRUE;
334
335   _exit:
336     if (hkAdapter)
337         RegCloseKey(hkAdapter);
338     if (hkEnum)
339         RegCloseKey(hkEnum);
340     if (hkDevConn)
341         RegCloseKey(hkDevConn);
342     if (hkDev)
343         RegCloseKey(hkDev);
344     if (hkNet)
345         RegCloseKey(hkNet);
346
347     return isloopback;
348 }
349
350 static int GetInterfaceList(HKEY skey, char **list);
351 static char *GetNextInterface(char *iflist);
352 static int GetIP(HKEY skey, char *ifname, int *addr, int *mask);
353
354 int syscfg_GetIFInfo_2000(int *count, int *addrs, int *masks, int *mtus, int *flags)
355 {
356     int maxCount = *count;
357     char *IFListBase = NULL;
358     char *IFList, *ifname;
359     HKEY skey;
360     int i, n, nConfig;
361
362     if (RegOpenKeyAlt(AFSREG_NULL_KEY, AFSREG_IPSRV_KEY,
363                       KEY_READ, 0, &skey, NULL))
364         return -1;
365
366     if ((nConfig = GetInterfaceList(skey, &IFListBase)) < 0) {
367         (void) RegCloseKey(skey);
368         return -1;
369     }
370
371     IFList = IFListBase;
372     n = 0;
373
374     while ((n < maxCount) && (ifname = GetNextInterface(IFList))) {
375         if (!IsLoopback(ifname) && GetIP(skey, ifname, &addrs[n], &masks[n]) == 0 && addrs[n] != 0) {
376             n++;
377         } else {
378             maxCount--;
379         }
380         IFList = ifname;
381     }
382
383     /* And until we get mtu's and flags */
384     for (i = 0; i < n; i++) {
385         mtus[i] = 1500;
386         flags[i] = 0;
387     }
388
389     (void) RegCloseKey(skey);
390     free(IFListBase);
391
392     *count = n;
393     return nConfig;
394 }
395
396
397 /* GetInterfaceList
398  *
399  * Get interface list; list is represented as a multistring.
400  * Returns number of elements in interface list or -1 on error.
401  */
402 static int GetInterfaceList(HKEY skey, char **list)
403 {
404     HKEY key;
405     long status;
406     char *str = NULL;
407     int size;
408     DWORD valType;
409
410     if (RegOpenKeyAlt(skey, AFSREG_IPSRV_IFACELIST_SUBKEY,
411                       KEY_READ, 0, &key, NULL))
412         return -1;
413
414     status = RegQueryValueAlt(key, AFSREG_IPSRV_IFACELIST_BIND_VALUE,
415                               &valType, &str, NULL);
416     (void) RegCloseKey(key);
417     if (status || (valType != REG_MULTI_SZ))
418         return -1;
419
420     /* Count strings in multistring. */
421     size = 0;
422
423     if (*str != '\0') {
424         int i;
425
426         for (i = 1; ; i++) {
427             if (str[i] == '\0') {
428                 /* hit end of string */
429                 size++;
430                 i++;
431                 if (str[i] == '\0') {
432                     /* hit end of multistring */
433                     break;
434                 }
435             }
436         }
437     }
438     *list = str;
439     return size;
440 }
441
442
443 /* GetNextInterface
444  *
445  * Parse interface list.  In first call to GetNextInterface(), iflist is
446  * the list returned by GetInterfaceList(); in successive calls, iflist is
447  * the pointer returned by the previous call to GetNextInterface().
448  *
449  * Returns pointer to next adapter name, or NULL if done.
450  */
451
452 static char *GetNextInterface(char *iflist)
453 {
454     char *ifname;
455
456     /* interface substrings are assumed to be of form \Device\<adapter name>
457      * \Tcpip\Parameters\Interfaces\<adapter name>
458      */
459     ifname = strrchr(iflist, '\\');
460
461     if (!ifname) {
462         /* subsequent (not first) call; advance to next interface substring */
463         iflist += strlen(iflist) + 1;
464         /* iflist now points to next interface or end-of-multistring char */
465         ifname = strrchr(iflist, '\\');
466     }
467
468     if (ifname) {
469         /* advance to first character of adapter name */
470         ifname++;
471     }
472
473     return ifname;
474 }
475
476
477 /* GetIP
478  *
479  * Get IP address associated with interface (adapter name).
480  * Returns 0 on success and -1 on error.
481  */
482
483 static int GetIP(HKEY skey, char *ifname, int *addr, int *mask)
484 {
485     HKEY key;
486     char *s;
487     long status;
488     int len;
489     char *ipStr = NULL;
490     char *snMask = NULL;
491     DWORD valType;
492     DWORD dwDHCP;
493     DWORD dwLease;
494     DWORD dwSize;
495
496     len = (int) strlen(ifname) + 1 + sizeof(AFSREG_IPSRV_ADAPTER_PARAM_SUBKEY);
497     s = malloc(len);
498     if (!s)
499         return -1;
500
501     sprintf(s, "%s\\%s", ifname, AFSREG_IPSRV_ADAPTER_PARAM_SUBKEY);
502
503     status = RegOpenKeyAlt(skey, s, KEY_READ, 0, &key, NULL);
504     free(s);
505
506     if (status)
507         return -1;
508
509     dwSize = sizeof(DWORD);
510     status = RegQueryValueEx(key, "EnableDHCP", NULL,
511                              &valType, (LPBYTE) &dwDHCP, &dwSize);
512     if (status || (valType != REG_DWORD))
513         dwDHCP = 0;
514
515     if (dwDHCP == 0) {
516         status = RegQueryValueAlt(key, AFSREG_IPSRV_ADAPTER_PARAM_ADDR_VALUE,
517                                   &valType, &ipStr, NULL);
518         if (status || (valType != REG_SZ && valType != REG_MULTI_SZ)) {
519             if (ipStr) free(ipStr);
520             (void) RegCloseKey(key);
521             return -1;
522         }
523
524         status = RegQueryValueAlt(key, AFSREG_IPSRV_ADAPTER_PARAM_MASK_VALUE,
525                                   &valType, &snMask, NULL);
526         if (status || (valType != REG_SZ && valType != REG_MULTI_SZ)) {
527             if (snMask) free(snMask);
528             snMask = NULL;
529         }
530     } else {
531         /* adapter configured via DHCP; address/mask in alternate values */
532         dwSize = sizeof(DWORD);
533         status = RegQueryValueEx(key, "Lease", NULL,
534                                  &valType, (LPBYTE)&dwLease, &dwSize);
535         if (status || (valType != REG_DWORD) || dwLease == 0) {
536             (void) RegCloseKey(key);
537             return -1;
538         }
539
540         status = RegQueryValueAlt(key,
541                                   AFSREG_IPSRV_ADAPTER_PARAM_DHCPADDR_VALUE,
542                                   &valType, &ipStr, NULL);
543
544         if (status || (valType != REG_SZ && valType != REG_MULTI_SZ)) {
545             if (ipStr) free(ipStr);
546             (void) RegCloseKey(key);
547             return -1;
548         }
549
550         status = RegQueryValueAlt(key,
551                                   AFSREG_IPSRV_ADAPTER_PARAM_DHCPMASK_VALUE,
552                                   &valType, &snMask, NULL);
553
554         if (status || (valType != REG_SZ && valType != REG_MULTI_SZ)) {
555             if (snMask) free(snMask);
556             snMask = NULL;
557         }
558     }
559
560     /* convert ip and subnet. */
561     *addr = (int)inet_addr(ipStr);
562     *addr = ntohl(*addr);
563     free(ipStr);
564
565     if (snMask) {
566         *mask = (int)inet_addr(snMask);
567         *mask = ntohl(*mask);
568         free(snMask);
569     } else {
570         *mask = 0;
571     }
572
573     (void) RegCloseKey(key);
574
575     return 0;
576 }
577