1/*-
2 * Copyright (c) 2006 The FreeBSD Project
3 * All rights reserved.
4 *
5 * Author: Shteryana Shopova <syrinx@FreeBSD.org>
6 *
7 * Redistribution of this software and documentation and use in source and
8 * binary forms, with or without modification, are permitted provided that
9 * the following conditions are met:
10 *
11 * 1. Redistributions of source code or documentation must retain the above
12 *    copyright notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * Textual conventions for OctetStrings
30 */
31
32#include <sys/param.h>
33#include <sys/queue.h>
34#include <sys/socket.h>
35#include <sys/uio.h>
36
37#include <ctype.h>
38#include <err.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <syslog.h>
45#include <unistd.h>
46
47#include <arpa/inet.h>
48#include <netinet/in.h>
49
50#include <bsnmp/asn1.h>
51#include <bsnmp/snmp.h>
52#include "bsnmptc.h"
53#include "bsnmptools.h"
54
55/* OctetString, DisplayString */
56static char *snmp_oct2str(uint32_t, char *, char *);
57static char *snmp_str2asn_oid(char *, struct asn_oid *);
58static int parse_octetstring(struct snmp_value *, char *);
59
60/* DateAndTime */
61static char *snmp_octstr2date(uint32_t, char *, char *);
62static char *snmp_date2asn_oid(char * , struct asn_oid *);
63static int parse_dateandtime(struct snmp_value *, char *);
64
65/* PhysAddress */
66static char *snmp_oct2physAddr(uint32_t, char *, char *);
67static char *snmp_addr2asn_oid(char *, struct asn_oid *);
68static int parse_physaddress(struct snmp_value *, char *);
69
70/* NTPTimeStamp */
71static char *snmp_oct2ntp_ts(uint32_t, char *, char *);
72static char *snmp_ntp_ts2asn_oid(char *, struct asn_oid *);
73static int parse_ntp_ts(struct snmp_value *, char *);
74
75/* BridgeId */
76static char *snmp_oct2bridgeid(uint32_t, char *, char *);
77static char *snmp_bridgeid2oct(char *, struct asn_oid *);
78static int parse_bridge_id(struct snmp_value *, char *);
79
80/* BridgePortId */
81static char *snmp_oct2bport_id(uint32_t, char *, char *);
82static char *snmp_bport_id2oct(char *, struct asn_oid *);
83static int parse_bport_id(struct snmp_value *, char *);
84
85/* InetAddress */
86static char *snmp_oct2inetaddr(uint32_t len, char *octets, char *buf);
87static char *snmp_inetaddr2oct(char *str, struct asn_oid *oid);
88static int32_t parse_inetaddr(struct snmp_value *value, char *string);
89
90static char *snmp_oct2bits(uint32_t len, char *octets, char *buf);
91static char *snmp_bits2oct(char *str, struct asn_oid *oid);
92static int32_t parse_bits(struct snmp_value *value, char *string);
93
94static struct snmp_text_conv {
95	enum snmp_tc	tc;
96	const char	*tc_str;
97	int32_t		len;
98	snmp_oct2tc_f	oct2tc;
99	snmp_tc2oid_f	tc2oid;
100	snmp_tc2oct_f	tc2oct;
101} text_convs[] = {
102	{ SNMP_STRING, "OctetString", SNMP_VAR_STRSZ,
103	  snmp_oct2str, snmp_str2asn_oid, parse_octetstring },
104
105	{ SNMP_DISPLAYSTRING, "DisplayString" , SNMP_VAR_STRSZ,
106	  snmp_oct2str, snmp_str2asn_oid, parse_octetstring },
107
108	{ SNMP_DATEANDTIME, "DateAndTime", SNMP_DATETIME_STRSZ,
109	  snmp_octstr2date, snmp_date2asn_oid, parse_dateandtime },
110
111	{ SNMP_PHYSADDR, "PhysAddress", SNMP_PHYSADDR_STRSZ,
112	  snmp_oct2physAddr, snmp_addr2asn_oid, parse_physaddress },
113
114	{ SNMP_ATMESI, "AtmESI", SNMP_PHYSADDR_STRSZ,
115	  snmp_oct2physAddr, snmp_addr2asn_oid, parse_physaddress },
116
117	{ SNMP_NTP_TIMESTAMP, "NTPTimeStamp", SNMP_NTP_TS_STRSZ,
118	  snmp_oct2ntp_ts, snmp_ntp_ts2asn_oid, parse_ntp_ts },
119
120	{ SNMP_MACADDRESS, "MacAddress", SNMP_PHYSADDR_STRSZ,
121	  snmp_oct2physAddr, snmp_addr2asn_oid, parse_physaddress },
122
123	{ SNMP_BRIDGE_ID, "BridgeId", SNMP_BRIDGEID_STRSZ,
124	  snmp_oct2bridgeid, snmp_bridgeid2oct, parse_bridge_id },
125
126	{ SNMP_BPORT_ID, "BridgePortId", SNMP_BPORT_STRSZ,
127	  snmp_oct2bport_id, snmp_bport_id2oct, parse_bport_id },
128
129	{ SNMP_INETADDRESS, "InetAddress", SNMP_INADDRS_STRSZ,
130	  snmp_oct2inetaddr, snmp_inetaddr2oct, parse_inetaddr },
131
132	{ SNMP_TC_OWN, "BITS", SNMP_VAR_STRSZ,
133	  snmp_oct2bits, snmp_bits2oct, parse_bits },
134
135	{ SNMP_UNKNOWN, "Unknown", SNMP_VAR_STRSZ, snmp_oct2str,
136	  snmp_str2asn_oid, parse_octetstring }	/* keep last */
137};
138
139/* Common API */
140enum snmp_tc
141snmp_get_tc(char *str)
142{
143	int i;
144	for (i = 0; i < SNMP_UNKNOWN; i++) {
145		if (!strncmp(text_convs[i].tc_str, str,
146		    strlen(text_convs[i].tc_str)))
147			return (text_convs[i].tc);
148	}
149
150	return (SNMP_STRING);
151}
152
153char *
154snmp_oct2tc(enum snmp_tc tc, uint32_t len, char *octets)
155{
156	uint32_t tc_len;
157	char * buf;
158
159	if (tc > SNMP_UNKNOWN)
160		tc = SNMP_UNKNOWN;
161
162	if (text_convs[tc].len > 0)
163		tc_len = text_convs[tc].len;
164	else
165		tc_len = 2 * len + 3;
166
167	if ((buf = malloc(tc_len)) == NULL ) {
168		syslog(LOG_ERR, "malloc failed - %s", strerror(errno));
169		return (NULL);
170	}
171
172	memset(buf, 0, tc_len);
173	if (text_convs[tc].oct2tc(len, octets, buf) == NULL) {
174		free(buf);
175		return (NULL);
176	}
177
178	return (buf);
179}
180
181char *
182snmp_tc2oid(enum snmp_tc tc, char *str, struct asn_oid *oid)
183{
184	if (tc > SNMP_UNKNOWN)
185		tc = SNMP_UNKNOWN;
186
187	return (text_convs[tc].tc2oid(str, oid));
188}
189
190int32_t
191snmp_tc2oct(enum snmp_tc tc, struct snmp_value *value, char *string)
192{
193	if (tc > SNMP_UNKNOWN)
194		tc = SNMP_UNKNOWN;
195
196	return (text_convs[tc].tc2oct(value, string));
197}
198
199/*****************************************************
200* Basic OctetString type.
201*/
202static char *
203snmp_oct2str(uint32_t len, char *octets, char *buf)
204{
205	uint8_t binary = 0;
206	uint32_t i;
207	char *ptr;
208
209	if (len > MAX_OCTSTRING_LEN || octets == NULL || buf == NULL)
210		return (NULL);
211
212	for (ptr = buf, i = 0; i < len; i++)
213		if (!isprint(octets[i])) {
214			binary = 1;
215			buf += sprintf(buf, "0x");
216			break;
217		}
218
219	for (ptr = buf, i = 0; i < len; i++)
220		if (!binary)
221			ptr += sprintf(ptr, "%c", octets[i]);
222		else
223			ptr += sprintf(ptr, "%2.2x", (u_char)octets[i]);
224
225	return (buf);
226}
227
228static char *
229snmp_str2asn_oid(char *str, struct asn_oid *oid)
230{
231	uint32_t i, len = 0;
232
233	/*
234	 * OctetStrings are allowed max length of ASN_MAXOCTETSTRING,
235	 * but trying to index an entry with such a long OctetString
236	 * will fail anyway.
237	 */
238	for (len = 0; len < ASN_MAXOIDLEN; len++) {
239		if (strchr(",]", *(str + len)) != NULL)
240			break;
241	}
242
243	if (len >= ASN_MAXOIDLEN)
244		return (NULL);
245
246	if (snmp_suboid_append(oid, (asn_subid_t) len) < 0)
247		return (NULL);
248
249	for (i = 0; i < len; i++)
250		if (snmp_suboid_append(oid, (asn_subid_t) *(str + i)) < 0)
251			return (NULL);
252
253	return (str + len);
254}
255
256static int32_t
257parse_octetstring(struct snmp_value *value, char *val)
258{
259	size_t len;
260
261	if ((len = strlen(val)) >= MAX_OCTSTRING_LEN) {
262		warnx("Octetstring too long - %d is max allowed",
263		    MAX_OCTSTRING_LEN - 1);
264		return (-1);
265	}
266
267	if ((value->v.octetstring.octets = malloc(len)) == NULL) {
268		value->v.octetstring.len = 0;
269		syslog(LOG_ERR, "malloc failed: %s", strerror(errno));
270		return (-1);
271	}
272
273	value->v.octetstring.len = len;
274	memcpy(value->v.octetstring.octets, val, len);
275	value->syntax = SNMP_SYNTAX_OCTETSTRING;
276
277	return (0);
278}
279
280/*************************************************************
281 * DateAndTime
282 *************************************************************
283 * rfc 2579 specification:
284 * DateAndTime ::= TEXTUAL-CONVENTION
285 *   DISPLAY-HINT "2d-1d-1d,1d:1d:1d.1d,1a1d:1d"
286 *   STATUS	  current
287 *   DESCRIPTION
288 *	"A date-time specification.
289 *
290 *	field	octets	contents		range
291 *	-----	------	--------		-----
292 *	1	1-2	year*			0..65536
293 *	2	3	month			1..12
294 *	3	4	day			1..31
295 *	4	5	hour			0..23
296 *	5	6	minutes			0..59
297 *	6	7	seconds			0..60
298 *			(use 60 for leap-second)
299 *	7	8	deci-seconds		0..9
300 *	8	9	direction from UTC	'+' / '-'
301 *	9	10	hours from UTC*		0..13
302 *	10	11	minutes from UTC	0..59
303 *
304 *	* Notes:
305 *	    - the value of year is in network-byte order
306 *	    - daylight saving time in New Zealand is +13
307 *
308 *	For example, Tuesday May 26, 1992 at 1:30:15 PM EDT would be
309 *	displayed as:
310 *
311 *		1992-5-26,13:30:15.0,-4:0
312 */
313static char *
314snmp_octstr2date(uint32_t len, char *octets, char *buf)
315{
316	int year;
317	char *ptr;
318
319	if (len != SNMP_DATETIME_OCTETS || octets == NULL || buf == NULL)
320		return (NULL);
321
322	buf[0]= '\0';
323	year = (octets[0] << 8);
324	year += (octets[1]);
325
326	ptr = buf;
327	ptr += sprintf(ptr, "%4.4d-%.2d-%.2d, ", year, octets[2],octets[3]);
328	ptr += sprintf(ptr, "%2.2d:%2.2d:%2.2d.%.2d, ", octets[4],octets[5],
329	    octets[6],octets[7]);
330	ptr += sprintf(ptr, "%c%.2d:%.2d", octets[8],octets[9],octets[10]);
331
332	return (buf);
333}
334
335static char *
336snmp_date2asn_oid(char *str, struct asn_oid *oid)
337{
338	char *endptr, *ptr;
339	static const char UTC[3] = "UTC";
340	int32_t saved_errno;
341	uint32_t v;
342
343	if (snmp_suboid_append(oid, (asn_subid_t) SNMP_DATETIME_OCTETS) < 0)
344		return (NULL);
345
346	/* Read 'YYYY-' and write it in two subs. */
347	ptr = str;
348	saved_errno = errno;
349	errno = 0;
350	v = strtoul(ptr, &endptr, 10);
351	if (v > 0xffff)
352		goto error;
353	else
354		errno = saved_errno;
355	if (*endptr != '-')
356		goto error1;
357	if (snmp_suboid_append(oid, (asn_subid_t) ((v & 0xff00) >> 8)) < 0)
358		return (NULL);
359	if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xff)) < 0)
360		return (NULL);
361
362	/* 'MM-' */
363	ptr = endptr + 1;
364	saved_errno = errno;
365	errno = 0;
366	v = strtoul(ptr, &endptr, 10);
367	if (errno != 0)
368		goto error;
369	else
370		errno = saved_errno;
371	if (*endptr != '-')
372		goto error1;
373	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
374		return (NULL);
375
376	/* 'DD,' */
377	ptr = endptr + 1;
378	saved_errno = errno;
379	errno = 0;
380	v = strtoul(ptr, &endptr, 10);
381	if (errno != 0)
382		goto error;
383	else
384		errno = saved_errno;
385	if (*endptr != '-')
386		goto error1;
387	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
388		return (NULL);
389
390	/* 'HH:' */
391	ptr = endptr + 1;
392	saved_errno = errno;
393	errno = 0;
394	v = strtoul(ptr, &endptr, 10);
395	if (errno != 0)
396		goto error;
397	else
398		errno = saved_errno;
399	if (*endptr != ':')
400		goto error1;
401	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
402		return (NULL);
403
404	/* 'MM:' */
405	ptr = endptr + 1;
406	saved_errno = errno;
407	errno = 0;
408	v = strtoul(ptr, &endptr, 10);
409	if (errno != 0)
410		goto error;
411	else
412		errno = saved_errno;
413	if (*endptr != ':')
414		goto error1;
415	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
416		return (NULL);
417
418	/* 'SS.' */
419	ptr = endptr + 1;
420	saved_errno = errno;
421	errno = 0;
422	v = strtoul(ptr, &endptr, 10);
423	if (errno != 0)
424		goto error;
425	else
426		errno = saved_errno;
427	if (*endptr != '.')
428		goto error1;
429	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
430		return (NULL);
431
432	/* 'M(mseconds),' */
433	ptr = endptr + 1;
434	saved_errno = errno;
435	errno = 0;
436	v = strtoul(ptr, &endptr, 10);
437	if (errno != 0)
438		goto error;
439	else
440		errno = saved_errno;
441	if (*endptr != ',')
442		goto error1;
443	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
444		return (NULL);
445
446	/* 'UTC' - optional */
447	ptr = endptr + 1;
448	if (strncmp(ptr, UTC, sizeof(UTC)) == 0)
449		ptr += sizeof(UTC);
450
451	/* '+/-' */
452	if (*ptr == '-' || *ptr == '+') {
453		if (snmp_suboid_append(oid, (asn_subid_t) (*ptr)) < 0)
454			return (NULL);
455	} else
456		goto error1;
457
458	/* 'HH:' */
459	ptr = endptr + 1;
460	saved_errno = errno;
461	errno = 0;
462	v = strtoul(ptr, &endptr, 10);
463	if (errno != 0)
464		goto error;
465	else
466		errno = saved_errno;
467	if (*endptr != ':')
468		goto error1;
469	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
470		return (NULL);
471
472	/* 'MM' - last one - ignore endptr here. */
473	ptr = endptr + 1;
474	saved_errno = errno;
475	errno = 0;
476	v = strtoul(ptr, &endptr, 10);
477	if (errno != 0)
478		goto error;
479	else
480		errno = saved_errno;
481	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
482		return (NULL);
483
484	return (endptr);
485
486  error:
487	errno = saved_errno;
488  error1:
489	warnx("Date value %s not supported", str);
490	return (NULL);
491}
492
493/* Read a DateAndTime string eg. 1992-5-26,13:30:15.0,-4:0. */
494static int32_t
495parse_dateandtime(struct snmp_value *sv, char *val)
496{
497	char *endptr;
498	uint32_t v;
499	uint8_t	date[SNMP_DATETIME_OCTETS];
500
501	/* 'YYYY-' */
502	v = strtoul(val, &endptr, 10);
503	if (v > 0xffff || *endptr != '-')
504		goto error;
505	date[0] = ((v & 0xff00) >> 8);
506	date[1] = (v & 0xff);
507	val = endptr + 1;
508
509	/* 'MM-' */
510	v = strtoul(val, &endptr, 10);
511	if (v == 0 || v > 12 || *endptr != '-')
512		goto error;
513	date[2] = v;
514	val = endptr + 1;
515
516	/* 'DD,' */
517	v = strtoul(val, &endptr, 10);
518	if (v == 0 || v > 31 || *endptr != ',')
519		goto error;
520	date[3] = v;
521	val = endptr + 1;
522
523	/* 'HH:' */
524	v = strtoul(val, &endptr, 10);
525	if (v > 23 || *endptr != ':')
526		goto error;
527	date[4] = v;
528	val = endptr + 1;
529
530	/* 'MM:' */
531	v = strtoul(val, &endptr, 10);
532	if (v > 59 || *endptr != ':')
533		goto error;
534	date[5] = v;
535	val = endptr + 1;
536
537	/* 'SS.' */
538	v = strtoul(val, &endptr, 10);
539	if (v > 60 || *endptr != '.')
540		goto error;
541	date[6] = v;
542	val = endptr + 1;
543
544	/* '(deci-)s,' */
545	v = strtoul(val, &endptr, 10);
546	if (v > 9 || *endptr != ',')
547		goto error;
548	date[7] = v;
549	val = endptr + 1;
550
551	/* offset - '+/-' */
552	if (*val != '-' && *val != '+')
553		goto error;
554	date[8] = (uint8_t) *val;
555	val = endptr + 1;
556
557	/* 'HH:' - offset from UTC */
558	v = strtoul(val, &endptr, 10);
559	if (v > 13 || *endptr != ':')
560		goto error;
561	date[9] = v;
562	val = endptr + 1;
563
564	/* 'MM'\0''  offset from UTC */
565	v = strtoul(val, &endptr, 10);
566	if (v > 59 || *endptr != '\0')
567		goto error;
568	date[10] = v;
569
570	if ((sv->v.octetstring.octets = malloc(SNMP_DATETIME_OCTETS)) == NULL) {
571		warn("malloc() failed");
572		return (-1);
573	}
574
575	sv->v.octetstring.len = SNMP_DATETIME_OCTETS;
576	memcpy(sv->v.octetstring.octets, date, SNMP_DATETIME_OCTETS);
577	sv->syntax = SNMP_SYNTAX_OCTETSTRING;
578	return (1);
579
580  error:
581	warnx("Date value %s not supported", val);
582	return (-1);
583}
584
585/**************************************************************
586 * PhysAddress
587 */
588static char *
589snmp_oct2physAddr(uint32_t len, char *octets, char *buf)
590{
591	char *ptr;
592	uint32_t i;
593
594	if (len != SNMP_PHYSADDR_OCTETS || octets == NULL || buf == NULL)
595		return (NULL);
596
597	buf[0]= '\0';
598
599	ptr = buf;
600	ptr += sprintf(ptr, "%2.2x", octets[0]);
601	for (i = 1; i < 6; i++)
602		ptr += sprintf(ptr, ":%2.2x", octets[i]);
603
604	return (buf);
605}
606
607static char *
608snmp_addr2asn_oid(char *str, struct asn_oid *oid)
609{
610	char *endptr, *ptr;
611	uint32_t v, i;
612	int saved_errno;
613
614	if (snmp_suboid_append(oid, (asn_subid_t) SNMP_PHYSADDR_OCTETS) < 0)
615		return (NULL);
616
617	ptr = str;
618	for (i = 0; i < 5; i++) {
619		saved_errno = errno;
620		v = strtoul(ptr, &endptr, 16);
621		errno = saved_errno;
622		if (v > 0xff) {
623			warnx("Integer value %s not supported", str);
624			return (NULL);
625		}
626		if (*endptr != ':') {
627			warnx("Failed adding oid - %s", str);
628			return (NULL);
629		}
630		if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
631			return (NULL);
632		ptr = endptr + 1;
633	}
634
635	/* The last one - don't check the ending char here. */
636	saved_errno = errno;
637	v = strtoul(ptr, &endptr, 16);
638	errno = saved_errno;
639	if (v > 0xff) {
640		warnx("Integer value %s not supported", str);
641		return (NULL);
642	}
643	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
644		return (NULL);
645
646	return (endptr);
647}
648
649static int32_t
650parse_physaddress(struct snmp_value *sv, char *val)
651{
652	char *endptr;
653	int32_t i;
654	uint32_t v;
655	uint8_t	phys_addr[SNMP_PHYSADDR_OCTETS];
656
657	for (i = 0; i < 5; i++) {
658		v = strtoul(val, &endptr, 16);
659		if (v > 0xff) {
660			warnx("Integer value %s not supported", val);
661			return (-1);
662		}
663		if(*endptr != ':') {
664			warnx("Failed reading octet - %s", val);
665			return (-1);
666		}
667		phys_addr[i] = v;
668		val = endptr + 1;
669	}
670
671	/* The last one - don't check the ending char here. */
672	v = strtoul(val, &endptr, 16);
673	if (v > 0xff) {
674		warnx("Integer value %s not supported", val);
675		return (-1);
676	}
677	phys_addr[5] = v;
678
679	if ((sv->v.octetstring.octets = malloc(SNMP_PHYSADDR_OCTETS)) == NULL) {
680		syslog(LOG_ERR, "malloc failed: %s", strerror(errno));
681		return (-1);
682	}
683
684	sv->v.octetstring.len = SNMP_PHYSADDR_OCTETS;
685	memcpy(sv->v.octetstring.octets, phys_addr, SNMP_PHYSADDR_OCTETS);
686	sv->syntax = SNMP_SYNTAX_OCTETSTRING;
687	return (1);
688}
689
690/**************************************************************
691 * NTPTimeStamp
692 **************************************************************
693 * NTP MIB, Revision 0.2, 7/25/97:
694 * NTPTimeStamp ::= TEXTUAL-CONVENTION
695 *    DISPLAY-HINT "4x.4x"
696 *    STATUS	current
697 *    DESCRIPTION
698 *	""
699 *    SYNTAX	OCTET STRING (SIZE(8))
700 */
701static char *
702snmp_oct2ntp_ts(uint32_t len, char *octets, char *buf)
703{
704	char *ptr;
705	uint32_t i;
706
707	if (len != SNMP_NTP_TS_OCTETS || octets == NULL || buf == NULL)
708		return (NULL);
709
710	buf[0]= '\0';
711
712	ptr = buf;
713	i = octets[0] * 1000 + octets[1] * 100 + octets[2] * 10 + octets[3];
714	ptr += sprintf(ptr, "%4.4d", i);
715	i = octets[4] * 1000 + octets[5] * 100 + octets[6] * 10 + octets[7];
716	ptr += sprintf(ptr, ".%4.4d", i);
717
718	return (buf);
719}
720
721static char *
722snmp_ntp_ts2asn_oid(char *str, struct asn_oid *oid)
723{
724	char *endptr, *ptr;
725	uint32_t v, i, d;
726	struct asn_oid suboid;
727	int saved_errno;
728
729	if (snmp_suboid_append(oid, (asn_subid_t) SNMP_NTP_TS_OCTETS) < 0)
730		return (NULL);
731
732	ptr = str;
733	saved_errno = errno;
734	errno = 0;
735	v = strtoul(ptr, &endptr, 10);
736	if (errno != 0 || (v / 1000) > 9) {
737		warnx("Integer value %s not supported", str);
738		errno = saved_errno;
739		return (NULL);
740	} else
741		errno = saved_errno;
742
743	if (*endptr != '.') {
744		warnx("Failed adding oid - %s", str);
745		return (NULL);
746	}
747
748	memset(&suboid, 0, sizeof(struct asn_oid));
749	suboid.len = SNMP_NTP_TS_OCTETS;
750
751	for (i = 0, d = 1000; i < 4; i++) {
752		suboid.subs[i] = v / d;
753		v = v % d;
754		d = d / 10;
755	}
756
757	ptr = endptr + 1;
758	saved_errno = errno;
759	errno = 0;
760	v = strtoul(ptr, &endptr, 10);
761	if (errno != 0 || (v / 1000) > 9) {
762		warnx("Integer value %s not supported", str);
763		errno = saved_errno;
764		return (NULL);
765	} else
766		errno = saved_errno;
767
768	for (i = 0, d = 1000; i < 4; i++) {
769		suboid.subs[i + 4] = v / d;
770		v = v % d;
771		d = d / 10;
772	}
773
774	asn_append_oid(oid, &suboid);
775	return (endptr);
776}
777
778static int32_t
779parse_ntp_ts(struct snmp_value *sv, char *val)
780{
781	char *endptr;
782	int32_t i, d, saved_errno;
783	uint32_t v;
784	uint8_t	ntp_ts[SNMP_NTP_TS_OCTETS];
785
786	saved_errno = errno;
787	errno = 0;
788	v = strtoul(val, &endptr, 10);
789	if (errno != 0 || (v / 1000) > 9) {
790		errno = saved_errno;
791		warnx("Integer value %s not supported", val);
792		return (-1);
793	} else
794		errno = saved_errno;
795
796	if (*endptr != '.') {
797		warnx("Failed reading octet - %s", val);
798		return (-1);
799	}
800
801	for (i = 0, d = 1000; i < 4; i++) {
802		ntp_ts[i] = v / d;
803		v = v % d;
804		d = d / 10;
805	}
806	val = endptr + 1;
807
808	saved_errno = errno;
809	errno = 0;
810	v = strtoul(val, &endptr, 10);
811	if (errno != 0 || (v / 1000) > 9) {
812		errno = saved_errno;
813		warnx("Integer value %s not supported", val);
814		return (-1);
815	} else
816		errno = saved_errno;
817
818	for (i = 0, d = 1000; i < 4; i++) {
819		ntp_ts[i + 4] = v / d;
820		v = v % d;
821		d = d / 10;
822	}
823
824	if ((sv->v.octetstring.octets = malloc(SNMP_NTP_TS_OCTETS)) == NULL) {
825		syslog(LOG_ERR, "malloc failed: %s", strerror(errno));
826		return (-1);
827	}
828
829	sv->v.octetstring.len = SNMP_NTP_TS_OCTETS;
830	memcpy(sv->v.octetstring.octets, ntp_ts, SNMP_NTP_TS_OCTETS);
831	sv->syntax = SNMP_SYNTAX_OCTETSTRING;
832	return (1);
833}
834
835/**************************************************************
836 * BridgeId
837 **************************************************************
838 * BRIDGE-MIB, REVISION		"200509190000Z"
839 * BridgeId ::= TEXTUAL-CONVENTION
840 *    STATUS	current
841 *    DESCRIPTION
842 *	"The Bridge-Identifier, as used in the Spanning Tree
843 *	Protocol, to uniquely identify a bridge.  Its first two
844 *	octets (in network byte order) contain a priority value,
845 *	and its last 6 octets contain the MAC address used to
846 *	refer to a bridge in a unique fashion (typically, the
847 *	numerically smallest MAC address of all ports on the
848 *	bridge)."
849 *    SYNTAX	OCTET STRING (SIZE (8))
850 */
851static char *
852snmp_oct2bridgeid(uint32_t len, char *octets, char *buf)
853{
854	char *ptr;
855	uint32_t i, priority;
856
857	if (len != SNMP_BRIDGEID_OCTETS || octets == NULL || buf == NULL)
858		return (NULL);
859
860	buf[0]= '\0';
861	ptr = buf;
862
863	priority = octets[0] << 8;
864	priority += octets[1];
865	if (priority > SNMP_MAX_BRIDGE_PRIORITY) {
866		warnx("Invalid bridge priority %d", priority);
867		return (NULL);
868	} else
869		ptr += sprintf(ptr, "%d.", octets[0]);
870
871	ptr += sprintf(ptr, "%2.2x", octets[2]);
872
873	for (i = 1; i < 6; i++)
874		ptr += sprintf(ptr, ":%2.2x", octets[i + 2]);
875
876	return (buf);
877}
878
879static char *
880snmp_bridgeid2oct(char *str, struct asn_oid *oid)
881{
882	char *endptr, *ptr;
883	uint32_t v, i;
884	int32_t saved_errno;
885
886	if (snmp_suboid_append(oid, (asn_subid_t) SNMP_BRIDGEID_OCTETS) < 0)
887		return (NULL);
888
889	ptr = str;
890	/* Read the priority. */
891	saved_errno = errno;
892	errno = 0;
893	v = strtoul(ptr, &endptr, 10);
894
895	if (v > SNMP_MAX_BRIDGE_PRIORITY || errno != 0 || *endptr != '.') {
896		errno = saved_errno;
897		warnx("Bad bridge priority value %d", v);
898		return (NULL);
899	}
900
901	if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xff00)) < 0)
902		return (NULL);
903
904	if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xff)) < 0)
905		return (NULL);
906
907	ptr = endptr + 1;
908	for (i = 0; i < 5; i++) {
909		saved_errno = errno;
910		errno = 0;
911		v = strtoul(ptr, &endptr, 16);
912		errno = saved_errno;
913		if (v > 0xff) {
914			warnx("Integer value %s not supported", str);
915			return (NULL);
916		}
917		if (*endptr != ':') {
918			warnx("Failed adding oid - %s",str);
919			return (NULL);
920		}
921		if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
922			return (NULL);
923		ptr = endptr + 1;
924	}
925
926	/* The last one - don't check the ending char here. */
927	saved_errno = errno;
928	errno = 0;
929	v = strtoul(ptr, &endptr, 16);
930	errno = saved_errno;
931	if (v > 0xff) {
932		warnx("Integer value %s not supported", str);
933		return (NULL);
934	}
935	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
936		return (NULL);
937
938	return (endptr);
939}
940
941static int32_t
942parse_bridge_id(struct snmp_value *sv, char *string)
943{
944	char *endptr;
945	int32_t i, saved_errno;
946	uint32_t v;
947	uint8_t	bridge_id[SNMP_BRIDGEID_OCTETS];
948
949	/* Read the priority. */
950	saved_errno = errno;
951	errno = 0;
952	v = strtoul(string, &endptr, 10);
953
954	if (v > SNMP_MAX_BRIDGE_PRIORITY || errno != 0 || *endptr != '.') {
955		errno = saved_errno;
956		warnx("Bad bridge priority value %d", v);
957		return (-1);
958	}
959
960	bridge_id[0] = (v & 0xff00);
961	bridge_id[1] = (v & 0xff);
962
963	string = endptr + 1;
964
965	for (i = 0; i < 5; i++) {
966		v = strtoul(string, &endptr, 16);
967		if (v > 0xff) {
968			warnx("Integer value %s not supported", string);
969			return (-1);
970		}
971		if(*endptr != ':') {
972			warnx("Failed reading octet - %s", string);
973			return (-1);
974		}
975		bridge_id[i + 2] = v;
976		string = endptr + 1;
977	}
978
979	/* The last one - don't check the ending char here. */
980	v = strtoul(string, &endptr, 16);
981	if (v > 0xff) {
982		warnx("Integer value %s not supported", string);
983		return (-1);
984	}
985	bridge_id[7] = v;
986
987	if ((sv->v.octetstring.octets = malloc(SNMP_BRIDGEID_OCTETS)) == NULL) {
988		syslog(LOG_ERR, "malloc failed: %s", strerror(errno));
989		return (-1);
990	}
991
992	sv->v.octetstring.len = SNMP_BRIDGEID_OCTETS;
993	memcpy(sv->v.octetstring.octets, bridge_id, SNMP_BRIDGEID_OCTETS);
994	sv->syntax = SNMP_SYNTAX_OCTETSTRING;
995	return (1);
996}
997
998/**************************************************************
999 * BridgePortId
1000 **************************************************************
1001 * BEGEMOT-BRIDGE-MIB, LAST-UPDATED "200608100000Z"
1002 * BridgePortId ::= TEXTUAL-CONVENTION
1003 *    DISPLAY-HINT "1x.1x"
1004 *    STATUS	current
1005 *    DESCRIPTION
1006 *	"A port identifier that contains a bridge port's STP priority
1007 *	in the first octet and the port number in the second octet."
1008 *    SYNTAX	OCTET STRING (SIZE(2))
1009 */
1010static char *
1011snmp_oct2bport_id(uint32_t len, char *octets, char *buf)
1012{
1013	char *ptr;
1014
1015	if (len != SNMP_BPORT_OCTETS || octets == NULL || buf == NULL)
1016		return (NULL);
1017
1018	buf[0]= '\0';
1019	ptr = buf;
1020
1021	ptr += sprintf(ptr, "%d.", octets[0]);
1022	ptr += sprintf(ptr, "%d", octets[1]);
1023
1024	return (buf);
1025}
1026
1027static char *
1028snmp_bport_id2oct(char *str, struct asn_oid *oid)
1029{
1030	char *endptr, *ptr;
1031	uint32_t v;
1032	int saved_errno;
1033
1034	if (snmp_suboid_append(oid, (asn_subid_t) SNMP_BPORT_OCTETS) < 0)
1035		return (NULL);
1036
1037	ptr = str;
1038	/* Read the priority. */
1039	saved_errno = errno;
1040	errno = 0;
1041	v = strtoul(ptr, &endptr, 10);
1042
1043	if (v > SNMP_MAX_BPORT_PRIORITY || errno != 0 || *endptr != '.') {
1044		errno = saved_errno;
1045		warnx("Bad bridge port priority value %d", v);
1046		return (NULL);
1047	}
1048
1049	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
1050		return (NULL);
1051
1052	saved_errno = errno;
1053	errno = 0;
1054	v = strtoul(ptr, &endptr, 16);
1055	errno = saved_errno;
1056
1057	if (v > 0xff) {
1058		warnx("Bad port number - %d", v);
1059		return (NULL);
1060	}
1061
1062	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
1063		return (NULL);
1064
1065	return (endptr);
1066}
1067
1068static int32_t
1069parse_bport_id(struct snmp_value *value, char *string)
1070{
1071	char *endptr;
1072	int saved_errno;
1073	uint32_t v;
1074	uint8_t	bport_id[SNMP_BPORT_OCTETS];
1075
1076	/* Read the priority. */
1077	saved_errno = errno;
1078	errno = 0;
1079	v = strtoul(string, &endptr, 10);
1080
1081	if (v > SNMP_MAX_BPORT_PRIORITY || errno != 0 || *endptr != '.') {
1082		errno = saved_errno;
1083		warnx("Bad bridge port priority value %d", v);
1084		return (-1);
1085	}
1086
1087	bport_id[0] = v;
1088
1089	string = endptr + 1;
1090	v = strtoul(string, &endptr, 16);
1091	if (v > 0xff) {
1092		warnx("Bad port number - %d", v);
1093		return (-1);
1094	}
1095
1096	bport_id[1] = v;
1097
1098	if ((value->v.octetstring.octets = malloc(SNMP_BPORT_OCTETS)) == NULL) {
1099		syslog(LOG_ERR, "malloc failed: %s", strerror(errno));
1100		return (-1);
1101	}
1102
1103	value->v.octetstring.len = SNMP_BPORT_OCTETS;
1104	memcpy(value->v.octetstring.octets, bport_id, SNMP_BPORT_OCTETS);
1105	value->syntax = SNMP_SYNTAX_OCTETSTRING;
1106	return (1);
1107}
1108/**************************************************************
1109 * InetAddress
1110 **************************************************************
1111 * INET-ADDRESS-MIB, REVISION     "200502040000Z"
1112 * InetAddress ::= TEXTUAL-CONVENTION
1113 *   STATUS      current
1114 *   DESCRIPTION
1115 *       "Denotes a generic Internet address.
1116 *
1117 *        An InetAddress value is always interpreted within the context
1118 *        of an InetAddressType value.  Every usage of the InetAddress
1119 *        textual convention is required to specify the InetAddressType
1120 *        object that provides the context.  It is suggested that the
1121 *        InetAddressType object be logically registered before the
1122 *        object(s) that use the InetAddress textual convention, if
1123 *        they appear in the same logical row.
1124 *
1125 *        The value of an InetAddress object must always be
1126 *        consistent with the value of the associated InetAddressType
1127 *        object.  Attempts to set an InetAddress object to a value
1128 *        inconsistent with the associated InetAddressType
1129 *        must fail with an inconsistentValue error.
1130 *
1131 *        When this textual convention is used as the syntax of an
1132 *        index object, there may be issues with the limit of 128
1133 *        sub-identifiers specified in SMIv2, STD 58.  In this case,
1134 *        the object definition MUST include a 'SIZE' clause to
1135 *        limit the number of potential instance sub-identifiers;
1136 *        otherwise the applicable constraints MUST be stated in
1137 *        the appropriate conceptual row DESCRIPTION clauses, or
1138 *        in the surrounding documentation if there is no single
1139 *        DESCRIPTION clause that is appropriate."
1140 *   SYNTAX       OCTET STRING (SIZE (0..255))
1141 **************************************************************
1142 * TODO: FIXME!!! syrinx: Since we do not support checking the
1143 * consistency of a varbinding based on the value of a previous
1144 * one, try to guess the type of address based on the
1145 * OctetString SIZE - 4 for IPv4, 16 for IPv6, others currently
1146 * not supported.
1147 */
1148static char *
1149snmp_oct2inetaddr(uint32_t len, char *octets, char *buf)
1150{
1151	int af;
1152	void *ip;
1153	struct in_addr	ipv4;
1154	struct in6_addr	ipv6;
1155
1156	if (len > MAX_OCTSTRING_LEN || octets == NULL || buf == NULL)
1157		return (NULL);
1158
1159	switch (len) {
1160		/* XXX: FIXME - IPv4*/
1161		case 4:
1162			memcpy(&ipv4.s_addr, octets, sizeof(ipv4.s_addr));
1163			af = AF_INET;
1164			ip = &ipv4;
1165			break;
1166
1167		/* XXX: FIXME - IPv4*/
1168		case 16:
1169			memcpy(ipv6.s6_addr, octets, sizeof(ipv6.s6_addr));
1170			af = AF_INET6;
1171			ip = &ipv6;
1172			break;
1173
1174		default:
1175			return (NULL);
1176	}
1177
1178	if (inet_ntop(af, ip, buf, SNMP_INADDRS_STRSZ) == NULL) {
1179		warn("inet_ntop failed");
1180		return (NULL);
1181	}
1182
1183	return (buf);
1184}
1185
1186static char *
1187snmp_inetaddr2oct(char *str __unused, struct asn_oid *oid __unused)
1188{
1189	return (NULL);
1190}
1191
1192static int32_t
1193parse_inetaddr(struct snmp_value *value __unused, char *string __unused)
1194{
1195	return (-1);
1196}
1197
1198/**************************************************************
1199 * SNMP BITS type - XXX: FIXME
1200 **************************************************************/
1201static char *
1202snmp_oct2bits(uint32_t len, char *octets, char *buf)
1203{
1204	int i, bits;
1205	uint64_t value;
1206
1207	if (len > sizeof(value) || octets == NULL || buf == NULL)
1208		return (NULL);
1209
1210	for (i = len, value = 0, bits = 0; i > 0; i--, bits += 8)
1211		value += octets[i] << bits;
1212
1213	buf[0]= '\0';
1214	sprintf(buf, "0x%llx.",(long long unsigned) value);
1215
1216	return (buf);
1217}
1218
1219static char *
1220snmp_bits2oct(char *str, struct asn_oid *oid)
1221{
1222	char *endptr;
1223	int i, size, bits, saved_errno;
1224	uint64_t v, mask = 0xFF00000000000000;
1225
1226	saved_errno = errno;
1227	errno = 0;
1228
1229	v = strtoull(str, &endptr, 16);
1230	if (errno != 0) {
1231		warn("Bad BITS value %s", str);
1232		errno = saved_errno;
1233		return (NULL);
1234	}
1235
1236	bits = 8;
1237	/* Determine length - up to 8 octets supported so far. */
1238	for (size = sizeof(v); size > 0; size--) {
1239		if ((v & mask) != 0)
1240			break;
1241		mask = mask >> bits;
1242	}
1243
1244	if (size == 0)
1245		size = 1;
1246
1247	if (snmp_suboid_append(oid, (asn_subid_t) size) < 0)
1248		return (NULL);
1249
1250	for (i = 0, bits = 0; i < size; i++, bits += 8)
1251		if (snmp_suboid_append(oid,
1252		    (asn_subid_t)((v & mask) >> bits)) < 0)
1253			return (NULL);
1254
1255	return (endptr);
1256}
1257
1258static int32_t
1259parse_bits(struct snmp_value *value, char *string)
1260{
1261	char *endptr;
1262	int i, size, bits, saved_errno;
1263	uint64_t v, mask = 0xFF00000000000000;
1264
1265	saved_errno = errno;
1266	errno = 0;
1267
1268	v = strtoull(string, &endptr, 16);
1269
1270	if (errno != 0) {
1271		warn("Bad BITS value %s", string);
1272		errno = saved_errno;
1273		return (-1);
1274	}
1275
1276	bits = 8;
1277	/* Determine length - up to 8 octets supported so far. */
1278	for (size = sizeof(v); size > 0; size--) {
1279		if ((v & mask) != 0)
1280			break;
1281		mask = mask >> bits;
1282	}
1283
1284	if (size == 0)
1285		size = 1;
1286
1287	if ((value->v.octetstring.octets = malloc(size)) == NULL) {
1288		syslog(LOG_ERR, "malloc failed: %s", strerror(errno));
1289		return (-1);
1290	}
1291
1292	value->v.octetstring.len = size;
1293	for (i = 0, bits = 0; i < size; i++, bits += 8)
1294		value->v.octetstring.octets[i] = (v & mask) >> bits;
1295	value->syntax = SNMP_SYNTAX_OCTETSTRING;
1296	return (1);
1297}
1298