c0719c1d6350d85391a67c156ece23aeec8c3e72
[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 "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 if (lpvReserved) break;
1458 cleanup();
1459 break;
1460 }
1461
1462 return TRUE;
1463 }