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 = sizeof(bogus) / sizeof(bogus[0]); 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 = sizeof(mib2IfTable) / sizeof(mib2IfTable[0]); 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 = sizeof(mib2IfDescr) / sizeof(mib2IfDescr[0]); 165 vars[0].name.ids = mib2IfDescr; 166 vars[1].name.idLength = 167 sizeof(mib2IfAdminStatus) / sizeof(mib2IfAdminStatus[0]); 168 vars[1].name.ids = mib2IfAdminStatus; 169 vars[2].name.idLength = 170 sizeof(mib2IfOperStatus) / sizeof(mib2IfOperStatus[0]); 171 vars[2].name.ids = mib2IfOperStatus; 172 list.len = 3; 173 SetLastError(0xdeadbeef); 174 error = 0xdeadbeef; 175 index = 0xdeadbeef; 176 ret = pSnmpExtensionQuery(SNMP_PDU_GET, &list, &error, &index); 177 ok(ret, "SnmpExtensionQuery failed: %d, %d\n", error, index); 178 ok(error == SNMP_ERRORSTATUS_NOSUCHNAME, 179 "expected SNMP_ERRORSTATUS_NOSUCHNAME, got %d\n", error); 180 ok(index == 1, "expected index 1, got %d\n", index); 181 /* but a GetNext succeeds with the same values, because GetNext gets the 182 * entry after the specified OID, not the entry specified by it. The 183 * successor to the table is the first entry in the table. 184 * The OIDs need to be allocated, because GetNext modifies them to indicate 185 * the end of data. 186 */ 187 SnmpUtilOidCpy(&vars2[0].name, &vars[0].name); 188 SnmpUtilOidCpy(&vars2[1].name, &vars[1].name); 189 SnmpUtilOidCpy(&vars2[2].name, &vars[2].name); 190 list.list = vars2; 191 moreData = TRUE; 192 noChange = FALSE; 193 entry = 0; 194 do { 195 SetLastError(0xdeadbeef); 196 error = 0xdeadbeef; 197 index = 0xdeadbeef; 198 ret = pSnmpExtensionQuery(SNMP_PDU_GETNEXT, &list, &error, &index); 199 ok(ret, "SnmpExtensionQuery failed: %d, %d\n", error, index); 200 ok(error == SNMP_ERRORSTATUS_NOERROR, 201 "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error); 202 ok(index == 0, "expected index 0, got %d\n", index); 203 if (!ret) 204 moreData = FALSE; 205 else if (error) 206 moreData = FALSE; 207 else if (SnmpUtilOidNCmp(&vars2[0].name, &vars[0].name, 208 vars[0].name.idLength)) 209 moreData = FALSE; 210 else if (SnmpUtilOidNCmp(&vars2[1].name, &vars[1].name, 211 vars[1].name.idLength)) 212 moreData = FALSE; 213 else if (SnmpUtilOidNCmp(&vars2[2].name, &vars[2].name, 214 vars[2].name.idLength)) 215 moreData = FALSE; 216 else if (!SnmpUtilOidCmp(&vars[0].name, &vars2[0].name) || 217 !SnmpUtilOidCmp(&vars[1].name, &vars2[1].name) || 218 !SnmpUtilOidCmp(&vars[2].name, &vars2[2].name)) 219 { 220 /* If the OID isn't modified, the function isn't implemented on this 221 * platform, skip the remaining tests. 222 */ 223 noChange = TRUE; 224 } 225 if (moreData) 226 { 227 UINT lastID; 228 229 /* Check the OIDs. For these types of values (display strings and 230 * integers) they should increase by 1 for each element of the table 231 * according to RFC 1158. Windows sometimes has a weird value in the 232 * table, so allow any value as long as it's greater than the previous 233 * value on Windows. 234 */ 235 ok(vars2[0].name.idLength == vars[0].name.idLength + 1, 236 "expected length %d, got %d\n", vars[0].name.idLength + 1, 237 vars2[0].name.idLength); 238 lastID = vars2[0].name.ids[vars2[0].name.idLength - 1]; 239 ok(lastID == entry + 1 || broken(lastID > entry), 240 "expected %d, got %d\n", entry + 1, lastID); 241 ok(vars2[1].name.idLength == vars[1].name.idLength + 1, 242 "expected length %d, got %d\n", vars[1].name.idLength + 1, 243 vars2[1].name.idLength); 244 lastID = vars2[1].name.ids[vars2[1].name.idLength - 1]; 245 ok(lastID == entry + 1 || broken(lastID > entry), 246 "expected %d, got %d\n", entry + 1, lastID); 247 ok(vars2[2].name.idLength == vars[2].name.idLength + 1, 248 "expected length %d, got %d\n", vars[2].name.idLength + 1, 249 vars2[2].name.idLength); 250 lastID = vars2[2].name.ids[vars2[2].name.idLength - 1]; 251 ok(lastID == entry + 1 || broken(lastID > entry), 252 "expected %d, got %d\n", entry + 1, lastID); 253 entry = lastID; 254 /* Check the types while we're at it */ 255 ok(vars2[0].value.asnType == ASN_OCTETSTRING, 256 "expected ASN_OCTETSTRING, got %02x\n", vars2[0].value.asnType); 257 ok(vars2[1].value.asnType == ASN_INTEGER, 258 "expected ASN_INTEGER, got %02x\n", vars2[1].value.asnType); 259 ok(vars2[2].value.asnType == ASN_INTEGER, 260 "expected ASN_INTEGER, got %02x\n", vars2[2].value.asnType); 261 } 262 else if (noChange) 263 skip("no change in OID, no MIB2 IF table implementation\n"); 264 } while (moreData && !noChange); 265 SnmpUtilVarBindFree(&vars2[0]); 266 SnmpUtilVarBindFree(&vars2[1]); 267 SnmpUtilVarBindFree(&vars2[2]); 268 269 /* Even though SnmpExtensionInit says this DLL supports the MIB2 system 270 * variables, on recent systems (at least Win2k) the first variable it 271 * returns a value for is the first interface. 272 */ 273 vars[0].name.idLength = sizeof(mib2System) / sizeof(mib2System[0]); 274 vars[0].name.ids = mib2System; 275 SnmpUtilOidCpy(&vars2[0].name, &vars[0].name); 276 vars2[0].value.asnType = 0; 277 list.len = 1; 278 list.list = vars2; 279 noChange = FALSE; 280 ret = pSnmpExtensionQuery(SNMP_PDU_GETNEXT, &list, &error, &index); 281 ok(ret, "SnmpExtensionQuery failed: %d, %d\n", error, index); 282 ok(error == SNMP_ERRORSTATUS_NOERROR, 283 "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error); 284 ok(index == 0, "expected index 0, got %d\n", index); 285 vars3[0].name.idLength = sizeof(mib2If) / sizeof(mib2If[0]); 286 vars3[0].name.ids = mib2If; 287 ok(!SnmpUtilOidNCmp(&vars2[0].name, &vars[0].name, vars[0].name.idLength) || 288 !SnmpUtilOidNCmp(&vars2[0].name, &vars3[0].name, vars3[0].name.idLength), 289 "expected 1.3.6.1.2.1.1 or 1.3.6.1.2.1.2, got %s\n", 290 SnmpUtilOidToA(&vars2[0].name)); 291 SnmpUtilVarBindFree(&vars2[0]); 292 293 /* Check the type and OIDs of the IP address table */ 294 vars[0].name.idLength = sizeof(mib2IpAddr) / sizeof(mib2IpAddr[0]); 295 vars[0].name.ids = mib2IpAddr; 296 SnmpUtilOidCpy(&vars2[0].name, &vars[0].name); 297 vars2[0].value.asnType = 0; 298 list.len = 1; 299 list.list = vars2; 300 moreData = TRUE; 301 do { 302 ret = pSnmpExtensionQuery(SNMP_PDU_GETNEXT, &list, &error, &index); 303 ok(ret, "SnmpExtensionQuery failed: %d, %d\n", error, index); 304 ok(error == SNMP_ERRORSTATUS_NOERROR, 305 "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error); 306 ok(index == 0, "expected index 0, got %d\n", index); 307 if (!ret) 308 moreData = FALSE; 309 else if (error) 310 moreData = FALSE; 311 else if (SnmpUtilOidNCmp(&vars2[0].name, &vars[0].name, 312 vars[0].name.idLength)) 313 moreData = FALSE; 314 else if (!SnmpUtilOidCmp(&vars2[0].name, &vars[0].name)) 315 { 316 /* If the OID isn't modified, the function isn't implemented on this 317 * platform, skip the remaining tests. 318 */ 319 noChange = TRUE; 320 } 321 if (moreData) 322 { 323 /* Make sure the size of the OID is right. 324 * FIXME: don't know if IPv6 addrs are shared with this table. 325 * Don't think so, but I'm not certain. 326 */ 327 ok(vars2[0].name.idLength == vars[0].name.idLength + 4, 328 "expected length %d, got %d\n", vars[0].name.idLength + 4, 329 vars2[0].name.idLength); 330 /* Make sure the type is right */ 331 ok(vars2[0].value.asnType == ASN_IPADDRESS, 332 "expected type ASN_IPADDRESS, got %02x\n", 333 vars2[0].value.asnType); 334 if (vars2[0].value.asnType == ASN_IPADDRESS) 335 { 336 UINT i; 337 338 /* This looks uglier than it is: the base OID for the IP 339 * address, 1.3.6.1.2.1.4.20.1.1, is appended with the IP 340 * address of the entry. So e.g. the loopback address is 341 * identified in MIB2 as 1.3.6.1.2.1.4.20.1.1.127.0.0.1 342 */ 343 for (i = 0; i < vars2[0].value.asnValue.address.length; i++) 344 { 345 ok(vars2[0].value.asnValue.address.stream[i] == 346 vars2[0].name.ids[vars2[0].name.idLength - 4 + i], 347 "expected ident byte %d to be %d, got %d\n", i, 348 vars2[0].value.asnValue.address.stream[i], 349 vars2[0].name.ids[vars2[0].name.idLength - 4 + i]); 350 } 351 } 352 } 353 else if (noChange) 354 skip("no change in OID, no MIB2 IP address table implementation\n"); 355 } while (moreData && !noChange); 356 SnmpUtilVarBindFree(&vars2[0]); 357 358 /* Check the type and OIDs of the IP route table */ 359 vars[0].name.idLength = DEFINE_SIZEOF(mib2IpRouteTable); 360 vars[0].name.ids = mib2IpRouteTable; 361 SnmpUtilOidCpy(&vars2[0].name, &vars[0].name); 362 vars2[0].value.asnType = 0; 363 list.len = 1; 364 list.list = vars2; 365 moreData = TRUE; 366 noChange = FALSE; 367 do { 368 ret = pSnmpExtensionQuery(SNMP_PDU_GETNEXT, &list, &error, &index); 369 ok(ret, "SnmpExtensionQuery failed: %d, %d\n", error, index); 370 ok(error == SNMP_ERRORSTATUS_NOERROR, 371 "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error); 372 ok(index == 0, "expected index 0, got %d\n", index); 373 if (!ret) 374 moreData = FALSE; 375 else if (error) 376 moreData = FALSE; 377 else if (SnmpUtilOidNCmp(&vars2[0].name, &vars[0].name, 378 vars[0].name.idLength)) 379 moreData = FALSE; 380 else if (!SnmpUtilOidCmp(&vars2[0].name, &vars[0].name)) 381 { 382 /* If the OID isn't modified, the function isn't implemented on this 383 * platform, skip the remaining tests. 384 */ 385 noChange = TRUE; 386 } 387 if (moreData) 388 { 389 /* Make sure the size of the OID is right. 390 * FIXME: don't know if IPv6 addrs are shared with this table. 391 * Don't think so, but I'm not certain. 392 */ 393 ok(vars2[0].name.idLength == vars[0].name.idLength + 4, 394 "expected length %d, got %d\n", vars[0].name.idLength + 4, 395 vars2[0].name.idLength); 396 /* Make sure the type is right */ 397 ok(vars2[0].value.asnType == ASN_IPADDRESS, 398 "expected type ASN_IPADDRESS, got %02x\n", 399 vars2[0].value.asnType); 400 if (vars2[0].value.asnType == ASN_IPADDRESS) 401 { 402 UINT i; 403 404 /* The base OID for the route table, 1.3.6.1.2.1.4.21.1.1, is 405 * appended with the dest IP address of the entry. So e.g. a 406 * route entry for 224.0.0.0 is identified in MIB2 as 407 * 1.3.6.1.2.1.4.21.1.1.224.0.0.0 408 */ 409 for (i = 0; i < vars2[0].value.asnValue.address.length; i++) 410 { 411 ok(vars2[0].value.asnValue.address.stream[i] == 412 vars2[0].name.ids[vars2[0].name.idLength - 4 + i], 413 "expected ident byte %d to be %d, got %d\n", i, 414 vars2[0].value.asnValue.address.stream[i], 415 vars2[0].name.ids[vars2[0].name.idLength - 4 + i]); 416 } 417 } 418 } 419 else if (noChange) 420 skip("no change in OID, no MIB2 IP route table implementation\n"); 421 } while (moreData && !noChange); 422 SnmpUtilVarBindFree(&vars2[0]); 423 424 /* Check the type and OIDs of the UDP table */ 425 vars[0].name.idLength = DEFINE_SIZEOF(mib2UdpTable); 426 vars[0].name.ids = mib2UdpTable; 427 SnmpUtilOidCpy(&vars2[0].name, &vars[0].name); 428 vars2[0].value.asnType = 0; 429 list.len = 1; 430 list.list = vars2; 431 moreData = TRUE; 432 noChange = FALSE; 433 do { 434 ret = pSnmpExtensionQuery(SNMP_PDU_GETNEXT, &list, &error, &index); 435 ok(ret, "SnmpExtensionQuery failed: %d, %d\n", error, index); 436 /* FIXME: error and index aren't checked here because the UDP table is 437 * the last OID currently supported by Wine, so the last GetNext fails. 438 * todo_wine is also not effective because it will succeed for all but 439 * the last GetNext. Remove the if (0) if any later OID is supported 440 * by Wine. 441 */ 442 if (0) { 443 ok(error == SNMP_ERRORSTATUS_NOERROR, 444 "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error); 445 ok(index == 0, "expected index 0, got %d\n", index); 446 } 447 if (!ret) 448 moreData = FALSE; 449 else if (error) 450 moreData = FALSE; 451 else if (SnmpUtilOidNCmp(&vars2[0].name, &vars[0].name, 452 vars[0].name.idLength)) 453 moreData = FALSE; 454 else if (!SnmpUtilOidCmp(&vars2[0].name, &vars[0].name)) 455 { 456 /* If the OID isn't modified, the function isn't implemented on this 457 * platform, skip the remaining tests. 458 */ 459 noChange = TRUE; 460 } 461 if (moreData) 462 { 463 /* Make sure the size of the OID is right. */ 464 ok(vars2[0].name.idLength == vars[0].name.idLength + 5, 465 "expected length %d, got %d\n", vars[0].name.idLength + 5, 466 vars2[0].name.idLength); 467 /* Make sure the type is right */ 468 ok(vars2[0].value.asnType == ASN_IPADDRESS, 469 "expected type ASN_IPADDRESS, got %02x\n", 470 vars2[0].value.asnType); 471 if (vars2[0].value.asnType == ASN_IPADDRESS) 472 { 473 UINT i; 474 475 /* Again with the ugly: the base OID for the UDP table, 476 * 1.3.6.1.2.1.7.5.1, is appended with the local IP address and 477 * port number of the entry. So e.g. an entry for 478 * 192.168.1.1:4000 is identified in MIB2 as 479 * 1.3.6.1.2.1.7.5.1.192.168.1.1.4000 480 */ 481 for (i = 0; i < vars2[0].value.asnValue.address.length; i++) 482 { 483 ok(vars2[0].value.asnValue.address.stream[i] == 484 vars2[0].name.ids[vars2[0].name.idLength - 5 + i], 485 "expected ident byte %d to be %d, got %d\n", i, 486 vars2[0].value.asnValue.address.stream[i], 487 vars2[0].name.ids[vars2[0].name.idLength - 5 + i]); 488 } 489 } 490 } 491 else if (noChange) 492 skip("no change in OID, no MIB2 UDP table implementation\n"); 493 } while (moreData && !noChange); 494 SnmpUtilVarBindFree(&vars2[0]); 495 } 496 497 START_TEST(main) 498 { 499 HMODULE mod; 500 501 if (!(mod = init_test_functions())) 502 return; 503 504 testInit(); 505 testQuery(); 506 507 uninit_test_functions(mod); 508 } 509