xref: /freebsd/sys/netinet/libalias/alias_proxy.c (revision d37ea99837e6ad50837fd9fe1771ddf1c3ba6002)
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 *link,
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 *)((char *)pip + (pip->ip_hl << 2));
287 
288 /* Don't modify if once already modified */
289 
290 	if (GetAckModified(link))
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(link)), (u_int) ntohs(GetProxyPort(link)));
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 ((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(link);
339 		delta = GetDeltaSeqOut(pip, link);
340 		AddSeq(pip, link, 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 /* Check to see that there is room to add an IP option */
376 	if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
377 		return;
378 
379 /* Build option and copy into packet */
380 	{
381 		u_char *ptr;
382 		struct tcphdr *tc;
383 
384 		ptr = (u_char *) pip;
385 		ptr += 20;
386 		memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
387 
388 		option[0] = 0x64;	/* class: 3 (reserved), option 4 */
389 		option[1] = OPTION_LEN_BYTES;
390 
391 		memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
392 
393 		tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2));
394 		memcpy(&option[6], (u_char *) & tc->th_sport, 2);
395 
396 		memcpy(ptr, option, 8);
397 	}
398 
399 /* Update checksum, header length and packet length */
400 	{
401 		int i;
402 		int accumulate;
403 		u_short *sptr;
404 
405 		sptr = (u_short *) option;
406 		accumulate = 0;
407 		for (i = 0; i < OPTION_LEN_INT16; i++)
408 			accumulate -= *(sptr++);
409 
410 		sptr = (u_short *) pip;
411 		accumulate += *sptr;
412 		pip->ip_hl += OPTION_LEN_INT32;
413 		accumulate -= *sptr;
414 
415 		accumulate += pip->ip_len;
416 		pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
417 		accumulate -= pip->ip_len;
418 
419 		ADJUST_CHECKSUM(accumulate, pip->ip_sum);
420 	}
421 #undef OPTION_LEN_BYTES
422 #undef OPTION_LEN_INT16
423 #undef OPTION_LEN_INT32
424 #ifdef DEBUG
425 	fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
426 	fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
427 #endif
428 }
429 
430 
431 /* Functions by other packet alias source files
432 
433     ProxyCheck()         -- Checks whether an outgoing packet should
434                             be proxied.
435     ProxyModify()        -- Encodes the original destination address/port
436                             for a packet which is to be redirected to
437                             a proxy server.
438 */
439 
440 int
441 ProxyCheck(struct libalias *la, struct ip *pip,
442     struct in_addr *proxy_server_addr,
443     u_short * proxy_server_port)
444 {
445 	u_short dst_port;
446 	struct in_addr src_addr;
447 	struct in_addr dst_addr;
448 	struct proxy_entry *ptr;
449 
450 	src_addr = pip->ip_src;
451 	dst_addr = pip->ip_dst;
452 	dst_port = ((struct tcphdr *)((char *)pip + (pip->ip_hl << 2)))
453 	    ->th_dport;
454 
455 	ptr = la->proxyList;
456 	while (ptr != NULL) {
457 		u_short proxy_port;
458 
459 		proxy_port = ptr->proxy_port;
460 		if ((dst_port == proxy_port || proxy_port == 0)
461 		    && pip->ip_p == ptr->proto
462 		    && src_addr.s_addr != ptr->server_addr.s_addr) {
463 			struct in_addr src_addr_masked;
464 			struct in_addr dst_addr_masked;
465 
466 			src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
467 			dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
468 
469 			if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
470 			    && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
471 				if ((*proxy_server_port = ptr->server_port) == 0)
472 					*proxy_server_port = dst_port;
473 				*proxy_server_addr = ptr->server_addr;
474 				return ptr->proxy_type;
475 			}
476 		}
477 		ptr = ptr->next;
478 	}
479 
480 	return 0;
481 }
482 
483 void
484 ProxyModify(struct libalias *la, struct alias_link *link,
485     struct ip *pip,
486     int maxpacketsize,
487     int proxy_type)
488 {
489 	switch (proxy_type) {
490 		case PROXY_TYPE_ENCODE_IPHDR:
491 		ProxyEncodeIpHeader(pip, maxpacketsize);
492 		break;
493 
494 	case PROXY_TYPE_ENCODE_TCPSTREAM:
495 		ProxyEncodeTcpStream(link, pip, maxpacketsize);
496 		break;
497 	}
498 }
499 
500 
501 /*
502     Public API functions
503 */
504 
505 int
506 LibAliasProxyRule(struct libalias *la, const char *cmd)
507 {
508 /*
509  * This function takes command strings of the form:
510  *
511  *   server <addr>[:<port>]
512  *   [port <port>]
513  *   [rule n]
514  *   [proto tcp|udp]
515  *   [src <addr>[/n]]
516  *   [dst <addr>[/n]]
517  *   [type encode_tcp_stream|encode_ip_hdr|no_encode]
518  *
519  *   delete <rule number>
520  *
521  * Subfields can be in arbitrary order.  Port numbers and addresses
522  * must be in either numeric or symbolic form. An optional rule number
523  * is used to control the order in which rules are searched.  If two
524  * rules have the same number, then search order cannot be guaranteed,
525  * and the rules should be disjoint.  If no rule number is specified,
526  * then 0 is used, and group 0 rules are always checked before any
527  * others.
528  */
529 	int i, n, len;
530 	int cmd_len;
531 	int token_count;
532 	int state;
533 	char *token;
534 	char buffer[256];
535 	char str_port[sizeof(buffer)];
536 	char str_server_port[sizeof(buffer)];
537 	char *res = buffer;
538 
539 	int rule_index;
540 	int proto;
541 	int proxy_type;
542 	int proxy_port;
543 	int server_port;
544 	struct in_addr server_addr;
545 	struct in_addr src_addr, src_mask;
546 	struct in_addr dst_addr, dst_mask;
547 	struct proxy_entry *proxy_entry;
548 
549 /* Copy command line into a buffer */
550 	cmd += strspn(cmd, " \t");
551 	cmd_len = strlen(cmd);
552 	if (cmd_len > (sizeof(buffer) - 1))
553 		return -1;
554 	strcpy(buffer, cmd);
555 
556 /* Convert to lower case */
557 	len = strlen(buffer);
558 	for (i = 0; i < len; i++)
559 		buffer[i] = tolower((unsigned char)buffer[i]);
560 
561 /* Set default proxy type */
562 
563 /* Set up default values */
564 	rule_index = 0;
565 	proxy_type = PROXY_TYPE_ENCODE_NONE;
566 	proto = IPPROTO_TCP;
567 	proxy_port = 0;
568 	server_addr.s_addr = 0;
569 	server_port = 0;
570 	src_addr.s_addr = 0;
571 	IpMask(0, &src_mask);
572 	dst_addr.s_addr = 0;
573 	IpMask(0, &dst_mask);
574 
575 	str_port[0] = 0;
576 	str_server_port[0] = 0;
577 
578 /* Parse command string with state machine */
579 #define STATE_READ_KEYWORD    0
580 #define STATE_READ_TYPE       1
581 #define STATE_READ_PORT       2
582 #define STATE_READ_SERVER     3
583 #define STATE_READ_RULE       4
584 #define STATE_READ_DELETE     5
585 #define STATE_READ_PROTO      6
586 #define STATE_READ_SRC        7
587 #define STATE_READ_DST        8
588 	state = STATE_READ_KEYWORD;
589 	token = strsep(&res, " \t");
590 	token_count = 0;
591 	while (token != NULL) {
592 		token_count++;
593 		switch (state) {
594 		case STATE_READ_KEYWORD:
595 			if (strcmp(token, "type") == 0)
596 				state = STATE_READ_TYPE;
597 			else if (strcmp(token, "port") == 0)
598 				state = STATE_READ_PORT;
599 			else if (strcmp(token, "server") == 0)
600 				state = STATE_READ_SERVER;
601 			else if (strcmp(token, "rule") == 0)
602 				state = STATE_READ_RULE;
603 			else if (strcmp(token, "delete") == 0)
604 				state = STATE_READ_DELETE;
605 			else if (strcmp(token, "proto") == 0)
606 				state = STATE_READ_PROTO;
607 			else if (strcmp(token, "src") == 0)
608 				state = STATE_READ_SRC;
609 			else if (strcmp(token, "dst") == 0)
610 				state = STATE_READ_DST;
611 			else
612 				return -1;
613 			break;
614 
615 		case STATE_READ_TYPE:
616 			if (strcmp(token, "encode_ip_hdr") == 0)
617 				proxy_type = PROXY_TYPE_ENCODE_IPHDR;
618 			else if (strcmp(token, "encode_tcp_stream") == 0)
619 				proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
620 			else if (strcmp(token, "no_encode") == 0)
621 				proxy_type = PROXY_TYPE_ENCODE_NONE;
622 			else
623 				return -1;
624 			state = STATE_READ_KEYWORD;
625 			break;
626 
627 		case STATE_READ_PORT:
628 			strcpy(str_port, token);
629 			state = STATE_READ_KEYWORD;
630 			break;
631 
632 		case STATE_READ_SERVER:
633 			{
634 				int err;
635 				char *p;
636 				char s[sizeof(buffer)];
637 
638 				p = token;
639 				while (*p != ':' && *p != 0)
640 					p++;
641 
642 				if (*p != ':') {
643 					err = IpAddr(token, &server_addr);
644 					if (err)
645 						return -1;
646 				} else {
647 					*p = ' ';
648 
649 					n = sscanf(token, "%s %s", s, str_server_port);
650 					if (n != 2)
651 						return -1;
652 
653 					err = IpAddr(s, &server_addr);
654 					if (err)
655 						return -1;
656 				}
657 			}
658 			state = STATE_READ_KEYWORD;
659 			break;
660 
661 		case STATE_READ_RULE:
662 			n = sscanf(token, "%d", &rule_index);
663 			if (n != 1 || rule_index < 0)
664 				return -1;
665 			state = STATE_READ_KEYWORD;
666 			break;
667 
668 		case STATE_READ_DELETE:
669 			{
670 				int err;
671 				int rule_to_delete;
672 
673 				if (token_count != 2)
674 					return -1;
675 
676 				n = sscanf(token, "%d", &rule_to_delete);
677 				if (n != 1)
678 					return -1;
679 				err = RuleNumberDelete(la, rule_to_delete);
680 				if (err)
681 					return -1;
682 				return 0;
683 			}
684 
685 		case STATE_READ_PROTO:
686 			if (strcmp(token, "tcp") == 0)
687 				proto = IPPROTO_TCP;
688 			else if (strcmp(token, "udp") == 0)
689 				proto = IPPROTO_UDP;
690 			else
691 				return -1;
692 			state = STATE_READ_KEYWORD;
693 			break;
694 
695 		case STATE_READ_SRC:
696 		case STATE_READ_DST:
697 			{
698 				int err;
699 				char *p;
700 				struct in_addr mask;
701 				struct in_addr addr;
702 
703 				p = token;
704 				while (*p != '/' && *p != 0)
705 					p++;
706 
707 				if (*p != '/') {
708 					IpMask(32, &mask);
709 					err = IpAddr(token, &addr);
710 					if (err)
711 						return -1;
712 				} else {
713 					int nbits;
714 					char s[sizeof(buffer)];
715 
716 					*p = ' ';
717 					n = sscanf(token, "%s %d", s, &nbits);
718 					if (n != 2)
719 						return -1;
720 
721 					err = IpAddr(s, &addr);
722 					if (err)
723 						return -1;
724 
725 					err = IpMask(nbits, &mask);
726 					if (err)
727 						return -1;
728 				}
729 
730 				if (state == STATE_READ_SRC) {
731 					src_addr = addr;
732 					src_mask = mask;
733 				} else {
734 					dst_addr = addr;
735 					dst_mask = mask;
736 				}
737 			}
738 			state = STATE_READ_KEYWORD;
739 			break;
740 
741 		default:
742 			return -1;
743 			break;
744 		}
745 
746 		do {
747 			token = strsep(&res, " \t");
748 		} while (token != NULL && !*token);
749 	}
750 #undef STATE_READ_KEYWORD
751 #undef STATE_READ_TYPE
752 #undef STATE_READ_PORT
753 #undef STATE_READ_SERVER
754 #undef STATE_READ_RULE
755 #undef STATE_READ_DELETE
756 #undef STATE_READ_PROTO
757 #undef STATE_READ_SRC
758 #undef STATE_READ_DST
759 
760 /* Convert port strings to numbers.  This needs to be done after
761    the string is parsed, because the prototype might not be designated
762    before the ports (which might be symbolic entries in /etc/services) */
763 
764 	if (strlen(str_port) != 0) {
765 		int err;
766 
767 		err = IpPort(str_port, proto, &proxy_port);
768 		if (err)
769 			return -1;
770 	} else {
771 		proxy_port = 0;
772 	}
773 
774 	if (strlen(str_server_port) != 0) {
775 		int err;
776 
777 		err = IpPort(str_server_port, proto, &server_port);
778 		if (err)
779 			return -1;
780 	} else {
781 		server_port = 0;
782 	}
783 
784 /* Check that at least the server address has been defined */
785 	if (server_addr.s_addr == 0)
786 		return -1;
787 
788 /* Add to linked list */
789 	proxy_entry = malloc(sizeof(struct proxy_entry));
790 	if (proxy_entry == NULL)
791 		return -1;
792 
793 	proxy_entry->proxy_type = proxy_type;
794 	proxy_entry->rule_index = rule_index;
795 	proxy_entry->proto = proto;
796 	proxy_entry->proxy_port = htons(proxy_port);
797 	proxy_entry->server_port = htons(server_port);
798 	proxy_entry->server_addr = server_addr;
799 	proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
800 	proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
801 	proxy_entry->src_mask = src_mask;
802 	proxy_entry->dst_mask = dst_mask;
803 
804 	RuleAdd(la, proxy_entry);
805 
806 	return 0;
807 }
808