xref: /freebsd/sys/netinet/libalias/alias_proxy.c (revision 87569f75a91f298c52a71823c04d41cf53c88889)
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 #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 ip *pip,
567     struct in_addr *proxy_server_addr,
568     u_short * proxy_server_port)
569 {
570 	u_short dst_port;
571 	struct in_addr src_addr;
572 	struct in_addr dst_addr;
573 	struct proxy_entry *ptr;
574 
575 	src_addr = pip->ip_src;
576 	dst_addr = pip->ip_dst;
577 	dst_port = ((struct tcphdr *)ip_next(pip))
578 	    ->th_dport;
579 
580 	ptr = la->proxyList;
581 	while (ptr != NULL) {
582 		u_short proxy_port;
583 
584 		proxy_port = ptr->proxy_port;
585 		if ((dst_port == proxy_port || proxy_port == 0)
586 		    && pip->ip_p == ptr->proto
587 		    && src_addr.s_addr != ptr->server_addr.s_addr) {
588 			struct in_addr src_addr_masked;
589 			struct in_addr dst_addr_masked;
590 
591 			src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
592 			dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
593 
594 			if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
595 			    && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
596 				if ((*proxy_server_port = ptr->server_port) == 0)
597 					*proxy_server_port = dst_port;
598 				*proxy_server_addr = ptr->server_addr;
599 				return (ptr->proxy_type);
600 			}
601 		}
602 		ptr = ptr->next;
603 	}
604 
605 	return (0);
606 }
607 
608 void
609 ProxyModify(struct libalias *la, struct alias_link *lnk,
610     struct ip *pip,
611     int maxpacketsize,
612     int proxy_type)
613 {
614 
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;
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 /* Copy command line into a buffer */
678 	cmd += strspn(cmd, " \t");
679 	cmd_len = strlen(cmd);
680 	if (cmd_len > (int)(sizeof(buffer) - 1))
681 		return (-1);
682 	strcpy(buffer, cmd);
683 
684 /* Convert to lower case */
685 	len = strlen(buffer);
686 	for (i = 0; i < len; i++)
687 		buffer[i] = tolower((unsigned char)buffer[i]);
688 
689 /* Set default proxy type */
690 
691 /* Set up default values */
692 	rule_index = 0;
693 	proxy_type = PROXY_TYPE_ENCODE_NONE;
694 	proto = IPPROTO_TCP;
695 	proxy_port = 0;
696 	server_addr.s_addr = 0;
697 	server_port = 0;
698 	src_addr.s_addr = 0;
699 	IpMask(0, &src_mask);
700 	dst_addr.s_addr = 0;
701 	IpMask(0, &dst_mask);
702 
703 	str_port[0] = 0;
704 	str_server_port[0] = 0;
705 
706 /* Parse command string with state machine */
707 #define STATE_READ_KEYWORD    0
708 #define STATE_READ_TYPE       1
709 #define STATE_READ_PORT       2
710 #define STATE_READ_SERVER     3
711 #define STATE_READ_RULE       4
712 #define STATE_READ_DELETE     5
713 #define STATE_READ_PROTO      6
714 #define STATE_READ_SRC        7
715 #define STATE_READ_DST        8
716 	state = STATE_READ_KEYWORD;
717 	token = strsep(&res, " \t");
718 	token_count = 0;
719 	while (token != NULL) {
720 		token_count++;
721 		switch (state) {
722 		case STATE_READ_KEYWORD:
723 			if (strcmp(token, "type") == 0)
724 				state = STATE_READ_TYPE;
725 			else if (strcmp(token, "port") == 0)
726 				state = STATE_READ_PORT;
727 			else if (strcmp(token, "server") == 0)
728 				state = STATE_READ_SERVER;
729 			else if (strcmp(token, "rule") == 0)
730 				state = STATE_READ_RULE;
731 			else if (strcmp(token, "delete") == 0)
732 				state = STATE_READ_DELETE;
733 			else if (strcmp(token, "proto") == 0)
734 				state = STATE_READ_PROTO;
735 			else if (strcmp(token, "src") == 0)
736 				state = STATE_READ_SRC;
737 			else if (strcmp(token, "dst") == 0)
738 				state = STATE_READ_DST;
739 			else
740 				return (-1);
741 			break;
742 
743 		case STATE_READ_TYPE:
744 			if (strcmp(token, "encode_ip_hdr") == 0)
745 				proxy_type = PROXY_TYPE_ENCODE_IPHDR;
746 			else if (strcmp(token, "encode_tcp_stream") == 0)
747 				proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
748 			else if (strcmp(token, "no_encode") == 0)
749 				proxy_type = PROXY_TYPE_ENCODE_NONE;
750 			else
751 				return (-1);
752 			state = STATE_READ_KEYWORD;
753 			break;
754 
755 		case STATE_READ_PORT:
756 			strcpy(str_port, token);
757 			state = STATE_READ_KEYWORD;
758 			break;
759 
760 		case STATE_READ_SERVER:
761 			{
762 				int err;
763 				char *p;
764 				char s[sizeof(buffer)];
765 
766 				p = token;
767 				while (*p != ':' && *p != 0)
768 					p++;
769 
770 				if (*p != ':') {
771 					err = IpAddr(token, &server_addr);
772 					if (err)
773 						return (-1);
774 				} else {
775 					*p = ' ';
776 
777 					n = sscanf(token, "%s %s", s, str_server_port);
778 					if (n != 2)
779 						return (-1);
780 
781 					err = IpAddr(s, &server_addr);
782 					if (err)
783 						return (-1);
784 				}
785 			}
786 			state = STATE_READ_KEYWORD;
787 			break;
788 
789 		case STATE_READ_RULE:
790 			n = sscanf(token, "%d", &rule_index);
791 			if (n != 1 || rule_index < 0)
792 				return (-1);
793 			state = STATE_READ_KEYWORD;
794 			break;
795 
796 		case STATE_READ_DELETE:
797 			{
798 				int err;
799 				int rule_to_delete;
800 
801 				if (token_count != 2)
802 					return (-1);
803 
804 				n = sscanf(token, "%d", &rule_to_delete);
805 				if (n != 1)
806 					return (-1);
807 				err = RuleNumberDelete(la, rule_to_delete);
808 				if (err)
809 					return (-1);
810 				return (0);
811 			}
812 
813 		case STATE_READ_PROTO:
814 			if (strcmp(token, "tcp") == 0)
815 				proto = IPPROTO_TCP;
816 			else if (strcmp(token, "udp") == 0)
817 				proto = IPPROTO_UDP;
818 			else
819 				return (-1);
820 			state = STATE_READ_KEYWORD;
821 			break;
822 
823 		case STATE_READ_SRC:
824 		case STATE_READ_DST:
825 			{
826 				int err;
827 				char *p;
828 				struct in_addr mask;
829 				struct in_addr addr;
830 
831 				p = token;
832 				while (*p != '/' && *p != 0)
833 					p++;
834 
835 				if (*p != '/') {
836 					IpMask(32, &mask);
837 					err = IpAddr(token, &addr);
838 					if (err)
839 						return (-1);
840 				} else {
841 					int nbits;
842 					char s[sizeof(buffer)];
843 
844 					*p = ' ';
845 					n = sscanf(token, "%s %d", s, &nbits);
846 					if (n != 2)
847 						return (-1);
848 
849 					err = IpAddr(s, &addr);
850 					if (err)
851 						return (-1);
852 
853 					err = IpMask(nbits, &mask);
854 					if (err)
855 						return (-1);
856 				}
857 
858 				if (state == STATE_READ_SRC) {
859 					src_addr = addr;
860 					src_mask = mask;
861 				} else {
862 					dst_addr = addr;
863 					dst_mask = mask;
864 				}
865 			}
866 			state = STATE_READ_KEYWORD;
867 			break;
868 
869 		default:
870 			return (-1);
871 			break;
872 		}
873 
874 		do {
875 			token = strsep(&res, " \t");
876 		} while (token != NULL && !*token);
877 	}
878 #undef STATE_READ_KEYWORD
879 #undef STATE_READ_TYPE
880 #undef STATE_READ_PORT
881 #undef STATE_READ_SERVER
882 #undef STATE_READ_RULE
883 #undef STATE_READ_DELETE
884 #undef STATE_READ_PROTO
885 #undef STATE_READ_SRC
886 #undef STATE_READ_DST
887 
888 /* Convert port strings to numbers.  This needs to be done after
889    the string is parsed, because the prototype might not be designated
890    before the ports (which might be symbolic entries in /etc/services) */
891 
892 	if (strlen(str_port) != 0) {
893 		int err;
894 
895 		err = IpPort(str_port, proto, &proxy_port);
896 		if (err)
897 			return (-1);
898 	} else {
899 		proxy_port = 0;
900 	}
901 
902 	if (strlen(str_server_port) != 0) {
903 		int err;
904 
905 		err = IpPort(str_server_port, proto, &server_port);
906 		if (err)
907 			return (-1);
908 	} else {
909 		server_port = 0;
910 	}
911 
912 /* Check that at least the server address has been defined */
913 	if (server_addr.s_addr == 0)
914 		return (-1);
915 
916 /* Add to linked list */
917 	proxy_entry = malloc(sizeof(struct proxy_entry));
918 	if (proxy_entry == NULL)
919 		return (-1);
920 
921 	proxy_entry->proxy_type = proxy_type;
922 	proxy_entry->rule_index = rule_index;
923 	proxy_entry->proto = proto;
924 	proxy_entry->proxy_port = htons(proxy_port);
925 	proxy_entry->server_port = htons(server_port);
926 	proxy_entry->server_addr = server_addr;
927 	proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
928 	proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
929 	proxy_entry->src_mask = src_mask;
930 	proxy_entry->dst_mask = dst_mask;
931 
932 	RuleAdd(la, proxy_entry);
933 
934 	return (0);
935 }
936