GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/smtpd/smtpctl/../parser.c Lines: 0 158 0.0 %
Date: 2017-11-07 Branches: 0 124 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: parser.c,v 1.41 2017/07/31 16:38:33 gilles Exp $	*/
2
3
/*
4
 * Copyright (c) 2013 Eric Faurot	<eric@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
#include <sys/queue.h>
21
#include <sys/socket.h>
22
23
#include <netinet/in.h>
24
#include <net/if.h>
25
#include <arpa/inet.h>
26
27
#include <err.h>
28
#include <inttypes.h>
29
#include <limits.h>
30
#include <netdb.h>
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <string.h>
34
35
#include "parser.h"
36
37
uint64_t text_to_evpid(const char *);
38
uint32_t text_to_msgid(const char *);
39
40
struct node {
41
	int			 type;
42
	const char		*token;
43
	struct node		*parent;
44
	TAILQ_ENTRY(node)	 entry;
45
	TAILQ_HEAD(, node)	 children;
46
	int			(*cmd)(int, struct parameter*);
47
};
48
49
static struct node	*root;
50
51
static int text_to_sockaddr(struct sockaddr *, int, const char *);
52
53
#define ARGVMAX	64
54
55
int
56
cmd_install(const char *pattern, int (*cmd)(int, struct parameter*))
57
{
58
	struct node	*node, *tmp;
59
	char		*s, *str, *argv[ARGVMAX], **ap;
60
	int		 i, n;
61
62
	/* Tokenize */
63
	str = s = strdup(pattern);
64
	if (str == NULL)
65
		err(1, "strdup");
66
	n = 0;
67
	for (ap = argv; n < ARGVMAX && (*ap = strsep(&str, " \t")) != NULL;) {
68
		if (**ap != '\0') {
69
			ap++;
70
			n++;
71
		}
72
	}
73
	*ap = NULL;
74
75
	if (root == NULL) {
76
		root = calloc(1, sizeof (*root));
77
		TAILQ_INIT(&root->children);
78
	}
79
	node = root;
80
81
	for (i = 0; i < n; i++) {
82
		TAILQ_FOREACH(tmp, &node->children, entry) {
83
			if (!strcmp(tmp->token, argv[i])) {
84
				node = tmp;
85
				break;
86
			}
87
		}
88
		if (tmp == NULL) {
89
			tmp = calloc(1, sizeof (*tmp));
90
			TAILQ_INIT(&tmp->children);
91
			if (!strcmp(argv[i], "<str>"))
92
				tmp->type = P_STR;
93
			else if (!strcmp(argv[i], "<int>"))
94
				tmp->type = P_INT;
95
			else if (!strcmp(argv[i], "<msgid>"))
96
				tmp->type = P_MSGID;
97
			else if (!strcmp(argv[i], "<evpid>"))
98
				tmp->type = P_EVPID;
99
			else if (!strcmp(argv[i], "<routeid>"))
100
				tmp->type = P_ROUTEID;
101
			else if (!strcmp(argv[i], "<addr>"))
102
				tmp->type = P_ADDR;
103
			else
104
				tmp->type = P_TOKEN;
105
			tmp->token = strdup(argv[i]);
106
			tmp->parent = node;
107
			TAILQ_INSERT_TAIL(&node->children, tmp, entry);
108
			node = tmp;
109
		}
110
	}
111
112
	if (node->cmd)
113
		errx(1, "duplicate pattern: %s", pattern);
114
	node->cmd = cmd;
115
116
	free(s);
117
	return (n);
118
}
119
120
static int
121
cmd_check(const char *str, struct node *node, struct parameter *res)
122
{
123
	const char *e;
124
125
	switch (node->type) {
126
	case P_TOKEN:
127
		if (!strcmp(str, node->token))
128
			return (1);
129
		return (0);
130
131
	case P_STR:
132
		res->u.u_str = str;
133
		return (1);
134
135
	case P_INT:
136
		res->u.u_int = strtonum(str, INT_MIN, INT_MAX, &e);
137
		if (e)
138
			return (0);
139
		return (1);
140
141
	case P_MSGID:
142
		if (strlen(str) != 8)
143
			return (0);
144
		res->u.u_msgid = text_to_msgid(str);
145
		if (res->u.u_msgid == 0)
146
			return (0);
147
		return (1);
148
149
	case P_EVPID:
150
		if (strlen(str) != 16)
151
			return (0);
152
		res->u.u_evpid = text_to_evpid(str);
153
		if (res->u.u_evpid == 0)
154
			return (0);
155
		return (1);
156
157
	case P_ROUTEID:
158
		res->u.u_routeid = strtonum(str, 1, LLONG_MAX, &e);
159
		if (e)
160
			return (0);
161
		return (1);
162
163
	case P_ADDR:
164
		if (text_to_sockaddr((struct sockaddr *)&res->u.u_ss, PF_UNSPEC, str) == 0)
165
			return (1);
166
		return (0);
167
168
	default:
169
		errx(1, "bad token type: %d", node->type);
170
		return (0);
171
	}
172
}
173
174
int
175
cmd_run(int argc, char **argv)
176
{
177
	struct parameter param[ARGVMAX];
178
	struct node	*node, *tmp, *stack[ARGVMAX], *best;
179
	int		 i, j, np;
180
181
	node = root;
182
	np = 0;
183
184
	for (i = 0; i < argc; i++) {
185
		TAILQ_FOREACH(tmp, &node->children, entry) {
186
			if (cmd_check(argv[i], tmp, &param[np])) {
187
				stack[i] = tmp;
188
				node = tmp;
189
				param[np].type = node->type;
190
				if (node->type != P_TOKEN)
191
					np++;
192
				break;
193
			}
194
		}
195
		if (tmp == NULL) {
196
			best = NULL;
197
			TAILQ_FOREACH(tmp, &node->children, entry) {
198
				if (tmp->type != P_TOKEN)
199
					continue;
200
				if (strstr(tmp->token, argv[i]) != tmp->token)
201
					continue;
202
				if (best)
203
					goto fail;
204
				best = tmp;
205
			}
206
			if (best == NULL)
207
				goto fail;
208
			stack[i] = best;
209
			node = best;
210
			param[np].type = node->type;
211
			if (node->type != P_TOKEN)
212
				np++;
213
		}
214
	}
215
216
	if (node->cmd == NULL)
217
		goto fail;
218
219
	return (node->cmd(np, np ? param : NULL));
220
221
fail:
222
	fprintf(stderr, "possibilities are:\n");
223
	TAILQ_FOREACH(tmp, &node->children, entry) {
224
		for (j = 0; j < i; j++)
225
			fprintf(stderr, "%s%s", j?" ":"", stack[j]->token);
226
		fprintf(stderr, "%s%s\n", i?" ":"", tmp->token);
227
	}
228
229
	return (-1);
230
}
231
232
int
233
cmd_show_params(int argc, struct parameter *argv)
234
{
235
	int	i;
236
237
	for (i = 0; i < argc; i++) {
238
		switch(argv[i].type) {
239
		case P_STR:
240
			printf(" str:\"%s\"", argv[i].u.u_str);
241
			break;
242
		case P_INT:
243
			printf(" int:%d", argv[i].u.u_int);
244
			break;
245
		case P_MSGID:
246
			printf(" msgid:%08"PRIx32, argv[i].u.u_msgid);
247
			break;
248
		case P_EVPID:
249
			printf(" evpid:%016"PRIx64, argv[i].u.u_evpid);
250
			break;
251
		case P_ROUTEID:
252
			printf(" routeid:%016"PRIx64, argv[i].u.u_routeid);
253
			break;
254
		default:
255
			printf(" ???:%d", argv[i].type);
256
		}
257
	}
258
	printf ("\n");
259
	return (1);
260
}
261
262
static int
263
text_to_sockaddr(struct sockaddr *sa, int family, const char *str)
264
{
265
	struct in_addr		 ina;
266
	struct in6_addr		 in6a;
267
	struct sockaddr_in	*in;
268
	struct sockaddr_in6	*in6;
269
	char			*cp, *str2;
270
	const char		*errstr;
271
272
	switch (family) {
273
	case PF_UNSPEC:
274
		if (text_to_sockaddr(sa, PF_INET, str) == 0)
275
			return (0);
276
		return text_to_sockaddr(sa, PF_INET6, str);
277
278
	case PF_INET:
279
		if (inet_pton(PF_INET, str, &ina) != 1)
280
			return (-1);
281
282
		in = (struct sockaddr_in *)sa;
283
		memset(in, 0, sizeof *in);
284
		in->sin_len = sizeof(struct sockaddr_in);
285
		in->sin_family = PF_INET;
286
		in->sin_addr.s_addr = ina.s_addr;
287
		return (0);
288
289
	case PF_INET6:
290
		cp = strchr(str, SCOPE_DELIMITER);
291
		if (cp) {
292
			str2 = strdup(str);
293
			if (str2 == NULL)
294
				return (-1);
295
			str2[cp - str] = '\0';
296
			if (inet_pton(PF_INET6, str2, &in6a) != 1) {
297
				free(str2);
298
				return (-1);
299
			}
300
			cp++;
301
			free(str2);
302
		} else if (inet_pton(PF_INET6, str, &in6a) != 1)
303
			return (-1);
304
305
		in6 = (struct sockaddr_in6 *)sa;
306
		memset(in6, 0, sizeof *in6);
307
		in6->sin6_len = sizeof(struct sockaddr_in6);
308
		in6->sin6_family = PF_INET6;
309
		in6->sin6_addr = in6a;
310
311
		if (cp == NULL)
312
			return (0);
313
314
		if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
315
		    IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
316
		    IN6_IS_ADDR_MC_INTFACELOCAL(&in6a))
317
			if ((in6->sin6_scope_id = if_nametoindex(cp)))
318
				return (0);
319
320
		in6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr);
321
		if (errstr)
322
			return (-1);
323
		return (0);
324
325
	default:
326
		break;
327
	}
328
329
	return (-1);
330
}