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