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