xref: /freebsd/sbin/ipf/libipf/ipft_tx.c (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
1 /*	$FreeBSD$	*/
2 
3 /*
4  * Copyright (C) 2012 by Darren Reed.
5  *
6  * See the IPFILTER.LICENCE file for details on licencing.
7  *
8  * $Id$
9  */
10 #if !defined(lint)
11 static const char sccsid[] = "@(#)ipft_tx.c	1.7 6/5/96 (C) 1993 Darren Reed";
12 static const char rcsid[] = "@(#)$Id$";
13 #endif
14 
15 #include <ctype.h>
16 
17 #include "ipf.h"
18 #include "ipt.h"
19 
20 extern	int	opts;
21 
22 static	char	*tx_proto = "";
23 
24 static	int	text_open(char *), text_close(void);
25 static	int	text_readip(mb_t *, char **, int *);
26 static	int	parseline(char *, ip_t *, char **, int *);
27 
28 static	char	myflagset[] = "FSRPAUEC";
29 static	u_char	myflags[] = { TH_FIN, TH_SYN, TH_RST, TH_PUSH,
30 				TH_ACK, TH_URG, TH_ECN, TH_CWR };
31 
32 struct	ipread	iptext = { text_open, text_close, text_readip, R_DO_CKSUM };
33 static	FILE	*tfp = NULL;
34 static	int	tfd = -1;
35 
36 static	u_32_t	tx_hostnum(char *, int *);
37 static	u_short	tx_portnum(char *);
38 
39 #ifdef USE_INET6
40 int parseipv6(char **, ip6_t *, char **, int *);
41 #endif
42 
43 /*
44  * returns an ip address as a long var as a result of either a DNS lookup or
45  * straight inet_addr() call
46  */
47 static u_32_t
48 tx_hostnum(char *host, int *resolved)
49 {
50 	i6addr_t	ipa;
51 
52 	*resolved = 0;
53 	if (!strcasecmp("any", host))
54 		return (0L);
55 	if (ISDIGIT(*host))
56 		return (inet_addr(host));
57 
58 	if (gethost(AF_INET, host, &ipa) == -1) {
59 		*resolved = -1;
60 		fprintf(stderr, "can't resolve hostname: %s\n", host);
61 		return (0);
62 	}
63 	return (ipa.in4.s_addr);
64 }
65 
66 
67 /*
68  * find the port number given by the name, either from getservbyname() or
69  * straight atoi()
70  */
71 static u_short
72 tx_portnum(char *name)
73 {
74 	struct	servent	*sp;
75 
76 	if (ISDIGIT(*name))
77 		return (u_short)atoi(name);
78 	sp = getservbyname(name, tx_proto);
79 	if (sp)
80 		return (ntohs(sp->s_port));
81 	(void) fprintf(stderr, "unknown service \"%s\".\n", name);
82 	return (0);
83 }
84 
85 
86 static int
87 text_open(char *fname)
88 {
89 	if (tfp && tfd != -1) {
90 		rewind(tfp);
91 		return (tfd);
92 	}
93 
94 	if (!strcmp(fname, "-")) {
95 		tfd = 0;
96 		tfp = stdin;
97 	} else {
98 		tfd = open(fname, O_RDONLY);
99 		if (tfd != -1)
100 			tfp = fdopen(tfd, "r");
101 	}
102 	return (tfd);
103 }
104 
105 
106 static int
107 text_close(void)
108 {
109 	int	cfd = tfd;
110 
111 	tfd = -1;
112 	return (close(cfd));
113 }
114 
115 
116 static int
117 text_readip(mb_t *mb, char **ifn, int *dir)
118 {
119 	register char *s;
120 	char	line[513];
121 	ip_t	*ip;
122 	char	*buf;
123 
124 	buf = (char *)mb->mb_buf;
125 
126 	*ifn = NULL;
127 	while (fgets(line, sizeof(line)-1, tfp)) {
128 		if ((s = strchr(line, '\n')))
129 			*s = '\0';
130 		if ((s = strchr(line, '\r')))
131 			*s = '\0';
132 		if ((s = strchr(line, '#')))
133 			*s = '\0';
134 		if (!*line)
135 			continue;
136 		if ((opts & OPT_DEBUG) != 0)
137 			printf("input: %s\n", line);
138 		*ifn = NULL;
139 		*dir = 0;
140 		if (!parseline(line, (ip_t *)buf, ifn, dir)) {
141 			ip = (ip_t *)buf;
142 			if (IP_V(ip) == 6) {
143 #ifdef USE_INET6
144 				mb->mb_len = ntohs(((ip6_t *)ip)->ip6_plen) +
145 				       sizeof(ip6_t);
146 #else
147 				mb->mb_len = 0;
148 #endif
149 			} else {
150 				mb->mb_len = ntohs(ip->ip_len);
151 			}
152 			return (mb->mb_len);
153 		}
154 	}
155 	if (feof(tfp))
156 		return (0);
157 	return (-1);
158 }
159 
160 static int
161 parseline(char *line, ip_t *ip, char **ifn, int *out)
162 {
163 	tcphdr_t	th, *tcp = &th;
164 	struct	icmp	icmp, *ic = &icmp;
165 	char	*cps[20], **cpp, c, ipopts[68];
166 	int	i, r;
167 
168 	if (*ifn)
169 		free(*ifn);
170 	bzero((char *)ip, MAX(sizeof(*tcp), sizeof(*ic)) + sizeof(*ip));
171 	bzero((char *)tcp, sizeof(*tcp));
172 	bzero((char *)ic, sizeof(*ic));
173 	bzero(ipopts, sizeof(ipopts));
174 	IP_HL_A(ip, sizeof(*ip) >> 2);
175 	IP_V_A(ip, IPVERSION);
176 	ip->ip_ttl = 63;
177 	for (i = 0, cps[0] = strtok(line, " \b\t\r\n"); cps[i] && i < 19; )
178 		cps[++i] = strtok(NULL, " \b\t\r\n");
179 
180 	cpp = cps;
181 	if (!*cpp)
182 		return (1);
183 
184 	c = **cpp;
185 	if (!ISALPHA(c) || (TOLOWER(c) != 'o' && TOLOWER(c) != 'i')) {
186 		fprintf(stderr, "bad direction \"%s\"\n", *cpp);
187 		return (1);
188 	}
189 
190 #ifdef USE_INET6
191 	if (!strcasecmp(*cpp, "out6") || !strcasecmp(*cpp, "in6")) {
192 		return (parseipv6(cpp, (ip6_t *)ip, ifn, out));
193 	}
194 #endif
195 
196 	*out = (TOLOWER(c) == 'o') ? 1 : 0;
197 	cpp++;
198 	if (!*cpp)
199 		return (1);
200 
201 	if (!strcasecmp(*cpp, "on")) {
202 		cpp++;
203 		if (!*cpp)
204 			return (1);
205 		*ifn = strdup(*cpp++);
206 		if (!*cpp)
207 			return (1);
208 	}
209 
210 	c = **cpp;
211 	ip->ip_len = sizeof(ip_t);
212 	if (!strcasecmp(*cpp, "tcp") || !strcasecmp(*cpp, "udp") ||
213 	    !strcasecmp(*cpp, "icmp")) {
214 		if (c == 't') {
215 			ip->ip_p = IPPROTO_TCP;
216 			ip->ip_len += sizeof(struct tcphdr);
217 			tx_proto = "tcp";
218 		} else if (c == 'u') {
219 			ip->ip_p = IPPROTO_UDP;
220 			ip->ip_len += sizeof(struct udphdr);
221 			tx_proto = "udp";
222 		} else {
223 			ip->ip_p = IPPROTO_ICMP;
224 			ip->ip_len += ICMPERR_IPICMPHLEN;
225 			tx_proto = "icmp";
226 		}
227 		cpp++;
228 	} else if (ISDIGIT(**cpp) && !index(*cpp, '.')) {
229 		ip->ip_p = atoi(*cpp);
230 		cpp++;
231 	} else
232 		ip->ip_p = IPPROTO_IP;
233 
234 	if (!*cpp)
235 		return (1);
236 	if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) {
237 		char	*last;
238 
239 		last = strchr(*cpp, ',');
240 		if (!last) {
241 			fprintf(stderr, "tcp/udp with no source port\n");
242 			return (1);
243 		}
244 		*last++ = '\0';
245 		tcp->th_sport = htons(tx_portnum(last));
246 		if (ip->ip_p == IPPROTO_TCP) {
247 			tcp->th_win = htons(4096);
248 			TCP_OFF_A(tcp, sizeof(*tcp) >> 2);
249 		}
250 	}
251 	ip->ip_src.s_addr = tx_hostnum(*cpp, &r);
252 	cpp++;
253 	if (!*cpp)
254 		return (1);
255 
256 	if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) {
257 		char	*last;
258 
259 		last = strchr(*cpp, ',');
260 		if (!last) {
261 			fprintf(stderr, "tcp/udp with no destination port\n");
262 			return (1);
263 		}
264 		*last++ = '\0';
265 		tcp->th_dport = htons(tx_portnum(last));
266 	}
267 	ip->ip_dst.s_addr = tx_hostnum(*cpp, &r);
268 	cpp++;
269 	if (ip->ip_p == IPPROTO_TCP) {
270 		if (*cpp != NULL) {
271 			char	*s, *t;
272 
273 			tcp->th_flags = 0;
274 			for (s = *cpp; *s; s++)
275 				if ((t  = strchr(myflagset, *s)))
276 					tcp->th_flags |= myflags[t-myflagset];
277 			if (tcp->th_flags)
278 				cpp++;
279 		}
280 
281 		if (tcp->th_flags & TH_URG)
282 			tcp->th_urp = htons(1);
283 
284 		if (*cpp && !strncasecmp(*cpp, "seq=", 4)) {
285 			tcp->th_seq = htonl(atoi(*cpp + 4));
286 			cpp++;
287 		}
288 
289 		if (*cpp && !strncasecmp(*cpp, "ack=", 4)) {
290 			tcp->th_ack = htonl(atoi(*cpp + 4));
291 			cpp++;
292 		}
293 	} else if (*cpp && ip->ip_p == IPPROTO_ICMP) {
294 		char	*t;
295 
296 		t = strchr(*cpp, ',');
297 		if (t != NULL)
298 			*t = '\0';
299 
300 		ic->icmp_type = geticmptype(AF_INET, *cpp);
301 		if (t != NULL)
302 			ic->icmp_code = atoi(t + 1);
303 		cpp++;
304 
305 		if (ic->icmp_type == ICMP_ECHO ||
306 		    ic->icmp_type == ICMP_ECHOREPLY)
307 			ic->icmp_id = htons(getpid());
308 		if (t != NULL)
309 			*t = ',';
310 	}
311 
312 	if (*cpp && !strcasecmp(*cpp, "opt")) {
313 		u_long	olen;
314 
315 		cpp++;
316 		olen = buildopts(*cpp, ipopts, (IP_HL(ip) - 5) << 2);
317 		if (olen) {
318 			bcopy(ipopts, (char *)(ip + 1), olen);
319 			IP_HL_A(ip, IP_HL(ip) + (olen >> 2));
320 			ip->ip_len += olen;
321 		}
322 	}
323 	if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP)
324 		bcopy((char *)tcp, ((char *)ip) + (IP_HL(ip) << 2),
325 			sizeof(*tcp));
326 	else if (ip->ip_p == IPPROTO_ICMP)
327 		bcopy((char *)ic, ((char *)ip) + (IP_HL(ip) << 2),
328 			sizeof(*ic));
329 	ip->ip_len = htons(ip->ip_len);
330 	return (0);
331 }
332 
333 
334 #ifdef USE_INET6
335 int
336 parseipv6(char **cpp, ip6_t *ip6, char **ifn, int *out)
337 {
338 	tcphdr_t th, *tcp = &th;
339 	struct icmp6_hdr icmp, *ic6 = &icmp;
340 
341 	bzero((char *)ip6, MAX(sizeof(*tcp), sizeof(*ic6)) + sizeof(*ip6));
342 	bzero((char *)tcp, sizeof(*tcp));
343 	bzero((char *)ic6, sizeof(*ic6));
344 	ip6->ip6_vfc = 0x60;
345 
346 	*out = (**cpp == 'o') ? 1 : 0;
347 	cpp++;
348 	if (!*cpp)
349 		return (1);
350 
351 	if (!strcasecmp(*cpp, "on")) {
352 		cpp++;
353 		if (!*cpp)
354 			return (1);
355 		*ifn = strdup(*cpp++);
356 		if (!*cpp)
357 			return (1);
358 	}
359 
360 	if (!strcasecmp(*cpp, "tcp")) {
361 		ip6->ip6_nxt = IPPROTO_TCP;
362 		tx_proto = "tcp";
363 		cpp++;
364 	} else if (!strcasecmp(*cpp, "udp")) {
365 		ip6->ip6_nxt = IPPROTO_UDP;
366 		tx_proto = "udp";
367 		cpp++;
368 	} else if (!strcasecmp(*cpp, "icmpv6")) {
369 		ip6->ip6_nxt = IPPROTO_ICMPV6;
370 		tx_proto = "icmpv6";
371 		cpp++;
372 	} else if (ISDIGIT(**cpp) && !index(*cpp, ':')) {
373 		ip6->ip6_nxt = atoi(*cpp);
374 		cpp++;
375 	} else
376 		ip6->ip6_nxt = IPPROTO_IPV6;
377 
378 	if (!*cpp)
379 		return (1);
380 
381 	switch (ip6->ip6_nxt)
382 	{
383 	case IPPROTO_TCP :
384 		ip6->ip6_plen = sizeof(struct tcphdr);
385 		break;
386 	case IPPROTO_UDP :
387 		ip6->ip6_plen = sizeof(struct udphdr);
388 		break;
389 	case IPPROTO_ICMPV6 :
390 		ip6->ip6_plen = ICMP6ERR_IPICMPHLEN;
391 		break;
392 	default :
393 		break;
394 	}
395 
396 	if (ip6->ip6_nxt == IPPROTO_TCP || ip6->ip6_nxt == IPPROTO_UDP) {
397 		char *last;
398 
399 		last = strchr(*cpp, ',');
400 		if (!last) {
401 			fprintf(stderr, "tcp/udp with no source port\n");
402 			return (1);
403 		}
404 		*last++ = '\0';
405 		tcp->th_sport = htons(tx_portnum(last));
406 		if (ip6->ip6_nxt == IPPROTO_TCP) {
407 			tcp->th_win = htons(4096);
408 			TCP_OFF_A(tcp, sizeof(*tcp) >> 2);
409 		}
410 	}
411 
412 	if (inet_pton(AF_INET6, *cpp, &ip6->ip6_src) != 1) {
413 		fprintf(stderr, "cannot parse source address '%s'\n", *cpp);
414 		return (1);
415 	}
416 
417 	cpp++;
418 	if (!*cpp)
419 		return (1);
420 
421 	if (ip6->ip6_nxt == IPPROTO_TCP || ip6->ip6_nxt == IPPROTO_UDP) {
422 		char *last;
423 
424 		last = strchr(*cpp, ',');
425 		if (!last) {
426 			fprintf(stderr, "tcp/udp with no destination port\n");
427 			return (1);
428 		}
429 		*last++ = '\0';
430 		tcp->th_dport = htons(tx_portnum(last));
431 	}
432 
433 	if (inet_pton(AF_INET6, *cpp, &ip6->ip6_dst) != 1) {
434 		fprintf(stderr, "cannot parse destination address '%s'\n",
435 			*cpp);
436 		return (1);
437 	}
438 
439 	cpp++;
440 	if (ip6->ip6_nxt == IPPROTO_TCP) {
441 		if (*cpp != NULL) {
442 			char *s, *t;
443 
444 			tcp->th_flags = 0;
445 			for (s = *cpp; *s; s++)
446 				if ((t  = strchr(myflagset, *s)))
447 					tcp->th_flags |= myflags[t-myflagset];
448 			if (tcp->th_flags)
449 				cpp++;
450 		}
451 
452 		if (tcp->th_flags & TH_URG)
453 			tcp->th_urp = htons(1);
454 
455 		if (*cpp && !strncasecmp(*cpp, "seq=", 4)) {
456 			tcp->th_seq = htonl(atoi(*cpp + 4));
457 			cpp++;
458 		}
459 
460 		if (*cpp && !strncasecmp(*cpp, "ack=", 4)) {
461 			tcp->th_ack = htonl(atoi(*cpp + 4));
462 			cpp++;
463 		}
464 	} else if (*cpp && ip6->ip6_nxt == IPPROTO_ICMPV6) {
465 		char *t;
466 
467 		t = strchr(*cpp, ',');
468 		if (t != NULL)
469 			*t = '\0';
470 
471 		ic6->icmp6_type = geticmptype(AF_INET6, *cpp);
472 		if (t != NULL)
473 			ic6->icmp6_code = atoi(t + 1);
474 
475 		if (ic6->icmp6_type == ICMP6_ECHO_REQUEST ||
476 		    ic6->icmp6_type == ICMP6_ECHO_REPLY)
477 			ic6->icmp6_id = htons(getpid());
478 
479 		if (t != NULL)
480 			*t = ',';
481 	}
482 
483 	if (ip6->ip6_nxt == IPPROTO_TCP || ip6->ip6_nxt == IPPROTO_UDP) {
484 		bcopy((char *)tcp, (char *)ip6 + sizeof(*ip6),
485 			sizeof(*tcp));
486 	} else if (ip6->ip6_nxt == IPPROTO_ICMPV6) {
487 		bcopy((char *)ic6, (char *)ip6 + sizeof(*ip6),
488 			sizeof(*ic6));
489 	}
490 
491 	/*
492 	 * Because a length of 0 == jumbo gram...
493 	 */
494 	if (ip6->ip6_plen == 0) {
495 		ip6->ip6_plen++;
496 	}
497 	ip6->ip6_plen = htons(ip6->ip6_plen);
498 	return (0);
499 }
500 #endif
501