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