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