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 */
92struct 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
137static int	IpMask(int, struct in_addr *);
138static int	IpAddr(char *, struct in_addr *);
139static int	IpPort(char *, int, int *);
140static void	RuleAdd(struct libalias *la, struct proxy_entry *);
141static void	RuleDelete(struct proxy_entry *);
142static int	RuleNumberDelete(struct libalias *la, int);
143static void	ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
144static void	ProxyEncodeIpHeader(struct ip *, int);
145
146static int
147IpMask(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
163static int
164IpAddr(char *s, struct in_addr *addr)
165{
166	if (inet_aton(s, addr) == 0)
167		return (-1);
168	else
169		return (0);
170}
171
172static int
173IpPort(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
201void
202RuleAdd(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
245static void
246RuleDelete(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
263static int
264RuleNumberDelete(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
286static void
287ProxyEncodeTcpStream(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 modfied 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
377static void
378ProxyEncodeIpHeader(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
456int
457ProxyCheck(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
493void
494ProxyModify(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 */
538int
539LibAliasProxyRule(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
859getout:
860	LIBALIAS_UNLOCK(la);
861	return (ret);
862}
863