1 /*
2  * Copyright 2008 Juan Lang
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 #include <stdio.h>
19 #include <stdarg.h>
20 #include <windef.h>
21 #include <winbase.h>
22 #include <snmp.h>
23 
24 #include "wine/test.h"
25 
26 static BOOL (WINAPI *pSnmpExtensionInit)(DWORD, HANDLE*, AsnObjectIdentifier*);
27 static BOOL (WINAPI *pSnmpExtensionQuery)(BYTE, SnmpVarBindList*, AsnInteger32*, AsnInteger32*);
28 
29 static HMODULE init_test_functions(void)
30 {
31     HMODULE mod = LoadLibraryA("inetmib1");
32 
33     ok(mod != NULL, "failed to load inetmib1.dll\n");
34 
35     if (!mod) return NULL;
36 
37     pSnmpExtensionInit = (void *)GetProcAddress(mod, "SnmpExtensionInit");
38     pSnmpExtensionQuery = (void *)GetProcAddress(mod, "SnmpExtensionQuery");
39 
40     return mod;
41 }
42 
43 static void uninit_test_functions(HMODULE mod)
44 {
45     FreeLibrary(mod);
46 }
47 
48 static void testInit(void)
49 {
50     BOOL ret;
51     HANDLE event;
52     AsnObjectIdentifier oid;
53 
54     if (!pSnmpExtensionInit)
55     {
56         win_skip("no SnmpExtensionInit\n");
57         return;
58     }
59 
60 if (0) /* crashes on native */
61 {
62     ret = pSnmpExtensionInit(0, NULL, NULL);
63     ret = pSnmpExtensionInit(0, NULL, &oid);
64     ret = pSnmpExtensionInit(0, &event, NULL);
65 }
66 
67     ret = pSnmpExtensionInit(0, &event, &oid);
68     ok(ret, "SnmpExtensionInit failed: %d\n", GetLastError());
69     ok(!strcmp("1.3.6.1.2.1.1", SnmpUtilOidToA(&oid)),
70         "Expected 1.3.6.1.2.1.1, got %s\n", SnmpUtilOidToA(&oid));
71 
72 
73 if (0)
74 {
75     /* Fails when called on win8, documentation suggests that
76        extension itself is responsible for freeing this oid */
77     SnmpUtilOidFree(&oid);
78 }
79 }
80 
81 static void testQuery(void)
82 {
83     BOOL ret, moreData, noChange;
84     SnmpVarBindList list;
85     AsnInteger32 error, index;
86     UINT bogus[] = { 1,2,3,4 };
87     UINT mib2System[] = { 1,3,6,1,2,1,1 };
88     UINT mib2If[] = { 1,3,6,1,2,1,2 };
89     UINT mib2IfTable[] = { 1,3,6,1,2,1,2,2 };
90     UINT mib2IfDescr[] = { 1,3,6,1,2,1,2,2,1,2 };
91     UINT mib2IfAdminStatus[] = { 1,3,6,1,2,1,2,2,1,7 };
92     UINT mib2IfOperStatus[] = { 1,3,6,1,2,1,2,2,1,8 };
93     UINT mib2IpAddr[] = { 1,3,6,1,2,1,4,20,1,1 };
94     UINT mib2IpRouteTable[] = { 1,3,6,1,2,1,4,21,1,1 };
95     UINT mib2UdpTable[] = { 1,3,6,1,2,1,7,5,1,1 };
96     SnmpVarBind vars[3], vars2[3], vars3[3];
97     UINT entry;
98 
99     if (!pSnmpExtensionQuery)
100     {
101         win_skip("couldn't find SnmpExtensionQuery\n");
102         return;
103     }
104 
105 if (0) /* crashes on native */
106 {
107     ret = pSnmpExtensionQuery(0, NULL, NULL, NULL);
108     ret = pSnmpExtensionQuery(0, NULL, &error, NULL);
109     ret = pSnmpExtensionQuery(0, NULL, NULL, &index);
110     ret = pSnmpExtensionQuery(0, &list, NULL, NULL);
111     ret = pSnmpExtensionQuery(0, &list, &error, NULL);
112 }
113     /* An empty list succeeds */
114     list.len = 0;
115     error = 0xdeadbeef;
116     index = 0xdeadbeef;
117     ret = pSnmpExtensionQuery(SNMP_PDU_GET, &list, &error, &index);
118     ok(ret, "SnmpExtensionQuery failed: %d, %d\n", error, index);
119     ok(error == SNMP_ERRORSTATUS_NOERROR,
120         "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error);
121     ok(index == 0, "expected index 0, got %d\n", index);
122 
123     /* Oddly enough, this "succeeds," even though the OID is clearly
124      * unsupported.
125      */
126     vars[0].name.idLength = ARRAY_SIZE(bogus);
127     vars[0].name.ids = bogus;
128     vars[0].value.asnType = 0;
129     list.len = 1;
130     list.list = vars;
131     SetLastError(0xdeadbeef);
132     error = 0xdeadbeef;
133     index = 0xdeadbeef;
134     ret = pSnmpExtensionQuery(SNMP_PDU_GET, &list, &error, &index);
135     ok(ret, "SnmpExtensionQuery failed: %d, %d\n", error, index);
136     ok(error == SNMP_ERRORSTATUS_NOERROR ||
137         broken(error == ERROR_FILE_NOT_FOUND) /* NT4 */,
138         "expected SNMP_ERRORSTATUS_NOERROR or ERROR_FILE_NOT_FOUND, got %d\n",
139         error);
140     if (error == SNMP_ERRORSTATUS_NOERROR)
141         ok(index == 0, "expected index 0, got %d\n", index);
142     else if (error == ERROR_FILE_NOT_FOUND)
143         ok(index == 1, "expected index 1, got %d\n", index);
144     /* The OID isn't changed either: */
145     ok(!strcmp("1.2.3.4", SnmpUtilOidToA(&vars[0].name)),
146         "expected 1.2.3.4, got %s\n", SnmpUtilOidToA(&vars[0].name));
147 
148     /* The table is not an accessible variable, so it fails */
149     vars[0].name.idLength = ARRAY_SIZE(mib2IfTable);
150     vars[0].name.ids = mib2IfTable;
151     SetLastError(0xdeadbeef);
152     error = 0xdeadbeef;
153     index = 0xdeadbeef;
154     ret = pSnmpExtensionQuery(SNMP_PDU_GET, &list, &error, &index);
155     ok(ret, "SnmpExtensionQuery failed: %d, %d\n", error, index);
156     ok(error == SNMP_ERRORSTATUS_NOSUCHNAME,
157         "expected SNMP_ERRORSTATUS_NOSUCHNAME, got %d\n", error);
158     /* The index is 1-based rather than 0-based */
159     ok(index == 1, "expected index 1, got %d\n", index);
160 
161     /* A Get fails on something that specifies a table (but not a particular
162      * entry in it)...
163      */
164     vars[0].name.idLength = ARRAY_SIZE(mib2IfDescr);
165     vars[0].name.ids = mib2IfDescr;
166     vars[1].name.idLength = ARRAY_SIZE(mib2IfAdminStatus);
167     vars[1].name.ids = mib2IfAdminStatus;
168     vars[2].name.idLength = ARRAY_SIZE(mib2IfOperStatus);
169     vars[2].name.ids = mib2IfOperStatus;
170     list.len = 3;
171     SetLastError(0xdeadbeef);
172     error = 0xdeadbeef;
173     index = 0xdeadbeef;
174     ret = pSnmpExtensionQuery(SNMP_PDU_GET, &list, &error, &index);
175     ok(ret, "SnmpExtensionQuery failed: %d, %d\n", error, index);
176     ok(error == SNMP_ERRORSTATUS_NOSUCHNAME,
177         "expected SNMP_ERRORSTATUS_NOSUCHNAME, got %d\n", error);
178     ok(index == 1, "expected index 1, got %d\n", index);
179     /* but a GetNext succeeds with the same values, because GetNext gets the
180      * entry after the specified OID, not the entry specified by it.  The
181      * successor to the table is the first entry in the table.
182      * The OIDs need to be allocated, because GetNext modifies them to indicate
183      * the end of data.
184      */
185     SnmpUtilOidCpy(&vars2[0].name, &vars[0].name);
186     SnmpUtilOidCpy(&vars2[1].name, &vars[1].name);
187     SnmpUtilOidCpy(&vars2[2].name, &vars[2].name);
188     list.list = vars2;
189     moreData = TRUE;
190     noChange = FALSE;
191     entry = 0;
192     do {
193         SetLastError(0xdeadbeef);
194         error = 0xdeadbeef;
195         index = 0xdeadbeef;
196         ret = pSnmpExtensionQuery(SNMP_PDU_GETNEXT, &list, &error, &index);
197         ok(ret, "SnmpExtensionQuery failed: %d, %d\n", error, index);
198         ok(error == SNMP_ERRORSTATUS_NOERROR,
199             "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error);
200         ok(index == 0, "expected index 0, got %d\n", index);
201         if (!ret)
202             moreData = FALSE;
203         else if (error)
204             moreData = FALSE;
205         else if (SnmpUtilOidNCmp(&vars2[0].name, &vars[0].name,
206             vars[0].name.idLength))
207             moreData = FALSE;
208         else if (SnmpUtilOidNCmp(&vars2[1].name, &vars[1].name,
209             vars[1].name.idLength))
210             moreData = FALSE;
211         else if (SnmpUtilOidNCmp(&vars2[2].name, &vars[2].name,
212             vars[2].name.idLength))
213             moreData = FALSE;
214         else if (!SnmpUtilOidCmp(&vars[0].name, &vars2[0].name) ||
215          !SnmpUtilOidCmp(&vars[1].name, &vars2[1].name) ||
216          !SnmpUtilOidCmp(&vars[2].name, &vars2[2].name))
217         {
218             /* If the OID isn't modified, the function isn't implemented on this
219              * platform, skip the remaining tests.
220              */
221             noChange = TRUE;
222         }
223         if (moreData)
224         {
225             UINT lastID;
226 
227             /* Check the OIDs.  For these types of values (display strings and
228              * integers) they should increase by 1 for each element of the table
229              * according to RFC 1158.  Windows sometimes has a weird value in the
230              * table, so allow any value as long as it's greater than the previous
231              * value on Windows.
232              */
233             ok(vars2[0].name.idLength == vars[0].name.idLength + 1,
234                 "expected length %d, got %d\n", vars[0].name.idLength + 1,
235                 vars2[0].name.idLength);
236             lastID = vars2[0].name.ids[vars2[0].name.idLength - 1];
237             ok(lastID == entry + 1 || broken(lastID > entry),
238                 "expected %d, got %d\n", entry + 1, lastID);
239             ok(vars2[1].name.idLength == vars[1].name.idLength + 1,
240                 "expected length %d, got %d\n", vars[1].name.idLength + 1,
241                 vars2[1].name.idLength);
242             lastID = vars2[1].name.ids[vars2[1].name.idLength - 1];
243             ok(lastID == entry + 1 || broken(lastID > entry),
244                 "expected %d, got %d\n", entry + 1, lastID);
245             ok(vars2[2].name.idLength == vars[2].name.idLength + 1,
246                 "expected length %d, got %d\n", vars[2].name.idLength + 1,
247                 vars2[2].name.idLength);
248             lastID = vars2[2].name.ids[vars2[2].name.idLength - 1];
249             ok(lastID == entry + 1 || broken(lastID > entry),
250                 "expected %d, got %d\n", entry + 1, lastID);
251             entry = lastID;
252             /* Check the types while we're at it */
253             ok(vars2[0].value.asnType == ASN_OCTETSTRING,
254                 "expected ASN_OCTETSTRING, got %02x\n", vars2[0].value.asnType);
255             ok(vars2[1].value.asnType == ASN_INTEGER,
256                 "expected ASN_INTEGER, got %02x\n", vars2[1].value.asnType);
257             ok(vars2[2].value.asnType == ASN_INTEGER,
258                 "expected ASN_INTEGER, got %02x\n", vars2[2].value.asnType);
259         }
260         else if (noChange)
261             skip("no change in OID, no MIB2 IF table implementation\n");
262     } while (moreData && !noChange);
263     SnmpUtilVarBindFree(&vars2[0]);
264     SnmpUtilVarBindFree(&vars2[1]);
265     SnmpUtilVarBindFree(&vars2[2]);
266 
267     /* Even though SnmpExtensionInit says this DLL supports the MIB2 system
268      * variables, on recent systems (at least Win2k) the first variable it
269      * returns a value for is the first interface.
270      */
271     vars[0].name.idLength = ARRAY_SIZE(mib2System);
272     vars[0].name.ids = mib2System;
273     SnmpUtilOidCpy(&vars2[0].name, &vars[0].name);
274     vars2[0].value.asnType = 0;
275     list.len = 1;
276     list.list = vars2;
277     noChange = FALSE;
278     ret = pSnmpExtensionQuery(SNMP_PDU_GETNEXT, &list, &error, &index);
279     ok(ret, "SnmpExtensionQuery failed: %d, %d\n", error, index);
280     ok(error == SNMP_ERRORSTATUS_NOERROR,
281         "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error);
282     ok(index == 0, "expected index 0, got %d\n", index);
283     vars3[0].name.idLength = ARRAY_SIZE(mib2If);
284     vars3[0].name.ids = mib2If;
285     ok(!SnmpUtilOidNCmp(&vars2[0].name, &vars[0].name, vars[0].name.idLength) ||
286        !SnmpUtilOidNCmp(&vars2[0].name, &vars3[0].name, vars3[0].name.idLength),
287         "expected 1.3.6.1.2.1.1 or 1.3.6.1.2.1.2, got %s\n",
288         SnmpUtilOidToA(&vars2[0].name));
289     SnmpUtilVarBindFree(&vars2[0]);
290 
291     /* Check the type and OIDs of the IP address table */
292     vars[0].name.idLength = ARRAY_SIZE(mib2IpAddr);
293     vars[0].name.ids = mib2IpAddr;
294     SnmpUtilOidCpy(&vars2[0].name, &vars[0].name);
295     vars2[0].value.asnType = 0;
296     list.len = 1;
297     list.list = vars2;
298     moreData = TRUE;
299     do {
300         ret = pSnmpExtensionQuery(SNMP_PDU_GETNEXT, &list, &error, &index);
301         ok(ret, "SnmpExtensionQuery failed: %d, %d\n", error, index);
302         ok(error == SNMP_ERRORSTATUS_NOERROR,
303             "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error);
304         ok(index == 0, "expected index 0, got %d\n", index);
305         if (!ret)
306             moreData = FALSE;
307         else if (error)
308             moreData = FALSE;
309         else if (SnmpUtilOidNCmp(&vars2[0].name, &vars[0].name,
310             vars[0].name.idLength))
311             moreData = FALSE;
312         else if (!SnmpUtilOidCmp(&vars2[0].name, &vars[0].name))
313         {
314             /* If the OID isn't modified, the function isn't implemented on this
315              * platform, skip the remaining tests.
316              */
317             noChange = TRUE;
318         }
319         if (moreData)
320         {
321             /* Make sure the size of the OID is right.
322              * FIXME: don't know if IPv6 addrs are shared with this table.
323              * Don't think so, but I'm not certain.
324              */
325             ok(vars2[0].name.idLength == vars[0].name.idLength + 4,
326                 "expected length %d, got %d\n", vars[0].name.idLength + 4,
327                 vars2[0].name.idLength);
328             /* Make sure the type is right */
329             ok(vars2[0].value.asnType == ASN_IPADDRESS,
330                 "expected type ASN_IPADDRESS, got %02x\n",
331                 vars2[0].value.asnType);
332             if (vars2[0].value.asnType == ASN_IPADDRESS)
333             {
334                 UINT i;
335 
336                 /* This looks uglier than it is:  the base OID for the IP
337                  * address, 1.3.6.1.2.1.4.20.1.1, is appended with the IP
338                  * address of the entry.  So e.g. the loopback address is
339                  * identified in MIB2 as 1.3.6.1.2.1.4.20.1.1.127.0.0.1
340                  */
341                 for (i = 0; i < vars2[0].value.asnValue.address.length; i++)
342                 {
343                     ok(vars2[0].value.asnValue.address.stream[i] ==
344                         vars2[0].name.ids[vars2[0].name.idLength - 4 + i],
345                         "expected ident byte %d to be %d, got %d\n", i,
346                         vars2[0].value.asnValue.address.stream[i],
347                         vars2[0].name.ids[vars2[0].name.idLength - 4 + i]);
348                 }
349             }
350         }
351         else if (noChange)
352             skip("no change in OID, no MIB2 IP address table implementation\n");
353     } while (moreData && !noChange);
354     SnmpUtilVarBindFree(&vars2[0]);
355 
356     /* Check the type and OIDs of the IP route table */
357     vars[0].name.idLength = DEFINE_SIZEOF(mib2IpRouteTable);
358     vars[0].name.ids = mib2IpRouteTable;
359     SnmpUtilOidCpy(&vars2[0].name, &vars[0].name);
360     vars2[0].value.asnType = 0;
361     list.len = 1;
362     list.list = vars2;
363     moreData = TRUE;
364     noChange = FALSE;
365     do {
366         ret = pSnmpExtensionQuery(SNMP_PDU_GETNEXT, &list, &error, &index);
367         ok(ret, "SnmpExtensionQuery failed: %d, %d\n", error, index);
368         ok(error == SNMP_ERRORSTATUS_NOERROR,
369             "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error);
370         ok(index == 0, "expected index 0, got %d\n", index);
371         if (!ret)
372             moreData = FALSE;
373         else if (error)
374             moreData = FALSE;
375         else if (SnmpUtilOidNCmp(&vars2[0].name, &vars[0].name,
376             vars[0].name.idLength))
377             moreData = FALSE;
378         else if (!SnmpUtilOidCmp(&vars2[0].name, &vars[0].name))
379         {
380             /* If the OID isn't modified, the function isn't implemented on this
381              * platform, skip the remaining tests.
382              */
383             noChange = TRUE;
384         }
385         if (moreData)
386         {
387             /* Make sure the size of the OID is right.
388              * FIXME: don't know if IPv6 addrs are shared with this table.
389              * Don't think so, but I'm not certain.
390              */
391             ok(vars2[0].name.idLength == vars[0].name.idLength + 4,
392                 "expected length %d, got %d\n", vars[0].name.idLength + 4,
393                 vars2[0].name.idLength);
394             /* Make sure the type is right */
395             ok(vars2[0].value.asnType == ASN_IPADDRESS,
396                 "expected type ASN_IPADDRESS, got %02x\n",
397                 vars2[0].value.asnType);
398             if (vars2[0].value.asnType == ASN_IPADDRESS)
399             {
400                 UINT i;
401 
402                 /* The base OID for the route table, 1.3.6.1.2.1.4.21.1.1, is
403                  * appended with the dest IP address of the entry.  So e.g. a
404                  * route entry for 224.0.0.0 is identified in MIB2 as
405                  * 1.3.6.1.2.1.4.21.1.1.224.0.0.0
406                  */
407                 for (i = 0; i < vars2[0].value.asnValue.address.length; i++)
408                 {
409                     ok(vars2[0].value.asnValue.address.stream[i] ==
410                         vars2[0].name.ids[vars2[0].name.idLength - 4 + i],
411                         "expected ident byte %d to be %d, got %d\n", i,
412                         vars2[0].value.asnValue.address.stream[i],
413                         vars2[0].name.ids[vars2[0].name.idLength - 4 + i]);
414                 }
415             }
416         }
417         else if (noChange)
418             skip("no change in OID, no MIB2 IP route table implementation\n");
419     } while (moreData && !noChange);
420     SnmpUtilVarBindFree(&vars2[0]);
421 
422     /* Check the type and OIDs of the UDP table */
423     vars[0].name.idLength = DEFINE_SIZEOF(mib2UdpTable);
424     vars[0].name.ids = mib2UdpTable;
425     SnmpUtilOidCpy(&vars2[0].name, &vars[0].name);
426     vars2[0].value.asnType = 0;
427     list.len = 1;
428     list.list = vars2;
429     moreData = TRUE;
430     noChange = FALSE;
431     do {
432         ret = pSnmpExtensionQuery(SNMP_PDU_GETNEXT, &list, &error, &index);
433         ok(ret, "SnmpExtensionQuery failed: %d, %d\n", error, index);
434         /* FIXME:  error and index aren't checked here because the UDP table is
435          * the last OID currently supported by Wine, so the last GetNext fails.
436          * todo_wine is also not effective because it will succeed for all but
437          * the last GetNext.  Remove the if (0) if any later OID is supported
438          * by Wine.
439          */
440         if (0) {
441         ok(error == SNMP_ERRORSTATUS_NOERROR,
442             "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error);
443         ok(index == 0, "expected index 0, got %d\n", index);
444         }
445         if (!ret)
446             moreData = FALSE;
447         else if (error)
448             moreData = FALSE;
449         else if (SnmpUtilOidNCmp(&vars2[0].name, &vars[0].name,
450             vars[0].name.idLength))
451             moreData = FALSE;
452         else if (!SnmpUtilOidCmp(&vars2[0].name, &vars[0].name))
453         {
454             /* If the OID isn't modified, the function isn't implemented on this
455              * platform, skip the remaining tests.
456              */
457             noChange = TRUE;
458         }
459         if (moreData)
460         {
461             /* Make sure the size of the OID is right. */
462             ok(vars2[0].name.idLength == vars[0].name.idLength + 5,
463                 "expected length %d, got %d\n", vars[0].name.idLength + 5,
464                 vars2[0].name.idLength);
465             /* Make sure the type is right */
466             ok(vars2[0].value.asnType == ASN_IPADDRESS,
467                 "expected type ASN_IPADDRESS, got %02x\n",
468                 vars2[0].value.asnType);
469             if (vars2[0].value.asnType == ASN_IPADDRESS)
470             {
471                 UINT i;
472 
473                 /* Again with the ugly:  the base OID for the UDP table,
474                  * 1.3.6.1.2.1.7.5.1, is appended with the local IP address and
475                  * port number of the entry.  So e.g. an entry for
476                  * 192.168.1.1:4000 is identified in MIB2 as
477                  * 1.3.6.1.2.1.7.5.1.192.168.1.1.4000
478                  */
479                 for (i = 0; i < vars2[0].value.asnValue.address.length; i++)
480                 {
481                     ok(vars2[0].value.asnValue.address.stream[i] ==
482                         vars2[0].name.ids[vars2[0].name.idLength - 5 + i],
483                         "expected ident byte %d to be %d, got %d\n", i,
484                         vars2[0].value.asnValue.address.stream[i],
485                         vars2[0].name.ids[vars2[0].name.idLength - 5 + i]);
486                 }
487             }
488         }
489         else if (noChange)
490             skip("no change in OID, no MIB2 UDP table implementation\n");
491     } while (moreData && !noChange);
492     SnmpUtilVarBindFree(&vars2[0]);
493 }
494 
495 START_TEST(main)
496 {
497     HMODULE mod;
498 
499     if (!(mod = init_test_functions()))
500         return;
501 
502     testInit();
503     testQuery();
504 
505     uninit_test_functions(mod);
506 }
507