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