xref: /freebsd/sys/netinet/libalias/alias_proxy.c (revision 39beb93c3f8bdbf72a61fda42300b5ebed7390c8)
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 && endptr == c))
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 	LIBALIAS_LOCK_ASSERT(la);
316 
317 	if (la->proxyList == NULL) {
318 		la->proxyList = entry;
319 		entry->last = NULL;
320 		entry->next = NULL;
321 		return;
322 	}
323 	entry->la = la;
324 
325 	rule_index = entry->rule_index;
326 	ptr = la->proxyList;
327 	ptr_last = NULL;
328 	while (ptr != NULL) {
329 		if (ptr->rule_index >= rule_index) {
330 			if (ptr_last == NULL) {
331 				entry->next = la->proxyList;
332 				entry->last = NULL;
333 				la->proxyList->last = entry;
334 				la->proxyList = entry;
335 				return;
336 			}
337 			ptr_last->next = entry;
338 			ptr->last = entry;
339 			entry->last = ptr->last;
340 			entry->next = ptr;
341 			return;
342 		}
343 		ptr_last = ptr;
344 		ptr = ptr->next;
345 	}
346 
347 	ptr_last->next = entry;
348 	entry->last = ptr_last;
349 	entry->next = NULL;
350 }
351 
352 static void
353 RuleDelete(struct proxy_entry *entry)
354 {
355 	struct libalias *la;
356 
357 	la = entry->la;
358 	LIBALIAS_LOCK_ASSERT(la);
359 	if (entry->last != NULL)
360 		entry->last->next = entry->next;
361 	else
362 		la->proxyList = entry->next;
363 
364 	if (entry->next != NULL)
365 		entry->next->last = entry->last;
366 
367 	free(entry);
368 }
369 
370 static int
371 RuleNumberDelete(struct libalias *la, int rule_index)
372 {
373 	int err;
374 	struct proxy_entry *ptr;
375 
376 	LIBALIAS_LOCK_ASSERT(la);
377 	err = -1;
378 	ptr = la->proxyList;
379 	while (ptr != NULL) {
380 		struct proxy_entry *ptr_next;
381 
382 		ptr_next = ptr->next;
383 		if (ptr->rule_index == rule_index) {
384 			err = 0;
385 			RuleDelete(ptr);
386 		}
387 		ptr = ptr_next;
388 	}
389 
390 	return (err);
391 }
392 
393 static void
394 ProxyEncodeTcpStream(struct alias_link *lnk,
395     struct ip *pip,
396     int maxpacketsize)
397 {
398 	int slen;
399 	char buffer[40];
400 	struct tcphdr *tc;
401 
402 /* Compute pointer to tcp header */
403 	tc = (struct tcphdr *)ip_next(pip);
404 
405 /* Don't modify if once already modified */
406 
407 	if (GetAckModified(lnk))
408 		return;
409 
410 /* Translate destination address and port to string form */
411 	snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
412 	    inet_ntoa(GetProxyAddress(lnk)), (u_int) ntohs(GetProxyPort(lnk)));
413 
414 /* Pad string out to a multiple of two in length */
415 	slen = strlen(buffer);
416 	switch (slen % 2) {
417 	case 0:
418 		strcat(buffer, " \n");
419 		slen += 2;
420 		break;
421 	case 1:
422 		strcat(buffer, "\n");
423 		slen += 1;
424 	}
425 
426 /* Check for packet overflow */
427 	if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
428 		return;
429 
430 /* Shift existing TCP data and insert destination string */
431 	{
432 		int dlen;
433 		int hlen;
434 		char *p;
435 
436 		hlen = (pip->ip_hl + tc->th_off) << 2;
437 		dlen = ntohs(pip->ip_len) - hlen;
438 
439 /* Modify first packet that has data in it */
440 
441 		if (dlen == 0)
442 			return;
443 
444 		p = (char *)pip;
445 		p += hlen;
446 
447 		bcopy(p, p + slen, dlen);
448 		memcpy(p, buffer, slen);
449 	}
450 
451 /* Save information about modfied sequence number */
452 	{
453 		int delta;
454 
455 		SetAckModified(lnk);
456 		tc = (struct tcphdr *)ip_next(pip);
457 		delta = GetDeltaSeqOut(tc->th_seq, lnk);
458 		AddSeq(lnk, delta + slen, pip->ip_hl, pip->ip_len, tc->th_seq,
459 		    tc->th_off);
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 #ifdef _KERNEL
478 	tc->th_x2 = 1;
479 #else
480 	tc->th_sum = TcpChecksum(pip);
481 #endif
482 }
483 
484 static void
485 ProxyEncodeIpHeader(struct ip *pip,
486     int maxpacketsize)
487 {
488 #define OPTION_LEN_BYTES  8
489 #define OPTION_LEN_INT16  4
490 #define OPTION_LEN_INT32  2
491 	u_char option[OPTION_LEN_BYTES];
492 
493 #ifdef LIBALIAS_DEBUG
494 	fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
495 	fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
496 #endif
497 
498 	(void)maxpacketsize;
499 
500 /* Check to see that there is room to add an IP option */
501 	if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
502 		return;
503 
504 /* Build option and copy into packet */
505 	{
506 		u_char *ptr;
507 		struct tcphdr *tc;
508 
509 		ptr = (u_char *) pip;
510 		ptr += 20;
511 		memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
512 
513 		option[0] = 0x64;	/* class: 3 (reserved), option 4 */
514 		option[1] = OPTION_LEN_BYTES;
515 
516 		memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
517 
518 		tc = (struct tcphdr *)ip_next(pip);
519 		memcpy(&option[6], (u_char *) & tc->th_sport, 2);
520 
521 		memcpy(ptr, option, 8);
522 	}
523 
524 /* Update checksum, header length and packet length */
525 	{
526 		int i;
527 		int accumulate;
528 		u_short *sptr;
529 
530 		sptr = (u_short *) option;
531 		accumulate = 0;
532 		for (i = 0; i < OPTION_LEN_INT16; i++)
533 			accumulate -= *(sptr++);
534 
535 		sptr = (u_short *) pip;
536 		accumulate += *sptr;
537 		pip->ip_hl += OPTION_LEN_INT32;
538 		accumulate -= *sptr;
539 
540 		accumulate += pip->ip_len;
541 		pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
542 		accumulate -= pip->ip_len;
543 
544 		ADJUST_CHECKSUM(accumulate, pip->ip_sum);
545 	}
546 #undef OPTION_LEN_BYTES
547 #undef OPTION_LEN_INT16
548 #undef OPTION_LEN_INT32
549 #ifdef LIBALIAS_DEBUG
550 	fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
551 	fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
552 #endif
553 }
554 
555 
556 /* Functions by other packet alias source files
557 
558     ProxyCheck()         -- Checks whether an outgoing packet should
559 			    be proxied.
560     ProxyModify()        -- Encodes the original destination address/port
561 			    for a packet which is to be redirected to
562 			    a proxy server.
563 */
564 
565 int
566 ProxyCheck(struct libalias *la, struct in_addr *proxy_server_addr,
567     u_short * proxy_server_port, struct in_addr src_addr,
568     struct in_addr dst_addr, u_short dst_port, u_char ip_p)
569 {
570 	struct proxy_entry *ptr;
571 
572 	LIBALIAS_LOCK_ASSERT(la);
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 		    && 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 	LIBALIAS_LOCK_ASSERT(la);
610 	(void)la;
611 
612 	switch (proxy_type) {
613 		case PROXY_TYPE_ENCODE_IPHDR:
614 		ProxyEncodeIpHeader(pip, maxpacketsize);
615 		break;
616 
617 	case PROXY_TYPE_ENCODE_TCPSTREAM:
618 		ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
619 		break;
620 	}
621 }
622 
623 
624 /*
625     Public API functions
626 */
627 
628 int
629 LibAliasProxyRule(struct libalias *la, const char *cmd)
630 {
631 /*
632  * This function takes command strings of the form:
633  *
634  *   server <addr>[:<port>]
635  *   [port <port>]
636  *   [rule n]
637  *   [proto tcp|udp]
638  *   [src <addr>[/n]]
639  *   [dst <addr>[/n]]
640  *   [type encode_tcp_stream|encode_ip_hdr|no_encode]
641  *
642  *   delete <rule number>
643  *
644  * Subfields can be in arbitrary order.  Port numbers and addresses
645  * must be in either numeric or symbolic form. An optional rule number
646  * is used to control the order in which rules are searched.  If two
647  * rules have the same number, then search order cannot be guaranteed,
648  * and the rules should be disjoint.  If no rule number is specified,
649  * then 0 is used, and group 0 rules are always checked before any
650  * others.
651  */
652 	int i, n, len, ret;
653 	int cmd_len;
654 	int token_count;
655 	int state;
656 	char *token;
657 	char buffer[256];
658 	char str_port[sizeof(buffer)];
659 	char str_server_port[sizeof(buffer)];
660 	char *res = buffer;
661 
662 	int rule_index;
663 	int proto;
664 	int proxy_type;
665 	int proxy_port;
666 	int server_port;
667 	struct in_addr server_addr;
668 	struct in_addr src_addr, src_mask;
669 	struct in_addr dst_addr, dst_mask;
670 	struct proxy_entry *proxy_entry;
671 
672 	LIBALIAS_LOCK(la);
673 	ret = 0;
674 /* Copy command line into a buffer */
675 	cmd += strspn(cmd, " \t");
676 	cmd_len = strlen(cmd);
677 	if (cmd_len > (int)(sizeof(buffer) - 1)) {
678 		ret = -1;
679 		goto getout;
680 	}
681 	strcpy(buffer, cmd);
682 
683 /* Convert to lower case */
684 	len = strlen(buffer);
685 	for (i = 0; i < len; i++)
686 		buffer[i] = tolower((unsigned char)buffer[i]);
687 
688 /* Set default proxy type */
689 
690 /* Set up default values */
691 	rule_index = 0;
692 	proxy_type = PROXY_TYPE_ENCODE_NONE;
693 	proto = IPPROTO_TCP;
694 	proxy_port = 0;
695 	server_addr.s_addr = 0;
696 	server_port = 0;
697 	src_addr.s_addr = 0;
698 	IpMask(0, &src_mask);
699 	dst_addr.s_addr = 0;
700 	IpMask(0, &dst_mask);
701 
702 	str_port[0] = 0;
703 	str_server_port[0] = 0;
704 
705 /* Parse command string with state machine */
706 #define STATE_READ_KEYWORD    0
707 #define STATE_READ_TYPE       1
708 #define STATE_READ_PORT       2
709 #define STATE_READ_SERVER     3
710 #define STATE_READ_RULE       4
711 #define STATE_READ_DELETE     5
712 #define STATE_READ_PROTO      6
713 #define STATE_READ_SRC        7
714 #define STATE_READ_DST        8
715 	state = STATE_READ_KEYWORD;
716 	token = strsep(&res, " \t");
717 	token_count = 0;
718 	while (token != NULL) {
719 		token_count++;
720 		switch (state) {
721 		case STATE_READ_KEYWORD:
722 			if (strcmp(token, "type") == 0)
723 				state = STATE_READ_TYPE;
724 			else if (strcmp(token, "port") == 0)
725 				state = STATE_READ_PORT;
726 			else if (strcmp(token, "server") == 0)
727 				state = STATE_READ_SERVER;
728 			else if (strcmp(token, "rule") == 0)
729 				state = STATE_READ_RULE;
730 			else if (strcmp(token, "delete") == 0)
731 				state = STATE_READ_DELETE;
732 			else if (strcmp(token, "proto") == 0)
733 				state = STATE_READ_PROTO;
734 			else if (strcmp(token, "src") == 0)
735 				state = STATE_READ_SRC;
736 			else if (strcmp(token, "dst") == 0)
737 				state = STATE_READ_DST;
738 			else {
739 				ret = -1;
740 				goto getout;
741 			}
742 			break;
743 
744 		case STATE_READ_TYPE:
745 			if (strcmp(token, "encode_ip_hdr") == 0)
746 				proxy_type = PROXY_TYPE_ENCODE_IPHDR;
747 			else if (strcmp(token, "encode_tcp_stream") == 0)
748 				proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
749 			else if (strcmp(token, "no_encode") == 0)
750 				proxy_type = PROXY_TYPE_ENCODE_NONE;
751 			else {
752 				ret = -1;
753 				goto getout;
754 			}
755 			state = STATE_READ_KEYWORD;
756 			break;
757 
758 		case STATE_READ_PORT:
759 			strcpy(str_port, token);
760 			state = STATE_READ_KEYWORD;
761 			break;
762 
763 		case STATE_READ_SERVER:
764 			{
765 				int err;
766 				char *p;
767 				char s[sizeof(buffer)];
768 
769 				p = token;
770 				while (*p != ':' && *p != 0)
771 					p++;
772 
773 				if (*p != ':') {
774 					err = IpAddr(token, &server_addr);
775 					if (err) {
776 						ret = -1;
777 						goto getout;
778 					}
779 				} else {
780 					*p = ' ';
781 
782 					n = sscanf(token, "%s %s", s, str_server_port);
783 					if (n != 2) {
784 						ret = -1;
785 						goto getout;
786 					}
787 
788 					err = IpAddr(s, &server_addr);
789 					if (err) {
790 						ret = -1;
791 						goto getout;
792 					}
793 				}
794 			}
795 			state = STATE_READ_KEYWORD;
796 			break;
797 
798 		case STATE_READ_RULE:
799 			n = sscanf(token, "%d", &rule_index);
800 			if (n != 1 || rule_index < 0) {
801 				ret = -1;
802 				goto getout;
803 			}
804 			state = STATE_READ_KEYWORD;
805 			break;
806 
807 		case STATE_READ_DELETE:
808 			{
809 				int err;
810 				int rule_to_delete;
811 
812 				if (token_count != 2) {
813 					ret = -1;
814 					goto getout;
815 				}
816 
817 				n = sscanf(token, "%d", &rule_to_delete);
818 				if (n != 1) {
819 					ret = -1;
820 					goto getout;
821 				}
822 				err = RuleNumberDelete(la, rule_to_delete);
823 				if (err)
824 					ret = -1;
825 				ret = 0;
826 				goto getout;
827 			}
828 
829 		case STATE_READ_PROTO:
830 			if (strcmp(token, "tcp") == 0)
831 				proto = IPPROTO_TCP;
832 			else if (strcmp(token, "udp") == 0)
833 				proto = IPPROTO_UDP;
834 			else {
835 				ret = -1;
836 				goto getout;
837 			}
838 			state = STATE_READ_KEYWORD;
839 			break;
840 
841 		case STATE_READ_SRC:
842 		case STATE_READ_DST:
843 			{
844 				int err;
845 				char *p;
846 				struct in_addr mask;
847 				struct in_addr addr;
848 
849 				p = token;
850 				while (*p != '/' && *p != 0)
851 					p++;
852 
853 				if (*p != '/') {
854 					IpMask(32, &mask);
855 					err = IpAddr(token, &addr);
856 					if (err) {
857 						ret = -1;
858 						goto getout;
859 					}
860 				} else {
861 					int nbits;
862 					char s[sizeof(buffer)];
863 
864 					*p = ' ';
865 					n = sscanf(token, "%s %d", s, &nbits);
866 					if (n != 2) {
867 						ret = -1;
868 						goto getout;
869 					}
870 
871 					err = IpAddr(s, &addr);
872 					if (err) {
873 						ret = -1;
874 						goto getout;
875 					}
876 
877 					err = IpMask(nbits, &mask);
878 					if (err) {
879 						ret = -1;
880 						goto getout;
881 					}
882 				}
883 
884 				if (state == STATE_READ_SRC) {
885 					src_addr = addr;
886 					src_mask = mask;
887 				} else {
888 					dst_addr = addr;
889 					dst_mask = mask;
890 				}
891 			}
892 			state = STATE_READ_KEYWORD;
893 			break;
894 
895 		default:
896 			ret = -1;
897 			goto getout;
898 			break;
899 		}
900 
901 		do {
902 			token = strsep(&res, " \t");
903 		} while (token != NULL && !*token);
904 	}
905 #undef STATE_READ_KEYWORD
906 #undef STATE_READ_TYPE
907 #undef STATE_READ_PORT
908 #undef STATE_READ_SERVER
909 #undef STATE_READ_RULE
910 #undef STATE_READ_DELETE
911 #undef STATE_READ_PROTO
912 #undef STATE_READ_SRC
913 #undef STATE_READ_DST
914 
915 /* Convert port strings to numbers.  This needs to be done after
916    the string is parsed, because the prototype might not be designated
917    before the ports (which might be symbolic entries in /etc/services) */
918 
919 	if (strlen(str_port) != 0) {
920 		int err;
921 
922 		err = IpPort(str_port, proto, &proxy_port);
923 		if (err) {
924 			ret = -1;
925 			goto getout;
926 		}
927 	} else {
928 		proxy_port = 0;
929 	}
930 
931 	if (strlen(str_server_port) != 0) {
932 		int err;
933 
934 		err = IpPort(str_server_port, proto, &server_port);
935 		if (err) {
936 			ret = -1;
937 			goto getout;
938 		}
939 	} else {
940 		server_port = 0;
941 	}
942 
943 /* Check that at least the server address has been defined */
944 	if (server_addr.s_addr == 0) {
945 		ret = -1;
946 		goto getout;
947 	}
948 
949 /* Add to linked list */
950 	proxy_entry = malloc(sizeof(struct proxy_entry));
951 	if (proxy_entry == NULL) {
952 		ret = -1;
953 		goto getout;
954 	}
955 
956 	proxy_entry->proxy_type = proxy_type;
957 	proxy_entry->rule_index = rule_index;
958 	proxy_entry->proto = proto;
959 	proxy_entry->proxy_port = htons(proxy_port);
960 	proxy_entry->server_port = htons(server_port);
961 	proxy_entry->server_addr = server_addr;
962 	proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
963 	proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
964 	proxy_entry->src_mask = src_mask;
965 	proxy_entry->dst_mask = dst_mask;
966 
967 	RuleAdd(la, proxy_entry);
968 
969 getout:
970 	LIBALIAS_UNLOCK(la);
971 	return (ret);
972 }
973