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