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