xref: /freebsd/sys/netinet/libalias/alias_proxy.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
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 		delta = GetDeltaSeqOut(pip, lnk);
457 		AddSeq(pip, lnk, delta + slen);
458 	}
459 
460 /* Update IP header packet length and checksum */
461 	{
462 		int accumulate;
463 
464 		accumulate = pip->ip_len;
465 		pip->ip_len = htons(ntohs(pip->ip_len) + slen);
466 		accumulate -= pip->ip_len;
467 
468 		ADJUST_CHECKSUM(accumulate, pip->ip_sum);
469 	}
470 
471 /* Update TCP checksum, Use TcpChecksum since so many things have
472    already changed. */
473 
474 	tc->th_sum = 0;
475 #ifdef _KERNEL
476 	tc->th_x2 = 1;
477 #else
478 	tc->th_sum = TcpChecksum(pip);
479 #endif
480 }
481 
482 static void
483 ProxyEncodeIpHeader(struct ip *pip,
484     int maxpacketsize)
485 {
486 #define OPTION_LEN_BYTES  8
487 #define OPTION_LEN_INT16  4
488 #define OPTION_LEN_INT32  2
489 	u_char option[OPTION_LEN_BYTES];
490 
491 #ifdef LIBALIAS_DEBUG
492 	fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
493 	fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
494 #endif
495 
496 	(void)maxpacketsize;
497 
498 /* Check to see that there is room to add an IP option */
499 	if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
500 		return;
501 
502 /* Build option and copy into packet */
503 	{
504 		u_char *ptr;
505 		struct tcphdr *tc;
506 
507 		ptr = (u_char *) pip;
508 		ptr += 20;
509 		memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
510 
511 		option[0] = 0x64;	/* class: 3 (reserved), option 4 */
512 		option[1] = OPTION_LEN_BYTES;
513 
514 		memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
515 
516 		tc = (struct tcphdr *)ip_next(pip);
517 		memcpy(&option[6], (u_char *) & tc->th_sport, 2);
518 
519 		memcpy(ptr, option, 8);
520 	}
521 
522 /* Update checksum, header length and packet length */
523 	{
524 		int i;
525 		int accumulate;
526 		u_short *sptr;
527 
528 		sptr = (u_short *) option;
529 		accumulate = 0;
530 		for (i = 0; i < OPTION_LEN_INT16; i++)
531 			accumulate -= *(sptr++);
532 
533 		sptr = (u_short *) pip;
534 		accumulate += *sptr;
535 		pip->ip_hl += OPTION_LEN_INT32;
536 		accumulate -= *sptr;
537 
538 		accumulate += pip->ip_len;
539 		pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
540 		accumulate -= pip->ip_len;
541 
542 		ADJUST_CHECKSUM(accumulate, pip->ip_sum);
543 	}
544 #undef OPTION_LEN_BYTES
545 #undef OPTION_LEN_INT16
546 #undef OPTION_LEN_INT32
547 #ifdef LIBALIAS_DEBUG
548 	fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
549 	fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
550 #endif
551 }
552 
553 
554 /* Functions by other packet alias source files
555 
556     ProxyCheck()         -- Checks whether an outgoing packet should
557 			    be proxied.
558     ProxyModify()        -- Encodes the original destination address/port
559 			    for a packet which is to be redirected to
560 			    a proxy server.
561 */
562 
563 int
564 ProxyCheck(struct libalias *la, struct ip *pip,
565     struct in_addr *proxy_server_addr,
566     u_short * proxy_server_port)
567 {
568 	u_short dst_port;
569 	struct in_addr src_addr;
570 	struct in_addr dst_addr;
571 	struct proxy_entry *ptr;
572 
573 	LIBALIAS_LOCK_ASSERT(la);
574 	src_addr = pip->ip_src;
575 	dst_addr = pip->ip_dst;
576 	dst_port = ((struct tcphdr *)ip_next(pip))
577 	    ->th_dport;
578 
579 	ptr = la->proxyList;
580 	while (ptr != NULL) {
581 		u_short proxy_port;
582 
583 		proxy_port = ptr->proxy_port;
584 		if ((dst_port == proxy_port || proxy_port == 0)
585 		    && pip->ip_p == ptr->proto
586 		    && src_addr.s_addr != ptr->server_addr.s_addr) {
587 			struct in_addr src_addr_masked;
588 			struct in_addr dst_addr_masked;
589 
590 			src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
591 			dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
592 
593 			if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
594 			    && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
595 				if ((*proxy_server_port = ptr->server_port) == 0)
596 					*proxy_server_port = dst_port;
597 				*proxy_server_addr = ptr->server_addr;
598 				return (ptr->proxy_type);
599 			}
600 		}
601 		ptr = ptr->next;
602 	}
603 
604 	return (0);
605 }
606 
607 void
608 ProxyModify(struct libalias *la, struct alias_link *lnk,
609     struct ip *pip,
610     int maxpacketsize,
611     int proxy_type)
612 {
613 
614 	LIBALIAS_LOCK_ASSERT(la);
615 	(void)la;
616 
617 	switch (proxy_type) {
618 		case PROXY_TYPE_ENCODE_IPHDR:
619 		ProxyEncodeIpHeader(pip, maxpacketsize);
620 		break;
621 
622 	case PROXY_TYPE_ENCODE_TCPSTREAM:
623 		ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
624 		break;
625 	}
626 }
627 
628 
629 /*
630     Public API functions
631 */
632 
633 int
634 LibAliasProxyRule(struct libalias *la, const char *cmd)
635 {
636 /*
637  * This function takes command strings of the form:
638  *
639  *   server <addr>[:<port>]
640  *   [port <port>]
641  *   [rule n]
642  *   [proto tcp|udp]
643  *   [src <addr>[/n]]
644  *   [dst <addr>[/n]]
645  *   [type encode_tcp_stream|encode_ip_hdr|no_encode]
646  *
647  *   delete <rule number>
648  *
649  * Subfields can be in arbitrary order.  Port numbers and addresses
650  * must be in either numeric or symbolic form. An optional rule number
651  * is used to control the order in which rules are searched.  If two
652  * rules have the same number, then search order cannot be guaranteed,
653  * and the rules should be disjoint.  If no rule number is specified,
654  * then 0 is used, and group 0 rules are always checked before any
655  * others.
656  */
657 	int i, n, len, ret;
658 	int cmd_len;
659 	int token_count;
660 	int state;
661 	char *token;
662 	char buffer[256];
663 	char str_port[sizeof(buffer)];
664 	char str_server_port[sizeof(buffer)];
665 	char *res = buffer;
666 
667 	int rule_index;
668 	int proto;
669 	int proxy_type;
670 	int proxy_port;
671 	int server_port;
672 	struct in_addr server_addr;
673 	struct in_addr src_addr, src_mask;
674 	struct in_addr dst_addr, dst_mask;
675 	struct proxy_entry *proxy_entry;
676 
677 	LIBALIAS_LOCK(la);
678 	ret = 0;
679 /* Copy command line into a buffer */
680 	cmd += strspn(cmd, " \t");
681 	cmd_len = strlen(cmd);
682 	if (cmd_len > (int)(sizeof(buffer) - 1)) {
683 		ret = -1;
684 		goto getout;
685 	}
686 	strcpy(buffer, cmd);
687 
688 /* Convert to lower case */
689 	len = strlen(buffer);
690 	for (i = 0; i < len; i++)
691 		buffer[i] = tolower((unsigned char)buffer[i]);
692 
693 /* Set default proxy type */
694 
695 /* Set up default values */
696 	rule_index = 0;
697 	proxy_type = PROXY_TYPE_ENCODE_NONE;
698 	proto = IPPROTO_TCP;
699 	proxy_port = 0;
700 	server_addr.s_addr = 0;
701 	server_port = 0;
702 	src_addr.s_addr = 0;
703 	IpMask(0, &src_mask);
704 	dst_addr.s_addr = 0;
705 	IpMask(0, &dst_mask);
706 
707 	str_port[0] = 0;
708 	str_server_port[0] = 0;
709 
710 /* Parse command string with state machine */
711 #define STATE_READ_KEYWORD    0
712 #define STATE_READ_TYPE       1
713 #define STATE_READ_PORT       2
714 #define STATE_READ_SERVER     3
715 #define STATE_READ_RULE       4
716 #define STATE_READ_DELETE     5
717 #define STATE_READ_PROTO      6
718 #define STATE_READ_SRC        7
719 #define STATE_READ_DST        8
720 	state = STATE_READ_KEYWORD;
721 	token = strsep(&res, " \t");
722 	token_count = 0;
723 	while (token != NULL) {
724 		token_count++;
725 		switch (state) {
726 		case STATE_READ_KEYWORD:
727 			if (strcmp(token, "type") == 0)
728 				state = STATE_READ_TYPE;
729 			else if (strcmp(token, "port") == 0)
730 				state = STATE_READ_PORT;
731 			else if (strcmp(token, "server") == 0)
732 				state = STATE_READ_SERVER;
733 			else if (strcmp(token, "rule") == 0)
734 				state = STATE_READ_RULE;
735 			else if (strcmp(token, "delete") == 0)
736 				state = STATE_READ_DELETE;
737 			else if (strcmp(token, "proto") == 0)
738 				state = STATE_READ_PROTO;
739 			else if (strcmp(token, "src") == 0)
740 				state = STATE_READ_SRC;
741 			else if (strcmp(token, "dst") == 0)
742 				state = STATE_READ_DST;
743 			else {
744 				ret = -1;
745 				goto getout;
746 			}
747 			break;
748 
749 		case STATE_READ_TYPE:
750 			if (strcmp(token, "encode_ip_hdr") == 0)
751 				proxy_type = PROXY_TYPE_ENCODE_IPHDR;
752 			else if (strcmp(token, "encode_tcp_stream") == 0)
753 				proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
754 			else if (strcmp(token, "no_encode") == 0)
755 				proxy_type = PROXY_TYPE_ENCODE_NONE;
756 			else {
757 				ret = -1;
758 				goto getout;
759 			}
760 			state = STATE_READ_KEYWORD;
761 			break;
762 
763 		case STATE_READ_PORT:
764 			strcpy(str_port, token);
765 			state = STATE_READ_KEYWORD;
766 			break;
767 
768 		case STATE_READ_SERVER:
769 			{
770 				int err;
771 				char *p;
772 				char s[sizeof(buffer)];
773 
774 				p = token;
775 				while (*p != ':' && *p != 0)
776 					p++;
777 
778 				if (*p != ':') {
779 					err = IpAddr(token, &server_addr);
780 					if (err) {
781 						ret = -1;
782 						goto getout;
783 					}
784 				} else {
785 					*p = ' ';
786 
787 					n = sscanf(token, "%s %s", s, str_server_port);
788 					if (n != 2) {
789 						ret = -1;
790 						goto getout;
791 					}
792 
793 					err = IpAddr(s, &server_addr);
794 					if (err) {
795 						ret = -1;
796 						goto getout;
797 					}
798 				}
799 			}
800 			state = STATE_READ_KEYWORD;
801 			break;
802 
803 		case STATE_READ_RULE:
804 			n = sscanf(token, "%d", &rule_index);
805 			if (n != 1 || rule_index < 0) {
806 				ret = -1;
807 				goto getout;
808 			}
809 			state = STATE_READ_KEYWORD;
810 			break;
811 
812 		case STATE_READ_DELETE:
813 			{
814 				int err;
815 				int rule_to_delete;
816 
817 				if (token_count != 2) {
818 					ret = -1;
819 					goto getout;
820 				}
821 
822 				n = sscanf(token, "%d", &rule_to_delete);
823 				if (n != 1) {
824 					ret = -1;
825 					goto getout;
826 				}
827 				err = RuleNumberDelete(la, rule_to_delete);
828 				if (err)
829 					ret = -1;
830 				ret = 0;
831 				goto getout;
832 			}
833 
834 		case STATE_READ_PROTO:
835 			if (strcmp(token, "tcp") == 0)
836 				proto = IPPROTO_TCP;
837 			else if (strcmp(token, "udp") == 0)
838 				proto = IPPROTO_UDP;
839 			else {
840 				ret = -1;
841 				goto getout;
842 			}
843 			state = STATE_READ_KEYWORD;
844 			break;
845 
846 		case STATE_READ_SRC:
847 		case STATE_READ_DST:
848 			{
849 				int err;
850 				char *p;
851 				struct in_addr mask;
852 				struct in_addr addr;
853 
854 				p = token;
855 				while (*p != '/' && *p != 0)
856 					p++;
857 
858 				if (*p != '/') {
859 					IpMask(32, &mask);
860 					err = IpAddr(token, &addr);
861 					if (err) {
862 						ret = -1;
863 						goto getout;
864 					}
865 				} else {
866 					int nbits;
867 					char s[sizeof(buffer)];
868 
869 					*p = ' ';
870 					n = sscanf(token, "%s %d", s, &nbits);
871 					if (n != 2) {
872 						ret = -1;
873 						goto getout;
874 					}
875 
876 					err = IpAddr(s, &addr);
877 					if (err) {
878 						ret = -1;
879 						goto getout;
880 					}
881 
882 					err = IpMask(nbits, &mask);
883 					if (err) {
884 						ret = -1;
885 						goto getout;
886 					}
887 				}
888 
889 				if (state == STATE_READ_SRC) {
890 					src_addr = addr;
891 					src_mask = mask;
892 				} else {
893 					dst_addr = addr;
894 					dst_mask = mask;
895 				}
896 			}
897 			state = STATE_READ_KEYWORD;
898 			break;
899 
900 		default:
901 			ret = -1;
902 			goto getout;
903 			break;
904 		}
905 
906 		do {
907 			token = strsep(&res, " \t");
908 		} while (token != NULL && !*token);
909 	}
910 #undef STATE_READ_KEYWORD
911 #undef STATE_READ_TYPE
912 #undef STATE_READ_PORT
913 #undef STATE_READ_SERVER
914 #undef STATE_READ_RULE
915 #undef STATE_READ_DELETE
916 #undef STATE_READ_PROTO
917 #undef STATE_READ_SRC
918 #undef STATE_READ_DST
919 
920 /* Convert port strings to numbers.  This needs to be done after
921    the string is parsed, because the prototype might not be designated
922    before the ports (which might be symbolic entries in /etc/services) */
923 
924 	if (strlen(str_port) != 0) {
925 		int err;
926 
927 		err = IpPort(str_port, proto, &proxy_port);
928 		if (err) {
929 			ret = -1;
930 			goto getout;
931 		}
932 	} else {
933 		proxy_port = 0;
934 	}
935 
936 	if (strlen(str_server_port) != 0) {
937 		int err;
938 
939 		err = IpPort(str_server_port, proto, &server_port);
940 		if (err) {
941 			ret = -1;
942 			goto getout;
943 		}
944 	} else {
945 		server_port = 0;
946 	}
947 
948 /* Check that at least the server address has been defined */
949 	if (server_addr.s_addr == 0) {
950 		ret = -1;
951 		goto getout;
952 	}
953 
954 /* Add to linked list */
955 	proxy_entry = malloc(sizeof(struct proxy_entry));
956 	if (proxy_entry == NULL) {
957 		ret = -1;
958 		goto getout;
959 	}
960 
961 	proxy_entry->proxy_type = proxy_type;
962 	proxy_entry->rule_index = rule_index;
963 	proxy_entry->proto = proto;
964 	proxy_entry->proxy_port = htons(proxy_port);
965 	proxy_entry->server_port = htons(server_port);
966 	proxy_entry->server_addr = server_addr;
967 	proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
968 	proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
969 	proxy_entry->src_mask = src_mask;
970 	proxy_entry->dst_mask = dst_mask;
971 
972 	RuleAdd(la, proxy_entry);
973 
974 getout:
975 	LIBALIAS_UNLOCK(la);
976 	return (ret);
977 }
978