xref: /freebsd/sys/netinet/libalias/alias_proxy.c (revision 9dba3024c3f1a2df6f42689aac5a2ab4acc7561d)
1 /*-
2  * Copyright (c) 2001 Charles Mott <cm@linktel.net>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 /* file: alias_proxy.c
31 
32     This file encapsulates special operations related to transparent
33     proxy redirection.  This is where packets with a particular destination,
34     usually tcp port 80, are redirected to a proxy server.
35 
36     When packets are proxied, the destination address and port are
37     modified.  In certain cases, it is necessary to somehow encode
38     the original address/port info into the packet.  Two methods are
39     presently supported: addition of a [DEST addr port] string at the
40     beginning of a tcp stream, or inclusion of an optional field
41     in the IP header.
42 
43     There is one public API function:
44 
45 	PacketAliasProxyRule()    -- Adds and deletes proxy
46 				     rules.
47 
48     Rules are stored in a linear linked list, so lookup efficiency
49     won't be too good for large lists.
50 
51 
52     Initial development: April, 1998 (cjm)
53 */
54 
55 
56 /* System includes */
57 #ifdef _KERNEL
58 #include <sys/param.h>
59 #include <sys/ctype.h>
60 #include <sys/libkern.h>
61 #include <sys/kernel.h>
62 #include <sys/malloc.h>
63 #include <sys/limits.h>
64 #else
65 #include <sys/types.h>
66 #include <sys/socket.h>
67 #include <ctype.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include <netdb.h>
72 #include <arpa/inet.h>
73 #endif
74 
75 /* BSD IPV4 includes */
76 #include <netinet/in_systm.h>
77 #include <netinet/in.h>
78 #include <netinet/ip.h>
79 #include <netinet/tcp.h>
80 
81 #ifdef _KERNEL
82 #include <netinet/libalias/alias.h>
83 #include <netinet/libalias/alias_local.h>
84 #else
85 #include "alias.h"		/* Public API functions for libalias */
86 #include "alias_local.h"	/* Functions used by alias*.c */
87 #endif
88 
89 /*
90     Data structures
91  */
92 
93 /*
94  * A linked list of arbitrary length, based on struct proxy_entry is
95  * used to store proxy rules.
96  */
97 struct proxy_entry {
98 	struct libalias *la;
99 #define PROXY_TYPE_ENCODE_NONE      1
100 #define PROXY_TYPE_ENCODE_TCPSTREAM 2
101 #define PROXY_TYPE_ENCODE_IPHDR     3
102 	int		rule_index;
103 	int		proxy_type;
104 	u_char		proto;
105 	u_short		proxy_port;
106 	u_short		server_port;
107 
108 	struct in_addr	server_addr;
109 
110 	struct in_addr	src_addr;
111 	struct in_addr	src_mask;
112 
113 	struct in_addr	dst_addr;
114 	struct in_addr	dst_mask;
115 
116 	struct proxy_entry *next;
117 	struct proxy_entry *last;
118 };
119 
120 
121 
122 /*
123     File scope variables
124 */
125 
126 
127 
128 /* Local (static) functions:
129 
130     IpMask()                 -- Utility function for creating IP
131 				masks from integer (1-32) specification.
132     IpAddr()                 -- Utility function for converting string
133 				to IP address
134     IpPort()                 -- Utility function for converting string
135 				to port number
136     RuleAdd()                -- Adds an element to the rule list.
137     RuleDelete()             -- Removes an element from the rule list.
138     RuleNumberDelete()       -- Removes all elements from the rule list
139 				having a certain rule number.
140     ProxyEncodeTcpStream()   -- Adds [DEST x.x.x.x xxxx] to the beginning
141 				of a TCP stream.
142     ProxyEncodeIpHeader()    -- Adds an IP option indicating the true
143 				destination of a proxied IP packet
144 */
145 
146 #ifdef	_KERNEL		/* XXX: can it be moved to libkern? */
147 static int inet_aton(const char *cp, struct in_addr *addr);
148 #endif
149 static int	IpMask(int, struct in_addr *);
150 static int	IpAddr(char *, struct in_addr *);
151 static int	IpPort(char *, int, int *);
152 static void	RuleAdd(struct libalias *la, struct proxy_entry *);
153 static void	RuleDelete(struct proxy_entry *);
154 static int	RuleNumberDelete(struct libalias *la, int);
155 static void	ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
156 static void	ProxyEncodeIpHeader(struct ip *, int);
157 
158 #ifdef	_KERNEL
159 static int
160 inet_aton(cp, addr)
161         const char *cp;
162         struct in_addr *addr;
163 {
164 	u_long parts[4];
165 	in_addr_t val;
166 	const char *c;
167 	char *endptr;
168 	int gotend, n;
169 
170 	c = (const char *)cp;
171 	n = 0;
172 	/*
173 	 * Run through the string, grabbing numbers until
174 	 * the end of the string, or some error
175 	 */
176 	gotend = 0;
177 	while (!gotend) {
178 		unsigned long l;
179 
180 		l = strtoul(c, &endptr, 0);
181 
182 		if (l == ULONG_MAX || l == 0)
183 			return (0);
184 
185 		val = (in_addr_t)l;
186 		/*
187 		 * If the whole string is invalid, endptr will equal
188 		 * c.. this way we can make sure someone hasn't
189 		 * gone '.12' or something which would get past
190 		 * the next check.
191 		 */
192 		if (endptr == c)
193 			return (0);
194 		parts[n] = val;
195 		c = endptr;
196 
197 		/* Check the next character past the previous number's end */
198 		switch (*c) {
199 		case '.' :
200 			/* Make sure we only do 3 dots .. */
201 			if (n == 3)	/* Whoops. Quit. */
202 				return (0);
203 			n++;
204 			c++;
205 			break;
206 
207 		case '\0':
208 			gotend = 1;
209 			break;
210 
211 		default:
212 			if (isspace((unsigned char)*c)) {
213 				gotend = 1;
214 				break;
215 			} else
216 				return (0);	/* Invalid character, so fail */
217 		}
218 
219 	}
220 
221 	/*
222 	 * Concoct the address according to
223 	 * the number of parts specified.
224 	 */
225 
226 	switch (n) {
227 	case 0:				/* a -- 32 bits */
228 		/*
229 		 * Nothing is necessary here.  Overflow checking was
230 		 * already done in strtoul().
231 		 */
232 		break;
233 	case 1:				/* a.b -- 8.24 bits */
234 		if (val > 0xffffff || parts[0] > 0xff)
235 			return (0);
236 		val |= parts[0] << 24;
237 		break;
238 
239 	case 2:				/* a.b.c -- 8.8.16 bits */
240 		if (val > 0xffff || parts[0] > 0xff || parts[1] > 0xff)
241 			return (0);
242 		val |= (parts[0] << 24) | (parts[1] << 16);
243 		break;
244 
245 	case 3:				/* a.b.c.d -- 8.8.8.8 bits */
246 		if (val > 0xff || parts[0] > 0xff || parts[1] > 0xff ||
247 		    parts[2] > 0xff)
248 			return (0);
249 		val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
250 		break;
251 	}
252 
253 	if (addr != NULL)
254 		addr->s_addr = htonl(val);
255 	return (1);
256 }
257 #endif
258 
259 static int
260 IpMask(int nbits, struct in_addr *mask)
261 {
262 	int i;
263 	u_int imask;
264 
265 	if (nbits < 0 || nbits > 32)
266 		return (-1);
267 
268 	imask = 0;
269 	for (i = 0; i < nbits; i++)
270 		imask = (imask >> 1) + 0x80000000;
271 	mask->s_addr = htonl(imask);
272 
273 	return (0);
274 }
275 
276 static int
277 IpAddr(char *s, struct in_addr *addr)
278 {
279 	if (inet_aton(s, addr) == 0)
280 		return (-1);
281 	else
282 		return (0);
283 }
284 
285 static int
286 IpPort(char *s, int proto, int *port)
287 {
288 	int n;
289 
290 	n = sscanf(s, "%d", port);
291 	if (n != 1)
292 #ifndef _KERNEL	/* XXX: we accept only numeric ports in kernel */
293 	{
294 		struct servent *se;
295 
296 		if (proto == IPPROTO_TCP)
297 			se = getservbyname(s, "tcp");
298 		else if (proto == IPPROTO_UDP)
299 			se = getservbyname(s, "udp");
300 		else
301 			return (-1);
302 
303 		if (se == NULL)
304 			return (-1);
305 
306 		*port = (u_int) ntohs(se->s_port);
307 	}
308 #else
309 		return (-1);
310 #endif
311 	return (0);
312 }
313 
314 void
315 RuleAdd(struct libalias *la, struct proxy_entry *entry)
316 {
317 	int rule_index;
318 	struct proxy_entry *ptr;
319 	struct proxy_entry *ptr_last;
320 
321 	if (la->proxyList == NULL) {
322 		la->proxyList = entry;
323 		entry->last = NULL;
324 		entry->next = NULL;
325 		return;
326 	}
327 	entry->la = la;
328 
329 	rule_index = entry->rule_index;
330 	ptr = la->proxyList;
331 	ptr_last = NULL;
332 	while (ptr != NULL) {
333 		if (ptr->rule_index >= rule_index) {
334 			if (ptr_last == NULL) {
335 				entry->next = la->proxyList;
336 				entry->last = NULL;
337 				la->proxyList->last = entry;
338 				la->proxyList = entry;
339 				return;
340 			}
341 			ptr_last->next = entry;
342 			ptr->last = entry;
343 			entry->last = ptr->last;
344 			entry->next = ptr;
345 			return;
346 		}
347 		ptr_last = ptr;
348 		ptr = ptr->next;
349 	}
350 
351 	ptr_last->next = entry;
352 	entry->last = ptr_last;
353 	entry->next = NULL;
354 }
355 
356 static void
357 RuleDelete(struct proxy_entry *entry)
358 {
359 	struct libalias *la;
360 
361 	la = entry->la;
362 	if (entry->last != NULL)
363 		entry->last->next = entry->next;
364 	else
365 		la->proxyList = entry->next;
366 
367 	if (entry->next != NULL)
368 		entry->next->last = entry->last;
369 
370 	free(entry);
371 }
372 
373 static int
374 RuleNumberDelete(struct libalias *la, int rule_index)
375 {
376 	int err;
377 	struct proxy_entry *ptr;
378 
379 	err = -1;
380 	ptr = la->proxyList;
381 	while (ptr != NULL) {
382 		struct proxy_entry *ptr_next;
383 
384 		ptr_next = ptr->next;
385 		if (ptr->rule_index == rule_index) {
386 			err = 0;
387 			RuleDelete(ptr);
388 		}
389 		ptr = ptr_next;
390 	}
391 
392 	return (err);
393 }
394 
395 static void
396 ProxyEncodeTcpStream(struct alias_link *lnk,
397     struct ip *pip,
398     int maxpacketsize)
399 {
400 	int slen;
401 	char buffer[40];
402 	struct tcphdr *tc;
403 
404 /* Compute pointer to tcp header */
405 	tc = (struct tcphdr *)ip_next(pip);
406 
407 /* Don't modify if once already modified */
408 
409 	if (GetAckModified(lnk))
410 		return;
411 
412 /* Translate destination address and port to string form */
413 	snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
414 	    inet_ntoa(GetProxyAddress(lnk)), (u_int) ntohs(GetProxyPort(lnk)));
415 
416 /* Pad string out to a multiple of two in length */
417 	slen = strlen(buffer);
418 	switch (slen % 2) {
419 	case 0:
420 		strcat(buffer, " \n");
421 		slen += 2;
422 		break;
423 	case 1:
424 		strcat(buffer, "\n");
425 		slen += 1;
426 	}
427 
428 /* Check for packet overflow */
429 	if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
430 		return;
431 
432 /* Shift existing TCP data and insert destination string */
433 	{
434 		int dlen;
435 		int hlen;
436 		u_char *p;
437 
438 		hlen = (pip->ip_hl + tc->th_off) << 2;
439 		dlen = ntohs(pip->ip_len) - hlen;
440 
441 /* Modify first packet that has data in it */
442 
443 		if (dlen == 0)
444 			return;
445 
446 		p = (char *)pip;
447 		p += hlen;
448 
449 		bcopy(p, p + slen, dlen);
450 		memcpy(p, buffer, slen);
451 	}
452 
453 /* Save information about modfied sequence number */
454 	{
455 		int delta;
456 
457 		SetAckModified(lnk);
458 		delta = GetDeltaSeqOut(pip, lnk);
459 		AddSeq(pip, lnk, delta + slen);
460 	}
461 
462 /* Update IP header packet length and checksum */
463 	{
464 		int accumulate;
465 
466 		accumulate = pip->ip_len;
467 		pip->ip_len = htons(ntohs(pip->ip_len) + slen);
468 		accumulate -= pip->ip_len;
469 
470 		ADJUST_CHECKSUM(accumulate, pip->ip_sum);
471 	}
472 
473 /* Update TCP checksum, Use TcpChecksum since so many things have
474    already changed. */
475 
476 	tc->th_sum = 0;
477 	tc->th_sum = TcpChecksum(pip);
478 }
479 
480 static void
481 ProxyEncodeIpHeader(struct ip *pip,
482     int maxpacketsize)
483 {
484 #define OPTION_LEN_BYTES  8
485 #define OPTION_LEN_INT16  4
486 #define OPTION_LEN_INT32  2
487 	u_char option[OPTION_LEN_BYTES];
488 
489 #ifdef LIBALIAS_DEBUG
490 	fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
491 	fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
492 #endif
493 
494 	(void)maxpacketsize;
495 
496 /* Check to see that there is room to add an IP option */
497 	if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
498 		return;
499 
500 /* Build option and copy into packet */
501 	{
502 		u_char *ptr;
503 		struct tcphdr *tc;
504 
505 		ptr = (u_char *) pip;
506 		ptr += 20;
507 		memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
508 
509 		option[0] = 0x64;	/* class: 3 (reserved), option 4 */
510 		option[1] = OPTION_LEN_BYTES;
511 
512 		memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
513 
514 		tc = (struct tcphdr *)ip_next(pip);
515 		memcpy(&option[6], (u_char *) & tc->th_sport, 2);
516 
517 		memcpy(ptr, option, 8);
518 	}
519 
520 /* Update checksum, header length and packet length */
521 	{
522 		int i;
523 		int accumulate;
524 		u_short *sptr;
525 
526 		sptr = (u_short *) option;
527 		accumulate = 0;
528 		for (i = 0; i < OPTION_LEN_INT16; i++)
529 			accumulate -= *(sptr++);
530 
531 		sptr = (u_short *) pip;
532 		accumulate += *sptr;
533 		pip->ip_hl += OPTION_LEN_INT32;
534 		accumulate -= *sptr;
535 
536 		accumulate += pip->ip_len;
537 		pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
538 		accumulate -= pip->ip_len;
539 
540 		ADJUST_CHECKSUM(accumulate, pip->ip_sum);
541 	}
542 #undef OPTION_LEN_BYTES
543 #undef OPTION_LEN_INT16
544 #undef OPTION_LEN_INT32
545 #ifdef LIBALIAS_DEBUG
546 	fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
547 	fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
548 #endif
549 }
550 
551 
552 /* Functions by other packet alias source files
553 
554     ProxyCheck()         -- Checks whether an outgoing packet should
555 			    be proxied.
556     ProxyModify()        -- Encodes the original destination address/port
557 			    for a packet which is to be redirected to
558 			    a proxy server.
559 */
560 
561 int
562 ProxyCheck(struct libalias *la, struct ip *pip,
563     struct in_addr *proxy_server_addr,
564     u_short * proxy_server_port)
565 {
566 	u_short dst_port;
567 	struct in_addr src_addr;
568 	struct in_addr dst_addr;
569 	struct proxy_entry *ptr;
570 
571 	src_addr = pip->ip_src;
572 	dst_addr = pip->ip_dst;
573 	dst_port = ((struct tcphdr *)ip_next(pip))
574 	    ->th_dport;
575 
576 	ptr = la->proxyList;
577 	while (ptr != NULL) {
578 		u_short proxy_port;
579 
580 		proxy_port = ptr->proxy_port;
581 		if ((dst_port == proxy_port || proxy_port == 0)
582 		    && pip->ip_p == ptr->proto
583 		    && src_addr.s_addr != ptr->server_addr.s_addr) {
584 			struct in_addr src_addr_masked;
585 			struct in_addr dst_addr_masked;
586 
587 			src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
588 			dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
589 
590 			if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
591 			    && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
592 				if ((*proxy_server_port = ptr->server_port) == 0)
593 					*proxy_server_port = dst_port;
594 				*proxy_server_addr = ptr->server_addr;
595 				return (ptr->proxy_type);
596 			}
597 		}
598 		ptr = ptr->next;
599 	}
600 
601 	return (0);
602 }
603 
604 void
605 ProxyModify(struct libalias *la, struct alias_link *lnk,
606     struct ip *pip,
607     int maxpacketsize,
608     int proxy_type)
609 {
610 
611 	(void)la;
612 
613 	switch (proxy_type) {
614 		case PROXY_TYPE_ENCODE_IPHDR:
615 		ProxyEncodeIpHeader(pip, maxpacketsize);
616 		break;
617 
618 	case PROXY_TYPE_ENCODE_TCPSTREAM:
619 		ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
620 		break;
621 	}
622 }
623 
624 
625 /*
626     Public API functions
627 */
628 
629 int
630 LibAliasProxyRule(struct libalias *la, const char *cmd)
631 {
632 /*
633  * This function takes command strings of the form:
634  *
635  *   server <addr>[:<port>]
636  *   [port <port>]
637  *   [rule n]
638  *   [proto tcp|udp]
639  *   [src <addr>[/n]]
640  *   [dst <addr>[/n]]
641  *   [type encode_tcp_stream|encode_ip_hdr|no_encode]
642  *
643  *   delete <rule number>
644  *
645  * Subfields can be in arbitrary order.  Port numbers and addresses
646  * must be in either numeric or symbolic form. An optional rule number
647  * is used to control the order in which rules are searched.  If two
648  * rules have the same number, then search order cannot be guaranteed,
649  * and the rules should be disjoint.  If no rule number is specified,
650  * then 0 is used, and group 0 rules are always checked before any
651  * others.
652  */
653 	int i, n, len;
654 	int cmd_len;
655 	int token_count;
656 	int state;
657 	char *token;
658 	char buffer[256];
659 	char str_port[sizeof(buffer)];
660 	char str_server_port[sizeof(buffer)];
661 	char *res = buffer;
662 
663 	int rule_index;
664 	int proto;
665 	int proxy_type;
666 	int proxy_port;
667 	int server_port;
668 	struct in_addr server_addr;
669 	struct in_addr src_addr, src_mask;
670 	struct in_addr dst_addr, dst_mask;
671 	struct proxy_entry *proxy_entry;
672 
673 /* Copy command line into a buffer */
674 	cmd += strspn(cmd, " \t");
675 	cmd_len = strlen(cmd);
676 	if (cmd_len > (int)(sizeof(buffer) - 1))
677 		return (-1);
678 	strcpy(buffer, cmd);
679 
680 /* Convert to lower case */
681 	len = strlen(buffer);
682 	for (i = 0; i < len; i++)
683 		buffer[i] = tolower((unsigned char)buffer[i]);
684 
685 /* Set default proxy type */
686 
687 /* Set up default values */
688 	rule_index = 0;
689 	proxy_type = PROXY_TYPE_ENCODE_NONE;
690 	proto = IPPROTO_TCP;
691 	proxy_port = 0;
692 	server_addr.s_addr = 0;
693 	server_port = 0;
694 	src_addr.s_addr = 0;
695 	IpMask(0, &src_mask);
696 	dst_addr.s_addr = 0;
697 	IpMask(0, &dst_mask);
698 
699 	str_port[0] = 0;
700 	str_server_port[0] = 0;
701 
702 /* Parse command string with state machine */
703 #define STATE_READ_KEYWORD    0
704 #define STATE_READ_TYPE       1
705 #define STATE_READ_PORT       2
706 #define STATE_READ_SERVER     3
707 #define STATE_READ_RULE       4
708 #define STATE_READ_DELETE     5
709 #define STATE_READ_PROTO      6
710 #define STATE_READ_SRC        7
711 #define STATE_READ_DST        8
712 	state = STATE_READ_KEYWORD;
713 	token = strsep(&res, " \t");
714 	token_count = 0;
715 	while (token != NULL) {
716 		token_count++;
717 		switch (state) {
718 		case STATE_READ_KEYWORD:
719 			if (strcmp(token, "type") == 0)
720 				state = STATE_READ_TYPE;
721 			else if (strcmp(token, "port") == 0)
722 				state = STATE_READ_PORT;
723 			else if (strcmp(token, "server") == 0)
724 				state = STATE_READ_SERVER;
725 			else if (strcmp(token, "rule") == 0)
726 				state = STATE_READ_RULE;
727 			else if (strcmp(token, "delete") == 0)
728 				state = STATE_READ_DELETE;
729 			else if (strcmp(token, "proto") == 0)
730 				state = STATE_READ_PROTO;
731 			else if (strcmp(token, "src") == 0)
732 				state = STATE_READ_SRC;
733 			else if (strcmp(token, "dst") == 0)
734 				state = STATE_READ_DST;
735 			else
736 				return (-1);
737 			break;
738 
739 		case STATE_READ_TYPE:
740 			if (strcmp(token, "encode_ip_hdr") == 0)
741 				proxy_type = PROXY_TYPE_ENCODE_IPHDR;
742 			else if (strcmp(token, "encode_tcp_stream") == 0)
743 				proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
744 			else if (strcmp(token, "no_encode") == 0)
745 				proxy_type = PROXY_TYPE_ENCODE_NONE;
746 			else
747 				return (-1);
748 			state = STATE_READ_KEYWORD;
749 			break;
750 
751 		case STATE_READ_PORT:
752 			strcpy(str_port, token);
753 			state = STATE_READ_KEYWORD;
754 			break;
755 
756 		case STATE_READ_SERVER:
757 			{
758 				int err;
759 				char *p;
760 				char s[sizeof(buffer)];
761 
762 				p = token;
763 				while (*p != ':' && *p != 0)
764 					p++;
765 
766 				if (*p != ':') {
767 					err = IpAddr(token, &server_addr);
768 					if (err)
769 						return (-1);
770 				} else {
771 					*p = ' ';
772 
773 					n = sscanf(token, "%s %s", s, str_server_port);
774 					if (n != 2)
775 						return (-1);
776 
777 					err = IpAddr(s, &server_addr);
778 					if (err)
779 						return (-1);
780 				}
781 			}
782 			state = STATE_READ_KEYWORD;
783 			break;
784 
785 		case STATE_READ_RULE:
786 			n = sscanf(token, "%d", &rule_index);
787 			if (n != 1 || rule_index < 0)
788 				return (-1);
789 			state = STATE_READ_KEYWORD;
790 			break;
791 
792 		case STATE_READ_DELETE:
793 			{
794 				int err;
795 				int rule_to_delete;
796 
797 				if (token_count != 2)
798 					return (-1);
799 
800 				n = sscanf(token, "%d", &rule_to_delete);
801 				if (n != 1)
802 					return (-1);
803 				err = RuleNumberDelete(la, rule_to_delete);
804 				if (err)
805 					return (-1);
806 				return (0);
807 			}
808 
809 		case STATE_READ_PROTO:
810 			if (strcmp(token, "tcp") == 0)
811 				proto = IPPROTO_TCP;
812 			else if (strcmp(token, "udp") == 0)
813 				proto = IPPROTO_UDP;
814 			else
815 				return (-1);
816 			state = STATE_READ_KEYWORD;
817 			break;
818 
819 		case STATE_READ_SRC:
820 		case STATE_READ_DST:
821 			{
822 				int err;
823 				char *p;
824 				struct in_addr mask;
825 				struct in_addr addr;
826 
827 				p = token;
828 				while (*p != '/' && *p != 0)
829 					p++;
830 
831 				if (*p != '/') {
832 					IpMask(32, &mask);
833 					err = IpAddr(token, &addr);
834 					if (err)
835 						return (-1);
836 				} else {
837 					int nbits;
838 					char s[sizeof(buffer)];
839 
840 					*p = ' ';
841 					n = sscanf(token, "%s %d", s, &nbits);
842 					if (n != 2)
843 						return (-1);
844 
845 					err = IpAddr(s, &addr);
846 					if (err)
847 						return (-1);
848 
849 					err = IpMask(nbits, &mask);
850 					if (err)
851 						return (-1);
852 				}
853 
854 				if (state == STATE_READ_SRC) {
855 					src_addr = addr;
856 					src_mask = mask;
857 				} else {
858 					dst_addr = addr;
859 					dst_mask = mask;
860 				}
861 			}
862 			state = STATE_READ_KEYWORD;
863 			break;
864 
865 		default:
866 			return (-1);
867 			break;
868 		}
869 
870 		do {
871 			token = strsep(&res, " \t");
872 		} while (token != NULL && !*token);
873 	}
874 #undef STATE_READ_KEYWORD
875 #undef STATE_READ_TYPE
876 #undef STATE_READ_PORT
877 #undef STATE_READ_SERVER
878 #undef STATE_READ_RULE
879 #undef STATE_READ_DELETE
880 #undef STATE_READ_PROTO
881 #undef STATE_READ_SRC
882 #undef STATE_READ_DST
883 
884 /* Convert port strings to numbers.  This needs to be done after
885    the string is parsed, because the prototype might not be designated
886    before the ports (which might be symbolic entries in /etc/services) */
887 
888 	if (strlen(str_port) != 0) {
889 		int err;
890 
891 		err = IpPort(str_port, proto, &proxy_port);
892 		if (err)
893 			return (-1);
894 	} else {
895 		proxy_port = 0;
896 	}
897 
898 	if (strlen(str_server_port) != 0) {
899 		int err;
900 
901 		err = IpPort(str_server_port, proto, &server_port);
902 		if (err)
903 			return (-1);
904 	} else {
905 		server_port = 0;
906 	}
907 
908 /* Check that at least the server address has been defined */
909 	if (server_addr.s_addr == 0)
910 		return (-1);
911 
912 /* Add to linked list */
913 	proxy_entry = malloc(sizeof(struct proxy_entry));
914 	if (proxy_entry == NULL)
915 		return (-1);
916 
917 	proxy_entry->proxy_type = proxy_type;
918 	proxy_entry->rule_index = rule_index;
919 	proxy_entry->proto = proto;
920 	proxy_entry->proxy_port = htons(proxy_port);
921 	proxy_entry->server_port = htons(server_port);
922 	proxy_entry->server_addr = server_addr;
923 	proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
924 	proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
925 	proxy_entry->src_mask = src_mask;
926 	proxy_entry->dst_mask = dst_mask;
927 
928 	RuleAdd(la, proxy_entry);
929 
930 	return (0);
931 }
932