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