1 /*        $NetBSD: parseipfexpr.c,v 1.2 2021/12/05 08:19:57 msaitoh Exp $       */
2 
3 #include "ipf.h"
4 #include <ctype.h>
5 
6 
7 typedef struct ipfopentry {
8           int       ipoe_cmd;
9           int       ipoe_nbasearg;
10           int       ipoe_maxarg;
11           int       ipoe_argsize;
12           char      *ipoe_word;
13 } ipfopentry_t;
14 
15 static ipfopentry_t opwords[17] = {
16           { IPF_EXP_IP_ADDR, 2, 0, 1, "ip.addr" },
17           { IPF_EXP_IP6_ADDR, 2, 0, 4, "ip6.addr" },
18           { IPF_EXP_IP_PR, 1, 0, 1, "ip.p" },
19           { IPF_EXP_IP_SRCADDR, 2, 0, 1, "ip.src" },
20           { IPF_EXP_IP_DSTADDR, 2, 0, 1, "ip.dst" },
21           { IPF_EXP_IP6_SRCADDR, 2, 0, 4, "ip6.src" },
22           { IPF_EXP_IP6_DSTADDR, 2, 0, 4, "ip6.dst" },
23           { IPF_EXP_TCP_PORT, 1, 0, 1, "tcp.port" },
24           { IPF_EXP_TCP_DPORT, 1, 0, 1, "tcp.dport" },
25           { IPF_EXP_TCP_SPORT, 1, 0, 1, "tcp.sport" },
26           { IPF_EXP_TCP_FLAGS, 2, 0, 1, "tcp.flags" },
27           { IPF_EXP_UDP_PORT, 1, 0, 1, "udp.port" },
28           { IPF_EXP_UDP_DPORT, 1, 0, 1, "udp.dport" },
29           { IPF_EXP_UDP_SPORT, 1, 0, 1, "udp.sport" },
30           { IPF_EXP_TCP_STATE, 1, 0, 1, "tcp.state" },
31           { IPF_EXP_IDLE_GT, 1, 1, 1, "idle-gt" },
32           { -1, 0, 0, 0, NULL  }
33 };
34 
35 
36 int *
parseipfexpr(line,errorptr)37 parseipfexpr(line, errorptr)
38           char *line;
39           char **errorptr;
40 {
41           int not, items, asize, *oplist, osize, i;
42           char *temp, *arg, *s, *t, *ops, *error;
43           ipfopentry_t *e;
44           ipfexp_t *ipfe;
45 
46           asize = 0;
47           error = NULL;
48           oplist = NULL;
49 
50           temp = strdup(line);
51           if (temp == NULL) {
52                     error = "strdup failed";
53                     goto parseerror;
54           }
55 
56           /*
57            * Eliminate any white spaces to make parsing easier.
58            */
59           for (s = temp; *s != '\0'; ) {
60                     if (ISSPACE(*s))
61                               strcpy(s, s + 1);
62                     else
63                               s++;
64           }
65 
66           /*
67            * Parse the string.
68            * It should be sets of "ip.dst=1.2.3.4/32;" things.
69            * There must be a "=" or "!=" and it must end in ";".
70            */
71           if (temp[strlen(temp) - 1] != ';') {
72                     error = "last character not ';'";
73                     goto parseerror;
74           }
75 
76           /*
77            * Work through the list of complete operands present.
78            */
79           for (ops = strtok(temp, ";"); ops != NULL; ops = strtok(NULL, ";")) {
80                     arg = strchr(ops, '=');
81                     if ((arg < ops + 2) || (arg == NULL)) {
82                               error = "bad 'arg' value";
83                               goto parseerror;
84                     }
85 
86                     if (*(arg - 1) == '!') {
87                               *(arg - 1) = '\0';
88                               not = 1;
89                     } else {
90                               not = 0;
91                     }
92                     *arg++ = '\0';
93 
94 
95                     for (e = opwords; e->ipoe_word; e++) {
96                               if (strcmp(ops, e->ipoe_word) == 0)
97                                         break;
98                     }
99                     if (e->ipoe_word == NULL) {
100                               error = malloc(32);
101                               if (error != NULL) {
102                                         sprintf(error, "keyword (%.10s) not found",
103                                                   ops);
104                               }
105                               goto parseerror;
106                     }
107 
108                     /*
109                      * Count the number of commas so we know how big to
110                      * build the array
111                      */
112                     for (s = arg, items = 1; *s != '\0'; s++)
113                               if (*s == ',')
114                                         items++;
115 
116                     if ((e->ipoe_maxarg != 0) && (items > e->ipoe_maxarg)) {
117                               error = "too many items";
118                               goto parseerror;
119                     }
120 
121                     /*
122                      * osize will mark the end of where we have filled up to
123                      * and is thus where we start putting new data.
124                      */
125                     osize = asize;
126                     asize += 4 + (items * e->ipoe_nbasearg * e->ipoe_argsize);
127                     if (oplist == NULL)
128                               oplist = calloc(1, sizeof(int) * (asize + 2));
129                     else
130                               oplist = realloc(oplist, sizeof(int) * (asize + 2));
131                     if (oplist == NULL) {
132                               error = "oplist alloc failed";
133                               goto parseerror;
134                     }
135                     ipfe = (ipfexp_t *)(oplist + osize);
136                     osize += 4;
137                     ipfe->ipfe_cmd = e->ipoe_cmd;
138                     ipfe->ipfe_not = not;
139                     ipfe->ipfe_narg = items * e->ipoe_nbasearg;
140                     ipfe->ipfe_size = items * e->ipoe_nbasearg * e->ipoe_argsize;
141                     ipfe->ipfe_size += 4;
142 
143                     for (s = arg; (*s != '\0') && (osize < asize); s = t) {
144                               /*
145                                * Look for the end of this arg or the ',' to say
146                                * there is another following.
147                                */
148                               for (t = s; (*t != '\0') && (*t != ','); t++)
149                                         ;
150                               if (*t == ',')
151                                         *t++ = '\0';
152 
153                               if (!strcasecmp(ops, "ip.addr") ||
154                                   !strcasecmp(ops, "ip.src") ||
155                                   !strcasecmp(ops, "ip.dst")) {
156                                         i6addr_t mask, addr;
157                                         char *delim;
158 
159                                         delim = strchr(s, '/');
160                                         if (delim != NULL) {
161                                                   *delim++ = '\0';
162                                                   if (genmask(AF_INET, delim,
163                                                                 &mask) == -1) {
164                                                             error = "genmask failed";
165                                                             goto parseerror;
166                                                   }
167                                         } else {
168                                                   mask.in4.s_addr = 0xffffffff;
169                                         }
170                                         if (gethost(AF_INET, s, &addr) == -1) {
171                                                   error = "gethost failed";
172                                                   goto parseerror;
173                                         }
174 
175                                         oplist[osize++] = addr.in4.s_addr;
176                                         oplist[osize++] = mask.in4.s_addr;
177 
178 #ifdef USE_INET6
179                               } else if (!strcasecmp(ops, "ip6.addr") ||
180                                   !strcasecmp(ops, "ip6.src") ||
181                                   !strcasecmp(ops, "ip6.dst")) {
182                                         i6addr_t mask, addr;
183                                         char *delim;
184 
185                                         delim = strchr(s, '/');
186                                         if (delim != NULL) {
187                                                   *delim++ = '\0';
188                                                   if (genmask(AF_INET6, delim,
189                                                                 &mask) == -1) {
190                                                             error = "genmask failed";
191                                                             goto parseerror;
192                                                   }
193                                         } else {
194                                                   mask.i6[0] = 0xffffffff;
195                                                   mask.i6[1] = 0xffffffff;
196                                                   mask.i6[2] = 0xffffffff;
197                                                   mask.i6[3] = 0xffffffff;
198                                         }
199                                         if (gethost(AF_INET6, s, &addr) == -1) {
200                                                   error = "gethost failed";
201                                                   goto parseerror;
202                                         }
203 
204                                         oplist[osize++] = addr.i6[0];
205                                         oplist[osize++] = addr.i6[1];
206                                         oplist[osize++] = addr.i6[2];
207                                         oplist[osize++] = addr.i6[3];
208                                         oplist[osize++] = mask.i6[0];
209                                         oplist[osize++] = mask.i6[1];
210                                         oplist[osize++] = mask.i6[2];
211                                         oplist[osize++] = mask.i6[3];
212 #endif
213 
214                               } else if (!strcasecmp(ops, "ip.p")) {
215                                         int p;
216 
217                                         p = getproto(s);
218                                         if (p == -1)
219                                                   goto parseerror;
220                                         oplist[osize++] = p;
221 
222                               } else if (!strcasecmp(ops, "tcp.flags")) {
223                                         u_32_t mask, flags;
224                                         char *delim;
225 
226                                         delim = strchr(s, '/');
227                                         if (delim != NULL) {
228                                                   *delim++ = '\0';
229                                                   mask = tcpflags(delim);
230                                         } else {
231                                                   mask = 0xff;
232                                         }
233                                         flags = tcpflags(s);
234 
235                                         oplist[osize++] = flags;
236                                         oplist[osize++] = mask;
237 
238 
239                               } else if (!strcasecmp(ops, "tcp.port") ||
240                                   !strcasecmp(ops, "tcp.sport") ||
241                                   !strcasecmp(ops, "tcp.dport") ||
242                                   !strcasecmp(ops, "udp.port") ||
243                                   !strcasecmp(ops, "udp.sport") ||
244                                   !strcasecmp(ops, "udp.dport")) {
245                                         char proto[4];
246                                         u_short port;
247 
248                                         strncpy(proto, ops, 3);
249                                         proto[3] = '\0';
250                                         if (getport(NULL, s, &port, proto) == -1)
251                                                   goto parseerror;
252                                         oplist[osize++] = port;
253 
254                               } else if (!strcasecmp(ops, "tcp.state")) {
255                                         oplist[osize++] = atoi(s);
256 
257                               } else {
258                                         error = "unknown word";
259                                         goto parseerror;
260                               }
261                     }
262           }
263 
264           free(temp);
265 
266           if (errorptr != NULL)
267                     *errorptr = NULL;
268 
269           for (i = asize; i > 0; i--)
270                     oplist[i] = oplist[i - 1];
271 
272           oplist[0] = asize + 2;
273           oplist[asize + 1] = IPF_EXP_END;
274 
275           return oplist;
276 
277 parseerror:
278           if (errorptr != NULL)
279                     *errorptr = error;
280           if (oplist != NULL)
281                     free(oplist);
282           if (temp != NULL)
283                     free(temp);
284           return NULL;
285 }
286