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