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