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