| /* |
| * Copyright 2008 Juan Lang |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| #include <assert.h> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <limits.h> |
| |
| #define NONAMELESSUNION |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "snmp.h" |
| #include "iphlpapi.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(inetmib1); |
| |
| /** |
| * Utility functions |
| */ |
| static DWORD copyInt(AsnAny *value, void *src) |
| { |
| value->asnType = ASN_INTEGER; |
| value->asnValue.number = *(DWORD *)src; |
| return SNMP_ERRORSTATUS_NOERROR; |
| } |
| |
| static void setStringValue(AsnAny *value, BYTE type, DWORD len, BYTE *str) |
| { |
| AsnAny strValue; |
| |
| strValue.asnType = type; |
| strValue.asnValue.string.stream = str; |
| strValue.asnValue.string.length = len; |
| strValue.asnValue.string.dynamic = FALSE; |
| SnmpUtilAsnAnyCpy(value, &strValue); |
| } |
| |
| typedef DWORD (*copyValueFunc)(AsnAny *value, void *src); |
| |
| struct structToAsnValue |
| { |
| size_t offset; |
| copyValueFunc copy; |
| }; |
| |
| static AsnInteger32 mapStructEntryToValue(struct structToAsnValue *map, |
| UINT mapLen, void *record, UINT id, SnmpVarBind *pVarBind) |
| { |
| /* OIDs are 1-based */ |
| if (!id) |
| return SNMP_ERRORSTATUS_NOSUCHNAME; |
| --id; |
| if (id >= mapLen) |
| return SNMP_ERRORSTATUS_NOSUCHNAME; |
| if (!map[id].copy) |
| return SNMP_ERRORSTATUS_NOSUCHNAME; |
| return map[id].copy(&pVarBind->value, (BYTE *)record + map[id].offset); |
| } |
| |
| static DWORD copyIpAddr(AsnAny *value, void *src) |
| { |
| setStringValue(value, ASN_IPADDRESS, sizeof(DWORD), src); |
| return SNMP_ERRORSTATUS_NOERROR; |
| } |
| |
| static UINT mib2[] = { 1,3,6,1,2,1 }; |
| static UINT mib2System[] = { 1,3,6,1,2,1,1 }; |
| |
| typedef BOOL (*varqueryfunc)(BYTE bPduType, SnmpVarBind *pVarBind, |
| AsnInteger32 *pErrorStatus); |
| |
| struct mibImplementation |
| { |
| AsnObjectIdentifier name; |
| void (*init)(void); |
| varqueryfunc query; |
| void (*cleanup)(void); |
| }; |
| |
| static UINT mib2IfNumber[] = { 1,3,6,1,2,1,2,1 }; |
| static PMIB_IFTABLE ifTable; |
| |
| static void mib2IfNumberInit(void) |
| { |
| DWORD size = 0, ret = GetIfTable(NULL, &size, FALSE); |
| |
| if (ret == ERROR_INSUFFICIENT_BUFFER) |
| { |
| MIB_IFTABLE *table = HeapAlloc(GetProcessHeap(), 0, size); |
| if (table) |
| { |
| if (!GetIfTable(table, &size, FALSE)) ifTable = table; |
| else HeapFree(GetProcessHeap(), 0, table ); |
| } |
| } |
| } |
| |
| static void mib2IfNumberCleanup(void) |
| { |
| HeapFree(GetProcessHeap(), 0, ifTable); |
| } |
| |
| static BOOL mib2IfNumberQuery(BYTE bPduType, SnmpVarBind *pVarBind, |
| AsnInteger32 *pErrorStatus) |
| { |
| AsnObjectIdentifier numberOid = DEFINE_OID(mib2IfNumber); |
| BOOL ret = TRUE; |
| |
| TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), |
| pErrorStatus); |
| |
| switch (bPduType) |
| { |
| case SNMP_PDU_GET: |
| case SNMP_PDU_GETNEXT: |
| if ((bPduType == SNMP_PDU_GET && |
| !SnmpUtilOidNCmp(&pVarBind->name, &numberOid, numberOid.idLength)) |
| || SnmpUtilOidNCmp(&pVarBind->name, &numberOid, numberOid.idLength) |
| < 0) |
| { |
| DWORD numIfs = ifTable ? ifTable->dwNumEntries : 0; |
| |
| copyInt(&pVarBind->value, &numIfs); |
| if (bPduType == SNMP_PDU_GETNEXT) |
| { |
| SnmpUtilOidFree(&pVarBind->name); |
| SnmpUtilOidCpy(&pVarBind->name, &numberOid); |
| } |
| *pErrorStatus = SNMP_ERRORSTATUS_NOERROR; |
| } |
| else |
| { |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| /* Caller deals with OID if bPduType == SNMP_PDU_GETNEXT, so don't |
| * need to set it here. |
| */ |
| } |
| break; |
| case SNMP_PDU_SET: |
| *pErrorStatus = SNMP_ERRORSTATUS_READONLY; |
| ret = FALSE; |
| break; |
| default: |
| FIXME("0x%02x: unsupported PDU type\n", bPduType); |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| return ret; |
| } |
| |
| static DWORD copyOperStatus(AsnAny *value, void *src) |
| { |
| value->asnType = ASN_INTEGER; |
| /* The IPHlpApi definition of operational status differs from the MIB2 one, |
| * so map it to the MIB2 value. |
| */ |
| switch (*(DWORD *)src) |
| { |
| case MIB_IF_OPER_STATUS_OPERATIONAL: |
| value->asnValue.number = MIB_IF_ADMIN_STATUS_UP; |
| break; |
| case MIB_IF_OPER_STATUS_CONNECTING: |
| case MIB_IF_OPER_STATUS_CONNECTED: |
| value->asnValue.number = MIB_IF_ADMIN_STATUS_TESTING; |
| break; |
| default: |
| value->asnValue.number = MIB_IF_ADMIN_STATUS_DOWN; |
| }; |
| return SNMP_ERRORSTATUS_NOERROR; |
| } |
| |
| /* Given an OID and a base OID that it must begin with, finds the item and |
| * integer instance from the OID. E.g., given an OID foo.1.2 and a base OID |
| * foo, returns item 1 and instance 2. |
| * If bPduType is not SNMP_PDU_GETNEXT and either the item or instance is |
| * missing, returns SNMP_ERRORSTATUS_NOSUCHNAME. |
| * If bPduType is SNMP_PDU_GETNEXT, returns the successor to the item and |
| * instance, or item 1, instance 1 if either is missing. |
| */ |
| static AsnInteger32 getItemAndIntegerInstanceFromOid(AsnObjectIdentifier *oid, |
| AsnObjectIdentifier *base, BYTE bPduType, UINT *item, UINT *instance) |
| { |
| AsnInteger32 ret = SNMP_ERRORSTATUS_NOERROR; |
| |
| switch (bPduType) |
| { |
| case SNMP_PDU_GETNEXT: |
| if (SnmpUtilOidNCmp(oid, base, base->idLength) < 0) |
| { |
| *item = 1; |
| *instance = 1; |
| } |
| else if (!SnmpUtilOidNCmp(oid, base, base->idLength)) |
| { |
| if (oid->idLength == base->idLength || |
| oid->idLength == base->idLength + 1) |
| { |
| /* Either the table or an item within the table is specified, |
| * but the instance is not. Get the first instance. |
| */ |
| *instance = 1; |
| if (oid->idLength == base->idLength + 1) |
| *item = oid->ids[base->idLength]; |
| else |
| *item = 1; |
| } |
| else |
| { |
| *item = oid->ids[base->idLength]; |
| *instance = oid->ids[base->idLength + 1] + 1; |
| } |
| } |
| else |
| ret = SNMP_ERRORSTATUS_NOSUCHNAME; |
| break; |
| default: |
| if (!SnmpUtilOidNCmp(oid, base, base->idLength)) |
| { |
| if (oid->idLength == base->idLength || |
| oid->idLength == base->idLength + 1) |
| { |
| /* Either the table or an item within the table is specified, |
| * but the instance is not. |
| */ |
| ret = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| else |
| { |
| *item = oid->ids[base->idLength]; |
| *instance = oid->ids[base->idLength + 1]; |
| } |
| } |
| else |
| ret = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| return ret; |
| } |
| |
| /* Given an OID and a base OID that it must begin with, finds the item from the |
| * OID. E.g., given an OID foo.1 and a base OID foo, returns item 1. |
| * If bPduType is not SNMP_PDU_GETNEXT and the item is missing, returns |
| * SNMP_ERRORSTATUS_NOSUCHNAME. |
| * If bPduType is SNMP_PDU_GETNEXT, returns the successor to the item, or item |
| * 1 if the item is missing. |
| */ |
| static AsnInteger32 getItemFromOid(AsnObjectIdentifier *oid, |
| AsnObjectIdentifier *base, BYTE bPduType, UINT *item) |
| { |
| AsnInteger32 ret = SNMP_ERRORSTATUS_NOERROR; |
| |
| switch (bPduType) |
| { |
| case SNMP_PDU_GETNEXT: |
| if (SnmpUtilOidNCmp(oid, base, base->idLength) < 0) |
| *item = 1; |
| else if (!SnmpUtilOidNCmp(oid, base, base->idLength)) |
| { |
| if (oid->idLength == base->idLength) |
| { |
| /* The item is missing, assume the first item */ |
| *item = 1; |
| } |
| else |
| *item = oid->ids[base->idLength] + 1; |
| } |
| else |
| ret = SNMP_ERRORSTATUS_NOSUCHNAME; |
| break; |
| default: |
| if (!SnmpUtilOidNCmp(oid, base, base->idLength)) |
| { |
| if (oid->idLength == base->idLength) |
| { |
| /* The item is missing */ |
| ret = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| else |
| { |
| *item = oid->ids[base->idLength]; |
| if (!*item) |
| ret = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| } |
| else |
| ret = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| return ret; |
| } |
| |
| struct GenericTable |
| { |
| DWORD numEntries; |
| BYTE entries[1]; |
| }; |
| |
| static DWORD oidToIpAddr(AsnObjectIdentifier *oid) |
| { |
| assert(oid && oid->idLength >= 4); |
| /* Map the IDs to an IP address in little-endian order */ |
| return (BYTE)oid->ids[3] << 24 | (BYTE)oid->ids[2] << 16 | |
| (BYTE)oid->ids[1] << 8 | (BYTE)oid->ids[0]; |
| } |
| |
| typedef void (*oidToKeyFunc)(AsnObjectIdentifier *oid, void *dst); |
| typedef int (*compareFunc)(const void *key, const void *value); |
| |
| /* Finds the first value in the table that matches key. Returns its 1-based |
| * index if found, or 0 if not found. |
| */ |
| static UINT findValueInTable(const void *key, |
| struct GenericTable *table, size_t tableEntrySize, compareFunc compare) |
| { |
| UINT index = 0; |
| void *value; |
| |
| value = bsearch(key, table->entries, table->numEntries, tableEntrySize, |
| compare); |
| if (value) |
| index = ((BYTE *)value - (BYTE *)table->entries) / tableEntrySize + 1; |
| return index; |
| } |
| |
| /* Finds the first value in the table that matches oid, using makeKey to |
| * convert the oid to a key for comparison. Returns the value's 1-based |
| * index if found, or 0 if not found. |
| */ |
| static UINT findOidInTable(AsnObjectIdentifier *oid, |
| struct GenericTable *table, size_t tableEntrySize, oidToKeyFunc makeKey, |
| compareFunc compare) |
| { |
| UINT index = 0; |
| void *key = HeapAlloc(GetProcessHeap(), 0, tableEntrySize); |
| |
| if (key) |
| { |
| makeKey(oid, key); |
| index = findValueInTable(key, table, tableEntrySize, compare); |
| HeapFree(GetProcessHeap(), 0, key); |
| } |
| return index; |
| } |
| |
| /* Finds the first successor to the value in the table that does matches oid, |
| * using makeKey to convert the oid to a key for comparison. A successor is |
| * a value that does not match oid, so if multiple entries match an oid, only |
| * the first will ever be returned using this method. |
| * Returns the successor's 1-based index if found, or 0 if not found. |
| */ |
| static UINT findNextOidInTable(AsnObjectIdentifier *oid, |
| struct GenericTable *table, size_t tableEntrySize, oidToKeyFunc makeKey, |
| compareFunc compare) |
| { |
| UINT index = 0; |
| void *key = HeapAlloc(GetProcessHeap(), 0, tableEntrySize); |
| |
| if (key) |
| { |
| makeKey(oid, key); |
| index = findValueInTable(key, table, tableEntrySize, compare); |
| if (index == 0) |
| { |
| /* Not found in table. If it's less than the first entry, return |
| * the first index. Otherwise just return 0 and let the caller |
| * handle finding the successor. |
| */ |
| if (compare(key, table->entries) < 0) |
| index = 1; |
| } |
| else |
| { |
| /* Skip any entries that match the same key. This enumeration will |
| * be incomplete, but it's what Windows appears to do if there are |
| * multiple entries with the same index in a table, and it avoids |
| * an infinite loop. |
| */ |
| for (++index; index <= table->numEntries && compare(key, |
| &table->entries[tableEntrySize * (index - 1)]) == 0; ++index) |
| ; |
| } |
| HeapFree(GetProcessHeap(), 0, key); |
| } |
| return index; |
| } |
| |
| /* Given an OID and a base OID that it must begin with, finds the item and |
| * element of the table whose value matches the instance from the OID. |
| * The OID is converted to a key with the function makeKey, and compared |
| * against entries in the table with the function compare. |
| * If bPduType is not SNMP_PDU_GETNEXT and either the item or instance is |
| * missing, returns SNMP_ERRORSTATUS_NOSUCHNAME. |
| * If bPduType is SNMP_PDU_GETNEXT, returns the successor to the item and |
| * instance, or item 1, instance 1 if either is missing. |
| */ |
| static AsnInteger32 getItemAndInstanceFromTable(AsnObjectIdentifier *oid, |
| AsnObjectIdentifier *base, UINT instanceLen, BYTE bPduType, |
| struct GenericTable *table, size_t tableEntrySize, oidToKeyFunc makeKey, |
| compareFunc compare, UINT *item, UINT *instance) |
| { |
| AsnInteger32 ret = SNMP_ERRORSTATUS_NOERROR; |
| |
| if (!table) |
| return SNMP_ERRORSTATUS_NOSUCHNAME; |
| |
| switch (bPduType) |
| { |
| case SNMP_PDU_GETNEXT: |
| if (SnmpUtilOidNCmp(oid, base, base->idLength) < 0) |
| { |
| /* Return the first item and instance from the table */ |
| *item = 1; |
| *instance = 1; |
| } |
| else if (!SnmpUtilOidNCmp(oid, base, base->idLength) && |
| oid->idLength < base->idLength + instanceLen + 1) |
| { |
| /* Either the table or an item is specified, but the instance is |
| * not. |
| */ |
| *instance = 1; |
| if (oid->idLength >= base->idLength + 1) |
| { |
| *item = oid->ids[base->idLength]; |
| if (!*item) |
| *item = 1; |
| } |
| else |
| *item = 1; |
| } |
| else if (!SnmpUtilOidNCmp(oid, base, base->idLength) && |
| oid->idLength == base->idLength + instanceLen + 1) |
| { |
| *item = oid->ids[base->idLength]; |
| if (!*item) |
| { |
| *instance = 1; |
| *item = 1; |
| } |
| else |
| { |
| AsnObjectIdentifier instanceOid = { instanceLen, |
| oid->ids + base->idLength + 1 }; |
| |
| *instance = findNextOidInTable(&instanceOid, table, |
| tableEntrySize, makeKey, compare); |
| if (!*instance || *instance > table->numEntries) |
| ret = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| } |
| else |
| ret = SNMP_ERRORSTATUS_NOSUCHNAME; |
| break; |
| default: |
| if (!SnmpUtilOidNCmp(oid, base, base->idLength) && |
| oid->idLength == base->idLength + instanceLen + 1) |
| { |
| *item = oid->ids[base->idLength]; |
| if (!*item) |
| ret = SNMP_ERRORSTATUS_NOSUCHNAME; |
| else |
| { |
| AsnObjectIdentifier instanceOid = { instanceLen, |
| oid->ids + base->idLength + 1 }; |
| |
| *instance = findOidInTable(&instanceOid, table, tableEntrySize, |
| makeKey, compare); |
| if (!*instance) |
| ret = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| } |
| else |
| ret = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| return ret; |
| } |
| |
| static INT setOidWithItem(AsnObjectIdentifier *dst, AsnObjectIdentifier *base, |
| UINT item) |
| { |
| UINT id; |
| AsnObjectIdentifier oid; |
| INT ret; |
| |
| SnmpUtilOidFree(dst); |
| ret = SnmpUtilOidCpy(dst, base); |
| if (ret) |
| { |
| oid.idLength = 1; |
| oid.ids = &id; |
| id = item; |
| ret = SnmpUtilOidAppend(dst, &oid); |
| } |
| return ret; |
| } |
| |
| static INT setOidWithItemAndIpAddr(AsnObjectIdentifier *dst, |
| AsnObjectIdentifier *base, UINT item, DWORD addr) |
| { |
| UINT id; |
| BYTE *ptr; |
| AsnObjectIdentifier oid; |
| INT ret; |
| |
| ret = setOidWithItem(dst, base, item); |
| if (ret) |
| { |
| oid.idLength = 1; |
| oid.ids = &id; |
| for (ptr = (BYTE *)&addr; ret && ptr < (BYTE *)&addr + sizeof(DWORD); |
| ptr++) |
| { |
| id = *ptr; |
| ret = SnmpUtilOidAppend(dst, &oid); |
| } |
| } |
| return ret; |
| } |
| |
| static INT setOidWithItemAndInteger(AsnObjectIdentifier *dst, |
| AsnObjectIdentifier *base, UINT item, UINT instance) |
| { |
| AsnObjectIdentifier oid; |
| INT ret; |
| |
| ret = setOidWithItem(dst, base, item); |
| if (ret) |
| { |
| oid.idLength = 1; |
| oid.ids = &instance; |
| ret = SnmpUtilOidAppend(dst, &oid); |
| } |
| return ret; |
| } |
| |
| static DWORD copyIfRowDescr(AsnAny *value, void *src) |
| { |
| PMIB_IFROW row = (PMIB_IFROW)((BYTE *)src - |
| FIELD_OFFSET(MIB_IFROW, dwDescrLen)); |
| DWORD ret; |
| |
| if (row->dwDescrLen) |
| { |
| setStringValue(value, ASN_OCTETSTRING, row->dwDescrLen, row->bDescr); |
| ret = SNMP_ERRORSTATUS_NOERROR; |
| } |
| else |
| ret = SNMP_ERRORSTATUS_NOSUCHNAME; |
| return ret; |
| } |
| |
| static DWORD copyIfRowPhysAddr(AsnAny *value, void *src) |
| { |
| PMIB_IFROW row = (PMIB_IFROW)((BYTE *)src - |
| FIELD_OFFSET(MIB_IFROW, dwPhysAddrLen)); |
| DWORD ret; |
| |
| if (row->dwPhysAddrLen) |
| { |
| setStringValue(value, ASN_OCTETSTRING, row->dwPhysAddrLen, |
| row->bPhysAddr); |
| ret = SNMP_ERRORSTATUS_NOERROR; |
| } |
| else |
| ret = SNMP_ERRORSTATUS_NOSUCHNAME; |
| return ret; |
| } |
| |
| static struct structToAsnValue mib2IfEntryMap[] = { |
| { FIELD_OFFSET(MIB_IFROW, dwIndex), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwDescrLen), copyIfRowDescr }, |
| { FIELD_OFFSET(MIB_IFROW, dwType), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwMtu), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwSpeed), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwPhysAddrLen), copyIfRowPhysAddr }, |
| { FIELD_OFFSET(MIB_IFROW, dwAdminStatus), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwOperStatus), copyOperStatus }, |
| { FIELD_OFFSET(MIB_IFROW, dwLastChange), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwInOctets), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwInUcastPkts), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwInNUcastPkts), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwInDiscards), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwInErrors), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwInUnknownProtos), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwOutOctets), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwOutUcastPkts), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwOutNUcastPkts), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwOutDiscards), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwOutErrors), copyInt }, |
| { FIELD_OFFSET(MIB_IFROW, dwOutQLen), copyInt }, |
| }; |
| |
| static UINT mib2IfEntry[] = { 1,3,6,1,2,1,2,2,1 }; |
| |
| static BOOL mib2IfEntryQuery(BYTE bPduType, SnmpVarBind *pVarBind, |
| AsnInteger32 *pErrorStatus) |
| { |
| AsnObjectIdentifier entryOid = DEFINE_OID(mib2IfEntry); |
| BOOL ret = TRUE; |
| |
| TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), |
| pErrorStatus); |
| |
| switch (bPduType) |
| { |
| case SNMP_PDU_GET: |
| case SNMP_PDU_GETNEXT: |
| if (!ifTable) |
| { |
| /* There is no interface present, so let the caller deal |
| * with finding the successor. |
| */ |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| else |
| { |
| UINT tableIndex = 0, item = 0; |
| |
| *pErrorStatus = getItemAndIntegerInstanceFromOid(&pVarBind->name, |
| &entryOid, bPduType, &item, &tableIndex); |
| if (!*pErrorStatus) |
| { |
| assert(tableIndex); |
| assert(item); |
| if (tableIndex > ifTable->dwNumEntries) |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| else |
| { |
| *pErrorStatus = mapStructEntryToValue(mib2IfEntryMap, |
| DEFINE_SIZEOF(mib2IfEntryMap), |
| &ifTable->table[tableIndex - 1], item, |
| pVarBind); |
| if (bPduType == SNMP_PDU_GETNEXT) |
| ret = setOidWithItemAndInteger(&pVarBind->name, |
| &entryOid, item, tableIndex); |
| } |
| } |
| } |
| break; |
| case SNMP_PDU_SET: |
| *pErrorStatus = SNMP_ERRORSTATUS_READONLY; |
| ret = FALSE; |
| break; |
| default: |
| FIXME("0x%02x: unsupported PDU type\n", bPduType); |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| return ret; |
| } |
| |
| static UINT mib2Ip[] = { 1,3,6,1,2,1,4 }; |
| static MIB_IPSTATS ipStats; |
| |
| static void mib2IpStatsInit(void) |
| { |
| GetIpStatistics(&ipStats); |
| } |
| |
| static struct structToAsnValue mib2IpMap[] = { |
| { FIELD_OFFSET(MIB_IPSTATS, u.dwForwarding), copyInt }, /* 1 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwDefaultTTL), copyInt }, /* 2 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwInReceives), copyInt }, /* 3 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwInHdrErrors), copyInt }, /* 4 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwInAddrErrors), copyInt }, /* 5 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwForwDatagrams), copyInt }, /* 6 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwInUnknownProtos), copyInt }, /* 7 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwInDiscards), copyInt }, /* 8 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwInDelivers), copyInt }, /* 9 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwOutRequests), copyInt }, /* 10 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwOutDiscards), copyInt }, /* 11 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwOutNoRoutes), copyInt }, /* 12 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwReasmTimeout), copyInt }, /* 13 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwReasmReqds), copyInt }, /* 14 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwReasmOks), copyInt }, /* 15 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwReasmFails), copyInt }, /* 16 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwFragOks), copyInt }, /* 17 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwFragFails), copyInt }, /* 18 */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwFragCreates), copyInt }, /* 19 */ |
| { 0, NULL }, /* 20: not used, IP addr table */ |
| { 0, NULL }, /* 21: not used, route table */ |
| { 0, NULL }, /* 22: not used, net to media (ARP) table */ |
| { FIELD_OFFSET(MIB_IPSTATS, dwRoutingDiscards), copyInt }, /* 23 */ |
| }; |
| |
| static BOOL mib2IpStatsQuery(BYTE bPduType, SnmpVarBind *pVarBind, |
| AsnInteger32 *pErrorStatus) |
| { |
| AsnObjectIdentifier myOid = DEFINE_OID(mib2Ip); |
| UINT item = 0; |
| BOOL ret = TRUE; |
| |
| TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), |
| pErrorStatus); |
| |
| switch (bPduType) |
| { |
| case SNMP_PDU_GET: |
| case SNMP_PDU_GETNEXT: |
| *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType, |
| &item); |
| if (!*pErrorStatus) |
| { |
| *pErrorStatus = mapStructEntryToValue(mib2IpMap, |
| DEFINE_SIZEOF(mib2IpMap), &ipStats, item, pVarBind); |
| if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) |
| ret = setOidWithItem(&pVarBind->name, &myOid, item); |
| } |
| break; |
| case SNMP_PDU_SET: |
| *pErrorStatus = SNMP_ERRORSTATUS_READONLY; |
| ret = FALSE; |
| break; |
| default: |
| FIXME("0x%02x: unsupported PDU type\n", bPduType); |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| return ret; |
| } |
| |
| static UINT mib2IpAddr[] = { 1,3,6,1,2,1,4,20,1 }; |
| static PMIB_IPADDRTABLE ipAddrTable; |
| |
| static struct structToAsnValue mib2IpAddrMap[] = { |
| { FIELD_OFFSET(MIB_IPADDRROW, dwAddr), copyIpAddr }, |
| { FIELD_OFFSET(MIB_IPADDRROW, dwIndex), copyInt }, |
| { FIELD_OFFSET(MIB_IPADDRROW, dwMask), copyIpAddr }, |
| { FIELD_OFFSET(MIB_IPADDRROW, dwBCastAddr), copyInt }, |
| { FIELD_OFFSET(MIB_IPADDRROW, dwReasmSize), copyInt }, |
| }; |
| |
| static void mib2IpAddrInit(void) |
| { |
| DWORD size = 0, ret = GetIpAddrTable(NULL, &size, TRUE); |
| |
| if (ret == ERROR_INSUFFICIENT_BUFFER) |
| { |
| MIB_IPADDRTABLE *table = HeapAlloc(GetProcessHeap(), 0, size); |
| if (table) |
| { |
| if (!GetIpAddrTable(table, &size, TRUE)) ipAddrTable = table; |
| else HeapFree(GetProcessHeap(), 0, table ); |
| } |
| } |
| } |
| |
| static void mib2IpAddrCleanup(void) |
| { |
| HeapFree(GetProcessHeap(), 0, ipAddrTable); |
| } |
| |
| static void oidToIpAddrRow(AsnObjectIdentifier *oid, void *dst) |
| { |
| MIB_IPADDRROW *row = dst; |
| |
| row->dwAddr = oidToIpAddr(oid); |
| } |
| |
| static int compareIpAddrRow(const void *a, const void *b) |
| { |
| const MIB_IPADDRROW *key = a, *value = b; |
| |
| return key->dwAddr - value->dwAddr; |
| } |
| |
| static BOOL mib2IpAddrQuery(BYTE bPduType, SnmpVarBind *pVarBind, |
| AsnInteger32 *pErrorStatus) |
| { |
| AsnObjectIdentifier myOid = DEFINE_OID(mib2IpAddr); |
| UINT tableIndex = 0, item = 0; |
| BOOL ret = TRUE; |
| |
| TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), |
| pErrorStatus); |
| |
| switch (bPduType) |
| { |
| case SNMP_PDU_GET: |
| case SNMP_PDU_GETNEXT: |
| *pErrorStatus = getItemAndInstanceFromTable(&pVarBind->name, |
| &myOid, 4, bPduType, (struct GenericTable *)ipAddrTable, |
| sizeof(MIB_IPADDRROW), oidToIpAddrRow, compareIpAddrRow, &item, |
| &tableIndex); |
| if (!*pErrorStatus) |
| { |
| assert(tableIndex); |
| assert(item); |
| *pErrorStatus = mapStructEntryToValue(mib2IpAddrMap, |
| DEFINE_SIZEOF(mib2IpAddrMap), |
| &ipAddrTable->table[tableIndex - 1], item, pVarBind); |
| if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) |
| ret = setOidWithItemAndIpAddr(&pVarBind->name, &myOid, item, |
| ipAddrTable->table[tableIndex - 1].dwAddr); |
| } |
| break; |
| case SNMP_PDU_SET: |
| *pErrorStatus = SNMP_ERRORSTATUS_READONLY; |
| ret = FALSE; |
| break; |
| default: |
| FIXME("0x%02x: unsupported PDU type\n", bPduType); |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| return ret; |
| } |
| |
| static UINT mib2IpRoute[] = { 1,3,6,1,2,1,4,21,1 }; |
| static PMIB_IPFORWARDTABLE ipRouteTable; |
| |
| static struct structToAsnValue mib2IpRouteMap[] = { |
| { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardDest), copyIpAddr }, |
| { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardIfIndex), copyInt }, |
| { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric1), copyInt }, |
| { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric2), copyInt }, |
| { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric3), copyInt }, |
| { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric4), copyInt }, |
| { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardNextHop), copyIpAddr }, |
| { FIELD_OFFSET(MIB_IPFORWARDROW, u1.dwForwardType), copyInt }, |
| { FIELD_OFFSET(MIB_IPFORWARDROW, u2.dwForwardProto), copyInt }, |
| { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardAge), copyInt }, |
| { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMask), copyIpAddr }, |
| { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric5), copyInt }, |
| }; |
| |
| static void mib2IpRouteInit(void) |
| { |
| DWORD size = 0, ret = GetIpForwardTable(NULL, &size, TRUE); |
| |
| if (ret == ERROR_INSUFFICIENT_BUFFER) |
| { |
| MIB_IPFORWARDTABLE *table = HeapAlloc(GetProcessHeap(), 0, size); |
| if (table) |
| { |
| if (!GetIpForwardTable(table, &size, TRUE)) ipRouteTable = table; |
| else HeapFree(GetProcessHeap(), 0, table ); |
| } |
| } |
| } |
| |
| static void mib2IpRouteCleanup(void) |
| { |
| HeapFree(GetProcessHeap(), 0, ipRouteTable); |
| } |
| |
| static void oidToIpForwardRow(AsnObjectIdentifier *oid, void *dst) |
| { |
| MIB_IPFORWARDROW *row = dst; |
| |
| row->dwForwardDest = oidToIpAddr(oid); |
| } |
| |
| static int compareIpForwardRow(const void *a, const void *b) |
| { |
| const MIB_IPFORWARDROW *key = a, *value = b; |
| |
| return key->dwForwardDest - value->dwForwardDest; |
| } |
| |
| static BOOL mib2IpRouteQuery(BYTE bPduType, SnmpVarBind *pVarBind, |
| AsnInteger32 *pErrorStatus) |
| { |
| AsnObjectIdentifier myOid = DEFINE_OID(mib2IpRoute); |
| UINT tableIndex = 0, item = 0; |
| BOOL ret = TRUE; |
| |
| TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), |
| pErrorStatus); |
| |
| switch (bPduType) |
| { |
| case SNMP_PDU_GET: |
| case SNMP_PDU_GETNEXT: |
| *pErrorStatus = getItemAndInstanceFromTable(&pVarBind->name, |
| &myOid, 4, bPduType, (struct GenericTable *)ipRouteTable, |
| sizeof(MIB_IPFORWARDROW), oidToIpForwardRow, compareIpForwardRow, |
| &item, &tableIndex); |
| if (!*pErrorStatus) |
| { |
| assert(tableIndex); |
| assert(item); |
| *pErrorStatus = mapStructEntryToValue(mib2IpRouteMap, |
| DEFINE_SIZEOF(mib2IpRouteMap), |
| &ipRouteTable->table[tableIndex - 1], item, pVarBind); |
| if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) |
| ret = setOidWithItemAndIpAddr(&pVarBind->name, &myOid, item, |
| ipRouteTable->table[tableIndex - 1].dwForwardDest); |
| } |
| break; |
| case SNMP_PDU_SET: |
| *pErrorStatus = SNMP_ERRORSTATUS_READONLY; |
| ret = FALSE; |
| break; |
| default: |
| FIXME("0x%02x: unsupported PDU type\n", bPduType); |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| return ret; |
| } |
| |
| static UINT mib2IpNet[] = { 1,3,6,1,2,1,4,22,1 }; |
| static PMIB_IPNETTABLE ipNetTable; |
| |
| static DWORD copyIpNetPhysAddr(AsnAny *value, void *src) |
| { |
| PMIB_IPNETROW row = (PMIB_IPNETROW)((BYTE *)src - FIELD_OFFSET(MIB_IPNETROW, |
| dwPhysAddrLen)); |
| |
| setStringValue(value, ASN_OCTETSTRING, row->dwPhysAddrLen, row->bPhysAddr); |
| return SNMP_ERRORSTATUS_NOERROR; |
| } |
| |
| static struct structToAsnValue mib2IpNetMap[] = { |
| { FIELD_OFFSET(MIB_IPNETROW, dwIndex), copyInt }, |
| { FIELD_OFFSET(MIB_IPNETROW, dwPhysAddrLen), copyIpNetPhysAddr }, |
| { FIELD_OFFSET(MIB_IPNETROW, dwAddr), copyIpAddr }, |
| { FIELD_OFFSET(MIB_IPNETROW, u.dwType), copyInt }, |
| }; |
| |
| static void mib2IpNetInit(void) |
| { |
| DWORD size = 0, ret = GetIpNetTable(NULL, &size, FALSE); |
| |
| if (ret == ERROR_INSUFFICIENT_BUFFER) |
| { |
| MIB_IPNETTABLE *table = HeapAlloc(GetProcessHeap(), 0, size); |
| if (table) |
| { |
| if (!GetIpNetTable(table, &size, FALSE)) ipNetTable = table; |
| else HeapFree(GetProcessHeap(), 0, table ); |
| } |
| } |
| } |
| |
| static void mib2IpNetCleanup(void) |
| { |
| HeapFree(GetProcessHeap(), 0, ipNetTable); |
| } |
| |
| static BOOL mib2IpNetQuery(BYTE bPduType, SnmpVarBind *pVarBind, |
| AsnInteger32 *pErrorStatus) |
| { |
| AsnObjectIdentifier myOid = DEFINE_OID(mib2IpNet); |
| BOOL ret = TRUE; |
| |
| TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), |
| pErrorStatus); |
| |
| switch (bPduType) |
| { |
| case SNMP_PDU_GET: |
| case SNMP_PDU_GETNEXT: |
| if (!ipNetTable) |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| else |
| { |
| UINT tableIndex = 0, item = 0; |
| |
| *pErrorStatus = getItemAndIntegerInstanceFromOid(&pVarBind->name, |
| &myOid, bPduType, &item, &tableIndex); |
| if (!*pErrorStatus) |
| { |
| assert(tableIndex); |
| assert(item); |
| if (tableIndex > ipNetTable->dwNumEntries) |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| else |
| { |
| *pErrorStatus = mapStructEntryToValue(mib2IpNetMap, |
| DEFINE_SIZEOF(mib2IpNetMap), |
| &ipNetTable[tableIndex - 1], item, pVarBind); |
| if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) |
| ret = setOidWithItemAndInteger(&pVarBind->name, &myOid, |
| item, tableIndex); |
| } |
| } |
| } |
| break; |
| case SNMP_PDU_SET: |
| *pErrorStatus = SNMP_ERRORSTATUS_READONLY; |
| ret = FALSE; |
| break; |
| default: |
| FIXME("0x%02x: unsupported PDU type\n", bPduType); |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| return ret; |
| } |
| |
| static UINT mib2Icmp[] = { 1,3,6,1,2,1,5 }; |
| static MIB_ICMP icmpStats; |
| |
| static void mib2IcmpInit(void) |
| { |
| GetIcmpStatistics(&icmpStats); |
| } |
| |
| static struct structToAsnValue mib2IcmpMap[] = { |
| { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwMsgs), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwErrors), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwDestUnreachs), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwTimeExcds), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwParmProbs), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwSrcQuenchs), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwRedirects), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwEchos), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwEchoReps), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwTimestamps), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwTimestampReps), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwAddrMasks), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwAddrMaskReps), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwMsgs), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwErrors), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwDestUnreachs), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwTimeExcds), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwParmProbs), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwSrcQuenchs), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwRedirects), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwEchos), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwEchoReps), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwTimestamps), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwTimestampReps), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwAddrMasks), copyInt }, |
| { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwAddrMaskReps), copyInt }, |
| }; |
| |
| static BOOL mib2IcmpQuery(BYTE bPduType, SnmpVarBind *pVarBind, |
| AsnInteger32 *pErrorStatus) |
| { |
| AsnObjectIdentifier myOid = DEFINE_OID(mib2Icmp); |
| UINT item = 0; |
| BOOL ret = TRUE; |
| |
| TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), |
| pErrorStatus); |
| |
| switch (bPduType) |
| { |
| case SNMP_PDU_GET: |
| case SNMP_PDU_GETNEXT: |
| *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType, |
| &item); |
| if (!*pErrorStatus) |
| { |
| *pErrorStatus = mapStructEntryToValue(mib2IcmpMap, |
| DEFINE_SIZEOF(mib2IcmpMap), &icmpStats, item, |
| pVarBind); |
| if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) |
| ret = setOidWithItem(&pVarBind->name, &myOid, item); |
| } |
| break; |
| case SNMP_PDU_SET: |
| *pErrorStatus = SNMP_ERRORSTATUS_READONLY; |
| ret = FALSE; |
| break; |
| default: |
| FIXME("0x%02x: unsupported PDU type\n", bPduType); |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| return ret; |
| } |
| |
| static UINT mib2Tcp[] = { 1,3,6,1,2,1,6 }; |
| static MIB_TCPSTATS tcpStats; |
| |
| static void mib2TcpInit(void) |
| { |
| GetTcpStatistics(&tcpStats); |
| } |
| |
| static struct structToAsnValue mib2TcpMap[] = { |
| { FIELD_OFFSET(MIB_TCPSTATS, u.dwRtoAlgorithm), copyInt }, |
| { FIELD_OFFSET(MIB_TCPSTATS, dwRtoMin), copyInt }, |
| { FIELD_OFFSET(MIB_TCPSTATS, dwRtoMax), copyInt }, |
| { FIELD_OFFSET(MIB_TCPSTATS, dwMaxConn), copyInt }, |
| { FIELD_OFFSET(MIB_TCPSTATS, dwActiveOpens), copyInt }, |
| { FIELD_OFFSET(MIB_TCPSTATS, dwPassiveOpens), copyInt }, |
| { FIELD_OFFSET(MIB_TCPSTATS, dwAttemptFails), copyInt }, |
| { FIELD_OFFSET(MIB_TCPSTATS, dwEstabResets), copyInt }, |
| { FIELD_OFFSET(MIB_TCPSTATS, dwCurrEstab), copyInt }, |
| { FIELD_OFFSET(MIB_TCPSTATS, dwInSegs), copyInt }, |
| { FIELD_OFFSET(MIB_TCPSTATS, dwOutSegs), copyInt }, |
| { FIELD_OFFSET(MIB_TCPSTATS, dwRetransSegs), copyInt }, |
| { FIELD_OFFSET(MIB_TCPSTATS, dwInErrs), copyInt }, |
| { FIELD_OFFSET(MIB_TCPSTATS, dwOutRsts), copyInt }, |
| { FIELD_OFFSET(MIB_TCPSTATS, dwNumConns), copyInt }, |
| }; |
| |
| static BOOL mib2TcpQuery(BYTE bPduType, SnmpVarBind *pVarBind, |
| AsnInteger32 *pErrorStatus) |
| { |
| AsnObjectIdentifier myOid = DEFINE_OID(mib2Tcp); |
| UINT item = 0; |
| BOOL ret = TRUE; |
| |
| TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), |
| pErrorStatus); |
| |
| switch (bPduType) |
| { |
| case SNMP_PDU_GET: |
| case SNMP_PDU_GETNEXT: |
| *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType, |
| &item); |
| if (!*pErrorStatus) |
| { |
| *pErrorStatus = mapStructEntryToValue(mib2TcpMap, |
| DEFINE_SIZEOF(mib2TcpMap), &tcpStats, item, pVarBind); |
| if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) |
| ret = setOidWithItem(&pVarBind->name, &myOid, item); |
| } |
| break; |
| case SNMP_PDU_SET: |
| *pErrorStatus = SNMP_ERRORSTATUS_READONLY; |
| ret = FALSE; |
| break; |
| default: |
| FIXME("0x%02x: unsupported PDU type\n", bPduType); |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| return ret; |
| } |
| |
| static UINT mib2Udp[] = { 1,3,6,1,2,1,7 }; |
| static MIB_UDPSTATS udpStats; |
| |
| static void mib2UdpInit(void) |
| { |
| GetUdpStatistics(&udpStats); |
| } |
| |
| static struct structToAsnValue mib2UdpMap[] = { |
| { FIELD_OFFSET(MIB_UDPSTATS, dwInDatagrams), copyInt }, |
| { FIELD_OFFSET(MIB_UDPSTATS, dwNoPorts), copyInt }, |
| { FIELD_OFFSET(MIB_UDPSTATS, dwInErrors), copyInt }, |
| { FIELD_OFFSET(MIB_UDPSTATS, dwOutDatagrams), copyInt }, |
| }; |
| |
| static BOOL mib2UdpQuery(BYTE bPduType, SnmpVarBind *pVarBind, |
| AsnInteger32 *pErrorStatus) |
| { |
| AsnObjectIdentifier myOid = DEFINE_OID(mib2Udp); |
| UINT item; |
| BOOL ret = TRUE; |
| |
| TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), |
| pErrorStatus); |
| |
| switch (bPduType) |
| { |
| case SNMP_PDU_GET: |
| case SNMP_PDU_GETNEXT: |
| *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType, |
| &item); |
| if (!*pErrorStatus) |
| { |
| *pErrorStatus = mapStructEntryToValue(mib2UdpMap, |
| DEFINE_SIZEOF(mib2UdpMap), &udpStats, item, pVarBind); |
| if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) |
| ret = setOidWithItem(&pVarBind->name, &myOid, item); |
| } |
| break; |
| case SNMP_PDU_SET: |
| *pErrorStatus = SNMP_ERRORSTATUS_READONLY; |
| ret = FALSE; |
| break; |
| default: |
| FIXME("0x%02x: unsupported PDU type\n", bPduType); |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| return ret; |
| } |
| |
| static UINT mib2UdpEntry[] = { 1,3,6,1,2,1,7,5,1 }; |
| static PMIB_UDPTABLE udpTable; |
| |
| static void mib2UdpEntryInit(void) |
| { |
| DWORD size = 0, ret = GetUdpTable(NULL, &size, TRUE); |
| |
| if (ret == ERROR_INSUFFICIENT_BUFFER) |
| { |
| MIB_UDPTABLE *table = HeapAlloc(GetProcessHeap(), 0, size); |
| if (table) |
| { |
| if (!GetUdpTable(table, &size, TRUE)) udpTable = table; |
| else HeapFree(GetProcessHeap(), 0, table); |
| } |
| } |
| } |
| |
| static void mib2UdpEntryCleanup(void) |
| { |
| HeapFree(GetProcessHeap(), 0, udpTable); |
| } |
| |
| static struct structToAsnValue mib2UdpEntryMap[] = { |
| { FIELD_OFFSET(MIB_UDPROW, dwLocalAddr), copyIpAddr }, |
| { FIELD_OFFSET(MIB_UDPROW, dwLocalPort), copyInt }, |
| }; |
| |
| static void oidToUdpRow(AsnObjectIdentifier *oid, void *dst) |
| { |
| MIB_UDPROW *row = dst; |
| |
| assert(oid && oid->idLength >= 5); |
| row->dwLocalAddr = oidToIpAddr(oid); |
| row->dwLocalPort = oid->ids[4]; |
| } |
| |
| static int compareUdpRow(const void *a, const void *b) |
| { |
| const MIB_UDPROW *key = a, *value = b; |
| int ret; |
| |
| ret = key->dwLocalAddr - value->dwLocalAddr; |
| if (ret == 0) |
| ret = key->dwLocalPort - value->dwLocalPort; |
| return ret; |
| } |
| |
| static BOOL mib2UdpEntryQuery(BYTE bPduType, SnmpVarBind *pVarBind, |
| AsnInteger32 *pErrorStatus) |
| { |
| AsnObjectIdentifier myOid = DEFINE_OID(mib2UdpEntry); |
| BOOL ret = TRUE; |
| |
| TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name), |
| pErrorStatus); |
| |
| switch (bPduType) |
| { |
| case SNMP_PDU_GET: |
| case SNMP_PDU_GETNEXT: |
| if (!udpTable) |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| else |
| { |
| UINT tableIndex = 0, item = 0; |
| |
| *pErrorStatus = getItemAndInstanceFromTable(&pVarBind->name, &myOid, |
| 5, bPduType, (struct GenericTable *)udpTable, |
| sizeof(MIB_UDPROW), oidToUdpRow, compareUdpRow, &item, |
| &tableIndex); |
| if (!*pErrorStatus) |
| { |
| assert(tableIndex); |
| assert(item); |
| *pErrorStatus = mapStructEntryToValue(mib2UdpEntryMap, |
| DEFINE_SIZEOF(mib2UdpEntryMap), |
| &udpTable->table[tableIndex - 1], item, pVarBind); |
| if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT) |
| { |
| AsnObjectIdentifier oid; |
| |
| ret = setOidWithItemAndIpAddr(&pVarBind->name, &myOid, item, |
| udpTable->table[tableIndex - 1].dwLocalAddr); |
| if (ret) |
| { |
| oid.idLength = 1; |
| oid.ids = &udpTable->table[tableIndex - 1].dwLocalPort; |
| ret = SnmpUtilOidAppend(&pVarBind->name, &oid); |
| } |
| } |
| } |
| } |
| break; |
| case SNMP_PDU_SET: |
| *pErrorStatus = SNMP_ERRORSTATUS_READONLY; |
| ret = FALSE; |
| break; |
| default: |
| FIXME("0x%02x: unsupported PDU type\n", bPduType); |
| *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| return ret; |
| } |
| |
| /* This list MUST BE lexicographically sorted */ |
| static struct mibImplementation supportedIDs[] = { |
| { DEFINE_OID(mib2IfNumber), mib2IfNumberInit, mib2IfNumberQuery, |
| mib2IfNumberCleanup }, |
| { DEFINE_OID(mib2IfEntry), NULL, mib2IfEntryQuery, NULL }, |
| { DEFINE_OID(mib2Ip), mib2IpStatsInit, mib2IpStatsQuery, NULL }, |
| { DEFINE_OID(mib2IpAddr), mib2IpAddrInit, mib2IpAddrQuery, |
| mib2IpAddrCleanup }, |
| { DEFINE_OID(mib2IpRoute), mib2IpRouteInit, mib2IpRouteQuery, |
| mib2IpRouteCleanup }, |
| { DEFINE_OID(mib2IpNet), mib2IpNetInit, mib2IpNetQuery, mib2IpNetCleanup }, |
| { DEFINE_OID(mib2Icmp), mib2IcmpInit, mib2IcmpQuery, NULL }, |
| { DEFINE_OID(mib2Tcp), mib2TcpInit, mib2TcpQuery, NULL }, |
| { DEFINE_OID(mib2Udp), mib2UdpInit, mib2UdpQuery, NULL }, |
| { DEFINE_OID(mib2UdpEntry), mib2UdpEntryInit, mib2UdpEntryQuery, |
| mib2UdpEntryCleanup }, |
| }; |
| static UINT minSupportedIDLength; |
| |
| /***************************************************************************** |
| * SnmpExtensionInit [INETMIB1.@] |
| */ |
| BOOL WINAPI SnmpExtensionInit(DWORD dwUptimeReference, |
| HANDLE *phSubagentTrapEvent, AsnObjectIdentifier *pFirstSupportedRegion) |
| { |
| AsnObjectIdentifier myOid = DEFINE_OID(mib2System); |
| UINT i; |
| |
| TRACE("(%d, %p, %p)\n", dwUptimeReference, phSubagentTrapEvent, |
| pFirstSupportedRegion); |
| |
| minSupportedIDLength = UINT_MAX; |
| for (i = 0; i < sizeof(supportedIDs) / sizeof(supportedIDs[0]); i++) |
| { |
| if (supportedIDs[i].init) |
| supportedIDs[i].init(); |
| if (supportedIDs[i].name.idLength < minSupportedIDLength) |
| minSupportedIDLength = supportedIDs[i].name.idLength; |
| } |
| *phSubagentTrapEvent = NULL; |
| SnmpUtilOidCpy(pFirstSupportedRegion, &myOid); |
| return TRUE; |
| } |
| |
| static void cleanup(void) |
| { |
| UINT i; |
| |
| for (i = 0; i < sizeof(supportedIDs) / sizeof(supportedIDs[0]); i++) |
| if (supportedIDs[i].cleanup) |
| supportedIDs[i].cleanup(); |
| } |
| |
| static struct mibImplementation *findSupportedQuery(UINT *ids, UINT idLength, |
| UINT *matchingIndex) |
| { |
| int indexHigh = DEFINE_SIZEOF(supportedIDs) - 1, indexLow = 0; |
| AsnObjectIdentifier oid1 = { idLength, ids}; |
| |
| if (!idLength) |
| return NULL; |
| |
| while (indexLow <= indexHigh) |
| { |
| INT cmp, i = (indexLow + indexHigh) / 2; |
| if (!(cmp = SnmpUtilOidNCmp(&oid1, &supportedIDs[i].name, idLength))) |
| { |
| *matchingIndex = i; |
| return &supportedIDs[i]; |
| } |
| if (cmp > 0) |
| indexLow = i + 1; |
| else |
| indexHigh = i - 1; |
| } |
| return NULL; |
| } |
| |
| /***************************************************************************** |
| * SnmpExtensionQuery [INETMIB1.@] |
| */ |
| BOOL WINAPI SnmpExtensionQuery(BYTE bPduType, SnmpVarBindList *pVarBindList, |
| AsnInteger32 *pErrorStatus, AsnInteger32 *pErrorIndex) |
| { |
| AsnObjectIdentifier mib2oid = DEFINE_OID(mib2); |
| AsnInteger32 error = SNMP_ERRORSTATUS_NOERROR, errorIndex = 0; |
| UINT i; |
| BOOL ret = TRUE; |
| |
| TRACE("(0x%02x, %p, %p, %p)\n", bPduType, pVarBindList, |
| pErrorStatus, pErrorIndex); |
| |
| for (i = 0; !error && i < pVarBindList->len; i++) |
| { |
| /* Ignore any OIDs not in MIB2 */ |
| if (!SnmpUtilOidNCmp(&pVarBindList->list[i].name, &mib2oid, |
| mib2oid.idLength)) |
| { |
| struct mibImplementation *impl = NULL; |
| UINT len, matchingIndex = 0; |
| |
| TRACE("%s\n", SnmpUtilOidToA(&pVarBindList->list[i].name)); |
| /* Search for an implementation matching as many octets as possible |
| */ |
| for (len = pVarBindList->list[i].name.idLength; |
| len >= minSupportedIDLength && !impl; len--) |
| impl = findSupportedQuery(pVarBindList->list[i].name.ids, len, |
| &matchingIndex); |
| if (impl && impl->query) |
| ret = impl->query(bPduType, &pVarBindList->list[i], &error); |
| else |
| error = SNMP_ERRORSTATUS_NOSUCHNAME; |
| if (error == SNMP_ERRORSTATUS_NOSUCHNAME && |
| bPduType == SNMP_PDU_GETNEXT) |
| { |
| /* GetNext is special: it finds the successor to the given OID, |
| * so we have to continue until an implementation handles the |
| * query or we exhaust the table of supported OIDs. |
| */ |
| for (matchingIndex++; error == SNMP_ERRORSTATUS_NOSUCHNAME && |
| matchingIndex < DEFINE_SIZEOF(supportedIDs); |
| matchingIndex++) |
| { |
| error = SNMP_ERRORSTATUS_NOERROR; |
| impl = &supportedIDs[matchingIndex]; |
| if (impl->query) |
| ret = impl->query(bPduType, &pVarBindList->list[i], |
| &error); |
| else |
| error = SNMP_ERRORSTATUS_NOSUCHNAME; |
| } |
| /* If the query still isn't resolved, set the OID to the |
| * successor to the last entry in the table. |
| */ |
| if (error == SNMP_ERRORSTATUS_NOSUCHNAME) |
| { |
| SnmpUtilOidFree(&pVarBindList->list[i].name); |
| ret = SnmpUtilOidCpy(&pVarBindList->list[i].name, |
| &supportedIDs[matchingIndex - 1].name); |
| pVarBindList->list[i].name.ids[ |
| pVarBindList->list[i].name.idLength - 1] += 1; |
| } |
| } |
| if (error) |
| errorIndex = i + 1; |
| } |
| } |
| *pErrorStatus = error; |
| *pErrorIndex = errorIndex; |
| return ret; |
| } |
| |
| /***************************************************************************** |
| * DllMain [INETMIB1.@] |
| */ |
| BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) |
| { |
| TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved); |
| |
| switch (fdwReason) |
| { |
| case DLL_PROCESS_ATTACH: |
| DisableThreadLibraryCalls(hinstDLL); |
| break; |
| case DLL_PROCESS_DETACH: |
| if (lpvReserved) break; |
| cleanup(); |
| break; |
| } |
| |
| return TRUE; |
| } |