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