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 */
91struct 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
140static int	IpMask(int, struct in_addr *);
141static int	IpAddr(char *, struct in_addr *);
142static int	IpPort(char *, int, int *);
143static void	RuleAdd(struct libalias *la, struct proxy_entry *);
144static void	RuleDelete(struct proxy_entry *);
145static int	RuleNumberDelete(struct libalias *la, int);
146static void	ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
147static void	ProxyEncodeIpHeader(struct ip *, int);
148
149static int
150IpMask(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
166static int
167IpAddr(char *s, struct in_addr *addr)
168{
169	if (inet_aton(s, addr) == 0)
170		return (-1);
171	else
172		return (0);
173}
174
175static int
176IpPort(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
204void
205RuleAdd(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
248static void
249RuleDelete(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
266static int
267RuleNumberDelete(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
289static void
290ProxyEncodeTcpStream(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
380static void
381ProxyEncodeIpHeader(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
461int
462ProxyCheck(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
498void
499ProxyModify(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
524int
525LibAliasProxyRule(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
865getout:
866	LIBALIAS_UNLOCK(la);
867	return (ret);
868}
869