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