inetmib1: Support the MIB2 UDP statistics.
[wine/wine64.git] / dlls / inetmib1 / main.c
blobd5146537e79ba7a9394c41b3915707cf0b4e6e2f
1 /*
2 * Copyright 2008 Juan Lang
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.
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.
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
19 #include "config.h"
20 #include <assert.h>
21 #include <stdarg.h>
22 #include <limits.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "snmp.h"
26 #include "iphlpapi.h"
27 #include "wine/debug.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(inetmib1);
31 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
33 TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
35 switch (fdwReason)
37 case DLL_WINE_PREATTACH:
38 return FALSE; /* prefer native version */
39 case DLL_PROCESS_ATTACH:
40 DisableThreadLibraryCalls(hinstDLL);
41 break;
42 case DLL_PROCESS_DETACH:
43 break;
44 default:
45 break;
48 return TRUE;
51 /**
52 * Utility functions
54 static void copyInt(AsnAny *value, void *src)
56 value->asnType = ASN_INTEGER;
57 value->asnValue.number = *(DWORD *)src;
60 static void setStringValue(AsnAny *value, BYTE type, DWORD len, BYTE *str)
62 AsnAny strValue;
64 strValue.asnType = type;
65 strValue.asnValue.string.stream = str;
66 strValue.asnValue.string.length = len;
67 strValue.asnValue.string.dynamic = TRUE;
68 SnmpUtilAsnAnyCpy(value, &strValue);
71 static void copyLengthPrecededString(AsnAny *value, void *src)
73 DWORD len = *(DWORD *)src;
75 setStringValue(value, ASN_OCTETSTRING, len, (BYTE *)src + sizeof(DWORD));
78 typedef void (*copyValueFunc)(AsnAny *value, void *src);
80 struct structToAsnValue
82 size_t offset;
83 copyValueFunc copy;
86 static AsnInteger32 mapStructEntryToValue(struct structToAsnValue *map,
87 UINT mapLen, void *record, UINT id, BYTE bPduType, SnmpVarBind *pVarBind)
89 /* OIDs are 1-based */
90 if (!id)
91 return SNMP_ERRORSTATUS_NOSUCHNAME;
92 --id;
93 if (id >= mapLen)
94 return SNMP_ERRORSTATUS_NOSUCHNAME;
95 if (!map[id].copy)
96 return SNMP_ERRORSTATUS_NOSUCHNAME;
97 map[id].copy(&pVarBind->value, (BYTE *)record + map[id].offset);
98 return SNMP_ERRORSTATUS_NOERROR;
101 static void copyIpAddr(AsnAny *value, void *src)
103 setStringValue(value, ASN_IPADDRESS, sizeof(DWORD), src);
106 static UINT mib2[] = { 1,3,6,1,2,1 };
107 static UINT mib2System[] = { 1,3,6,1,2,1,1 };
109 typedef BOOL (*varqueryfunc)(BYTE bPduType, SnmpVarBind *pVarBind,
110 AsnInteger32 *pErrorStatus);
112 struct mibImplementation
114 AsnObjectIdentifier name;
115 void (*init)(void);
116 varqueryfunc query;
119 static UINT mib2IfNumber[] = { 1,3,6,1,2,1,2,1 };
120 static PMIB_IFTABLE ifTable;
122 static void mib2IfNumberInit(void)
124 DWORD size = 0, ret = GetIfTable(NULL, &size, FALSE);
126 if (ret == ERROR_INSUFFICIENT_BUFFER)
128 ifTable = HeapAlloc(GetProcessHeap(), 0, size);
129 if (ifTable)
130 GetIfTable(ifTable, &size, FALSE);
134 static BOOL mib2IfNumberQuery(BYTE bPduType, SnmpVarBind *pVarBind,
135 AsnInteger32 *pErrorStatus)
137 AsnObjectIdentifier numberOid = DEFINE_OID(mib2IfNumber);
139 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
140 pErrorStatus);
142 switch (bPduType)
144 case SNMP_PDU_GET:
145 case SNMP_PDU_GETNEXT:
146 if ((bPduType == SNMP_PDU_GET &&
147 !SnmpUtilOidNCmp(&pVarBind->name, &numberOid, numberOid.idLength))
148 || SnmpUtilOidNCmp(&pVarBind->name, &numberOid, numberOid.idLength)
149 < 0)
151 DWORD numIfs = ifTable ? ifTable->dwNumEntries : 0;
153 copyInt(&pVarBind->value, &numIfs);
154 if (bPduType == SNMP_PDU_GETNEXT)
155 SnmpUtilOidCpy(&pVarBind->name, &numberOid);
156 *pErrorStatus = SNMP_ERRORSTATUS_NOERROR;
158 else
160 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
161 /* Caller deals with OID if bPduType == SNMP_PDU_GETNEXT, so don't
162 * need to set it here.
165 break;
166 case SNMP_PDU_SET:
167 *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
168 break;
169 default:
170 FIXME("0x%02x: unsupported PDU type\n", bPduType);
171 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
173 return TRUE;
176 static void copyOperStatus(AsnAny *value, void *src)
178 value->asnType = ASN_INTEGER;
179 /* The IPHlpApi definition of operational status differs from the MIB2 one,
180 * so map it to the MIB2 value.
182 switch (*(DWORD *)src)
184 case MIB_IF_OPER_STATUS_OPERATIONAL:
185 value->asnValue.number = MIB_IF_ADMIN_STATUS_UP;
186 break;
187 case MIB_IF_OPER_STATUS_CONNECTING:
188 case MIB_IF_OPER_STATUS_CONNECTED:
189 value->asnValue.number = MIB_IF_ADMIN_STATUS_TESTING;
190 break;
191 default:
192 value->asnValue.number = MIB_IF_ADMIN_STATUS_DOWN;
196 /* Given an OID and a base OID that it must begin with, finds the item and
197 * integer instance from the OID. E.g., given an OID foo.1.2 and a base OID
198 * foo, returns item 1 and instance 2.
199 * If bPduType is not SNMP_PDU_GETNEXT and either the item or instance is
200 * missing, returns SNMP_ERRORSTATUS_NOSUCHNAME.
201 * If bPduType is SNMP_PDU_GETNEXT, returns the successor to the item and
202 * instance, or item 1, instance 1 if either is missing.
204 static AsnInteger32 getItemAndIntegerInstanceFromOid(AsnObjectIdentifier *oid,
205 AsnObjectIdentifier *base, BYTE bPduType, UINT *item, UINT *instance)
207 AsnInteger32 ret = SNMP_ERRORSTATUS_NOERROR;
209 switch (bPduType)
211 case SNMP_PDU_GETNEXT:
212 if (SnmpUtilOidNCmp(oid, base, base->idLength) < 0)
214 *item = 1;
215 *instance = 1;
217 else if (!SnmpUtilOidNCmp(oid, base, base->idLength))
219 if (oid->idLength == base->idLength ||
220 oid->idLength == base->idLength + 1)
222 /* Either the table or an item within the table is specified,
223 * but the instance is not. Get the first instance.
225 *instance = 1;
226 if (oid->idLength == base->idLength + 1)
227 *item = oid->ids[base->idLength];
228 else
229 *item = 1;
231 else
233 *item = oid->ids[base->idLength];
234 *instance = oid->ids[base->idLength + 1] + 1;
237 else
238 ret = SNMP_ERRORSTATUS_NOSUCHNAME;
239 break;
240 default:
241 if (!SnmpUtilOidNCmp(oid, base, base->idLength))
243 if (oid->idLength == base->idLength ||
244 oid->idLength == base->idLength + 1)
246 /* Either the table or an item within the table is specified,
247 * but the instance is not.
249 ret = SNMP_ERRORSTATUS_NOSUCHNAME;
251 else
253 *item = oid->ids[base->idLength];
254 *instance = oid->ids[base->idLength + 1];
257 else
258 ret = SNMP_ERRORSTATUS_NOSUCHNAME;
260 return ret;
263 /* Given an OID and a base OID that it must begin with, finds the item from the
264 * OID. E.g., given an OID foo.1 and a base OID foo, returns item 1.
265 * If bPduType is not SNMP_PDU_GETNEXT and the item is missing, returns
266 * SNMP_ERRORSTATUS_NOSUCHNAME.
267 * If bPduType is SNMP_PDU_GETNEXT, returns the successor to the item, or item
268 * 1 if the item is missing.
270 static AsnInteger32 getItemFromOid(AsnObjectIdentifier *oid,
271 AsnObjectIdentifier *base, BYTE bPduType, UINT *item)
273 AsnInteger32 ret = SNMP_ERRORSTATUS_NOERROR;
275 switch (bPduType)
277 case SNMP_PDU_GETNEXT:
278 if (SnmpUtilOidNCmp(oid, base, base->idLength) < 0)
279 *item = 1;
280 else if (!SnmpUtilOidNCmp(oid, base, base->idLength))
282 if (oid->idLength == base->idLength)
284 /* The item is missing, assume the first item */
285 *item = 1;
287 else
288 *item = oid->ids[base->idLength] + 1;
290 else
291 ret = SNMP_ERRORSTATUS_NOSUCHNAME;
292 break;
293 default:
294 if (!SnmpUtilOidNCmp(oid, base, base->idLength))
296 if (oid->idLength == base->idLength)
298 /* The item is missing */
299 ret = SNMP_ERRORSTATUS_NOSUCHNAME;
301 else
303 *item = oid->ids[base->idLength];
304 if (!*item)
305 ret = SNMP_ERRORSTATUS_NOSUCHNAME;
308 else
309 ret = SNMP_ERRORSTATUS_NOSUCHNAME;
311 return ret;
314 struct GenericTable
316 DWORD numEntries;
317 BYTE entries[1];
320 /* Finds the index in table whose IP address (at offset addressOffset within the
321 * entry) matches that given the OID, which is assumed to have at least 4 IDs.
323 static UINT findOIDIPAddressInTable(AsnObjectIdentifier *oid,
324 struct GenericTable *table, size_t tableEntrySize, size_t addressOffset)
326 DWORD addr;
327 UINT i, index = 0;
329 /* Map the IDs to an IP address in little-endian order */
330 addr = (BYTE)oid->ids[3] << 24 | (BYTE)oid->ids[2] << 16 |
331 (BYTE)oid->ids[1] << 8 | (BYTE)oid->ids[0];
332 /* Find the item whose address matches */
333 for (i = 0; !index && i < table->numEntries; i++)
335 DWORD tableAddr =
336 *(DWORD *)(table->entries + i * tableEntrySize + addressOffset);
338 if (addr == tableAddr)
339 index = i + 1;
341 return index;
344 /* Given an OID and a base OID that it must begin with, finds the item and
345 * element of the table whose IP address matches the instance from the OID.
346 * E.g., given an OID foo.1.2.3.4.5 and a base OID foo, returns item 1 and the
347 * index of the entry in the table whose IP address is 2.3.4.5.
348 * If bPduType is not SNMP_PDU_GETNEXT and either the item or instance is
349 * missing, returns SNMP_ERRORSTATUS_NOSUCHNAME.
350 * If bPduType is SNMP_PDU_GETNEXT, returns the successor to the item and
351 * instance, or item 1, instance 1 if either is missing.
353 static AsnInteger32 getItemAndIpAddressInstanceFromOid(AsnObjectIdentifier *oid,
354 AsnObjectIdentifier *base, BYTE bPduType, struct GenericTable *table,
355 size_t tableEntrySize, size_t addressOffset, UINT *item, UINT *instance)
357 AsnInteger32 ret = SNMP_ERRORSTATUS_NOERROR;
359 if (!table)
360 return SNMP_ERRORSTATUS_NOSUCHNAME;
362 switch (bPduType)
364 case SNMP_PDU_GETNEXT:
365 if (SnmpUtilOidNCmp(oid, base, base->idLength) < 0)
367 /* Return the first item and instance from the table */
368 *item = 1;
369 *instance = 1;
371 else if (!SnmpUtilOidNCmp(oid, base, base->idLength) &&
372 oid->idLength < base->idLength + 5)
374 /* Either the table or an item is specified, but the instance is
375 * not.
377 *instance = 1;
378 if (oid->idLength >= base->idLength + 1)
380 *item = oid->ids[base->idLength];
381 if (!*item)
382 *item = 1;
384 else
385 *item = 1;
387 else if (!SnmpUtilOidNCmp(oid, base, base->idLength) &&
388 oid->idLength == base->idLength + 5)
390 *item = oid->ids[base->idLength];
391 if (!*item)
393 *instance = 1;
394 *item = 1;
396 else
398 AsnObjectIdentifier ipOid = { 4, oid->ids + base->idLength + 1
401 *instance = findOIDIPAddressInTable(&ipOid, table,
402 tableEntrySize, addressOffset) + 1;
403 if (*instance > table->numEntries)
404 ret = SNMP_ERRORSTATUS_NOSUCHNAME;
407 else
408 ret = SNMP_ERRORSTATUS_NOSUCHNAME;
409 break;
410 default:
411 if (!SnmpUtilOidNCmp(oid, base, base->idLength) &&
412 oid->idLength == base->idLength + 5)
414 *item = oid->ids[base->idLength];
415 if (!*item)
416 ret = SNMP_ERRORSTATUS_NOSUCHNAME;
417 else
419 AsnObjectIdentifier ipOid = { 4, oid->ids + base->idLength + 1
422 *instance = findOIDIPAddressInTable(&ipOid, table,
423 tableEntrySize, addressOffset);
424 if (!*instance)
425 ret = SNMP_ERRORSTATUS_NOSUCHNAME;
428 else
429 ret = SNMP_ERRORSTATUS_NOSUCHNAME;
431 return ret;
434 static void setOidWithItem(AsnObjectIdentifier *dst, AsnObjectIdentifier *base,
435 UINT item)
437 UINT id;
438 AsnObjectIdentifier oid;
440 SnmpUtilOidCpy(dst, base);
441 oid.idLength = 1;
442 oid.ids = &id;
443 id = item;
444 SnmpUtilOidAppend(dst, &oid);
447 static void setOidWithItemAndIpAddr(AsnObjectIdentifier *dst,
448 AsnObjectIdentifier *base, UINT item, DWORD addr)
450 UINT id;
451 BYTE *ptr;
452 AsnObjectIdentifier oid;
454 setOidWithItem(dst, base, item);
455 oid.idLength = 1;
456 oid.ids = &id;
457 for (ptr = (BYTE *)&addr; ptr < (BYTE *)&addr + sizeof(DWORD); ptr++)
459 id = *ptr;
460 SnmpUtilOidAppend(dst, &oid);
464 static void setOidWithItemAndInteger(AsnObjectIdentifier *dst,
465 AsnObjectIdentifier *base, UINT item, UINT instance)
467 AsnObjectIdentifier oid;
469 setOidWithItem(dst, base, item);
470 oid.idLength = 1;
471 oid.ids = &instance;
472 SnmpUtilOidAppend(dst, &oid);
475 static struct structToAsnValue mib2IfEntryMap[] = {
476 { FIELD_OFFSET(MIB_IFROW, dwIndex), copyInt },
477 { FIELD_OFFSET(MIB_IFROW, dwDescrLen), copyLengthPrecededString },
478 { FIELD_OFFSET(MIB_IFROW, dwType), copyInt },
479 { FIELD_OFFSET(MIB_IFROW, dwMtu), copyInt },
480 { FIELD_OFFSET(MIB_IFROW, dwSpeed), copyInt },
481 { FIELD_OFFSET(MIB_IFROW, dwPhysAddrLen), copyLengthPrecededString },
482 { FIELD_OFFSET(MIB_IFROW, dwAdminStatus), copyInt },
483 { FIELD_OFFSET(MIB_IFROW, dwOperStatus), copyOperStatus },
484 { FIELD_OFFSET(MIB_IFROW, dwLastChange), copyInt },
485 { FIELD_OFFSET(MIB_IFROW, dwInOctets), copyInt },
486 { FIELD_OFFSET(MIB_IFROW, dwInUcastPkts), copyInt },
487 { FIELD_OFFSET(MIB_IFROW, dwInNUcastPkts), copyInt },
488 { FIELD_OFFSET(MIB_IFROW, dwInDiscards), copyInt },
489 { FIELD_OFFSET(MIB_IFROW, dwInErrors), copyInt },
490 { FIELD_OFFSET(MIB_IFROW, dwInUnknownProtos), copyInt },
491 { FIELD_OFFSET(MIB_IFROW, dwOutOctets), copyInt },
492 { FIELD_OFFSET(MIB_IFROW, dwOutUcastPkts), copyInt },
493 { FIELD_OFFSET(MIB_IFROW, dwOutNUcastPkts), copyInt },
494 { FIELD_OFFSET(MIB_IFROW, dwOutDiscards), copyInt },
495 { FIELD_OFFSET(MIB_IFROW, dwOutErrors), copyInt },
496 { FIELD_OFFSET(MIB_IFROW, dwOutQLen), copyInt },
499 static UINT mib2IfEntry[] = { 1,3,6,1,2,1,2,2,1 };
501 static BOOL mib2IfEntryQuery(BYTE bPduType, SnmpVarBind *pVarBind,
502 AsnInteger32 *pErrorStatus)
504 AsnObjectIdentifier entryOid = DEFINE_OID(mib2IfEntry);
506 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
507 pErrorStatus);
509 switch (bPduType)
511 case SNMP_PDU_GET:
512 case SNMP_PDU_GETNEXT:
513 if (!ifTable)
515 /* There is no interface present, so let the caller deal
516 * with finding the successor.
518 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
520 else
522 UINT tableIndex = 0, item = 0;
524 *pErrorStatus = getItemAndIntegerInstanceFromOid(&pVarBind->name,
525 &entryOid, bPduType, &item, &tableIndex);
526 if (!*pErrorStatus)
528 assert(tableIndex);
529 assert(item);
530 if (tableIndex > ifTable->dwNumEntries)
531 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
532 else
534 *pErrorStatus = mapStructEntryToValue(mib2IfEntryMap,
535 DEFINE_SIZEOF(mib2IfEntryMap),
536 &ifTable->table[tableIndex - 1], item, bPduType,
537 pVarBind);
538 if (bPduType == SNMP_PDU_GETNEXT)
539 setOidWithItemAndInteger(&pVarBind->name, &entryOid,
540 item, tableIndex);
544 break;
545 case SNMP_PDU_SET:
546 *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
547 break;
548 default:
549 FIXME("0x%02x: unsupported PDU type\n", bPduType);
550 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
552 return TRUE;
555 static UINT mib2Ip[] = { 1,3,6,1,2,1,4 };
556 static MIB_IPSTATS ipStats;
558 static void mib2IpStatsInit(void)
560 GetIpStatistics(&ipStats);
563 static struct structToAsnValue mib2IpMap[] = {
564 { FIELD_OFFSET(MIB_IPSTATS, dwForwarding), copyInt }, /* 1 */
565 { FIELD_OFFSET(MIB_IPSTATS, dwDefaultTTL), copyInt }, /* 2 */
566 { FIELD_OFFSET(MIB_IPSTATS, dwInReceives), copyInt }, /* 3 */
567 { FIELD_OFFSET(MIB_IPSTATS, dwInHdrErrors), copyInt }, /* 4 */
568 { FIELD_OFFSET(MIB_IPSTATS, dwInAddrErrors), copyInt }, /* 5 */
569 { FIELD_OFFSET(MIB_IPSTATS, dwForwDatagrams), copyInt }, /* 6 */
570 { FIELD_OFFSET(MIB_IPSTATS, dwInUnknownProtos), copyInt }, /* 7 */
571 { FIELD_OFFSET(MIB_IPSTATS, dwInDiscards), copyInt }, /* 8 */
572 { FIELD_OFFSET(MIB_IPSTATS, dwInDelivers), copyInt }, /* 9 */
573 { FIELD_OFFSET(MIB_IPSTATS, dwOutRequests), copyInt }, /* 10 */
574 { FIELD_OFFSET(MIB_IPSTATS, dwOutDiscards), copyInt }, /* 11 */
575 { FIELD_OFFSET(MIB_IPSTATS, dwOutNoRoutes), copyInt }, /* 12 */
576 { FIELD_OFFSET(MIB_IPSTATS, dwReasmTimeout), copyInt }, /* 13 */
577 { FIELD_OFFSET(MIB_IPSTATS, dwReasmReqds), copyInt }, /* 14 */
578 { FIELD_OFFSET(MIB_IPSTATS, dwReasmOks), copyInt }, /* 15 */
579 { FIELD_OFFSET(MIB_IPSTATS, dwReasmFails), copyInt }, /* 16 */
580 { FIELD_OFFSET(MIB_IPSTATS, dwFragOks), copyInt }, /* 17 */
581 { FIELD_OFFSET(MIB_IPSTATS, dwFragFails), copyInt }, /* 18 */
582 { FIELD_OFFSET(MIB_IPSTATS, dwFragCreates), copyInt }, /* 19 */
583 { 0, NULL }, /* 20: not used, IP addr table */
584 { 0, NULL }, /* 21: not used, route table */
585 { 0, NULL }, /* 22: not used, net to media (ARP) table */
586 { FIELD_OFFSET(MIB_IPSTATS, dwRoutingDiscards), copyInt }, /* 23 */
589 static BOOL mib2IpStatsQuery(BYTE bPduType, SnmpVarBind *pVarBind,
590 AsnInteger32 *pErrorStatus)
592 AsnObjectIdentifier myOid = DEFINE_OID(mib2Ip);
593 UINT item = 0;
595 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
596 pErrorStatus);
598 switch (bPduType)
600 case SNMP_PDU_GET:
601 case SNMP_PDU_GETNEXT:
602 *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType,
603 &item);
604 if (!*pErrorStatus)
606 *pErrorStatus = mapStructEntryToValue(mib2IpMap,
607 DEFINE_SIZEOF(mib2IpMap), &ipStats, item, bPduType, pVarBind);
608 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
609 setOidWithItem(&pVarBind->name, &myOid, item);
611 break;
612 case SNMP_PDU_SET:
613 *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
614 break;
615 default:
616 FIXME("0x%02x: unsupported PDU type\n", bPduType);
617 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
619 return TRUE;
622 static UINT mib2IpAddr[] = { 1,3,6,1,2,1,4,20,1 };
623 static PMIB_IPADDRTABLE ipAddrTable;
625 static struct structToAsnValue mib2IpAddrMap[] = {
626 { FIELD_OFFSET(MIB_IPADDRROW, dwAddr), copyIpAddr },
627 { FIELD_OFFSET(MIB_IPADDRROW, dwIndex), copyInt },
628 { FIELD_OFFSET(MIB_IPADDRROW, dwMask), copyIpAddr },
629 { FIELD_OFFSET(MIB_IPADDRROW, dwBCastAddr), copyInt },
630 { FIELD_OFFSET(MIB_IPADDRROW, dwReasmSize), copyInt },
633 static void mib2IpAddrInit(void)
635 DWORD size = 0, ret = GetIpAddrTable(NULL, &size, FALSE);
637 if (ret == ERROR_INSUFFICIENT_BUFFER)
639 ipAddrTable = HeapAlloc(GetProcessHeap(), 0, size);
640 if (ipAddrTable)
641 GetIpAddrTable(ipAddrTable, &size, FALSE);
645 static BOOL mib2IpAddrQuery(BYTE bPduType, SnmpVarBind *pVarBind,
646 AsnInteger32 *pErrorStatus)
648 AsnObjectIdentifier myOid = DEFINE_OID(mib2IpAddr);
649 UINT tableIndex = 0, item = 0;
651 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
652 pErrorStatus);
654 switch (bPduType)
656 case SNMP_PDU_GET:
657 case SNMP_PDU_GETNEXT:
658 *pErrorStatus = getItemAndIpAddressInstanceFromOid(&pVarBind->name,
659 &myOid, bPduType, (struct GenericTable *)ipAddrTable,
660 sizeof(MIB_IPADDRROW), FIELD_OFFSET(MIB_IPADDRROW, dwAddr), &item,
661 &tableIndex);
662 if (!*pErrorStatus)
664 assert(tableIndex);
665 assert(item);
666 *pErrorStatus = mapStructEntryToValue(mib2IpAddrMap,
667 DEFINE_SIZEOF(mib2IpAddrMap),
668 &ipAddrTable->table[tableIndex - 1], item, bPduType, pVarBind);
669 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
670 setOidWithItemAndIpAddr(&pVarBind->name, &myOid, item,
671 ipAddrTable->table[tableIndex - 1].dwAddr);
673 break;
674 case SNMP_PDU_SET:
675 *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
676 break;
677 default:
678 FIXME("0x%02x: unsupported PDU type\n", bPduType);
679 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
681 return TRUE;
684 static UINT mib2IpRoute[] = { 1,3,6,1,2,1,4,21,1 };
685 static PMIB_IPFORWARDTABLE ipRouteTable;
687 static struct structToAsnValue mib2IpRouteMap[] = {
688 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardDest), copyIpAddr },
689 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardIfIndex), copyInt },
690 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric1), copyInt },
691 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric2), copyInt },
692 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric3), copyInt },
693 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric4), copyInt },
694 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardNextHop), copyIpAddr },
695 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardType), copyInt },
696 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardProto), copyInt },
697 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardAge), copyInt },
698 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMask), copyIpAddr },
699 { FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardMetric5), copyInt },
702 static void mib2IpRouteInit(void)
704 DWORD size = 0, ret = GetIpForwardTable(NULL, &size, FALSE);
706 if (ret == ERROR_INSUFFICIENT_BUFFER)
708 ipRouteTable = HeapAlloc(GetProcessHeap(), 0, size);
709 if (ipRouteTable)
710 GetIpForwardTable(ipRouteTable, &size, FALSE);
714 static BOOL mib2IpRouteQuery(BYTE bPduType, SnmpVarBind *pVarBind,
715 AsnInteger32 *pErrorStatus)
717 AsnObjectIdentifier myOid = DEFINE_OID(mib2IpRoute);
718 UINT tableIndex = 0, item = 0;
720 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
721 pErrorStatus);
723 switch (bPduType)
725 case SNMP_PDU_GET:
726 case SNMP_PDU_GETNEXT:
727 *pErrorStatus = getItemAndIpAddressInstanceFromOid(&pVarBind->name,
728 &myOid, bPduType, (struct GenericTable *)ipRouteTable,
729 sizeof(MIB_IPFORWARDROW),
730 FIELD_OFFSET(MIB_IPFORWARDROW, dwForwardDest), &item, &tableIndex);
731 if (!*pErrorStatus)
733 assert(tableIndex);
734 assert(item);
735 *pErrorStatus = mapStructEntryToValue(mib2IpRouteMap,
736 DEFINE_SIZEOF(mib2IpRouteMap),
737 &ipRouteTable->table[tableIndex - 1], item, bPduType, pVarBind);
738 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
739 setOidWithItemAndIpAddr(&pVarBind->name, &myOid, item,
740 ipRouteTable->table[tableIndex - 1].dwForwardDest);
742 break;
743 case SNMP_PDU_SET:
744 *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
745 break;
746 default:
747 FIXME("0x%02x: unsupported PDU type\n", bPduType);
748 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
750 return TRUE;
753 static UINT mib2IpNet[] = { 1,3,6,1,2,1,4,22,1 };
754 static PMIB_IPNETTABLE ipNetTable;
756 static struct structToAsnValue mib2IpNetMap[] = {
757 { FIELD_OFFSET(MIB_IPNETROW, dwIndex), copyInt },
758 { FIELD_OFFSET(MIB_IPNETROW, dwPhysAddrLen), copyLengthPrecededString },
759 { FIELD_OFFSET(MIB_IPNETROW, dwAddr), copyIpAddr },
760 { FIELD_OFFSET(MIB_IPNETROW, dwType), copyInt },
763 static void mib2IpNetInit(void)
765 DWORD size = 0, ret = GetIpNetTable(NULL, &size, FALSE);
767 if (ret == ERROR_INSUFFICIENT_BUFFER)
769 ipNetTable = HeapAlloc(GetProcessHeap(), 0, size);
770 if (ipNetTable)
771 GetIpNetTable(ipNetTable, &size, FALSE);
775 static BOOL mib2IpNetQuery(BYTE bPduType, SnmpVarBind *pVarBind,
776 AsnInteger32 *pErrorStatus)
778 AsnObjectIdentifier myOid = DEFINE_OID(mib2IpNet);
780 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
781 pErrorStatus);
783 switch (bPduType)
785 case SNMP_PDU_GET:
786 case SNMP_PDU_GETNEXT:
787 if (!ipNetTable)
788 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
789 else
791 UINT tableIndex = 0, item = 0;
793 *pErrorStatus = getItemAndIntegerInstanceFromOid(&pVarBind->name,
794 &myOid, bPduType, &item, &tableIndex);
795 if (!*pErrorStatus)
797 assert(tableIndex);
798 assert(item);
799 if (tableIndex > ipNetTable->dwNumEntries)
800 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
801 else
803 *pErrorStatus = mapStructEntryToValue(mib2IpNetMap,
804 DEFINE_SIZEOF(mib2IpNetMap),
805 &ipNetTable[tableIndex - 1], item, bPduType, pVarBind);
806 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
807 setOidWithItemAndInteger(&pVarBind->name, &myOid, item,
808 tableIndex);
812 break;
813 case SNMP_PDU_SET:
814 *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
815 break;
816 default:
817 FIXME("0x%02x: unsupported PDU type\n", bPduType);
818 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
820 return TRUE;
823 static UINT mib2Icmp[] = { 1,3,6,1,2,1,5 };
824 static MIB_ICMP icmpStats;
826 static void mib2IcmpInit(void)
828 GetIcmpStatistics(&icmpStats);
831 static struct structToAsnValue mib2IcmpMap[] = {
832 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwMsgs), copyInt },
833 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwErrors), copyInt },
834 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwDestUnreachs), copyInt },
835 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwTimeExcds), copyInt },
836 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwParmProbs), copyInt },
837 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwSrcQuenchs), copyInt },
838 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwRedirects), copyInt },
839 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwEchos), copyInt },
840 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwEchoReps), copyInt },
841 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwTimestamps), copyInt },
842 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwTimestampReps), copyInt },
843 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwAddrMasks), copyInt },
844 { FIELD_OFFSET(MIBICMPINFO, icmpInStats.dwAddrMaskReps), copyInt },
845 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwMsgs), copyInt },
846 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwErrors), copyInt },
847 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwDestUnreachs), copyInt },
848 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwTimeExcds), copyInt },
849 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwParmProbs), copyInt },
850 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwSrcQuenchs), copyInt },
851 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwRedirects), copyInt },
852 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwEchos), copyInt },
853 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwEchoReps), copyInt },
854 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwTimestamps), copyInt },
855 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwTimestampReps), copyInt },
856 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwAddrMasks), copyInt },
857 { FIELD_OFFSET(MIBICMPINFO, icmpOutStats.dwAddrMaskReps), copyInt },
860 static BOOL mib2IcmpQuery(BYTE bPduType, SnmpVarBind *pVarBind,
861 AsnInteger32 *pErrorStatus)
863 AsnObjectIdentifier myOid = DEFINE_OID(mib2Icmp);
864 UINT item = 0;
866 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
867 pErrorStatus);
869 switch (bPduType)
871 case SNMP_PDU_GET:
872 case SNMP_PDU_GETNEXT:
873 *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType,
874 &item);
875 if (!*pErrorStatus)
877 *pErrorStatus = mapStructEntryToValue(mib2IcmpMap,
878 DEFINE_SIZEOF(mib2IcmpMap), &icmpStats, item, bPduType,
879 pVarBind);
880 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
881 setOidWithItem(&pVarBind->name, &myOid, item);
883 break;
884 case SNMP_PDU_SET:
885 *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
886 break;
887 default:
888 FIXME("0x%02x: unsupported PDU type\n", bPduType);
889 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
891 return TRUE;
894 static UINT mib2Tcp[] = { 1,3,6,1,2,1,6 };
895 static MIB_TCPSTATS tcpStats;
897 static void mib2TcpInit(void)
899 GetTcpStatistics(&tcpStats);
902 static struct structToAsnValue mib2TcpMap[] = {
903 { FIELD_OFFSET(MIB_TCPSTATS, dwRtoAlgorithm), copyInt },
904 { FIELD_OFFSET(MIB_TCPSTATS, dwRtoMin), copyInt },
905 { FIELD_OFFSET(MIB_TCPSTATS, dwRtoMax), copyInt },
906 { FIELD_OFFSET(MIB_TCPSTATS, dwMaxConn), copyInt },
907 { FIELD_OFFSET(MIB_TCPSTATS, dwActiveOpens), copyInt },
908 { FIELD_OFFSET(MIB_TCPSTATS, dwPassiveOpens), copyInt },
909 { FIELD_OFFSET(MIB_TCPSTATS, dwAttemptFails), copyInt },
910 { FIELD_OFFSET(MIB_TCPSTATS, dwEstabResets), copyInt },
911 { FIELD_OFFSET(MIB_TCPSTATS, dwCurrEstab), copyInt },
912 { FIELD_OFFSET(MIB_TCPSTATS, dwInSegs), copyInt },
913 { FIELD_OFFSET(MIB_TCPSTATS, dwOutSegs), copyInt },
914 { FIELD_OFFSET(MIB_TCPSTATS, dwRetransSegs), copyInt },
915 { FIELD_OFFSET(MIB_TCPSTATS, dwInErrs), copyInt },
916 { FIELD_OFFSET(MIB_TCPSTATS, dwOutRsts), copyInt },
917 { FIELD_OFFSET(MIB_TCPSTATS, dwNumConns), copyInt },
920 static BOOL mib2TcpQuery(BYTE bPduType, SnmpVarBind *pVarBind,
921 AsnInteger32 *pErrorStatus)
923 AsnObjectIdentifier myOid = DEFINE_OID(mib2Tcp);
924 UINT item = 0;
926 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
927 pErrorStatus);
929 switch (bPduType)
931 case SNMP_PDU_GET:
932 case SNMP_PDU_GETNEXT:
933 *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType,
934 &item);
935 if (!*pErrorStatus)
937 *pErrorStatus = mapStructEntryToValue(mib2TcpMap,
938 DEFINE_SIZEOF(mib2TcpMap), &tcpStats, item, bPduType, pVarBind);
939 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
940 setOidWithItem(&pVarBind->name, &myOid, item);
942 break;
943 case SNMP_PDU_SET:
944 *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
945 break;
946 default:
947 FIXME("0x%02x: unsupported PDU type\n", bPduType);
948 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
950 return TRUE;
953 static UINT mib2Udp[] = { 1,3,6,1,2,1,7 };
954 static MIB_UDPSTATS udpStats;
956 static void mib2UdpInit(void)
958 GetUdpStatistics(&udpStats);
961 static struct structToAsnValue mib2UdpMap[] = {
962 { FIELD_OFFSET(MIB_UDPSTATS, dwInDatagrams), copyInt },
963 { FIELD_OFFSET(MIB_UDPSTATS, dwNoPorts), copyInt },
964 { FIELD_OFFSET(MIB_UDPSTATS, dwInErrors), copyInt },
965 { FIELD_OFFSET(MIB_UDPSTATS, dwOutDatagrams), copyInt },
968 static BOOL mib2UdpQuery(BYTE bPduType, SnmpVarBind *pVarBind,
969 AsnInteger32 *pErrorStatus)
971 AsnObjectIdentifier myOid = DEFINE_OID(mib2Udp);
972 UINT item;
974 TRACE("(0x%02x, %s, %p)\n", bPduType, SnmpUtilOidToA(&pVarBind->name),
975 pErrorStatus);
977 switch (bPduType)
979 case SNMP_PDU_GET:
980 case SNMP_PDU_GETNEXT:
981 *pErrorStatus = getItemFromOid(&pVarBind->name, &myOid, bPduType,
982 &item);
983 if (!*pErrorStatus)
985 *pErrorStatus = mapStructEntryToValue(mib2UdpMap,
986 DEFINE_SIZEOF(mib2UdpMap), &udpStats, item, bPduType, pVarBind);
987 if (!*pErrorStatus && bPduType == SNMP_PDU_GETNEXT)
988 setOidWithItem(&pVarBind->name, &myOid, item);
990 break;
991 case SNMP_PDU_SET:
992 *pErrorStatus = SNMP_ERRORSTATUS_READONLY;
993 break;
994 default:
995 FIXME("0x%02x: unsupported PDU type\n", bPduType);
996 *pErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
998 return TRUE;
1001 /* This list MUST BE lexicographically sorted */
1002 static struct mibImplementation supportedIDs[] = {
1003 { DEFINE_OID(mib2IfNumber), mib2IfNumberInit, mib2IfNumberQuery },
1004 { DEFINE_OID(mib2IfEntry), NULL, mib2IfEntryQuery },
1005 { DEFINE_OID(mib2Ip), mib2IpStatsInit, mib2IpStatsQuery },
1006 { DEFINE_OID(mib2IpAddr), mib2IpAddrInit, mib2IpAddrQuery },
1007 { DEFINE_OID(mib2IpRoute), mib2IpRouteInit, mib2IpRouteQuery },
1008 { DEFINE_OID(mib2IpNet), mib2IpNetInit, mib2IpNetQuery },
1009 { DEFINE_OID(mib2Icmp), mib2IcmpInit, mib2IcmpQuery },
1010 { DEFINE_OID(mib2Tcp), mib2TcpInit, mib2TcpQuery },
1011 { DEFINE_OID(mib2Udp), mib2UdpInit, mib2UdpQuery },
1013 static UINT minSupportedIDLength;
1015 BOOL WINAPI SnmpExtensionInit(DWORD dwUptimeReference,
1016 HANDLE *phSubagentTrapEvent, AsnObjectIdentifier *pFirstSupportedRegion)
1018 AsnObjectIdentifier myOid = DEFINE_OID(mib2System);
1019 UINT i;
1021 TRACE("(%d, %p, %p)\n", dwUptimeReference, phSubagentTrapEvent,
1022 pFirstSupportedRegion);
1024 minSupportedIDLength = UINT_MAX;
1025 for (i = 0; i < sizeof(supportedIDs) / sizeof(supportedIDs[0]); i++)
1027 if (supportedIDs[i].init)
1028 supportedIDs[i].init();
1029 if (supportedIDs[i].name.idLength < minSupportedIDLength)
1030 minSupportedIDLength = supportedIDs[i].name.idLength;
1032 *phSubagentTrapEvent = NULL;
1033 SnmpUtilOidCpy(pFirstSupportedRegion, &myOid);
1034 return TRUE;
1037 static struct mibImplementation *findSupportedQuery(UINT *ids, UINT idLength,
1038 UINT *matchingIndex)
1040 int indexHigh = DEFINE_SIZEOF(supportedIDs) - 1, indexLow = 0, i;
1041 struct mibImplementation *impl = NULL;
1042 AsnObjectIdentifier oid1 = { idLength, ids};
1044 if (!idLength)
1045 return NULL;
1046 for (i = (indexLow + indexHigh) / 2; !impl && indexLow <= indexHigh;
1047 i = (indexLow + indexHigh) / 2)
1049 INT cmp;
1051 cmp = SnmpUtilOidNCmp(&oid1, &supportedIDs[i].name, idLength);
1052 if (!cmp)
1054 impl = &supportedIDs[i];
1055 *matchingIndex = i;
1057 else if (cmp > 0)
1058 indexLow = i + 1;
1059 else
1060 indexHigh = i - 1;
1062 return impl;
1065 BOOL WINAPI SnmpExtensionQuery(BYTE bPduType, SnmpVarBindList *pVarBindList,
1066 AsnInteger32 *pErrorStatus, AsnInteger32 *pErrorIndex)
1068 AsnObjectIdentifier mib2oid = DEFINE_OID(mib2);
1069 AsnInteger32 error = SNMP_ERRORSTATUS_NOERROR, errorIndex = 0;
1070 UINT i;
1072 TRACE("(0x%02x, %p, %p, %p)\n", bPduType, pVarBindList,
1073 pErrorStatus, pErrorIndex);
1075 for (i = 0; !error && i < pVarBindList->len; i++)
1077 /* Ignore any OIDs not in MIB2 */
1078 if (!SnmpUtilOidNCmp(&pVarBindList->list[i].name, &mib2oid,
1079 mib2oid.idLength))
1081 struct mibImplementation *impl = NULL;
1082 UINT len, matchingIndex = 0;
1084 TRACE("%s\n", SnmpUtilOidToA(&pVarBindList->list[i].name));
1085 /* Search for an implementation matching as many octets as possible
1087 for (len = pVarBindList->list[i].name.idLength;
1088 len >= minSupportedIDLength && !impl; len--)
1089 impl = findSupportedQuery(pVarBindList->list[i].name.ids, len,
1090 &matchingIndex);
1091 if (impl && impl->query)
1092 impl->query(bPduType, &pVarBindList->list[i], &error);
1093 else
1094 error = SNMP_ERRORSTATUS_NOSUCHNAME;
1095 if (error == SNMP_ERRORSTATUS_NOSUCHNAME &&
1096 bPduType == SNMP_PDU_GETNEXT)
1098 /* GetNext is special: it finds the successor to the given OID,
1099 * so we have to continue until an implementation handles the
1100 * query or we exhaust the table of supported OIDs.
1102 for (; error == SNMP_ERRORSTATUS_NOSUCHNAME &&
1103 matchingIndex < DEFINE_SIZEOF(supportedIDs);
1104 matchingIndex++)
1106 error = SNMP_ERRORSTATUS_NOERROR;
1107 impl = &supportedIDs[matchingIndex];
1108 if (impl->query)
1109 impl->query(bPduType, &pVarBindList->list[i], &error);
1110 else
1111 error = SNMP_ERRORSTATUS_NOSUCHNAME;
1113 /* If the query still isn't resolved, set the OID to the
1114 * successor to the last entry in the table.
1116 if (error == SNMP_ERRORSTATUS_NOSUCHNAME)
1118 SnmpUtilOidFree(&pVarBindList->list[i].name);
1119 SnmpUtilOidCpy(&pVarBindList->list[i].name,
1120 &supportedIDs[matchingIndex - 1].name);
1121 pVarBindList->list[i].name.ids[
1122 pVarBindList->list[i].name.idLength - 1] += 1;
1125 if (error)
1126 errorIndex = i + 1;
1129 *pErrorStatus = error;
1130 *pErrorIndex = errorIndex;
1131 return TRUE;