1 /* $OpenBSD: parser.c,v 1.3 2015/01/19 01:48:57 deraadt Exp $ */
2
3 /* This file is derived from OpenBSD:src/usr.sbin/ikectl/parser.c 1.9 */
4 /*
5 * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net>
6 * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
7 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25
26 #include <ctype.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "parser.h"
34
35 enum token_type {
36 NOTOKEN,
37 ENDTOKEN,
38 KEYWORD,
39 PPP_ID,
40 ADDRESS,
41 INTERFACE,
42 PROTOCOL,
43 REALM,
44 USERNAME
45 };
46
47 struct token {
48 enum token_type type;
49 const char *keyword;
50 int value;
51 const struct token *next;
52 };
53
54 static struct parse_result res;
55
56 static const struct token t_main[];
57 static const struct token t_session[];
58 static const struct token t_clear[];
59 static const struct token t_monitor[];
60 static const struct token t_filter[];
61 static const struct token t_ppp_id[];
62 static const struct token t_address[];
63 static const struct token t_interface[];
64 static const struct token t_protocol[];
65 static const struct token t_realm[];
66 static const struct token t_username[];
67
68 static const struct token t_main[] = {
69 { KEYWORD, "session", NONE, t_session },
70 { KEYWORD, "clear", NONE, t_clear },
71 { KEYWORD, "monitor", NONE, t_monitor },
72 { ENDTOKEN, "", NONE, NULL }
73 };
74
75 static const struct token t_session[] = {
76 { KEYWORD, "brief", SESSION_BRIEF, NULL },
77 { KEYWORD, "packets", SESSION_PKTS, NULL },
78 { KEYWORD, "all", SESSION_ALL, t_filter },
79 { ENDTOKEN, "", NONE, NULL }
80 };
81
82 static const struct token t_clear[] = {
83 { KEYWORD, "all", CLEAR_SESSION, NULL },
84 { KEYWORD, "ppp-id", CLEAR_SESSION, t_ppp_id },
85 { KEYWORD, "address", CLEAR_SESSION, t_address },
86 { KEYWORD, "interface", CLEAR_SESSION, t_interface },
87 { KEYWORD, "protocol", CLEAR_SESSION, t_protocol },
88 { KEYWORD, "realm", CLEAR_SESSION, t_realm },
89 { KEYWORD, "username", CLEAR_SESSION, t_username },
90 { ENDTOKEN, "", CLEAR_SESSION, NULL }
91 };
92
93 static const struct token t_monitor[] = {
94 { KEYWORD, "all", MONITOR_SESSION, NULL },
95 { KEYWORD, "ppp-id", MONITOR_SESSION, t_ppp_id },
96 { KEYWORD, "address", MONITOR_SESSION, t_address },
97 { KEYWORD, "interface", MONITOR_SESSION, t_interface },
98 { KEYWORD, "protocol", MONITOR_SESSION, t_protocol },
99 { KEYWORD, "realm", MONITOR_SESSION, t_realm },
100 { KEYWORD, "username", MONITOR_SESSION, t_username },
101 { ENDTOKEN, "", MONITOR_SESSION, NULL }
102 };
103
104 static const struct token t_filter[] = {
105 { NOTOKEN, "", NONE, NULL },
106 { KEYWORD, "ppp-id", NONE, t_ppp_id },
107 { KEYWORD, "address", NONE, t_address },
108 { KEYWORD, "interface", NONE, t_interface },
109 { KEYWORD, "protocol", NONE, t_protocol },
110 { KEYWORD, "realm", NONE, t_realm },
111 { KEYWORD, "username", NONE, t_username },
112 { ENDTOKEN, "", NONE, NULL }
113 };
114
115 static const struct token t_ppp_id[] = {
116 { PPP_ID, "", NONE, t_filter },
117 { ENDTOKEN, "", NONE, NULL }
118 };
119 static const struct token t_address[] = {
120 { ADDRESS, "", NONE, t_filter },
121 { ENDTOKEN, "", NONE, NULL }
122 };
123 static const struct token t_interface[] = {
124 { INTERFACE, "", NONE, t_filter },
125 { ENDTOKEN, "", NONE, NULL }
126 };
127 static const struct token t_protocol[] = {
128 { PROTOCOL, "", NONE, t_filter },
129 { ENDTOKEN, "", NONE, NULL }
130 };
131 static const struct token t_realm[] = {
132 { REALM, "", NONE, t_filter },
133 { ENDTOKEN, "", NONE, NULL }
134 };
135 static const struct token t_username[] = {
136 { USERNAME, "", NONE, t_filter },
137 { ENDTOKEN, "", NONE, NULL }
138 };
139
140 static const struct token *match_token(char *, const struct token []);
141 static void show_valid_args(const struct token []);
142
143 struct parse_result *
parse(int argc,char * argv[])144 parse(int argc, char *argv[])
145 {
146 const struct token *table = t_main;
147 const struct token *match;
148
149 bzero(&res, sizeof(res));
150
151 while (argc >= 0) {
152 if ((match = match_token(argv[0], table)) == NULL) {
153 fprintf(stderr, "valid commands/args:\n");
154 show_valid_args(table);
155 return (NULL);
156 }
157
158 argc--;
159 argv++;
160
161 if (match->type == NOTOKEN || match->next == NULL)
162 break;
163
164 table = match->next;
165 }
166
167 if (argc > 0) {
168 fprintf(stderr, "superfluous argument: %s\n", argv[0]);
169 return (NULL);
170 }
171
172 return (&res);
173 }
174
175 static const struct token *
match_token(char * word,const struct token table[])176 match_token(char *word, const struct token table[])
177 {
178 u_int i, match = 0;
179 unsigned long int ulval;
180 const struct token *t = NULL;
181 char *ep;
182
183 for (i = 0; table[i].type != ENDTOKEN; i++) {
184 switch (table[i].type) {
185 case NOTOKEN:
186 if (word == NULL || strlen(word) == 0) {
187 match++;
188 t = &table[i];
189 }
190 break;
191 case KEYWORD:
192
193 if (word != NULL && strncmp(word, table[i].keyword,
194 strlen(word)) == 0) {
195 match++;
196 t = &table[i];
197 if (t->value)
198 res.action = t->value;
199 }
200 break;
201 case PPP_ID:
202 if (word == NULL)
203 break;
204 errno = 0;
205 ulval = strtoul(word, &ep, 10);
206 if (isdigit((unsigned char)*word) && *ep == '\0' &&
207 !(errno == ERANGE && ulval == ULONG_MAX)) {
208 res.ppp_id = ulval;
209 res.has_ppp_id = 1;
210 match++;
211 t = &table[i];
212 }
213 break;
214 case ADDRESS:
215 {
216 struct sockaddr_in sin4 = {
217 .sin_family = AF_INET,
218 .sin_len = sizeof(struct sockaddr_in)
219 };
220 struct sockaddr_in6 sin6 = {
221 .sin6_family = AF_INET6,
222 .sin6_len = sizeof(struct sockaddr_in6)
223 };
224 if (word == NULL)
225 break;
226 if (inet_pton(AF_INET, word, &sin4.sin_addr) == 1)
227 memcpy(&res.address, &sin4, sin4.sin_len);
228 else
229 if (inet_pton(AF_INET6, word, &sin6.sin6_addr) == 1)
230 memcpy(&res.address, &sin6, sin6.sin6_len);
231 else
232 break;
233 match++;
234 t = &table[i];
235 }
236 break;
237 case INTERFACE:
238 if (word == NULL)
239 break;
240 res.interface = word;
241 match++;
242 t = &table[i];
243 break;
244 case PROTOCOL:
245 if (word == NULL)
246 break;
247 if ((res.protocol = parse_protocol(word)) ==
248 PROTO_UNSPEC)
249 break;
250 match++;
251 t = &table[i];
252 break;
253 case REALM:
254 if (word == NULL)
255 break;
256 res.realm = word;
257 match++;
258 t = &table[i];
259 break;
260 case USERNAME:
261 if (word == NULL)
262 break;
263 res.username = word;
264 match++;
265 t = &table[i];
266 break;
267 case ENDTOKEN:
268 break;
269 }
270 }
271
272 if (match != 1) {
273 if (word == NULL)
274 fprintf(stderr, "missing argument:\n");
275 else if (match > 1)
276 fprintf(stderr, "ambiguous argument: %s\n", word);
277 else if (match < 1)
278 fprintf(stderr, "unknown argument: %s\n", word);
279 return (NULL);
280 }
281
282 return (t);
283 }
284
285 static void
show_valid_args(const struct token table[])286 show_valid_args(const struct token table[])
287 {
288 int i;
289
290 for (i = 0; table[i].type != ENDTOKEN; i++) {
291 switch (table[i].type) {
292 case NOTOKEN:
293 fprintf(stderr, " <cr>\n");
294 break;
295 case KEYWORD:
296 fprintf(stderr, " %s\n", table[i].keyword);
297 break;
298 case PPP_ID:
299 fprintf(stderr, " <ppp-id>\n");
300 break;
301 case ADDRESS:
302 fprintf(stderr, " <address>\n");
303 break;
304 case INTERFACE:
305 fprintf(stderr, " <interface>\n");
306 break;
307 case PROTOCOL:
308 fprintf(stderr, " [ pppoe | l2tp | pptp | sstp ]\n");
309 break;
310 case REALM:
311 fprintf(stderr, " <realm>\n");
312 break;
313 case USERNAME:
314 fprintf(stderr, " <username>\n");
315 break;
316 case ENDTOKEN:
317 break;
318 }
319 }
320 }
321
322 enum protocol
parse_protocol(const char * str)323 parse_protocol(const char *str)
324 {
325 return
326 (strcasecmp(str, "PPTP" ) == 0)? PPTP :
327 (strcasecmp(str, "L2TP" ) == 0)? L2TP :
328 (strcasecmp(str, "PPPoE") == 0)? PPPOE :
329 (strcasecmp(str, "SSTP" ) == 0)? SSTP : PROTO_UNSPEC;
330 }
331
332