xref: /freebsd/sys/netinet/libalias/alias_proxy.c (revision 8d20be1e22095c27faf8fe8b2f0d089739cc742e)
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 
298 /* Compute pointer to tcp header */
299 	tc = (struct tcphdr *)ip_next(pip);
300 
301 /* Don't modify if once already modified */
302 
303 	if (GetAckModified(lnk))
304 		return;
305 
306 /* Translate destination address and port to string form */
307 	snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
308 	    inet_ntoa(GetProxyAddress(lnk)), (u_int) ntohs(GetProxyPort(lnk)));
309 
310 /* Pad string out to a multiple of two in length */
311 	slen = strlen(buffer);
312 	switch (slen % 2) {
313 	case 0:
314 		strcat(buffer, " \n");
315 		slen += 2;
316 		break;
317 	case 1:
318 		strcat(buffer, "\n");
319 		slen += 1;
320 	}
321 
322 /* Check for packet overflow */
323 	if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
324 		return;
325 
326 /* Shift existing TCP data and insert destination string */
327 	{
328 		int dlen;
329 		int hlen;
330 		char *p;
331 
332 		hlen = (pip->ip_hl + tc->th_off) << 2;
333 		dlen = ntohs(pip->ip_len) - hlen;
334 
335 /* Modify first packet that has data in it */
336 
337 		if (dlen == 0)
338 			return;
339 
340 		p = (char *)pip;
341 		p += hlen;
342 
343 		bcopy(p, p + slen, dlen);
344 		memcpy(p, buffer, slen);
345 	}
346 
347 /* Save information about modfied sequence number */
348 	{
349 		int delta;
350 
351 		SetAckModified(lnk);
352 		tc = (struct tcphdr *)ip_next(pip);
353 		delta = GetDeltaSeqOut(tc->th_seq, lnk);
354 		AddSeq(lnk, delta + slen, pip->ip_hl, pip->ip_len, tc->th_seq,
355 		    tc->th_off);
356 	}
357 
358 /* Update IP header packet length and checksum */
359 	{
360 		int accumulate;
361 
362 		accumulate = pip->ip_len;
363 		pip->ip_len = htons(ntohs(pip->ip_len) + slen);
364 		accumulate -= pip->ip_len;
365 
366 		ADJUST_CHECKSUM(accumulate, pip->ip_sum);
367 	}
368 
369 /* Update TCP checksum, Use TcpChecksum since so many things have
370    already changed. */
371 
372 	tc->th_sum = 0;
373 #ifdef _KERNEL
374 	tc->th_x2 = 1;
375 #else
376 	tc->th_sum = TcpChecksum(pip);
377 #endif
378 }
379 
380 static void
381 ProxyEncodeIpHeader(struct ip *pip,
382     int maxpacketsize)
383 {
384 #define OPTION_LEN_BYTES  8
385 #define OPTION_LEN_INT16  4
386 #define OPTION_LEN_INT32  2
387 	u_char option[OPTION_LEN_BYTES];
388 
389 #ifdef LIBALIAS_DEBUG
390 	fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
391 	fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
392 #endif
393 
394 	(void)maxpacketsize;
395 
396 /* Check to see that there is room to add an IP option */
397 	if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
398 		return;
399 
400 /* Build option and copy into packet */
401 	{
402 		u_char *ptr;
403 		struct tcphdr *tc;
404 
405 		ptr = (u_char *) pip;
406 		ptr += 20;
407 		memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
408 
409 		option[0] = 0x64;	/* class: 3 (reserved), option 4 */
410 		option[1] = OPTION_LEN_BYTES;
411 
412 		memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
413 
414 		tc = (struct tcphdr *)ip_next(pip);
415 		memcpy(&option[6], (u_char *) & tc->th_sport, 2);
416 
417 		memcpy(ptr, option, 8);
418 	}
419 
420 /* Update checksum, header length and packet length */
421 	{
422 		int i;
423 		int accumulate;
424 		u_short *sptr;
425 
426 		sptr = (u_short *) option;
427 		accumulate = 0;
428 		for (i = 0; i < OPTION_LEN_INT16; i++)
429 			accumulate -= *(sptr++);
430 
431 		sptr = (u_short *) pip;
432 		accumulate += *sptr;
433 		pip->ip_hl += OPTION_LEN_INT32;
434 		accumulate -= *sptr;
435 
436 		accumulate += pip->ip_len;
437 		pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
438 		accumulate -= pip->ip_len;
439 
440 		ADJUST_CHECKSUM(accumulate, pip->ip_sum);
441 	}
442 #undef OPTION_LEN_BYTES
443 #undef OPTION_LEN_INT16
444 #undef OPTION_LEN_INT32
445 #ifdef LIBALIAS_DEBUG
446 	fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
447 	fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
448 #endif
449 }
450 
451 
452 /* Functions by other packet alias source files
453 
454     ProxyCheck()         -- Checks whether an outgoing packet should
455 			    be proxied.
456     ProxyModify()        -- Encodes the original destination address/port
457 			    for a packet which is to be redirected to
458 			    a proxy server.
459 */
460 
461 int
462 ProxyCheck(struct libalias *la, struct in_addr *proxy_server_addr,
463     u_short * proxy_server_port, struct in_addr src_addr,
464     struct in_addr dst_addr, u_short dst_port, u_char ip_p)
465 {
466 	struct proxy_entry *ptr;
467 
468 	LIBALIAS_LOCK_ASSERT(la);
469 
470 	ptr = la->proxyList;
471 	while (ptr != NULL) {
472 		u_short proxy_port;
473 
474 		proxy_port = ptr->proxy_port;
475 		if ((dst_port == proxy_port || proxy_port == 0)
476 		    && ip_p == ptr->proto
477 		    && src_addr.s_addr != ptr->server_addr.s_addr) {
478 			struct in_addr src_addr_masked;
479 			struct in_addr dst_addr_masked;
480 
481 			src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
482 			dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
483 
484 			if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
485 			    && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
486 				if ((*proxy_server_port = ptr->server_port) == 0)
487 					*proxy_server_port = dst_port;
488 				*proxy_server_addr = ptr->server_addr;
489 				return (ptr->proxy_type);
490 			}
491 		}
492 		ptr = ptr->next;
493 	}
494 
495 	return (0);
496 }
497 
498 void
499 ProxyModify(struct libalias *la, struct alias_link *lnk,
500     struct ip *pip,
501     int maxpacketsize,
502     int proxy_type)
503 {
504 
505 	LIBALIAS_LOCK_ASSERT(la);
506 	(void)la;
507 
508 	switch (proxy_type) {
509 		case PROXY_TYPE_ENCODE_IPHDR:
510 		ProxyEncodeIpHeader(pip, maxpacketsize);
511 		break;
512 
513 	case PROXY_TYPE_ENCODE_TCPSTREAM:
514 		ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
515 		break;
516 	}
517 }
518 
519 
520 /*
521     Public API functions
522 */
523 
524 int
525 LibAliasProxyRule(struct libalias *la, const char *cmd)
526 {
527 /*
528  * This function takes command strings of the form:
529  *
530  *   server <addr>[:<port>]
531  *   [port <port>]
532  *   [rule n]
533  *   [proto tcp|udp]
534  *   [src <addr>[/n]]
535  *   [dst <addr>[/n]]
536  *   [type encode_tcp_stream|encode_ip_hdr|no_encode]
537  *
538  *   delete <rule number>
539  *
540  * Subfields can be in arbitrary order.  Port numbers and addresses
541  * must be in either numeric or symbolic form. An optional rule number
542  * is used to control the order in which rules are searched.  If two
543  * rules have the same number, then search order cannot be guaranteed,
544  * and the rules should be disjoint.  If no rule number is specified,
545  * then 0 is used, and group 0 rules are always checked before any
546  * others.
547  */
548 	int i, n, len, ret;
549 	int cmd_len;
550 	int token_count;
551 	int state;
552 	char *token;
553 	char buffer[256];
554 	char str_port[sizeof(buffer)];
555 	char str_server_port[sizeof(buffer)];
556 	char *res = buffer;
557 
558 	int rule_index;
559 	int proto;
560 	int proxy_type;
561 	int proxy_port;
562 	int server_port;
563 	struct in_addr server_addr;
564 	struct in_addr src_addr, src_mask;
565 	struct in_addr dst_addr, dst_mask;
566 	struct proxy_entry *proxy_entry;
567 
568 	LIBALIAS_LOCK(la);
569 	ret = 0;
570 /* Copy command line into a buffer */
571 	cmd += strspn(cmd, " \t");
572 	cmd_len = strlen(cmd);
573 	if (cmd_len > (int)(sizeof(buffer) - 1)) {
574 		ret = -1;
575 		goto getout;
576 	}
577 	strcpy(buffer, cmd);
578 
579 /* Convert to lower case */
580 	len = strlen(buffer);
581 	for (i = 0; i < len; i++)
582 		buffer[i] = tolower((unsigned char)buffer[i]);
583 
584 /* Set default proxy type */
585 
586 /* Set up default values */
587 	rule_index = 0;
588 	proxy_type = PROXY_TYPE_ENCODE_NONE;
589 	proto = IPPROTO_TCP;
590 	proxy_port = 0;
591 	server_addr.s_addr = 0;
592 	server_port = 0;
593 	src_addr.s_addr = 0;
594 	IpMask(0, &src_mask);
595 	dst_addr.s_addr = 0;
596 	IpMask(0, &dst_mask);
597 
598 	str_port[0] = 0;
599 	str_server_port[0] = 0;
600 
601 /* Parse command string with state machine */
602 #define STATE_READ_KEYWORD    0
603 #define STATE_READ_TYPE       1
604 #define STATE_READ_PORT       2
605 #define STATE_READ_SERVER     3
606 #define STATE_READ_RULE       4
607 #define STATE_READ_DELETE     5
608 #define STATE_READ_PROTO      6
609 #define STATE_READ_SRC        7
610 #define STATE_READ_DST        8
611 	state = STATE_READ_KEYWORD;
612 	token = strsep(&res, " \t");
613 	token_count = 0;
614 	while (token != NULL) {
615 		token_count++;
616 		switch (state) {
617 		case STATE_READ_KEYWORD:
618 			if (strcmp(token, "type") == 0)
619 				state = STATE_READ_TYPE;
620 			else if (strcmp(token, "port") == 0)
621 				state = STATE_READ_PORT;
622 			else if (strcmp(token, "server") == 0)
623 				state = STATE_READ_SERVER;
624 			else if (strcmp(token, "rule") == 0)
625 				state = STATE_READ_RULE;
626 			else if (strcmp(token, "delete") == 0)
627 				state = STATE_READ_DELETE;
628 			else if (strcmp(token, "proto") == 0)
629 				state = STATE_READ_PROTO;
630 			else if (strcmp(token, "src") == 0)
631 				state = STATE_READ_SRC;
632 			else if (strcmp(token, "dst") == 0)
633 				state = STATE_READ_DST;
634 			else {
635 				ret = -1;
636 				goto getout;
637 			}
638 			break;
639 
640 		case STATE_READ_TYPE:
641 			if (strcmp(token, "encode_ip_hdr") == 0)
642 				proxy_type = PROXY_TYPE_ENCODE_IPHDR;
643 			else if (strcmp(token, "encode_tcp_stream") == 0)
644 				proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
645 			else if (strcmp(token, "no_encode") == 0)
646 				proxy_type = PROXY_TYPE_ENCODE_NONE;
647 			else {
648 				ret = -1;
649 				goto getout;
650 			}
651 			state = STATE_READ_KEYWORD;
652 			break;
653 
654 		case STATE_READ_PORT:
655 			strcpy(str_port, token);
656 			state = STATE_READ_KEYWORD;
657 			break;
658 
659 		case STATE_READ_SERVER:
660 			{
661 				int err;
662 				char *p;
663 				char s[sizeof(buffer)];
664 
665 				p = token;
666 				while (*p != ':' && *p != 0)
667 					p++;
668 
669 				if (*p != ':') {
670 					err = IpAddr(token, &server_addr);
671 					if (err) {
672 						ret = -1;
673 						goto getout;
674 					}
675 				} else {
676 					*p = ' ';
677 
678 					n = sscanf(token, "%s %s", s, str_server_port);
679 					if (n != 2) {
680 						ret = -1;
681 						goto getout;
682 					}
683 
684 					err = IpAddr(s, &server_addr);
685 					if (err) {
686 						ret = -1;
687 						goto getout;
688 					}
689 				}
690 			}
691 			state = STATE_READ_KEYWORD;
692 			break;
693 
694 		case STATE_READ_RULE:
695 			n = sscanf(token, "%d", &rule_index);
696 			if (n != 1 || rule_index < 0) {
697 				ret = -1;
698 				goto getout;
699 			}
700 			state = STATE_READ_KEYWORD;
701 			break;
702 
703 		case STATE_READ_DELETE:
704 			{
705 				int err;
706 				int rule_to_delete;
707 
708 				if (token_count != 2) {
709 					ret = -1;
710 					goto getout;
711 				}
712 
713 				n = sscanf(token, "%d", &rule_to_delete);
714 				if (n != 1) {
715 					ret = -1;
716 					goto getout;
717 				}
718 				err = RuleNumberDelete(la, rule_to_delete);
719 				if (err)
720 					ret = -1;
721 				ret = 0;
722 				goto getout;
723 			}
724 
725 		case STATE_READ_PROTO:
726 			if (strcmp(token, "tcp") == 0)
727 				proto = IPPROTO_TCP;
728 			else if (strcmp(token, "udp") == 0)
729 				proto = IPPROTO_UDP;
730 			else {
731 				ret = -1;
732 				goto getout;
733 			}
734 			state = STATE_READ_KEYWORD;
735 			break;
736 
737 		case STATE_READ_SRC:
738 		case STATE_READ_DST:
739 			{
740 				int err;
741 				char *p;
742 				struct in_addr mask;
743 				struct in_addr addr;
744 
745 				p = token;
746 				while (*p != '/' && *p != 0)
747 					p++;
748 
749 				if (*p != '/') {
750 					IpMask(32, &mask);
751 					err = IpAddr(token, &addr);
752 					if (err) {
753 						ret = -1;
754 						goto getout;
755 					}
756 				} else {
757 					int nbits;
758 					char s[sizeof(buffer)];
759 
760 					*p = ' ';
761 					n = sscanf(token, "%s %d", s, &nbits);
762 					if (n != 2) {
763 						ret = -1;
764 						goto getout;
765 					}
766 
767 					err = IpAddr(s, &addr);
768 					if (err) {
769 						ret = -1;
770 						goto getout;
771 					}
772 
773 					err = IpMask(nbits, &mask);
774 					if (err) {
775 						ret = -1;
776 						goto getout;
777 					}
778 				}
779 
780 				if (state == STATE_READ_SRC) {
781 					src_addr = addr;
782 					src_mask = mask;
783 				} else {
784 					dst_addr = addr;
785 					dst_mask = mask;
786 				}
787 			}
788 			state = STATE_READ_KEYWORD;
789 			break;
790 
791 		default:
792 			ret = -1;
793 			goto getout;
794 			break;
795 		}
796 
797 		do {
798 			token = strsep(&res, " \t");
799 		} while (token != NULL && !*token);
800 	}
801 #undef STATE_READ_KEYWORD
802 #undef STATE_READ_TYPE
803 #undef STATE_READ_PORT
804 #undef STATE_READ_SERVER
805 #undef STATE_READ_RULE
806 #undef STATE_READ_DELETE
807 #undef STATE_READ_PROTO
808 #undef STATE_READ_SRC
809 #undef STATE_READ_DST
810 
811 /* Convert port strings to numbers.  This needs to be done after
812    the string is parsed, because the prototype might not be designated
813    before the ports (which might be symbolic entries in /etc/services) */
814 
815 	if (strlen(str_port) != 0) {
816 		int err;
817 
818 		err = IpPort(str_port, proto, &proxy_port);
819 		if (err) {
820 			ret = -1;
821 			goto getout;
822 		}
823 	} else {
824 		proxy_port = 0;
825 	}
826 
827 	if (strlen(str_server_port) != 0) {
828 		int err;
829 
830 		err = IpPort(str_server_port, proto, &server_port);
831 		if (err) {
832 			ret = -1;
833 			goto getout;
834 		}
835 	} else {
836 		server_port = 0;
837 	}
838 
839 /* Check that at least the server address has been defined */
840 	if (server_addr.s_addr == 0) {
841 		ret = -1;
842 		goto getout;
843 	}
844 
845 /* Add to linked list */
846 	proxy_entry = malloc(sizeof(struct proxy_entry));
847 	if (proxy_entry == NULL) {
848 		ret = -1;
849 		goto getout;
850 	}
851 
852 	proxy_entry->proxy_type = proxy_type;
853 	proxy_entry->rule_index = rule_index;
854 	proxy_entry->proto = proto;
855 	proxy_entry->proxy_port = htons(proxy_port);
856 	proxy_entry->server_port = htons(server_port);
857 	proxy_entry->server_addr = server_addr;
858 	proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
859 	proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
860 	proxy_entry->src_mask = src_mask;
861 	proxy_entry->dst_mask = dst_mask;
862 
863 	RuleAdd(la, proxy_entry);
864 
865 getout:
866 	LIBALIAS_UNLOCK(la);
867 	return (ret);
868 }
869