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