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