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