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