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