GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/snmpctl/parser.c Lines: 32 145 22.1 %
Date: 2017-11-07 Branches: 25 150 16.7 %

Line Branch Exec Source
1
/*	$OpenBSD: parser.c,v 1.15 2014/04/14 12:56:21 blambert Exp $	*/
2
3
/*
4
 * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.org>
5
 * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
6
 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
7
 *
8
 * Permission to use, copy, modify, and distribute this software for any
9
 * purpose with or without fee is hereby granted, provided that the above
10
 * copyright notice and this permission notice appear in all copies.
11
 *
12
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
 */
20
21
#include <sys/types.h>
22
#include <sys/socket.h>
23
#include <sys/queue.h>
24
#include <sys/tree.h>
25
#include <sys/uio.h>
26
27
#include <netinet/in.h>
28
#include <net/if.h>
29
#include <arpa/inet.h>
30
31
#include <err.h>
32
#include <errno.h>
33
#include <limits.h>
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <stdint.h>
37
#include <string.h>
38
#include <event.h>
39
40
#include "snmpd.h"
41
#include "snmp.h"
42
#include "parser.h"
43
44
enum token_type {
45
	NOTOKEN,
46
	ENDTOKEN,
47
	KEYWORD,
48
	TRAPOID,
49
	ELEMENTOBJECT,
50
	VALTYPE,
51
	IPADDRVAL,
52
	INT32VAL,
53
	UINT32VAL,
54
	INT64VAL,
55
	STRINGVAL,
56
	SNMPOID,
57
	SNMPHOST,
58
	SNMPCOMMUNITY,
59
	SNMPVERSION
60
};
61
62
struct token {
63
	enum token_type		 type;
64
	const char		*keyword;
65
	int			 value;
66
	const struct token	*next;
67
};
68
69
static const struct token t_main[];
70
static const struct token t_show[];
71
static const struct token t_trap[];
72
static const struct token t_trapoid[];
73
static const struct token t_element[];
74
static const struct token t_oid[];
75
static const struct token t_type[];
76
static const struct token t_ipaddr[];
77
static const struct token t_int32[];
78
static const struct token t_uint32[];
79
static const struct token t_int64[];
80
static const struct token t_string[];
81
static const struct token t_snmp[];
82
static const struct token t_snmpclient[];
83
static const struct token t_snmphost[];
84
static const struct token t_snmpoid[];
85
static const struct token t_snmpcommunity[];
86
static const struct token t_snmpversion[];
87
88
static const struct token t_main[] = {
89
	{KEYWORD,	"monitor",	MONITOR,	NULL},
90
	{KEYWORD,	"show",		NONE,		t_show},
91
	{KEYWORD,	"snmp",		NONE,		t_snmp},
92
	{KEYWORD,	"trap",		NONE,		t_trap},
93
	{KEYWORD,	"walk",		WALK,		t_snmphost},
94
	{ENDTOKEN,	"",		NONE,		NULL}
95
};
96
97
static const struct token t_show[] = {
98
	{KEYWORD,	"mib",		SHOW_MIB,	NULL},
99
	{ENDTOKEN,	"",		NONE,		NULL}
100
};
101
102
static const struct token t_snmp[] = {
103
	{KEYWORD,	"bulkwalk",	BULKWALK,	t_snmphost},
104
	{KEYWORD,	"get",		GET,		t_snmphost},
105
	{KEYWORD,	"walk",		WALK,		t_snmphost},
106
	{ENDTOKEN,	"",		NONE,		NULL}
107
};
108
109
static const struct token t_snmphost[] = {
110
	{SNMPHOST,	"",		NONE,		t_snmpclient},
111
	{ENDTOKEN,	"",		NONE,		NULL}
112
};
113
114
static const struct token t_snmpclient[] = {
115
	{NOTOKEN,	"",		NONE,		NULL},
116
	{KEYWORD,	"oid",		NONE,		t_snmpoid},
117
	{KEYWORD,	"community",	NONE,		t_snmpcommunity},
118
	{KEYWORD,	"version",	NONE,		t_snmpversion},
119
	{ENDTOKEN,	"",		NONE,		NULL}
120
};
121
122
static const struct token t_snmpoid[] = {
123
	{SNMPOID,	"",		NONE,		t_snmpclient},
124
	{ENDTOKEN,	"",		NONE,		NULL}
125
};
126
127
static const struct token t_snmpcommunity[] = {
128
	{SNMPCOMMUNITY,	"",		NONE,		t_snmpclient},
129
	{ENDTOKEN,	"",		NONE,		NULL}
130
};
131
132
static const struct token t_snmpversion[] = {
133
	{SNMPVERSION,	"",		NONE,		t_snmpclient},
134
	{ENDTOKEN,	"",		NONE,		NULL}
135
};
136
137
static const struct token t_trap[] = {
138
	{KEYWORD,	"send",		TRAP,		t_trapoid},
139
	{ENDTOKEN,	"",		NONE,		NULL}
140
};
141
142
static const struct token t_trapoid[] = {
143
	{TRAPOID,	"",		NONE,		t_element},
144
	{ENDTOKEN,	"",		NONE,		NULL}
145
};
146
147
static const struct token t_element[] = {
148
	{NOTOKEN,	"",		NONE,		NULL},
149
	{KEYWORD,	"oid",		NONE,		t_oid},
150
	{ENDTOKEN,	"",		NONE,		NULL}
151
};
152
153
static const struct token t_oid[] = {
154
	{ELEMENTOBJECT,	"",		NONE,		t_type},
155
	{ENDTOKEN,	"",		NONE,		NULL}
156
};
157
158
static const struct token t_type[] = {
159
	{VALTYPE,	"ip",		SNMP_IPADDR,	t_ipaddr },
160
	{VALTYPE,	"counter",	SNMP_COUNTER32,	t_int32 },
161
	{VALTYPE,	"gauge",	SNMP_GAUGE32,	t_int32 },
162
	{VALTYPE,	"unsigned",	SNMP_GAUGE32,	t_uint32 },
163
	{VALTYPE,	"ticks",	SNMP_TIMETICKS,	t_int32 },
164
	{VALTYPE,	"opaque",	SNMP_OPAQUE,	t_int32 },
165
	{VALTYPE,	"nsap",		SNMP_NSAPADDR,	t_int32 },
166
	{VALTYPE,	"counter64",	SNMP_COUNTER64,	t_int64 },
167
	{VALTYPE,	"uint",		SNMP_UINTEGER32, t_uint32 },
168
	{VALTYPE,	"int",		SNMP_INTEGER32,	t_int32 },
169
	{VALTYPE,	"bitstring",	SNMP_BITSTRING,	t_string },
170
	{VALTYPE,	"string",	SNMP_OCTETSTRING, t_string },
171
	{VALTYPE,	"null",		SNMP_NULL,	t_element },
172
	{VALTYPE,	"oid",		SNMP_OBJECT,	t_string },
173
	{ENDTOKEN,	"",		NONE,		NULL}
174
};
175
176
static const struct token t_ipaddr[] = {
177
	{IPADDRVAL,	"",		NONE,		t_element},
178
	{ENDTOKEN,	"",		NONE,		NULL}
179
};
180
181
static const struct token t_int32[] = {
182
	{INT32VAL,	"",		NONE,		t_element},
183
	{ENDTOKEN,	"",		NONE,		NULL}
184
};
185
186
static const struct token t_uint32[] = {
187
	{UINT32VAL,	"",		NONE,		t_element},
188
	{ENDTOKEN,	"",		NONE,		NULL}
189
};
190
191
static const struct token t_int64[] = {
192
	{INT64VAL,	"",		NONE,		t_element},
193
	{ENDTOKEN,	"",		NONE,		NULL}
194
};
195
196
static const struct token t_string[] = {
197
	{STRINGVAL,	"",		NONE,		t_element},
198
	{ENDTOKEN,	"",		NONE,		NULL}
199
};
200
201
static struct parse_result	 res;
202
203
const struct token		*match_token(char *, const struct token []);
204
void				 show_valid_args(const struct token []);
205
206
struct parse_result *
207
parse(int argc, char *argv[])
208
{
209
	const struct token	*table = t_main;
210
	const struct token	*match;
211
212
8
	bzero(&res, sizeof(res));
213
4
	res.version = -1;
214
4
	TAILQ_INIT(&res.oids);
215
4
	TAILQ_INIT(&res.varbinds);
216
217
32
	while (argc >= 0) {
218
16
		if ((match = match_token(argv[0], table)) == NULL) {
219
			fprintf(stderr, "valid commands/args:\n");
220
			show_valid_args(table);
221
			return (NULL);
222
		}
223
224
16
		argc--;
225
16
		argv++;
226
227

28
		if (match->type == NOTOKEN || match->next == NULL)
228
			break;
229
230
		table = match->next;
231
	}
232
233
4
	if (argc > 0) {
234
		fprintf(stderr, "superfluous argument: %s\n", argv[0]);
235
		return (NULL);
236
	}
237
238
4
	return (&res);
239
4
}
240
241
const struct token *
242
match_token(char *word, const struct token table[])
243
{
244
	u_int			 i, match = 0;
245
	const struct token	*t = NULL;
246
32
	const char		*errs = NULL;
247
	int			 terminal = 0;
248
	struct parse_val	*val;
249
	struct parse_varbind	*vb = NULL;
250
251
104
	for (i = 0; table[i].type != ENDTOKEN; i++) {
252



72
		switch (table[i].type) {
253
		case NOTOKEN:
254

4
			if (word == NULL || strlen(word) == 0) {
255
4
				match++;
256
				t = &table[i];
257
4
			}
258
			break;
259
		case KEYWORD:
260

100
			if (word != NULL && strncmp(word, table[i].keyword,
261
48
			    strlen(word)) == 0) {
262
8
				match++;
263
				t = &table[i];
264
8
				if (t->value)
265
4
					res.action = t->value;
266
			}
267
			break;
268
		case SNMPHOST:
269
			if (!match && word != NULL && strlen(word) > 0 &&
270
			    res.host == NULL) {
271
				if ((res.host = strdup(word)) == NULL)
272
					err(1, "strdup");
273
				match++;
274
				t = &table[i];
275
			}
276
			break;
277
		case SNMPOID:
278
			if (!match && word != NULL && strlen(word) > 0) {
279
				if ((val = calloc(1, sizeof(*val))) == NULL ||
280
				    (val->val = strdup(word)) == NULL)
281
					err(1, "strdup");
282
				TAILQ_INSERT_TAIL(&res.oids, val, val_entry);
283
				match++;
284
				t = &table[i];
285
			}
286
			break;
287
		case SNMPCOMMUNITY:
288
			if (!match && word != NULL && strlen(word) > 0 &&
289
			    res.community == NULL) {
290
				if ((res.community = strdup(word)) == NULL)
291
					err(1, "strdup");
292
				match++;
293
				t = &table[i];
294
			}
295
			break;
296
		case SNMPVERSION:
297
			if (!match && word != NULL && strlen(word) > 0 &&
298
			    res.version == -1) {
299
				if (strcmp("1", word) == 0)
300
					res.version = SNMP_V1;
301
				else if (strcmp("2c", word) == 0)
302
					res.version = SNMP_V2;
303
				else
304
					break;
305
				match++;
306
				t = &table[i];
307
			}
308
			break;
309
		case VALTYPE:
310
			if (word != NULL && strncmp(word, table[i].keyword,
311
			    strlen(word)) == 0) {
312
				match++;
313
				t = &table[i];
314
				vb = TAILQ_LAST(&res.varbinds, parse_varbinds);
315
				if (vb == NULL)
316
					errx(1, "inconsistent varbind list");
317
				vb->sm.snmp_type = t->value;
318
				if (t->value == SNMP_NULL)
319
					terminal = 1;
320
			}
321
			break;
322
		case TRAPOID:
323

8
			if (word == NULL || strlen(word) == 0)
324
				break;
325
4
			if ((res.trapoid = strdup(word)) == NULL)
326
				err(1, "malloc");
327
4
			match++;
328
			t = &table[i];
329
4
			break;
330
		case ELEMENTOBJECT:
331
			if (word == NULL || strlen(word) == 0)
332
				break;
333
			if ((vb = calloc(1, sizeof(*vb))) == NULL)
334
				errx(1, "calloc");
335
			if (strlcpy(vb->sm.snmp_oid, word,
336
			    sizeof(vb->sm.snmp_oid)) >= sizeof(vb->sm.snmp_oid))
337
				errx(1, "oid too long");
338
339
			TAILQ_INSERT_TAIL(&res.varbinds, vb, vb_entry);
340
			match++;
341
			t = &table[i];
342
			break;
343
		case IPADDRVAL:
344
			if (word == NULL || strlen(word) == 0)
345
				break;
346
			vb = TAILQ_LAST(&res.varbinds, parse_varbinds);
347
			if (vb == NULL)
348
				errx(1, "inconsistent varbind list");
349
			if (inet_pton(AF_INET, word, &vb->u.in4) == -1) {
350
				/* XXX the SNMP_IPADDR type is IPv4-only? */
351
				if (inet_pton(AF_INET6, word, &vb->u.in6) == -1)
352
					errx(1, "invalid IP address");
353
				vb->sm.snmp_len = sizeof(vb->u.in6);
354
			} else {
355
				vb->sm.snmp_len = sizeof(vb->u.in4);
356
			}
357
			terminal = 1;
358
			break;
359
		case INT32VAL:
360
			if (word == NULL || strlen(word) == 0)
361
				break;
362
			vb = TAILQ_LAST(&res.varbinds, parse_varbinds);
363
			if (vb == NULL)
364
				errx(1, "inconsistent varbind list");
365
			vb->u.d = strtonum(word, INT_MIN, INT_MAX, &errs);
366
			vb->sm.snmp_len = sizeof(vb->u.d);
367
			terminal = 1;
368
			break;
369
		case UINT32VAL:
370
			if (word == NULL || strlen(word) == 0)
371
				break;
372
			vb = TAILQ_LAST(&res.varbinds, parse_varbinds);
373
			if (vb == NULL)
374
				errx(1, "inconsistent varbind list");
375
			vb->u.u = strtonum(word, 0, UINT_MAX, &errs);
376
			vb->sm.snmp_len = sizeof(vb->u.u);
377
			terminal = 1;
378
			break;
379
		case INT64VAL:
380
			if (word == NULL || strlen(word) == 0)
381
				break;
382
			vb = TAILQ_LAST(&res.varbinds, parse_varbinds);
383
			if (vb == NULL)
384
				errx(1, "inconsistent varbind list");
385
			vb->u.l = strtonum(word, INT64_MIN, INT64_MAX, &errs);
386
			vb->sm.snmp_len = sizeof(vb->u.l);
387
			terminal = 1;
388
			break;
389
		case STRINGVAL:
390
			if (word == NULL || strlen(word) == 0)
391
				break;
392
			vb = TAILQ_LAST(&res.varbinds, parse_varbinds);
393
			if (vb == NULL)
394
				errx(1, "inconsistent varbind list");
395
			vb->u.str = word;
396
			vb->sm.snmp_len = strlen(word);
397
			terminal = 1;
398
			break;
399
		case ENDTOKEN:
400
			break;
401
		}
402
36
		if (terminal)
403
			break;
404
	}
405
406
16
	if (terminal) {
407
		t = &table[i];
408
16
	} else if (match != 1) {
409
		if (word == NULL)
410
			fprintf(stderr, "missing argument:\n");
411
		else if (match > 1)
412
			fprintf(stderr, "ambiguous argument: %s\n", word);
413
		else if (match < 1)
414
			fprintf(stderr, "unknown argument: %s\n", word);
415
		return (NULL);
416
	}
417
418
16
	return (t);
419
16
}
420
421
void
422
show_valid_args(const struct token table[])
423
{
424
	int	i;
425
426
	for (i = 0; table[i].type != ENDTOKEN; i++) {
427
		switch (table[i].type) {
428
		case NOTOKEN:
429
			fprintf(stderr, "  <cr>\n");
430
			break;
431
		case KEYWORD:
432
			fprintf(stderr, "  %s\n", table[i].keyword);
433
			break;
434
		case VALTYPE:
435
			fprintf(stderr, "  %s <value>\n", table[i].keyword);
436
			break;
437
		case SNMPHOST:
438
			fprintf(stderr, "  <hostname>\n");
439
			break;
440
		case SNMPOID:
441
		case TRAPOID:
442
		case ELEMENTOBJECT:
443
			fprintf(stderr, "  <oid-string>\n");
444
			break;
445
		case IPADDRVAL:
446
			fprintf(stderr, "  <ip-address>\n");
447
			break;
448
		case INT32VAL:
449
			fprintf(stderr, "  <int32>\n");
450
			break;
451
		case UINT32VAL:
452
			fprintf(stderr, "  <uint32>\n");
453
			break;
454
		case INT64VAL:
455
			fprintf(stderr, "  <int64>\n");
456
			break;
457
		case STRINGVAL:
458
		case SNMPCOMMUNITY:
459
			fprintf(stderr, "  <string>\n");
460
			break;
461
		case SNMPVERSION:
462
			fprintf(stderr, "  [1|2c]\n");
463
			break;
464
		case ENDTOKEN:
465
			break;
466
		}
467
	}
468
}