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