blob: cdc5d51fd949ec503c05e6090015caf7446c4ca8 [file] [log] [blame]
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -03001/*
2 * Copyright (c) 2010 Christiano F. Haesbaert <haesbaert@haesbaert.org>
3 * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
4 * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
5 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <netinet/in.h>
23#include <arpa/inet.h>
Christiano F. Haesbaert36ff6882010-03-10 00:15:04 -030024#include <ctype.h>
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -030025#include <err.h>
26#include <errno.h>
27#include <limits.h>
28#include <stdio.h>
Christiano F. Haesbaert259edee2010-10-25 22:35:27 -020029#include <stdint.h>
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -030030#include <stdlib.h>
31#include <string.h>
32
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -030033#include "parser.h"
34
35enum token_type {
36 NOTOKEN,
37 ENDTOKEN,
38 KEYWORD,
39 ADDRESS,
Christiano F. Haesbaert4268cc32010-05-01 00:48:07 -030040 FLAGS,
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -030041 HOSTNAME,
Christiano F. Haesbaert4a7b1052010-05-06 19:44:11 -030042 PROTO,
Christiano F. Haesbaertbd49de02010-06-01 19:51:50 -030043 APPPROTO,
Christiano F. Haesbaertd1a20822010-10-17 17:39:55 -020044 BRFLAGS,
45 SRVNAME,
Christiano F. Haesbaert259edee2010-10-25 22:35:27 -020046 TXTSTRING,
47 PORT
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -030048};
49
50struct token {
51 enum token_type type;
52 const char *keyword;
53 int value;
54 const struct token *next;
55};
56
57static const struct token t_main[];
Christiano F. Haesbaert4093a2f2010-09-04 20:25:50 -030058static const struct token t_lookup[];
59static const struct token t_rlookup[];
Christiano F. Haesbaert307df702010-05-25 20:40:58 -030060static const struct token t_browse_proto[];
Christiano F. Haesbaert4a7b1052010-05-06 19:44:11 -030061static const struct token t_browse_app[];
Christiano F. Haesbaertd1a20822010-10-17 17:39:55 -020062static const struct token t_publish[];
63static const struct token t_publish_app[];
64static const struct token t_publish_app_proto[];
Christiano F. Haesbaert259edee2010-10-25 22:35:27 -020065static const struct token t_publish_app_proto_port[];
66static const struct token t_publish_app_proto_port_txt[];
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -030067
68static const struct token t_main[] = {
Christiano F. Haesbaert4093a2f2010-09-04 20:25:50 -030069 {KEYWORD, "lookup", NONE, t_lookup},
70 {KEYWORD, "rlookup", NONE, t_rlookup},
Christiano F. Haesbaert307df702010-05-25 20:40:58 -030071 {KEYWORD, "browse", NONE, t_browse_app},
Christiano F. Haesbaertd1a20822010-10-17 17:39:55 -020072 {KEYWORD, "publish", NONE, t_publish},
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -030073 {ENDTOKEN, "", NONE, NULL}
74};
75
Christiano F. Haesbaert4093a2f2010-09-04 20:25:50 -030076static const struct token t_lookup[] = {
77 { FLAGS , "-", NONE, t_lookup},
Christiano F. Haesbaertd8123622010-07-17 20:42:40 -030078 { HOSTNAME, "", LOOKUP, NULL},
79 { ENDTOKEN, "", NONE, NULL}
80};
81
Christiano F. Haesbaert4093a2f2010-09-04 20:25:50 -030082static const struct token t_rlookup[] = {
Christiano F. Haesbaertd8123622010-07-17 20:42:40 -030083 { ADDRESS, "", RLOOKUP, NULL},
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -030084 { ENDTOKEN, "", NONE, NULL}
85};
86
Christiano F. Haesbaert307df702010-05-25 20:40:58 -030087static const struct token t_browse_app[] = {
Christiano F. Haesbaertbd49de02010-06-01 19:51:50 -030088 { BRFLAGS, "-", NONE, t_browse_app},
89 { KEYWORD, "all", BROWSE_PROTO, NULL},
Christiano F. Haesbaert307df702010-05-25 20:40:58 -030090 { APPPROTO, "", NONE, t_browse_proto},
Christiano F. Haesbaert4a7b1052010-05-06 19:44:11 -030091 { ENDTOKEN, "", NONE, NULL}
92};
93
Christiano F. Haesbaert307df702010-05-25 20:40:58 -030094static const struct token t_browse_proto[] = {
95 { PROTO, "tcp", BROWSE_PROTO, NULL},
96 { PROTO, "udp", BROWSE_PROTO, NULL},
Christiano F. Haesbaert9d9d5ce2010-04-27 18:59:48 -030097 { ENDTOKEN, "", NONE, NULL}
98};
Christiano F. Haesbaert0e2793e2010-02-22 20:13:59 -030099
Christiano F. Haesbaertd1a20822010-10-17 17:39:55 -0200100static const struct token t_publish[] = {
101 { SRVNAME, "", NONE, t_publish_app},
102 { ENDTOKEN, "", NONE, NULL}
103};
104
105static const struct token t_publish_app[] = {
106 { APPPROTO, "", NONE, t_publish_app_proto},
107 { ENDTOKEN, "", NONE, NULL}
108};
109
110static const struct token t_publish_app_proto[] = {
Christiano F. Haesbaert5f44bfe2010-10-27 18:16:37 -0200111 { PROTO, "tcp", NONE, t_publish_app_proto_port},
112 { PROTO, "udp", NONE, t_publish_app_proto_port},
Christiano F. Haesbaertd1a20822010-10-17 17:39:55 -0200113 { ENDTOKEN, "", NONE, NULL}
114};
115
Christiano F. Haesbaert259edee2010-10-25 22:35:27 -0200116static const struct token t_publish_app_proto_port[] = {
Christiano F. Haesbaert5f44bfe2010-10-27 18:16:37 -0200117 { PORT, "", NONE, t_publish_app_proto_port_txt},
118 { ENDTOKEN, "", NONE, t_publish_app_proto_port_txt}
Christiano F. Haesbaert259edee2010-10-25 22:35:27 -0200119};
120
121static const struct token t_publish_app_proto_port_txt[] = {
Christiano F. Haesbaertd1a20822010-10-17 17:39:55 -0200122 { TXTSTRING, "", PUBLISH, NULL},
123 { ENDTOKEN, "", NONE, NULL}
124};
125
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -0300126static struct parse_result res;
127
128struct parse_result *
129parse(int argc, char *argv[])
130{
131 const struct token *table = t_main;
132 const struct token *match;
133
134 bzero(&res, sizeof(res));
135
136 while (argc >= 0) {
137 if ((match = match_token(argv[0], table)) == NULL) {
138 fprintf(stderr, "valid commands/args:\n");
139 show_valid_args(table);
140 return (NULL);
141 }
142
143 argc--;
144 argv++;
145
146 if (match->type == NOTOKEN || match->next == NULL)
147 break;
148
149 table = match->next;
150 }
151
152 if (argc > 0) {
153 fprintf(stderr, "superfluous argument: %s\n", argv[0]);
154 return (NULL);
155 }
156
157 return (&res);
158}
159
160const struct token *
161match_token(const char *word, const struct token *table)
162{
163 u_int i, match;
Christiano F. Haesbaert259edee2010-10-25 22:35:27 -0200164 const char *errstr;
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -0300165 const struct token *t = NULL;
166
167 match = 0;
168
169 for (i = 0; table[i].type != ENDTOKEN; i++) {
170 switch (table[i].type) {
171 case NOTOKEN:
172 if (word == NULL || strlen(word) == 0) {
173 match++;
174 t = &table[i];
175 }
176 break;
Christiano F. Haesbaertbd49de02010-06-01 19:51:50 -0300177 case BRFLAGS:
178 if (parse_brflags(word, &res.flags)) {
179 match++;
180 t = &table[i];
181 if (t->value)
182 res.action = t->value;
183 }
184 break;
Christiano F. Haesbaert4268cc32010-05-01 00:48:07 -0300185 case FLAGS:
186 if (parse_flags(word, &res.flags)) {
187 match++;
188 t = &table[i];
189 if (t->value)
190 res.action = t->value;
191 }
192 break;
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -0300193 case KEYWORD:
Christiano F. Haesbaert0e2793e2010-02-22 20:13:59 -0300194 if (word != NULL && strcmp(word, table[i].keyword)
195 == 0) {
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -0300196 match++;
197 t = &table[i];
198 if (t->value)
199 res.action = t->value;
200 }
201 break;
Christiano F. Haesbaert4a7b1052010-05-06 19:44:11 -0300202 case PROTO:
203 if (word != NULL && strcmp(word, table[i].keyword)
204 == 0) {
205 res.proto = word;
206 match++;
207 t = &table[i];
208 if (t->value)
209 res.action = t->value;
210 }
211 break;
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -0300212 case ADDRESS:
213 if (parse_addr(word, &res.addr)) {
214 match++;
215 t = &table[i];
216 if (t->value)
217 res.action = t->value;
218 }
219 break;
220 case HOSTNAME:
221 if (parse_hostname(word, res.hostname)) {
222 match++;
223 t = &table[i];
224 if (t->value)
225 res.action = t->value;
226 }
227 break;
Christiano F. Haesbaert4a7b1052010-05-06 19:44:11 -0300228 case APPPROTO:
Christiano F. Haesbaertbd49de02010-06-01 19:51:50 -0300229 if (word && *word != '-' &&
230 (strcmp(word, "all") != 0)) {
Christiano F. Haesbaert307df702010-05-25 20:40:58 -0300231 res.app = word;
Christiano F. Haesbaert9d9d5ce2010-04-27 18:59:48 -0300232 match++;
233 t = &table[i];
234 if (t->value)
235 res.action = t->value;
236 }
Christiano F. Haesbaertd1a20822010-10-17 17:39:55 -0200237 break;
238 case SRVNAME: /* match anything */
239 if (word != NULL) {
240 res.srvname = word;
241 match++;
242 t = &table[i];
243 if (t->value)
244 res.action = t->value;
245 }
246 break;
247 case TXTSTRING:
248 if (word != NULL) {
249 res.txtstring = word;
250 match++;
251 t = &table[i];
252 if (t->value)
253 res.action = t->value;
254 }
255 break;
Christiano F. Haesbaert259edee2010-10-25 22:35:27 -0200256 case PORT:
257 if (word != NULL) {
258 res.port = strtonum(word, 1, UINT16_MAX,
259 &errstr);
260 if (errstr)
261 errx(1, "strtonum: %s", errstr);
262 match++;
263 t = &table[i];
264 if (t->value)
265 res.action = t->value;
266 }
267 break;
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -0300268 case ENDTOKEN:
269 break;
270 }
271 }
272
273 if (match != 1) {
274 if (word == NULL)
275 fprintf(stderr, "missing argument:\n");
276 else if (match > 1)
277 fprintf(stderr, "ambiguous argument: %s\n", word);
278 else if (match < 1)
279 fprintf(stderr, "unknown argument: %s\n", word);
280 return (NULL);
281 }
282
283 return (t);
284}
285
286void
287show_valid_args(const struct token *table)
288{
289 int i;
290
291 for (i = 0; table[i].type != ENDTOKEN; i++) {
292 switch (table[i].type) {
293 case NOTOKEN:
294 fprintf(stderr, " <cr>\n");
295 break;
296 case KEYWORD:
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -0300297 fprintf(stderr, " %s\n", table[i].keyword);
298 break;
Christiano F. Haesbaert4a7b1052010-05-06 19:44:11 -0300299 case PROTO:
300 fprintf(stderr, " %s\n", table[i].keyword);
301 break;
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -0300302 case ADDRESS:
303 fprintf(stderr, " <address>\n");
304 break;
305 case HOSTNAME:
306 fprintf(stderr, " <hostname.local>\n");
307 break;
Christiano F. Haesbaert4a7b1052010-05-06 19:44:11 -0300308 case APPPROTO:
309 fprintf(stderr, " <application protocol>\n");
Christiano F. Haesbaert9d9d5ce2010-04-27 18:59:48 -0300310 break;
Christiano F. Haesbaert4268cc32010-05-01 00:48:07 -0300311 case FLAGS:
312 fprintf(stderr, " <-ahst>\n");
313 break;
Christiano F. Haesbaertbd49de02010-06-01 19:51:50 -0300314 case BRFLAGS:
315 fprintf(stderr, " <-r>\n");
316 break;
Christiano F. Haesbaertd1a20822010-10-17 17:39:55 -0200317 case SRVNAME:
318 fprintf(stderr, " <service name>\n");
319 break;
320 case TXTSTRING:
321 fprintf(stderr, " <text string>\n");
322 break;
Christiano F. Haesbaert259edee2010-10-25 22:35:27 -0200323 case PORT:
324 fprintf(stderr, " <port>\n");
325 break;
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -0300326 case ENDTOKEN:
327 break;
328 }
329 }
330}
331
332int
333parse_addr(const char *word, struct in_addr *addr)
334{
335 struct in_addr ina;
Christiano F. Haesbaertbeea4662010-07-02 02:57:14 -0300336
Christiano F. Haesbaert36ff6882010-03-10 00:15:04 -0300337 if (word == NULL || !isdigit(*word))
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -0300338 return (0);
339
340 bzero(addr, sizeof(struct in_addr));
341 bzero(&ina, sizeof(ina));
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -0300342 if (inet_pton(AF_INET, word, &ina)) {
343 addr->s_addr = ina.s_addr;
344 return (1);
345 }
346
347 return (0);
348}
349
350int
351parse_hostname(const char *word, char hostname[MAXHOSTNAMELEN])
352{
Christiano F. Haesbaertb8ad9112010-09-04 19:52:08 -0300353 if (word == NULL || *word == '-')
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -0300354 return (0);
Christiano F. Haesbaertb8ad9112010-09-04 19:52:08 -0300355
Christiano F. Haesbaert6162f3a2010-03-25 19:30:23 -0300356 if (strlen(word) < 7 || /* shorter host is a.local */
357 strcmp(&word[strlen(word) - 6], ".local") != 0) {
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -0300358 fprintf(stderr, "Invalid domain, must be .local\n");
359 return (0);
360 }
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -0300361 strlcpy(hostname, word, MAXHOSTNAMELEN);
Christiano F. Haesbaertbeea4662010-07-02 02:57:14 -0300362
Christiano F. Haesbaertd9774212010-02-21 21:31:58 -0300363 return (1);
364}
365
Christiano F. Haesbaert4268cc32010-05-01 00:48:07 -0300366int
367parse_flags(const char *word, int *flags)
368{
369 int r = 0;
Christiano F. Haesbaertb8ad9112010-09-04 19:52:08 -0300370
Christiano F. Haesbaert4268cc32010-05-01 00:48:07 -0300371 if (word == NULL || *word != '-')
372 return (r);
373 word++;
374 while(*word) {
375 switch (*word) {
376 case 'a':
377 *flags |= F_A;
378 r++;
379 break;
380 case 'h':
381 *flags |= F_HINFO;
382 r++;
383 break;
384 case 's':
385 *flags |= F_SRV;
386 r++;
387 break;
388 case 't':
389 *flags |= F_TXT;
390 r++;
391 break;
392 default:
393 errx(1, "unknown flag -%c", *word);
394 }
395 word++;
396 }
Christiano F. Haesbaertbeea4662010-07-02 02:57:14 -0300397
Christiano F. Haesbaert4268cc32010-05-01 00:48:07 -0300398 return (r);
399}
Christiano F. Haesbaertbd49de02010-06-01 19:51:50 -0300400
401int
402parse_brflags(const char *word, int *flags)
403{
404 int r = 0;
Christiano F. Haesbaertbeea4662010-07-02 02:57:14 -0300405
Christiano F. Haesbaertbd49de02010-06-01 19:51:50 -0300406 if (word == NULL || *word != '-')
407 return (r);
408 word++;
409 while(*word) {
410 switch (*word) {
411 case 'r':
412 *flags |= F_RESOLV;
413 r++;
414 break;
415 default:
416 errx(1, "unknown flag -%c", *word);
417 }
418 word++;
419 }
Christiano F. Haesbaertbeea4662010-07-02 02:57:14 -0300420
Christiano F. Haesbaertbd49de02010-06-01 19:51:50 -0300421 return (r);
422}