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 19 #define WIN32_NO_STATUS 20 21 #include <config.h> 22 #include <assert.h> 23 #include <stdarg.h> 24 #include <stdlib.h> 25 //#include <limits.h> 26 27 #define NONAMELESSUNION 28 29 #include <windef.h> 30 #include <winbase.h> 31 #include <snmp.h> 32 #include <iphlpapi.h> 33 #include <wine/debug.h> 34 35 WINE_DEFAULT_DEBUG_CHANNEL(inetmib1); 36 37 /** 38 * Utility functions 39 */ 40 static DWORD copyInt(AsnAny *value, void *src) 41 { 42 value->asnType = ASN_INTEGER; 43 value->asnValue.number = *(DWORD *)src; 44 return SNMP_ERRORSTATUS_NOERROR; 45 } 46 47 static void setStringValue(AsnAny *value, BYTE type, DWORD len, BYTE *str) 48 { 49 AsnAny strValue; 50 51 strValue.asnType = type; 52 strValue.asnValue.string.stream = str; 53 strValue.asnValue.string.length = len; 54 strValue.asnValue.string.dynamic = FALSE; 55 SnmpUtilAsnAnyCpy(value, &strValue); 56 } 57 58 typedef DWORD (*copyValueFunc)(AsnAny *value, void *src); 59 60 struct structToAsnValue 61 { 62 size_t offset; 63 copyValueFunc copy; 64 }; 65 66 static AsnInteger32 mapStructEntryToValue(struct structToAsnValue *map, 67 UINT mapLen, void *record, UINT id, SnmpVarBind *pVarBind) 68 { 69 /* OIDs are 1-based */ 70 if (!id) 71 return SNMP_ERRORSTATUS_NOSUCHNAME; 72 --id; 73 if (id >= mapLen) 74 return SNMP_ERRORSTATUS_NOSUCHNAME; 75 if (!map[id].copy) 76 return SNMP_ERRORSTATUS_NOSUCHNAME; 77 return map[id].copy(&pVarBind->value, (BYTE *)record + map[id].offset); 78 } 79 80 static DWORD copyIpAddr(AsnAny *value, void *src) 81 { 82 setStringValue(value, ASN_IPADDRESS, sizeof(DWORD), src); 83 return SNMP_ERRORSTATUS_NOERROR; 84 } 85 86 static UINT mib2[] = { 1,3,6,1,2,1 }; 87 static UINT mib2System[] = { 1,3,6,1,2,1,1 }; 88 89 typedef BOOL (*varqueryfunc)(BYTE bPduType, SnmpVarBind *pVarBind, 90 AsnInteger32 *pErrorStatus); 91 92 struct mibImplementation 93 { 94 AsnObjectIdentifier name; 95 void (*init)(void); 96 varqueryfunc query; 97 void (*cleanup)(void); 98 }; 99 100 static UINT mib2IfNumber[] = { 1,3,6,1,2,1,2,1 }; 101 static PMIB_IFTABLE ifTable; 102 103 static void mib2IfNumberInit(void) 104 { 105 DWORD size = 0, ret = GetIfTable(NULL, &size, FALSE); 106 107 if (ret == ERROR_INSUFFICIENT_BUFFER) 108 { 109 MIB_IFTABLE *table = HeapAlloc(GetProcessHeap(), 0, size); 110 if (table) 111 { 112 if (!GetIfTable(table, &size, FALSE)) ifTable = table; 113 else HeapFree(GetProcessHeap(), 0, table ); 114 } 115 } 116 } 117 118 static void mib2IfNumberCleanup(void) 119 { 120 HeapFree(GetProcessHeap(), 0, ifTable); 121 } 122 123 static BOOL mib2IfNumberQuery(BYTE bPduType, SnmpVarBind *pVarBind, 124 AsnInteger32 *pErrorStatus) 125 { 126 AsnObjectIdentifier numberOid = DEFINE_OID(mib2IfNumber); 127 BOOL ret = TRUE; 128 129 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), 130 pErrorStatus); 131 132 switch (bPduType) 133 { 134 case SNMP_PDU_GET: 135 case SNMP_PDU_GETNEXT: 136 if ((bPduType == SNMP_PDU_GET && 137 !SnmpUtilOidNCmp(&pVarBind->name, &numberOid, numberOid.idLength)) 138 || SnmpUtilOidNCmp(&pVarBind->name, &numberOid, numberOid.idLength) 139 < 0) 140 { 141 DWORD numIfs = ifTable ? ifTable->dwNumEntries : 0; 142 143 copyInt(&pVarBind->value, &numIfs); 144 if (bPduType == SNMP_PDU_GETNEXT) 145 { 146 SnmpUtilOidFree(&pVarBind->name); 147 SnmpUtilOidCpy(&pVarBind->name, &numberOid); 148 } 149 *pErrorStatus = SNMP_ERRORSTATUS_NOERROR; 150 } 151 else 152 { 153 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 154 /* Caller deals with OID if bPduType == SNMP_PDU_GETNEXT, so don't 155 * need to set it here. 156 */ 157 } 158 break; 159 case SNMP_PDU_SET: 160 *pErrorStatus = SNMP_ERRORSTATUS_READONLY; 161 ret = FALSE; 162 break; 163 default: 164 FIXME("0x%02x: unsupported PDU type\n", bPduType); 165 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 166 } 167 return ret; 168 } 169 170 static DWORD copyOperStatus(AsnAny *value, void *src) 171 { 172 value->asnType = ASN_INTEGER; 173 /* The IPHlpApi definition of operational status differs from the MIB2 one, 174 * so map it to the MIB2 value. 175 */ 176 switch (*(DWORD *)src) 177 { 178 case MIB_IF_OPER_STATUS_OPERATIONAL: 179 value->asnValue.number = MIB_IF_ADMIN_STATUS_UP; 180 break; 181 case MIB_IF_OPER_STATUS_CONNECTING: 182 case MIB_IF_OPER_STATUS_CONNECTED: 183 value->asnValue.number = MIB_IF_ADMIN_STATUS_TESTING; 184 break; 185 default: 186 value->asnValue.number = MIB_IF_ADMIN_STATUS_DOWN; 187 }; 188 return SNMP_ERRORSTATUS_NOERROR; 189 } 190 191 /* Given an OID and a base OID that it must begin with, finds the item and 192 * integer instance from the OID. E.g., given an OID foo.1.2 and a base OID 193 * foo, returns item 1 and instance 2. 194 * If bPduType is not SNMP_PDU_GETNEXT and either the item or instance is 195 * missing, returns SNMP_ERRORSTATUS_NOSUCHNAME. 196 * If bPduType is SNMP_PDU_GETNEXT, returns the successor to the item and 197 * instance, or item 1, instance 1 if either is missing. 198 */ 199 static AsnInteger32 getItemAndIntegerInstanceFromOid(AsnObjectIdentifier *oid, 200 AsnObjectIdentifier *base, BYTE bPduType, UINT *item, UINT *instance) 201 { 202 AsnInteger32 ret = SNMP_ERRORSTATUS_NOERROR; 203 204 switch (bPduType) 205 { 206 case SNMP_PDU_GETNEXT: 207 if (SnmpUtilOidNCmp(oid, base, base->idLength) < 0) 208 { 209 *item = 1; 210 *instance = 1; 211 } 212 else if (!SnmpUtilOidNCmp(oid, base, base->idLength)) 213 { 214 if (oid->idLength == base->idLength || 215 oid->idLength == base->idLength + 1) 216 { 217 /* Either the table or an item within the table is specified, 218 * but the instance is not. Get the first instance. 219 */ 220 *instance = 1; 221 if (oid->idLength == base->idLength + 1) 222 *item = oid->ids[base->idLength]; 223 else 224 *item = 1; 225 } 226 else 227 { 228 *item = oid->ids[base->idLength]; 229 *instance = oid->ids[base->idLength + 1] + 1; 230 } 231 } 232 else 233 ret = SNMP_ERRORSTATUS_NOSUCHNAME; 234 break; 235 default: 236 if (!SnmpUtilOidNCmp(oid, base, base->idLength)) 237 { 238 if (oid->idLength == base->idLength || 239 oid->idLength == base->idLength + 1) 240 { 241 /* Either the table or an item within the table is specified, 242 * but the instance is not. 243 */ 244 ret = SNMP_ERRORSTATUS_NOSUCHNAME; 245 } 246 else 247 { 248 *item = oid->ids[base->idLength]; 249 *instance = oid->ids[base->idLength + 1]; 250 } 251 } 252 else 253 ret = SNMP_ERRORSTATUS_NOSUCHNAME; 254 } 255 return ret; 256 } 257 258 /* Given an OID and a base OID that it must begin with, finds the item from the 259 * OID. E.g., given an OID foo.1 and a base OID foo, returns item 1. 260 * If bPduType is not SNMP_PDU_GETNEXT and the item is missing, returns 261 * SNMP_ERRORSTATUS_NOSUCHNAME. 262 * If bPduType is SNMP_PDU_GETNEXT, returns the successor to the item, or item 263 * 1 if the item is missing. 264 */ 265 static AsnInteger32 getItemFromOid(AsnObjectIdentifier *oid, 266 AsnObjectIdentifier *base, BYTE bPduType, UINT *item) 267 { 268 AsnInteger32 ret = SNMP_ERRORSTATUS_NOERROR; 269 270 switch (bPduType) 271 { 272 case SNMP_PDU_GETNEXT: 273 if (SnmpUtilOidNCmp(oid, base, base->idLength) < 0) 274 *item = 1; 275 else if (!SnmpUtilOidNCmp(oid, base, base->idLength)) 276 { 277 if (oid->idLength == base->idLength) 278 { 279 /* The item is missing, assume the first item */ 280 *item = 1; 281 } 282 else 283 *item = oid->ids[base->idLength] + 1; 284 } 285 else 286 ret = SNMP_ERRORSTATUS_NOSUCHNAME; 287 break; 288 default: 289 if (!SnmpUtilOidNCmp(oid, base, base->idLength)) 290 { 291 if (oid->idLength == base->idLength) 292 { 293 /* The item is missing */ 294 ret = SNMP_ERRORSTATUS_NOSUCHNAME; 295 } 296 else 297 { 298 *item = oid->ids[base->idLength]; 299 if (!*item) 300 ret = SNMP_ERRORSTATUS_NOSUCHNAME; 301 } 302 } 303 else 304 ret = SNMP_ERRORSTATUS_NOSUCHNAME; 305 } 306 return ret; 307 } 308 309 struct GenericTable 310 { 311 DWORD numEntries; 312 BYTE entries[1]; 313 }; 314 315 static DWORD oidToIpAddr(AsnObjectIdentifier *oid) 316 { 317 assert(oid && oid->idLength >= 4); 318 /* Map the IDs to an IP address in little-endian order */ 319 return (BYTE)oid->ids[3] << 24 | (BYTE)oid->ids[2] << 16 | 320 (BYTE)oid->ids[1] << 8 | (BYTE)oid->ids[0]; 321 } 322 323 typedef void (*oidToKeyFunc)(AsnObjectIdentifier *oid, void *dst); 324 typedef int (*compareFunc)(const void *key, const void *value); 325 326 /* Finds the first value in the table that matches key. Returns its 1-based 327 * index if found, or 0 if not found. 328 */ 329 static UINT findValueInTable(const void *key, 330 struct GenericTable *table, size_t tableEntrySize, compareFunc compare) 331 { 332 UINT index = 0; 333 void *value; 334 335 value = bsearch(key, table->entries, table->numEntries, tableEntrySize, 336 compare); 337 if (value) 338 index = ((BYTE *)value - (BYTE *)table->entries) / tableEntrySize + 1; 339 return index; 340 } 341 342 /* Finds the first value in the table that matches oid, using makeKey to 343 * convert the oid to a key for comparison. Returns the value's 1-based 344 * index if found, or 0 if not found. 345 */ 346 static UINT findOidInTable(AsnObjectIdentifier *oid, 347 struct GenericTable *table, size_t tableEntrySize, oidToKeyFunc makeKey, 348 compareFunc compare) 349 { 350 UINT index = 0; 351 void *key = HeapAlloc(GetProcessHeap(), 0, tableEntrySize); 352 353 if (key) 354 { 355 makeKey(oid, key); 356 index = findValueInTable(key, table, tableEntrySize, compare); 357 HeapFree(GetProcessHeap(), 0, key); 358 } 359 return index; 360 } 361 362 /* Finds the first successor to the value in the table that does matches oid, 363 * using makeKey to convert the oid to a key for comparison. A successor is 364 * a value that does not match oid, so if multiple entries match an oid, only 365 * the first will ever be returned using this method. 366 * Returns the successor's 1-based index if found, or 0 if not found. 367 */ 368 static UINT findNextOidInTable(AsnObjectIdentifier *oid, 369 struct GenericTable *table, size_t tableEntrySize, oidToKeyFunc makeKey, 370 compareFunc compare) 371 { 372 UINT index = 0; 373 void *key = HeapAlloc(GetProcessHeap(), 0, tableEntrySize); 374 375 if (key) 376 { 377 makeKey(oid, key); 378 index = findValueInTable(key, table, tableEntrySize, compare); 379 if (index == 0) 380 { 381 /* Not found in table. If it's less than the first entry, return 382 * the first index. Otherwise just return 0 and let the caller 383 * handle finding the successor. 384 */ 385 if (compare(key, table->entries) < 0) 386 index = 1; 387 } 388 else 389 { 390 /* Skip any entries that match the same key. This enumeration will 391 * be incomplete, but it's what Windows appears to do if there are 392 * multiple entries with the same index in a table, and it avoids 393 * an infinite loop. 394 */ 395 for (++index; index <= table->numEntries && compare(key, 396 &table->entries[tableEntrySize * (index - 1)]) == 0; ++index) 397 ; 398 } 399 HeapFree(GetProcessHeap(), 0, key); 400 } 401 return index; 402 } 403 404 /* Given an OID and a base OID that it must begin with, finds the item and 405 * element of the table whose value matches the instance from the OID. 406 * The OID is converted to a key with the function makeKey, and compared 407 * against entries in the table with the function compare. 408 * If bPduType is not SNMP_PDU_GETNEXT and either the item or instance is 409 * missing, returns SNMP_ERRORSTATUS_NOSUCHNAME. 410 * If bPduType is SNMP_PDU_GETNEXT, returns the successor to the item and 411 * instance, or item 1, instance 1 if either is missing. 412 */ 413 static AsnInteger32 getItemAndInstanceFromTable(AsnObjectIdentifier *oid, 414 AsnObjectIdentifier *base, UINT instanceLen, BYTE bPduType, 415 struct GenericTable *table, size_t tableEntrySize, oidToKeyFunc makeKey, 416 compareFunc compare, UINT *item, UINT *instance) 417 { 418 AsnInteger32 ret = SNMP_ERRORSTATUS_NOERROR; 419 420 if (!table) 421 return SNMP_ERRORSTATUS_NOSUCHNAME; 422 423 switch (bPduType) 424 { 425 case SNMP_PDU_GETNEXT: 426 if (SnmpUtilOidNCmp(oid, base, base->idLength) < 0) 427 { 428 /* Return the first item and instance from the table */ 429 *item = 1; 430 *instance = 1; 431 } 432 else if (!SnmpUtilOidNCmp(oid, base, base->idLength) && 433 oid->idLength < base->idLength + instanceLen + 1) 434 { 435 /* Either the table or an item is specified, but the instance is 436 * not. 437 */ 438 *instance = 1; 439 if (oid->idLength >= base->idLength + 1) 440 { 441 *item = oid->ids[base->idLength]; 442 if (!*item) 443 *item = 1; 444 } 445 else 446 *item = 1; 447 } 448 else if (!SnmpUtilOidNCmp(oid, base, base->idLength) && 449 oid->idLength == base->idLength + instanceLen + 1) 450 { 451 *item = oid->ids[base->idLength]; 452 if (!*item) 453 { 454 *instance = 1; 455 *item = 1; 456 } 457 else 458 { 459 AsnObjectIdentifier instanceOid = { instanceLen, 460 oid->ids + base->idLength + 1 }; 461 462 *instance = findNextOidInTable(&instanceOid, table, 463 tableEntrySize, makeKey, compare); 464 if (!*instance || *instance > table->numEntries) 465 ret = SNMP_ERRORSTATUS_NOSUCHNAME; 466 } 467 } 468 else 469 ret = SNMP_ERRORSTATUS_NOSUCHNAME; 470 break; 471 default: 472 if (!SnmpUtilOidNCmp(oid, base, base->idLength) && 473 oid->idLength == base->idLength + instanceLen + 1) 474 { 475 *item = oid->ids[base->idLength]; 476 if (!*item) 477 ret = SNMP_ERRORSTATUS_NOSUCHNAME; 478 else 479 { 480 AsnObjectIdentifier instanceOid = { instanceLen, 481 oid->ids + base->idLength + 1 }; 482 483 *instance = findOidInTable(&instanceOid, table, tableEntrySize, 484 makeKey, compare); 485 if (!*instance) 486 ret = SNMP_ERRORSTATUS_NOSUCHNAME; 487 } 488 } 489 else 490 ret = SNMP_ERRORSTATUS_NOSUCHNAME; 491 } 492 return ret; 493 } 494 495 static INT setOidWithItem(AsnObjectIdentifier *dst, AsnObjectIdentifier *base, 496 UINT item) 497 { 498 UINT id; 499 AsnObjectIdentifier oid; 500 INT ret; 501 502 SnmpUtilOidFree(dst); 503 ret = SnmpUtilOidCpy(dst, base); 504 if (ret) 505 { 506 oid.idLength = 1; 507 oid.ids = &id; 508 id = item; 509 ret = SnmpUtilOidAppend(dst, &oid); 510 } 511 return ret; 512 } 513 514 static INT setOidWithItemAndIpAddr(AsnObjectIdentifier *dst, 515 AsnObjectIdentifier *base, UINT item, DWORD addr) 516 { 517 UINT id; 518 BYTE *ptr; 519 AsnObjectIdentifier oid; 520 INT ret; 521 522 ret = setOidWithItem(dst, base, item); 523 if (ret) 524 { 525 oid.idLength = 1; 526 oid.ids = &id; 527 for (ptr = (BYTE *)&addr; ret && ptr < (BYTE *)&addr + sizeof(DWORD); 528 ptr++) 529 { 530 id = *ptr; 531 ret = SnmpUtilOidAppend(dst, &oid); 532 } 533 } 534 return ret; 535 } 536 537 static INT setOidWithItemAndInteger(AsnObjectIdentifier *dst, 538 AsnObjectIdentifier *base, UINT item, UINT instance) 539 { 540 AsnObjectIdentifier oid; 541 INT ret; 542 543 ret = setOidWithItem(dst, base, item); 544 if (ret) 545 { 546 oid.idLength = 1; 547 oid.ids = &instance; 548 ret = SnmpUtilOidAppend(dst, &oid); 549 } 550 return ret; 551 } 552 553 static DWORD copyIfRowDescr(AsnAny *value, void *src) 554 { 555 PMIB_IFROW row = (PMIB_IFROW)((BYTE *)src - 556 FIELD_OFFSET(MIB_IFROW, dwDescrLen)); 557 DWORD ret; 558 559 if (row->dwDescrLen) 560 { 561 setStringValue(value, ASN_OCTETSTRING, row->dwDescrLen, row->bDescr); 562 ret = SNMP_ERRORSTATUS_NOERROR; 563 } 564 else 565 ret = SNMP_ERRORSTATUS_NOSUCHNAME; 566 return ret; 567 } 568 569 static DWORD copyIfRowPhysAddr(AsnAny *value, void *src) 570 { 571 PMIB_IFROW row = (PMIB_IFROW)((BYTE *)src - 572 FIELD_OFFSET(MIB_IFROW, dwPhysAddrLen)); 573 DWORD ret; 574 575 if (row->dwPhysAddrLen) 576 { 577 setStringValue(value, ASN_OCTETSTRING, row->dwPhysAddrLen, 578 row->bPhysAddr); 579 ret = SNMP_ERRORSTATUS_NOERROR; 580 } 581 else 582 ret = SNMP_ERRORSTATUS_NOSUCHNAME; 583 return ret; 584 } 585 586 static struct structToAsnValue mib2IfEntryMap[] = { 587 { FIELD_OFFSET(MIB_IFROW, dwIndex), copyInt }, 588 { FIELD_OFFSET(MIB_IFROW, dwDescrLen), copyIfRowDescr }, 589 { FIELD_OFFSET(MIB_IFROW, dwType), copyInt }, 590 { FIELD_OFFSET(MIB_IFROW, dwMtu), copyInt }, 591 { FIELD_OFFSET(MIB_IFROW, dwSpeed), copyInt }, 592 { FIELD_OFFSET(MIB_IFROW, dwPhysAddrLen), copyIfRowPhysAddr }, 593 { FIELD_OFFSET(MIB_IFROW, dwAdminStatus), copyInt }, 594 { FIELD_OFFSET(MIB_IFROW, dwOperStatus), copyOperStatus }, 595 { FIELD_OFFSET(MIB_IFROW, dwLastChange), copyInt }, 596 { FIELD_OFFSET(MIB_IFROW, dwInOctets), copyInt }, 597 { FIELD_OFFSET(MIB_IFROW, dwInUcastPkts), copyInt }, 598 { FIELD_OFFSET(MIB_IFROW, dwInNUcastPkts), copyInt }, 599 { FIELD_OFFSET(MIB_IFROW, dwInDiscards), copyInt }, 600 { FIELD_OFFSET(MIB_IFROW, dwInErrors), copyInt }, 601 { FIELD_OFFSET(MIB_IFROW, dwInUnknownProtos), copyInt }, 602 { FIELD_OFFSET(MIB_IFROW, dwOutOctets), copyInt }, 603 { FIELD_OFFSET(MIB_IFROW, dwOutUcastPkts), copyInt }, 604 { FIELD_OFFSET(MIB_IFROW, dwOutNUcastPkts), copyInt }, 605 { FIELD_OFFSET(MIB_IFROW, dwOutDiscards), copyInt }, 606 { FIELD_OFFSET(MIB_IFROW, dwOutErrors), copyInt }, 607 { FIELD_OFFSET(MIB_IFROW, dwOutQLen), copyInt }, 608 }; 609 610 static UINT mib2IfEntry[] = { 1,3,6,1,2,1,2,2,1 }; 611 612 static BOOL mib2IfEntryQuery(BYTE bPduType, SnmpVarBind *pVarBind, 613 AsnInteger32 *pErrorStatus) 614 { 615 AsnObjectIdentifier entryOid = DEFINE_OID(mib2IfEntry); 616 BOOL ret = TRUE; 617 618 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), 619 pErrorStatus); 620 621 switch (bPduType) 622 { 623 case SNMP_PDU_GET: 624 case SNMP_PDU_GETNEXT: 625 if (!ifTable) 626 { 627 /* There is no interface present, so let the caller deal 628 * with finding the successor. 629 */ 630 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 631 } 632 else 633 { 634 UINT tableIndex = 0, item = 0; 635 636 *pErrorStatus = getItemAndIntegerInstanceFromOid(&pVarBind->name, 637 &entryOid, bPduType, &item, &tableIndex); 638 if (!*pErrorStatus) 639 { 640 assert(tableIndex); 641 assert(item); 642 if (tableIndex > ifTable->dwNumEntries) 643 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 644 else 645 { 646 *pErrorStatus = mapStructEntryToValue(mib2IfEntryMap, 647 DEFINE_SIZEOF(mib2IfEntryMap), 648 &ifTable->table[tableIndex - 1], item, 649 pVarBind); 650 if (bPduType == SNMP_PDU_GETNEXT) 651 ret = setOidWithItemAndInteger(&pVarBind->name, 652 &entryOid, item, tableIndex); 653 } 654 } 655 } 656 break; 657 case SNMP_PDU_SET: 658 *pErrorStatus = SNMP_ERRORSTATUS_READONLY; 659 ret = FALSE; 660 break; 661 default: 662 FIXME("0x%02x: unsupported PDU type\n", bPduType); 663 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 664 } 665 return ret; 666 } 667 668 static UINT mib2Ip[] = { 1,3,6,1,2,1,4 }; 669 static MIB_IPSTATS ipStats; 670 671 static void mib2IpStatsInit(void) 672 { 673 GetIpStatistics(&ipStats); 674 } 675 676 static struct structToAsnValue mib2IpMap[] = { 677 { FIELD_OFFSET(MIB_IPSTATS, u.dwForwarding), copyInt }, /* 1 */ 678 { FIELD_OFFSET(MIB_IPSTATS, dwDefaultTTL), copyInt }, /* 2 */ 679 { FIELD_OFFSET(MIB_IPSTATS, dwInReceives), copyInt }, /* 3 */ 680 { FIELD_OFFSET(MIB_IPSTATS, dwInHdrErrors), copyInt }, /* 4 */ 681 { FIELD_OFFSET(MIB_IPSTATS, dwInAddrErrors), copyInt }, /* 5 */ 682 { FIELD_OFFSET(MIB_IPSTATS, dwForwDatagrams), copyInt }, /* 6 */ 683 { FIELD_OFFSET(MIB_IPSTATS, dwInUnknownProtos), copyInt }, /* 7 */ 684 { FIELD_OFFSET(MIB_IPSTATS, dwInDiscards), copyInt }, /* 8 */ 685 { FIELD_OFFSET(MIB_IPSTATS, dwInDelivers), copyInt }, /* 9 */ 686 { FIELD_OFFSET(MIB_IPSTATS, dwOutRequests), copyInt }, /* 10 */ 687 { FIELD_OFFSET(MIB_IPSTATS, dwOutDiscards), copyInt }, /* 11 */ 688 { FIELD_OFFSET(MIB_IPSTATS, dwOutNoRoutes), copyInt }, /* 12 */ 689 { FIELD_OFFSET(MIB_IPSTATS, dwReasmTimeout), copyInt }, /* 13 */ 690 { FIELD_OFFSET(MIB_IPSTATS, dwReasmReqds), copyInt }, /* 14 */ 691 { FIELD_OFFSET(MIB_IPSTATS, dwReasmOks), copyInt }, /* 15 */ 692 { FIELD_OFFSET(MIB_IPSTATS, dwReasmFails), copyInt }, /* 16 */ 693 { FIELD_OFFSET(MIB_IPSTATS, dwFragOks), copyInt }, /* 17 */ 694 { FIELD_OFFSET(MIB_IPSTATS, dwFragFails), copyInt }, /* 18 */ 695 { FIELD_OFFSET(MIB_IPSTATS, dwFragCreates), copyInt }, /* 19 */ 696 { 0, NULL }, /* 20: not used, IP addr table */ 697 { 0, NULL }, /* 21: not used, route table */ 698 { 0, NULL }, /* 22: not used, net to media (ARP) table */ 699 { FIELD_OFFSET(MIB_IPSTATS, dwRoutingDiscards), copyInt }, /* 23 */ 700 }; 701 702 static BOOL mib2IpStatsQuery(BYTE bPduType, SnmpVarBind *pVarBind, 703 AsnInteger32 *pErrorStatus) 704 { 705 AsnObjectIdentifier myOid = DEFINE_OID(mib2Ip); 706 UINT item = 0; 707 BOOL ret = TRUE; 708 709 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), 710 pErrorStatus); 711 712 switch (bPduType) 713 { 714 case SNMP_PDU_GET: 715 case SNMP_PDU_GETNEXT: 716 *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType, 717 &item); 718 if (!*pErrorStatus) 719 { 720 *pErrorStatus = mapStructEntryToValue(mib2IpMap, 721 DEFINE_SIZEOF(mib2IpMap), &ipStats, item, pVarBind); 722 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) 723 ret = setOidWithItem(&pVarBind->name, &myOid, item); 724 } 725 break; 726 case SNMP_PDU_SET: 727 *pErrorStatus = SNMP_ERRORSTATUS_READONLY; 728 ret = FALSE; 729 break; 730 default: 731 FIXME("0x%02x: unsupported PDU type\n", bPduType); 732 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 733 } 734 return ret; 735 } 736 737 static UINT mib2IpAddr[] = { 1,3,6,1,2,1,4,20,1 }; 738 static PMIB_IPADDRTABLE ipAddrTable; 739 740 static struct structToAsnValue mib2IpAddrMap[] = { 741 { FIELD_OFFSET(MIB_IPADDRROW, dwAddr), copyIpAddr }, 742 { FIELD_OFFSET(MIB_IPADDRROW, dwIndex), copyInt }, 743 { FIELD_OFFSET(MIB_IPADDRROW, dwMask), copyIpAddr }, 744 { FIELD_OFFSET(MIB_IPADDRROW, dwBCastAddr), copyInt }, 745 { FIELD_OFFSET(MIB_IPADDRROW, dwReasmSize), copyInt }, 746 }; 747 748 static void mib2IpAddrInit(void) 749 { 750 DWORD size = 0, ret = GetIpAddrTable(NULL, &size, TRUE); 751 752 if (ret == ERROR_INSUFFICIENT_BUFFER) 753 { 754 MIB_IPADDRTABLE *table = HeapAlloc(GetProcessHeap(), 0, size); 755 if (table) 756 { 757 if (!GetIpAddrTable(table, &size, TRUE)) ipAddrTable = table; 758 else HeapFree(GetProcessHeap(), 0, table ); 759 } 760 } 761 } 762 763 static void mib2IpAddrCleanup(void) 764 { 765 HeapFree(GetProcessHeap(), 0, ipAddrTable); 766 } 767 768 static void oidToIpAddrRow(AsnObjectIdentifier *oid, void *dst) 769 { 770 MIB_IPADDRROW *row = dst; 771 772 row->dwAddr = oidToIpAddr(oid); 773 } 774 775 static int compareIpAddrRow(const void *a, const void *b) 776 { 777 const MIB_IPADDRROW *key = a, *value = b; 778 779 return key->dwAddr - value->dwAddr; 780 } 781 782 static BOOL mib2IpAddrQuery(BYTE bPduType, SnmpVarBind *pVarBind, 783 AsnInteger32 *pErrorStatus) 784 { 785 AsnObjectIdentifier myOid = DEFINE_OID(mib2IpAddr); 786 UINT tableIndex = 0, item = 0; 787 BOOL ret = TRUE; 788 789 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), 790 pErrorStatus); 791 792 switch (bPduType) 793 { 794 case SNMP_PDU_GET: 795 case SNMP_PDU_GETNEXT: 796 *pErrorStatus = getItemAndInstanceFromTable(&pVarBind->name, 797 &myOid, 4, bPduType, (struct GenericTable *)ipAddrTable, 798 sizeof(MIB_IPADDRROW), oidToIpAddrRow, compareIpAddrRow, &item, 799 &tableIndex); 800 if (!*pErrorStatus) 801 { 802 assert(tableIndex); 803 assert(item); 804 *pErrorStatus = mapStructEntryToValue(mib2IpAddrMap, 805 DEFINE_SIZEOF(mib2IpAddrMap), 806 &ipAddrTable->table[tableIndex - 1], item, pVarBind); 807 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) 808 ret = setOidWithItemAndIpAddr(&pVarBind->name, &myOid, item, 809 ipAddrTable->table[tableIndex - 1].dwAddr); 810 } 811 break; 812 case SNMP_PDU_SET: 813 *pErrorStatus = SNMP_ERRORSTATUS_READONLY; 814 ret = FALSE; 815 break; 816 default: 817 FIXME("0x%02x: unsupported PDU type\n", bPduType); 818 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 819 } 820 return ret; 821 } 822 823 static UINT mib2IpRoute[] = { 1,3,6,1,2,1,4,21,1 }; 824 static PMIB_IPFORWARDTABLE ipRouteTable; 825 826 static struct structToAsnValue mib2IpRouteMap[] = { 827 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardDest), copyIpAddr }, 828 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardIfIndex), copyInt }, 829 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric1), copyInt }, 830 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric2), copyInt }, 831 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric3), copyInt }, 832 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric4), copyInt }, 833 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardNextHop), copyIpAddr }, 834 { FIELD_OFFSET(MIB_IPFORWARDROW, u1.dwForwardType), copyInt }, 835 { FIELD_OFFSET(MIB_IPFORWARDROW, u2.dwForwardProto), copyInt }, 836 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardAge), copyInt }, 837 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMask), copyIpAddr }, 838 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric5), copyInt }, 839 }; 840 841 static void mib2IpRouteInit(void) 842 { 843 DWORD size = 0, ret = GetIpForwardTable(NULL, &size, TRUE); 844 845 if (ret == ERROR_INSUFFICIENT_BUFFER) 846 { 847 MIB_IPFORWARDTABLE *table = HeapAlloc(GetProcessHeap(), 0, size); 848 if (table) 849 { 850 if (!GetIpForwardTable(table, &size, TRUE)) ipRouteTable = table; 851 else HeapFree(GetProcessHeap(), 0, table ); 852 } 853 } 854 } 855 856 static void mib2IpRouteCleanup(void) 857 { 858 HeapFree(GetProcessHeap(), 0, ipRouteTable); 859 } 860 861 static void oidToIpForwardRow(AsnObjectIdentifier *oid, void *dst) 862 { 863 MIB_IPFORWARDROW *row = dst; 864 865 row->dwForwardDest = oidToIpAddr(oid); 866 } 867 868 static int compareIpForwardRow(const void *a, const void *b) 869 { 870 const MIB_IPFORWARDROW *key = a, *value = b; 871 872 return key->dwForwardDest - value->dwForwardDest; 873 } 874 875 static BOOL mib2IpRouteQuery(BYTE bPduType, SnmpVarBind *pVarBind, 876 AsnInteger32 *pErrorStatus) 877 { 878 AsnObjectIdentifier myOid = DEFINE_OID(mib2IpRoute); 879 UINT tableIndex = 0, item = 0; 880 BOOL ret = TRUE; 881 882 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), 883 pErrorStatus); 884 885 switch (bPduType) 886 { 887 case SNMP_PDU_GET: 888 case SNMP_PDU_GETNEXT: 889 *pErrorStatus = getItemAndInstanceFromTable(&pVarBind->name, 890 &myOid, 4, bPduType, (struct GenericTable *)ipRouteTable, 891 sizeof(MIB_IPFORWARDROW), oidToIpForwardRow, compareIpForwardRow, 892 &item, &tableIndex); 893 if (!*pErrorStatus) 894 { 895 assert(tableIndex); 896 assert(item); 897 *pErrorStatus = mapStructEntryToValue(mib2IpRouteMap, 898 DEFINE_SIZEOF(mib2IpRouteMap), 899 &ipRouteTable->table[tableIndex - 1], item, pVarBind); 900 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) 901 ret = setOidWithItemAndIpAddr(&pVarBind->name, &myOid, item, 902 ipRouteTable->table[tableIndex - 1].dwForwardDest); 903 } 904 break; 905 case SNMP_PDU_SET: 906 *pErrorStatus = SNMP_ERRORSTATUS_READONLY; 907 ret = FALSE; 908 break; 909 default: 910 FIXME("0x%02x: unsupported PDU type\n", bPduType); 911 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 912 } 913 return ret; 914 } 915 916 static UINT mib2IpNet[] = { 1,3,6,1,2,1,4,22,1 }; 917 static PMIB_IPNETTABLE ipNetTable; 918 919 static DWORD copyIpNetPhysAddr(AsnAny *value, void *src) 920 { 921 PMIB_IPNETROW row = (PMIB_IPNETROW)((BYTE *)src - FIELD_OFFSET(MIB_IPNETROW, 922 dwPhysAddrLen)); 923 924 setStringValue(value, ASN_OCTETSTRING, row->dwPhysAddrLen, row->bPhysAddr); 925 return SNMP_ERRORSTATUS_NOERROR; 926 } 927 928 static struct structToAsnValue mib2IpNetMap[] = { 929 { FIELD_OFFSET(MIB_IPNETROW, dwIndex), copyInt }, 930 { FIELD_OFFSET(MIB_IPNETROW, dwPhysAddrLen), copyIpNetPhysAddr }, 931 { FIELD_OFFSET(MIB_IPNETROW, dwAddr), copyIpAddr }, 932 { FIELD_OFFSET(MIB_IPNETROW, u.dwType), copyInt }, 933 }; 934 935 static void mib2IpNetInit(void) 936 { 937 DWORD size = 0, ret = GetIpNetTable(NULL, &size, FALSE); 938 939 if (ret == ERROR_INSUFFICIENT_BUFFER) 940 { 941 MIB_IPNETTABLE *table = HeapAlloc(GetProcessHeap(), 0, size); 942 if (table) 943 { 944 if (!GetIpNetTable(table, &size, FALSE)) ipNetTable = table; 945 else HeapFree(GetProcessHeap(), 0, table ); 946 } 947 } 948 } 949 950 static void mib2IpNetCleanup(void) 951 { 952 HeapFree(GetProcessHeap(), 0, ipNetTable); 953 } 954 955 static BOOL mib2IpNetQuery(BYTE bPduType, SnmpVarBind *pVarBind, 956 AsnInteger32 *pErrorStatus) 957 { 958 AsnObjectIdentifier myOid = DEFINE_OID(mib2IpNet); 959 BOOL ret = TRUE; 960 961 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), 962 pErrorStatus); 963 964 switch (bPduType) 965 { 966 case SNMP_PDU_GET: 967 case SNMP_PDU_GETNEXT: 968 if (!ipNetTable) 969 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 970 else 971 { 972 UINT tableIndex = 0, item = 0; 973 974 *pErrorStatus = getItemAndIntegerInstanceFromOid(&pVarBind->name, 975 &myOid, bPduType, &item, &tableIndex); 976 if (!*pErrorStatus) 977 { 978 assert(tableIndex); 979 assert(item); 980 if (tableIndex > ipNetTable->dwNumEntries) 981 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 982 else 983 { 984 *pErrorStatus = mapStructEntryToValue(mib2IpNetMap, 985 DEFINE_SIZEOF(mib2IpNetMap), 986 &ipNetTable[tableIndex - 1], item, pVarBind); 987 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) 988 ret = setOidWithItemAndInteger(&pVarBind->name, &myOid, 989 item, tableIndex); 990 } 991 } 992 } 993 break; 994 case SNMP_PDU_SET: 995 *pErrorStatus = SNMP_ERRORSTATUS_READONLY; 996 ret = FALSE; 997 break; 998 default: 999 FIXME("0x%02x: unsupported PDU type\n", bPduType); 1000 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 1001 } 1002 return ret; 1003 } 1004 1005 static UINT mib2Icmp[] = { 1,3,6,1,2,1,5 }; 1006 static MIB_ICMP icmpStats; 1007 1008 static void mib2IcmpInit(void) 1009 { 1010 GetIcmpStatistics(&icmpStats); 1011 } 1012 1013 static struct structToAsnValue mib2IcmpMap[] = { 1014 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwMsgs), copyInt }, 1015 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwErrors), copyInt }, 1016 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwDestUnreachs), copyInt }, 1017 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwTimeExcds), copyInt }, 1018 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwParmProbs), copyInt }, 1019 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwSrcQuenchs), copyInt }, 1020 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwRedirects), copyInt }, 1021 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwEchos), copyInt }, 1022 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwEchoReps), copyInt }, 1023 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwTimestamps), copyInt }, 1024 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwTimestampReps), copyInt }, 1025 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwAddrMasks), copyInt }, 1026 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwAddrMaskReps), copyInt }, 1027 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwMsgs), copyInt }, 1028 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwErrors), copyInt }, 1029 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwDestUnreachs), copyInt }, 1030 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwTimeExcds), copyInt }, 1031 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwParmProbs), copyInt }, 1032 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwSrcQuenchs), copyInt }, 1033 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwRedirects), copyInt }, 1034 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwEchos), copyInt }, 1035 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwEchoReps), copyInt }, 1036 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwTimestamps), copyInt }, 1037 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwTimestampReps), copyInt }, 1038 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwAddrMasks), copyInt }, 1039 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwAddrMaskReps), copyInt }, 1040 }; 1041 1042 static BOOL mib2IcmpQuery(BYTE bPduType, SnmpVarBind *pVarBind, 1043 AsnInteger32 *pErrorStatus) 1044 { 1045 AsnObjectIdentifier myOid = DEFINE_OID(mib2Icmp); 1046 UINT item = 0; 1047 BOOL ret = TRUE; 1048 1049 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), 1050 pErrorStatus); 1051 1052 switch (bPduType) 1053 { 1054 case SNMP_PDU_GET: 1055 case SNMP_PDU_GETNEXT: 1056 *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType, 1057 &item); 1058 if (!*pErrorStatus) 1059 { 1060 *pErrorStatus = mapStructEntryToValue(mib2IcmpMap, 1061 DEFINE_SIZEOF(mib2IcmpMap), &icmpStats, item, 1062 pVarBind); 1063 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) 1064 ret = setOidWithItem(&pVarBind->name, &myOid, item); 1065 } 1066 break; 1067 case SNMP_PDU_SET: 1068 *pErrorStatus = SNMP_ERRORSTATUS_READONLY; 1069 ret = FALSE; 1070 break; 1071 default: 1072 FIXME("0x%02x: unsupported PDU type\n", bPduType); 1073 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 1074 } 1075 return ret; 1076 } 1077 1078 static UINT mib2Tcp[] = { 1,3,6,1,2,1,6 }; 1079 static MIB_TCPSTATS tcpStats; 1080 1081 static void mib2TcpInit(void) 1082 { 1083 GetTcpStatistics(&tcpStats); 1084 } 1085 1086 static struct structToAsnValue mib2TcpMap[] = { 1087 { FIELD_OFFSET(MIB_TCPSTATS, u.dwRtoAlgorithm), copyInt }, 1088 { FIELD_OFFSET(MIB_TCPSTATS, dwRtoMin), copyInt }, 1089 { FIELD_OFFSET(MIB_TCPSTATS, dwRtoMax), copyInt }, 1090 { FIELD_OFFSET(MIB_TCPSTATS, dwMaxConn), copyInt }, 1091 { FIELD_OFFSET(MIB_TCPSTATS, dwActiveOpens), copyInt }, 1092 { FIELD_OFFSET(MIB_TCPSTATS, dwPassiveOpens), copyInt }, 1093 { FIELD_OFFSET(MIB_TCPSTATS, dwAttemptFails), copyInt }, 1094 { FIELD_OFFSET(MIB_TCPSTATS, dwEstabResets), copyInt }, 1095 { FIELD_OFFSET(MIB_TCPSTATS, dwCurrEstab), copyInt }, 1096 { FIELD_OFFSET(MIB_TCPSTATS, dwInSegs), copyInt }, 1097 { FIELD_OFFSET(MIB_TCPSTATS, dwOutSegs), copyInt }, 1098 { FIELD_OFFSET(MIB_TCPSTATS, dwRetransSegs), copyInt }, 1099 { FIELD_OFFSET(MIB_TCPSTATS, dwInErrs), copyInt }, 1100 { FIELD_OFFSET(MIB_TCPSTATS, dwOutRsts), copyInt }, 1101 { FIELD_OFFSET(MIB_TCPSTATS, dwNumConns), copyInt }, 1102 }; 1103 1104 static BOOL mib2TcpQuery(BYTE bPduType, SnmpVarBind *pVarBind, 1105 AsnInteger32 *pErrorStatus) 1106 { 1107 AsnObjectIdentifier myOid = DEFINE_OID(mib2Tcp); 1108 UINT item = 0; 1109 BOOL ret = TRUE; 1110 1111 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), 1112 pErrorStatus); 1113 1114 switch (bPduType) 1115 { 1116 case SNMP_PDU_GET: 1117 case SNMP_PDU_GETNEXT: 1118 *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType, 1119 &item); 1120 if (!*pErrorStatus) 1121 { 1122 *pErrorStatus = mapStructEntryToValue(mib2TcpMap, 1123 DEFINE_SIZEOF(mib2TcpMap), &tcpStats, item, pVarBind); 1124 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) 1125 ret = setOidWithItem(&pVarBind->name, &myOid, item); 1126 } 1127 break; 1128 case SNMP_PDU_SET: 1129 *pErrorStatus = SNMP_ERRORSTATUS_READONLY; 1130 ret = FALSE; 1131 break; 1132 default: 1133 FIXME("0x%02x: unsupported PDU type\n", bPduType); 1134 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 1135 } 1136 return ret; 1137 } 1138 1139 static UINT mib2Udp[] = { 1,3,6,1,2,1,7 }; 1140 static MIB_UDPSTATS udpStats; 1141 1142 static void mib2UdpInit(void) 1143 { 1144 GetUdpStatistics(&udpStats); 1145 } 1146 1147 static struct structToAsnValue mib2UdpMap[] = { 1148 { FIELD_OFFSET(MIB_UDPSTATS, dwInDatagrams), copyInt }, 1149 { FIELD_OFFSET(MIB_UDPSTATS, dwNoPorts), copyInt }, 1150 { FIELD_OFFSET(MIB_UDPSTATS, dwInErrors), copyInt }, 1151 { FIELD_OFFSET(MIB_UDPSTATS, dwOutDatagrams), copyInt }, 1152 }; 1153 1154 static BOOL mib2UdpQuery(BYTE bPduType, SnmpVarBind *pVarBind, 1155 AsnInteger32 *pErrorStatus) 1156 { 1157 AsnObjectIdentifier myOid = DEFINE_OID(mib2Udp); 1158 UINT item; 1159 BOOL ret = TRUE; 1160 1161 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), 1162 pErrorStatus); 1163 1164 switch (bPduType) 1165 { 1166 case SNMP_PDU_GET: 1167 case SNMP_PDU_GETNEXT: 1168 *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType, 1169 &item); 1170 if (!*pErrorStatus) 1171 { 1172 *pErrorStatus = mapStructEntryToValue(mib2UdpMap, 1173 DEFINE_SIZEOF(mib2UdpMap), &udpStats, item, pVarBind); 1174 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) 1175 ret = setOidWithItem(&pVarBind->name, &myOid, item); 1176 } 1177 break; 1178 case SNMP_PDU_SET: 1179 *pErrorStatus = SNMP_ERRORSTATUS_READONLY; 1180 ret = FALSE; 1181 break; 1182 default: 1183 FIXME("0x%02x: unsupported PDU type\n", bPduType); 1184 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 1185 } 1186 return ret; 1187 } 1188 1189 static UINT mib2UdpEntry[] = { 1,3,6,1,2,1,7,5,1 }; 1190 static PMIB_UDPTABLE udpTable; 1191 1192 static void mib2UdpEntryInit(void) 1193 { 1194 DWORD size = 0, ret = GetUdpTable(NULL, &size, TRUE); 1195 1196 if (ret == ERROR_INSUFFICIENT_BUFFER) 1197 { 1198 MIB_UDPTABLE *table = HeapAlloc(GetProcessHeap(), 0, size); 1199 if (table) 1200 { 1201 if (!GetUdpTable(table, &size, TRUE)) udpTable = table; 1202 else HeapFree(GetProcessHeap(), 0, table); 1203 } 1204 } 1205 } 1206 1207 static void mib2UdpEntryCleanup(void) 1208 { 1209 HeapFree(GetProcessHeap(), 0, udpTable); 1210 } 1211 1212 static struct structToAsnValue mib2UdpEntryMap[] = { 1213 { FIELD_OFFSET(MIB_UDPROW, dwLocalAddr), copyIpAddr }, 1214 { FIELD_OFFSET(MIB_UDPROW, dwLocalPort), copyInt }, 1215 }; 1216 1217 static void oidToUdpRow(AsnObjectIdentifier *oid, void *dst) 1218 { 1219 MIB_UDPROW *row = dst; 1220 1221 assert(oid && oid->idLength >= 5); 1222 row->dwLocalAddr = oidToIpAddr(oid); 1223 row->dwLocalPort = oid->ids[4]; 1224 } 1225 1226 static int compareUdpRow(const void *a, const void *b) 1227 { 1228 const MIB_UDPROW *key = a, *value = b; 1229 int ret; 1230 1231 ret = key->dwLocalAddr - value->dwLocalAddr; 1232 if (ret == 0) 1233 ret = key->dwLocalPort - value->dwLocalPort; 1234 return ret; 1235 } 1236 1237 static BOOL mib2UdpEntryQuery(BYTE bPduType, SnmpVarBind *pVarBind, 1238 AsnInteger32 *pErrorStatus) 1239 { 1240 AsnObjectIdentifier myOid = DEFINE_OID(mib2UdpEntry); 1241 BOOL ret = TRUE; 1242 1243 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), 1244 pErrorStatus); 1245 1246 switch (bPduType) 1247 { 1248 case SNMP_PDU_GET: 1249 case SNMP_PDU_GETNEXT: 1250 if (!udpTable) 1251 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 1252 else 1253 { 1254 UINT tableIndex = 0, item = 0; 1255 1256 *pErrorStatus = getItemAndInstanceFromTable(&pVarBind->name, &myOid, 1257 5, bPduType, (struct GenericTable *)udpTable, 1258 sizeof(MIB_UDPROW), oidToUdpRow, compareUdpRow, &item, 1259 &tableIndex); 1260 if (!*pErrorStatus) 1261 { 1262 assert(tableIndex); 1263 assert(item); 1264 *pErrorStatus = mapStructEntryToValue(mib2UdpEntryMap, 1265 DEFINE_SIZEOF(mib2UdpEntryMap), 1266 &udpTable->table[tableIndex - 1], item, pVarBind); 1267 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) 1268 { 1269 AsnObjectIdentifier oid; 1270 1271 ret = setOidWithItemAndIpAddr(&pVarBind->name, &myOid, item, 1272 udpTable->table[tableIndex - 1].dwLocalAddr); 1273 if (ret) 1274 { 1275 oid.idLength = 1; 1276 oid.ids = &udpTable->table[tableIndex - 1].dwLocalPort; 1277 ret = SnmpUtilOidAppend(&pVarBind->name, &oid); 1278 } 1279 } 1280 } 1281 } 1282 break; 1283 case SNMP_PDU_SET: 1284 *pErrorStatus = SNMP_ERRORSTATUS_READONLY; 1285 ret = FALSE; 1286 break; 1287 default: 1288 FIXME("0x%02x: unsupported PDU type\n", bPduType); 1289 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; 1290 } 1291 return ret; 1292 } 1293 1294 /* This list MUST BE lexicographically sorted */ 1295 static struct mibImplementation supportedIDs[] = { 1296 { DEFINE_OID(mib2IfNumber), mib2IfNumberInit, mib2IfNumberQuery, 1297 mib2IfNumberCleanup }, 1298 { DEFINE_OID(mib2IfEntry), NULL, mib2IfEntryQuery, NULL }, 1299 { DEFINE_OID(mib2Ip), mib2IpStatsInit, mib2IpStatsQuery, NULL }, 1300 { DEFINE_OID(mib2IpAddr), mib2IpAddrInit, mib2IpAddrQuery, 1301 mib2IpAddrCleanup }, 1302 { DEFINE_OID(mib2IpRoute), mib2IpRouteInit, mib2IpRouteQuery, 1303 mib2IpRouteCleanup }, 1304 { DEFINE_OID(mib2IpNet), mib2IpNetInit, mib2IpNetQuery, mib2IpNetCleanup }, 1305 { DEFINE_OID(mib2Icmp), mib2IcmpInit, mib2IcmpQuery, NULL }, 1306 { DEFINE_OID(mib2Tcp), mib2TcpInit, mib2TcpQuery, NULL }, 1307 { DEFINE_OID(mib2Udp), mib2UdpInit, mib2UdpQuery, NULL }, 1308 { DEFINE_OID(mib2UdpEntry), mib2UdpEntryInit, mib2UdpEntryQuery, 1309 mib2UdpEntryCleanup }, 1310 }; 1311 static UINT minSupportedIDLength; 1312 1313 /***************************************************************************** 1314 * SnmpExtensionInit [INETMIB1.@] 1315 */ 1316 BOOL WINAPI SnmpExtensionInit(DWORD dwUptimeReference, 1317 HANDLE *phSubagentTrapEvent, AsnObjectIdentifier *pFirstSupportedRegion) 1318 { 1319 AsnObjectIdentifier myOid = DEFINE_OID(mib2System); 1320 UINT i; 1321 1322 TRACE("(%d, %p, %p)\n", dwUptimeReference, phSubagentTrapEvent, 1323 pFirstSupportedRegion); 1324 1325 minSupportedIDLength = UINT_MAX; 1326 for (i = 0; i < sizeof(supportedIDs) / sizeof(supportedIDs[0]); i++) 1327 { 1328 if (supportedIDs[i].init) 1329 supportedIDs[i].init(); 1330 if (supportedIDs[i].name.idLength < minSupportedIDLength) 1331 minSupportedIDLength = supportedIDs[i].name.idLength; 1332 } 1333 *phSubagentTrapEvent = NULL; 1334 SnmpUtilOidCpy(pFirstSupportedRegion, &myOid); 1335 return TRUE; 1336 } 1337 1338 static void cleanup(void) 1339 { 1340 UINT i; 1341 1342 for (i = 0; i < sizeof(supportedIDs) / sizeof(supportedIDs[0]); i++) 1343 if (supportedIDs[i].cleanup) 1344 supportedIDs[i].cleanup(); 1345 } 1346 1347 static struct mibImplementation *findSupportedQuery(UINT *ids, UINT idLength, 1348 UINT *matchingIndex) 1349 { 1350 int indexHigh = DEFINE_SIZEOF(supportedIDs) - 1, indexLow = 0; 1351 AsnObjectIdentifier oid1 = { idLength, ids}; 1352 1353 if (!idLength) 1354 return NULL; 1355 1356 while (indexLow <= indexHigh) 1357 { 1358 INT cmp, i = (indexLow + indexHigh) / 2; 1359 if (!(cmp = SnmpUtilOidNCmp(&oid1, &supportedIDs[i].name, idLength))) 1360 { 1361 *matchingIndex = i; 1362 return &supportedIDs[i]; 1363 } 1364 if (cmp > 0) 1365 indexLow = i + 1; 1366 else 1367 indexHigh = i - 1; 1368 } 1369 return NULL; 1370 } 1371 1372 /***************************************************************************** 1373 * SnmpExtensionQuery [INETMIB1.@] 1374 */ 1375 BOOL WINAPI SnmpExtensionQuery(BYTE bPduType, SnmpVarBindList *pVarBindList, 1376 AsnInteger32 *pErrorStatus, AsnInteger32 *pErrorIndex) 1377 { 1378 AsnObjectIdentifier mib2oid = DEFINE_OID(mib2); 1379 AsnInteger32 error = SNMP_ERRORSTATUS_NOERROR, errorIndex = 0; 1380 UINT i; 1381 BOOL ret = TRUE; 1382 1383 TRACE("(0x%02x, %p, %p, %p)\n", bPduType, pVarBindList, 1384 pErrorStatus, pErrorIndex); 1385 1386 for (i = 0; !error && i < pVarBindList->len; i++) 1387 { 1388 /* Ignore any OIDs not in MIB2 */ 1389 if (!SnmpUtilOidNCmp(&pVarBindList->list[i].name, &mib2oid, 1390 mib2oid.idLength)) 1391 { 1392 struct mibImplementation *impl = NULL; 1393 UINT len, matchingIndex = 0; 1394 1395 TRACE("%s\n", SnmpUtilOidToA(&pVarBindList->list[i].name)); 1396 /* Search for an implementation matching as many octets as possible 1397 */ 1398 for (len = pVarBindList->list[i].name.idLength; 1399 len >= minSupportedIDLength && !impl; len--) 1400 impl = findSupportedQuery(pVarBindList->list[i].name.ids, len, 1401 &matchingIndex); 1402 if (impl && impl->query) 1403 ret = impl->query(bPduType, &pVarBindList->list[i], &error); 1404 else 1405 error = SNMP_ERRORSTATUS_NOSUCHNAME; 1406 if (error == SNMP_ERRORSTATUS_NOSUCHNAME && 1407 bPduType == SNMP_PDU_GETNEXT) 1408 { 1409 /* GetNext is special: it finds the successor to the given OID, 1410 * so we have to continue until an implementation handles the 1411 * query or we exhaust the table of supported OIDs. 1412 */ 1413 for (matchingIndex++; error == SNMP_ERRORSTATUS_NOSUCHNAME && 1414 matchingIndex < DEFINE_SIZEOF(supportedIDs); 1415 matchingIndex++) 1416 { 1417 error = SNMP_ERRORSTATUS_NOERROR; 1418 impl = &supportedIDs[matchingIndex]; 1419 if (impl->query) 1420 ret = impl->query(bPduType, &pVarBindList->list[i], 1421 &error); 1422 else 1423 error = SNMP_ERRORSTATUS_NOSUCHNAME; 1424 } 1425 /* If the query still isn't resolved, set the OID to the 1426 * successor to the last entry in the table. 1427 */ 1428 if (error == SNMP_ERRORSTATUS_NOSUCHNAME) 1429 { 1430 SnmpUtilOidFree(&pVarBindList->list[i].name); 1431 ret = SnmpUtilOidCpy(&pVarBindList->list[i].name, 1432 &supportedIDs[matchingIndex - 1].name); 1433 pVarBindList->list[i].name.ids[ 1434 pVarBindList->list[i].name.idLength - 1] += 1; 1435 } 1436 } 1437 if (error) 1438 errorIndex = i + 1; 1439 } 1440 } 1441 *pErrorStatus = error; 1442 *pErrorIndex = errorIndex; 1443 return ret; 1444 } 1445 1446 /***************************************************************************** 1447 * DllMain [INETMIB1.@] 1448 */ 1449 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 1450 { 1451 TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved); 1452 1453 switch (fdwReason) 1454 { 1455 case DLL_PROCESS_ATTACH: 1456 DisableThreadLibraryCalls(hinstDLL); 1457 break; 1458 case DLL_PROCESS_DETACH: 1459 if (lpvReserved) break; 1460 cleanup(); 1461 break; 1462 } 1463 1464 return TRUE; 1465 } 1466