177701Sbrian/*-
285964Sbrian * Copyright (c) 2001 Charles Mott <cm@linktel.net>
377701Sbrian * All rights reserved.
477701Sbrian *
577701Sbrian * Redistribution and use in source and binary forms, with or without
677701Sbrian * modification, are permitted provided that the following conditions
777701Sbrian * are met:
877701Sbrian * 1. Redistributions of source code must retain the above copyright
977701Sbrian *    notice, this list of conditions and the following disclaimer.
1077701Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1177701Sbrian *    notice, this list of conditions and the following disclaimer in the
1277701Sbrian *    documentation and/or other materials provided with the distribution.
1377701Sbrian *
1477701Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1577701Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1677701Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1777701Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1877701Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1977701Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2077701Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2177701Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2277701Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2377701Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2477701Sbrian * SUCH DAMAGE.
2577701Sbrian */
2677701Sbrian
2784195Sdillon#include <sys/cdefs.h>
2884195Sdillon__FBSDID("$FreeBSD: stable/11/sys/netinet/libalias/alias_proxy.c 315456 2017-03-17 14:54:10Z vangyzen $");
2984195Sdillon
3044307Sbrian/* file: alias_proxy.c
3144307Sbrian
3244307Sbrian    This file encapsulates special operations related to transparent
3344307Sbrian    proxy redirection.  This is where packets with a particular destination,
3444307Sbrian    usually tcp port 80, are redirected to a proxy server.
3544307Sbrian
3644307Sbrian    When packets are proxied, the destination address and port are
3744307Sbrian    modified.  In certain cases, it is necessary to somehow encode
3844307Sbrian    the original address/port info into the packet.  Two methods are
3944307Sbrian    presently supported: addition of a [DEST addr port] string at the
40108533Sschweikh    beginning of a tcp stream, or inclusion of an optional field
4144307Sbrian    in the IP header.
4299207Sbrian
4344307Sbrian    There is one public API function:
4444307Sbrian
45131612Sdes	PacketAliasProxyRule()    -- Adds and deletes proxy
46131612Sdes				     rules.
4744307Sbrian
4844307Sbrian    Rules are stored in a linear linked list, so lookup efficiency
4944307Sbrian    won't be too good for large lists.
5044307Sbrian
5144307Sbrian
5244307Sbrian    Initial development: April, 1998 (cjm)
5344307Sbrian*/
5444307Sbrian
5544307Sbrian
5644307Sbrian/* System includes */
57145921Sglebius#ifdef _KERNEL
58145921Sglebius#include <sys/param.h>
59145921Sglebius#include <sys/ctype.h>
60145921Sglebius#include <sys/libkern.h>
61145921Sglebius#include <sys/limits.h>
62145921Sglebius#else
63145921Sglebius#include <sys/types.h>
6444307Sbrian#include <ctype.h>
6544307Sbrian#include <stdio.h>
6644307Sbrian#include <stdlib.h>
67162674Spiso#include <netdb.h>
6844307Sbrian#include <string.h>
69145921Sglebius#endif
7044307Sbrian
7144307Sbrian#include <netinet/tcp.h>
7244307Sbrian
73145921Sglebius#ifdef _KERNEL
74145932Sglebius#include <netinet/libalias/alias.h>
75145921Sglebius#include <netinet/libalias/alias_local.h>
76162674Spiso#include <netinet/libalias/alias_mod.h>
77145921Sglebius#else
78162674Spiso#include <arpa/inet.h>
79145932Sglebius#include "alias.h"		/* Public API functions for libalias */
80127094Sdes#include "alias_local.h"	/* Functions used by alias*.c */
81145921Sglebius#endif
8244307Sbrian
8344307Sbrian/*
8444307Sbrian    Data structures
8544307Sbrian */
8644307Sbrian
8744307Sbrian/*
8844307Sbrian * A linked list of arbitrary length, based on struct proxy_entry is
8944307Sbrian * used to store proxy rules.
9044307Sbrian */
91127094Sdesstruct proxy_entry {
92127094Sdes	struct libalias *la;
9344307Sbrian#define PROXY_TYPE_ENCODE_NONE      1
9444307Sbrian#define PROXY_TYPE_ENCODE_TCPSTREAM 2
9544307Sbrian#define PROXY_TYPE_ENCODE_IPHDR     3
96127094Sdes	int		rule_index;
97127094Sdes	int		proxy_type;
98127094Sdes	u_char		proto;
99127094Sdes	u_short		proxy_port;
100127094Sdes	u_short		server_port;
10144307Sbrian
102127094Sdes	struct in_addr	server_addr;
10344307Sbrian
104127094Sdes	struct in_addr	src_addr;
105127094Sdes	struct in_addr	src_mask;
10644307Sbrian
107127094Sdes	struct in_addr	dst_addr;
108127094Sdes	struct in_addr	dst_mask;
10944307Sbrian
110127094Sdes	struct proxy_entry *next;
111127094Sdes	struct proxy_entry *last;
11244307Sbrian};
11344307Sbrian
11444307Sbrian
11544307Sbrian
11644307Sbrian/*
11744307Sbrian    File scope variables
11844307Sbrian*/
11944307Sbrian
12044307Sbrian
12144307Sbrian
12244307Sbrian/* Local (static) functions:
12344307Sbrian
12444307Sbrian    IpMask()                 -- Utility function for creating IP
125131612Sdes				masks from integer (1-32) specification.
12644307Sbrian    IpAddr()                 -- Utility function for converting string
127131612Sdes				to IP address
12844307Sbrian    IpPort()                 -- Utility function for converting string
129131612Sdes				to port number
13044307Sbrian    RuleAdd()                -- Adds an element to the rule list.
13144307Sbrian    RuleDelete()             -- Removes an element from the rule list.
13244307Sbrian    RuleNumberDelete()       -- Removes all elements from the rule list
133131612Sdes				having a certain rule number.
13444307Sbrian    ProxyEncodeTcpStream()   -- Adds [DEST x.x.x.x xxxx] to the beginning
135131612Sdes				of a TCP stream.
13644307Sbrian    ProxyEncodeIpHeader()    -- Adds an IP option indicating the true
137131612Sdes				destination of a proxied IP packet
13844307Sbrian*/
13944307Sbrian
140127094Sdesstatic int	IpMask(int, struct in_addr *);
141127094Sdesstatic int	IpAddr(char *, struct in_addr *);
142127094Sdesstatic int	IpPort(char *, int, int *);
143127094Sdesstatic void	RuleAdd(struct libalias *la, struct proxy_entry *);
144127094Sdesstatic void	RuleDelete(struct proxy_entry *);
145127094Sdesstatic int	RuleNumberDelete(struct libalias *la, int);
146127094Sdesstatic void	ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
147127094Sdesstatic void	ProxyEncodeIpHeader(struct ip *, int);
14844307Sbrian
14944307Sbrianstatic int
15044307SbrianIpMask(int nbits, struct in_addr *mask)
15144307Sbrian{
152127094Sdes	int i;
153127094Sdes	u_int imask;
15444307Sbrian
155127094Sdes	if (nbits < 0 || nbits > 32)
156131613Sdes		return (-1);
15744307Sbrian
158127094Sdes	imask = 0;
159127094Sdes	for (i = 0; i < nbits; i++)
160127094Sdes		imask = (imask >> 1) + 0x80000000;
161127094Sdes	mask->s_addr = htonl(imask);
16244307Sbrian
163131613Sdes	return (0);
16444307Sbrian}
16544307Sbrian
16644307Sbrianstatic int
16744307SbrianIpAddr(char *s, struct in_addr *addr)
16844307Sbrian{
169127094Sdes	if (inet_aton(s, addr) == 0)
170131613Sdes		return (-1);
171127094Sdes	else
172131613Sdes		return (0);
17344307Sbrian}
17444307Sbrian
17544307Sbrianstatic int
17644307SbrianIpPort(char *s, int proto, int *port)
17744307Sbrian{
178127094Sdes	int n;
17944307Sbrian
180127094Sdes	n = sscanf(s, "%d", port);
181145933Sglebius	if (n != 1)
182145933Sglebius#ifndef _KERNEL	/* XXX: we accept only numeric ports in kernel */
183145933Sglebius	{
184127094Sdes		struct servent *se;
18544307Sbrian
186127094Sdes		if (proto == IPPROTO_TCP)
187127094Sdes			se = getservbyname(s, "tcp");
188127094Sdes		else if (proto == IPPROTO_UDP)
189127094Sdes			se = getservbyname(s, "udp");
190127094Sdes		else
191131613Sdes			return (-1);
19244307Sbrian
193127094Sdes		if (se == NULL)
194131613Sdes			return (-1);
19544307Sbrian
196127094Sdes		*port = (u_int) ntohs(se->s_port);
197127094Sdes	}
198145933Sglebius#else
199145933Sglebius		return (-1);
200145933Sglebius#endif
201131613Sdes	return (0);
20244307Sbrian}
20344307Sbrian
20444307Sbrianvoid
205124621SphkRuleAdd(struct libalias *la, struct proxy_entry *entry)
20644307Sbrian{
207127094Sdes	int rule_index;
208127094Sdes	struct proxy_entry *ptr;
209127094Sdes	struct proxy_entry *ptr_last;
21044307Sbrian
211165243Spiso	LIBALIAS_LOCK_ASSERT(la);
212165243Spiso
213241648Semaste	entry->la = la;
214127094Sdes	if (la->proxyList == NULL) {
215127094Sdes		la->proxyList = entry;
216127094Sdes		entry->last = NULL;
217127094Sdes		entry->next = NULL;
218127094Sdes		return;
219127094Sdes	}
22044307Sbrian
221127094Sdes	rule_index = entry->rule_index;
222127094Sdes	ptr = la->proxyList;
223127094Sdes	ptr_last = NULL;
224127094Sdes	while (ptr != NULL) {
225127094Sdes		if (ptr->rule_index >= rule_index) {
226127094Sdes			if (ptr_last == NULL) {
227127094Sdes				entry->next = la->proxyList;
228127094Sdes				entry->last = NULL;
229127094Sdes				la->proxyList->last = entry;
230127094Sdes				la->proxyList = entry;
231127094Sdes				return;
232127094Sdes			}
233127094Sdes			ptr_last->next = entry;
234127094Sdes			ptr->last = entry;
235127094Sdes			entry->last = ptr->last;
236127094Sdes			entry->next = ptr;
237127094Sdes			return;
238127094Sdes		}
239127094Sdes		ptr_last = ptr;
240127094Sdes		ptr = ptr->next;
241127094Sdes	}
24244307Sbrian
243127094Sdes	ptr_last->next = entry;
244127094Sdes	entry->last = ptr_last;
245127094Sdes	entry->next = NULL;
24644307Sbrian}
24744307Sbrian
24844307Sbrianstatic void
24944307SbrianRuleDelete(struct proxy_entry *entry)
25044307Sbrian{
251127094Sdes	struct libalias *la;
25244307Sbrian
253127094Sdes	la = entry->la;
254165243Spiso	LIBALIAS_LOCK_ASSERT(la);
255127094Sdes	if (entry->last != NULL)
256127094Sdes		entry->last->next = entry->next;
257127094Sdes	else
258127094Sdes		la->proxyList = entry->next;
25944307Sbrian
260127094Sdes	if (entry->next != NULL)
261127094Sdes		entry->next->last = entry->last;
262127094Sdes
263127094Sdes	free(entry);
26444307Sbrian}
26544307Sbrian
26644307Sbrianstatic int
267124621SphkRuleNumberDelete(struct libalias *la, int rule_index)
26844307Sbrian{
269127094Sdes	int err;
270127094Sdes	struct proxy_entry *ptr;
27144307Sbrian
272165243Spiso	LIBALIAS_LOCK_ASSERT(la);
273127094Sdes	err = -1;
274127094Sdes	ptr = la->proxyList;
275127094Sdes	while (ptr != NULL) {
276127094Sdes		struct proxy_entry *ptr_next;
27744307Sbrian
278127094Sdes		ptr_next = ptr->next;
279127094Sdes		if (ptr->rule_index == rule_index) {
280127094Sdes			err = 0;
281127094Sdes			RuleDelete(ptr);
282127094Sdes		}
283127094Sdes		ptr = ptr_next;
284127094Sdes	}
28544307Sbrian
286131613Sdes	return (err);
28744307Sbrian}
28844307Sbrian
28944307Sbrianstatic void
290131614SdesProxyEncodeTcpStream(struct alias_link *lnk,
291127094Sdes    struct ip *pip,
292127094Sdes    int maxpacketsize)
29344307Sbrian{
294127094Sdes	int slen;
295127094Sdes	char buffer[40];
296127094Sdes	struct tcphdr *tc;
297315456Svangyzen	char addrbuf[INET_ADDRSTRLEN];
29844307Sbrian
29944307Sbrian/* Compute pointer to tcp header */
300131699Sdes	tc = (struct tcphdr *)ip_next(pip);
30144307Sbrian
30244307Sbrian/* Don't modify if once already modified */
30344307Sbrian
304131614Sdes	if (GetAckModified(lnk))
305127094Sdes		return;
30644307Sbrian
30744307Sbrian/* Translate destination address and port to string form */
308127094Sdes	snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
309315456Svangyzen	    inet_ntoa_r(GetProxyAddress(lnk), INET_NTOA_BUF(addrbuf)),
310315456Svangyzen	    (u_int) ntohs(GetProxyPort(lnk)));
31199207Sbrian
31244307Sbrian/* Pad string out to a multiple of two in length */
313127094Sdes	slen = strlen(buffer);
314127094Sdes	switch (slen % 2) {
315127094Sdes	case 0:
316127094Sdes		strcat(buffer, " \n");
317127094Sdes		slen += 2;
318127094Sdes		break;
319127094Sdes	case 1:
320127094Sdes		strcat(buffer, "\n");
321127094Sdes		slen += 1;
322127094Sdes	}
32344307Sbrian
32444307Sbrian/* Check for packet overflow */
325131614Sdes	if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
326127094Sdes		return;
32744307Sbrian
32844307Sbrian/* Shift existing TCP data and insert destination string */
329127094Sdes	{
330127094Sdes		int dlen;
331127094Sdes		int hlen;
332168342Skan		char *p;
33344307Sbrian
334127094Sdes		hlen = (pip->ip_hl + tc->th_off) << 2;
335127094Sdes		dlen = ntohs(pip->ip_len) - hlen;
33644307Sbrian
33744307Sbrian/* Modify first packet that has data in it */
33844307Sbrian
339127094Sdes		if (dlen == 0)
340127094Sdes			return;
34144307Sbrian
342127094Sdes		p = (char *)pip;
343127094Sdes		p += hlen;
34444307Sbrian
345145930Sglebius		bcopy(p, p + slen, dlen);
346127094Sdes		memcpy(p, buffer, slen);
347127094Sdes	}
34844307Sbrian
34944307Sbrian/* Save information about modfied sequence number */
350127094Sdes	{
351127094Sdes		int delta;
35244307Sbrian
353131614Sdes		SetAckModified(lnk);
354176884Spiso		tc = (struct tcphdr *)ip_next(pip);
355176884Spiso		delta = GetDeltaSeqOut(tc->th_seq, lnk);
356176884Spiso		AddSeq(lnk, delta + slen, pip->ip_hl, pip->ip_len, tc->th_seq,
357176884Spiso		    tc->th_off);
358127094Sdes	}
35944307Sbrian
36044307Sbrian/* Update IP header packet length and checksum */
361127094Sdes	{
362127094Sdes		int accumulate;
36344307Sbrian
364127094Sdes		accumulate = pip->ip_len;
365127094Sdes		pip->ip_len = htons(ntohs(pip->ip_len) + slen);
366127094Sdes		accumulate -= pip->ip_len;
36744307Sbrian
368127094Sdes		ADJUST_CHECKSUM(accumulate, pip->ip_sum);
369127094Sdes	}
37044307Sbrian
37144307Sbrian/* Update TCP checksum, Use TcpChecksum since so many things have
37244307Sbrian   already changed. */
37344307Sbrian
374127094Sdes	tc->th_sum = 0;
375147623Sglebius#ifdef _KERNEL
376147623Sglebius	tc->th_x2 = 1;
377147623Sglebius#else
378127094Sdes	tc->th_sum = TcpChecksum(pip);
379147623Sglebius#endif
38044307Sbrian}
38144307Sbrian
38244307Sbrianstatic void
38344307SbrianProxyEncodeIpHeader(struct ip *pip,
384127094Sdes    int maxpacketsize)
38544307Sbrian{
38644307Sbrian#define OPTION_LEN_BYTES  8
38744307Sbrian#define OPTION_LEN_INT16  4
38844307Sbrian#define OPTION_LEN_INT32  2
389127094Sdes	u_char option[OPTION_LEN_BYTES];
39044307Sbrian
391145961Sglebius#ifdef LIBALIAS_DEBUG
392127094Sdes	fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
393127094Sdes	fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
39444616Sbrian#endif
39544307Sbrian
396131614Sdes	(void)maxpacketsize;
397131614Sdes
39844307Sbrian/* Check to see that there is room to add an IP option */
399127094Sdes	if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
400127094Sdes		return;
40144307Sbrian
40244307Sbrian/* Build option and copy into packet */
403127094Sdes	{
404127094Sdes		u_char *ptr;
405127094Sdes		struct tcphdr *tc;
40644307Sbrian
407127094Sdes		ptr = (u_char *) pip;
408127094Sdes		ptr += 20;
409127094Sdes		memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
41044307Sbrian
411127094Sdes		option[0] = 0x64;	/* class: 3 (reserved), option 4 */
412127094Sdes		option[1] = OPTION_LEN_BYTES;
41344307Sbrian
414127094Sdes		memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
41544307Sbrian
416131699Sdes		tc = (struct tcphdr *)ip_next(pip);
417127094Sdes		memcpy(&option[6], (u_char *) & tc->th_sport, 2);
41844307Sbrian
419127094Sdes		memcpy(ptr, option, 8);
420127094Sdes	}
42144307Sbrian
42244307Sbrian/* Update checksum, header length and packet length */
423127094Sdes	{
424127094Sdes		int i;
425127094Sdes		int accumulate;
426127094Sdes		u_short *sptr;
42744307Sbrian
428127094Sdes		sptr = (u_short *) option;
429127094Sdes		accumulate = 0;
430127094Sdes		for (i = 0; i < OPTION_LEN_INT16; i++)
431127094Sdes			accumulate -= *(sptr++);
43244307Sbrian
433127094Sdes		sptr = (u_short *) pip;
434127094Sdes		accumulate += *sptr;
435127094Sdes		pip->ip_hl += OPTION_LEN_INT32;
436127094Sdes		accumulate -= *sptr;
43744307Sbrian
438127094Sdes		accumulate += pip->ip_len;
439127094Sdes		pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
440127094Sdes		accumulate -= pip->ip_len;
44144307Sbrian
442127094Sdes		ADJUST_CHECKSUM(accumulate, pip->ip_sum);
443127094Sdes	}
44444307Sbrian#undef OPTION_LEN_BYTES
44544307Sbrian#undef OPTION_LEN_INT16
44644307Sbrian#undef OPTION_LEN_INT32
447145961Sglebius#ifdef LIBALIAS_DEBUG
448127094Sdes	fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
449127094Sdes	fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
45044616Sbrian#endif
45144307Sbrian}
45244307Sbrian
45344307Sbrian
45444307Sbrian/* Functions by other packet alias source files
45544307Sbrian
45644307Sbrian    ProxyCheck()         -- Checks whether an outgoing packet should
457131612Sdes			    be proxied.
45844307Sbrian    ProxyModify()        -- Encodes the original destination address/port
459131612Sdes			    for a packet which is to be redirected to
460131612Sdes			    a proxy server.
46144307Sbrian*/
46244307Sbrian
46344307Sbrianint
464176884SpisoProxyCheck(struct libalias *la, struct in_addr *proxy_server_addr,
465176884Spiso    u_short * proxy_server_port, struct in_addr src_addr,
466176884Spiso    struct in_addr dst_addr, u_short dst_port, u_char ip_p)
46744307Sbrian{
468127094Sdes	struct proxy_entry *ptr;
46944307Sbrian
470165243Spiso	LIBALIAS_LOCK_ASSERT(la);
47144307Sbrian
472127094Sdes	ptr = la->proxyList;
473127094Sdes	while (ptr != NULL) {
474127094Sdes		u_short proxy_port;
47544307Sbrian
476127094Sdes		proxy_port = ptr->proxy_port;
477127094Sdes		if ((dst_port == proxy_port || proxy_port == 0)
478176884Spiso		    && ip_p == ptr->proto
479127094Sdes		    && src_addr.s_addr != ptr->server_addr.s_addr) {
480127094Sdes			struct in_addr src_addr_masked;
481127094Sdes			struct in_addr dst_addr_masked;
48244307Sbrian
483127094Sdes			src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
484127094Sdes			dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
48544307Sbrian
486127094Sdes			if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
487127094Sdes			    && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
488127094Sdes				if ((*proxy_server_port = ptr->server_port) == 0)
489127094Sdes					*proxy_server_port = dst_port;
490127094Sdes				*proxy_server_addr = ptr->server_addr;
491131613Sdes				return (ptr->proxy_type);
492127094Sdes			}
493127094Sdes		}
494127094Sdes		ptr = ptr->next;
495127094Sdes	}
49644307Sbrian
497131613Sdes	return (0);
49844307Sbrian}
49944307Sbrian
50044307Sbrianvoid
501131614SdesProxyModify(struct libalias *la, struct alias_link *lnk,
502127094Sdes    struct ip *pip,
503127094Sdes    int maxpacketsize,
504127094Sdes    int proxy_type)
50544307Sbrian{
506131614Sdes
507165243Spiso	LIBALIAS_LOCK_ASSERT(la);
508131614Sdes	(void)la;
509131614Sdes
510127094Sdes	switch (proxy_type) {
511127094Sdes		case PROXY_TYPE_ENCODE_IPHDR:
512127094Sdes		ProxyEncodeIpHeader(pip, maxpacketsize);
513127094Sdes		break;
51444307Sbrian
515127094Sdes	case PROXY_TYPE_ENCODE_TCPSTREAM:
516131614Sdes		ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
517127094Sdes		break;
518127094Sdes	}
51944307Sbrian}
52044307Sbrian
52144307Sbrian
52244307Sbrian/*
52344307Sbrian    Public API functions
52444307Sbrian*/
52544307Sbrian
52644307Sbrianint
527124621SphkLibAliasProxyRule(struct libalias *la, const char *cmd)
52844307Sbrian{
52944307Sbrian/*
53044307Sbrian * This function takes command strings of the form:
53144307Sbrian *
53244307Sbrian *   server <addr>[:<port>]
53344307Sbrian *   [port <port>]
53444307Sbrian *   [rule n]
53544307Sbrian *   [proto tcp|udp]
53644307Sbrian *   [src <addr>[/n]]
53744307Sbrian *   [dst <addr>[/n]]
53844307Sbrian *   [type encode_tcp_stream|encode_ip_hdr|no_encode]
53944307Sbrian *
54044307Sbrian *   delete <rule number>
54144307Sbrian *
54244307Sbrian * Subfields can be in arbitrary order.  Port numbers and addresses
54344307Sbrian * must be in either numeric or symbolic form. An optional rule number
54444307Sbrian * is used to control the order in which rules are searched.  If two
54544307Sbrian * rules have the same number, then search order cannot be guaranteed,
54644307Sbrian * and the rules should be disjoint.  If no rule number is specified,
54744307Sbrian * then 0 is used, and group 0 rules are always checked before any
54844307Sbrian * others.
54944307Sbrian */
550165243Spiso	int i, n, len, ret;
551127094Sdes	int cmd_len;
552127094Sdes	int token_count;
553127094Sdes	int state;
554127094Sdes	char *token;
555127094Sdes	char buffer[256];
556127094Sdes	char str_port[sizeof(buffer)];
557127094Sdes	char str_server_port[sizeof(buffer)];
558127094Sdes	char *res = buffer;
55944307Sbrian
560127094Sdes	int rule_index;
561127094Sdes	int proto;
562127094Sdes	int proxy_type;
563127094Sdes	int proxy_port;
564127094Sdes	int server_port;
565127094Sdes	struct in_addr server_addr;
566127094Sdes	struct in_addr src_addr, src_mask;
567127094Sdes	struct in_addr dst_addr, dst_mask;
568127094Sdes	struct proxy_entry *proxy_entry;
56944307Sbrian
570165243Spiso	LIBALIAS_LOCK(la);
571165243Spiso	ret = 0;
57244307Sbrian/* Copy command line into a buffer */
573127094Sdes	cmd += strspn(cmd, " \t");
574127094Sdes	cmd_len = strlen(cmd);
575165243Spiso	if (cmd_len > (int)(sizeof(buffer) - 1)) {
576165243Spiso		ret = -1;
577165243Spiso		goto getout;
578165243Spiso	}
579127094Sdes	strcpy(buffer, cmd);
58044307Sbrian
58144307Sbrian/* Convert to lower case */
582127094Sdes	len = strlen(buffer);
583127094Sdes	for (i = 0; i < len; i++)
584127094Sdes		buffer[i] = tolower((unsigned char)buffer[i]);
58544307Sbrian
58644307Sbrian/* Set default proxy type */
58744307Sbrian
58844307Sbrian/* Set up default values */
589127094Sdes	rule_index = 0;
590127094Sdes	proxy_type = PROXY_TYPE_ENCODE_NONE;
591127094Sdes	proto = IPPROTO_TCP;
592127094Sdes	proxy_port = 0;
593127094Sdes	server_addr.s_addr = 0;
594127094Sdes	server_port = 0;
595127094Sdes	src_addr.s_addr = 0;
596127094Sdes	IpMask(0, &src_mask);
597127094Sdes	dst_addr.s_addr = 0;
598127094Sdes	IpMask(0, &dst_mask);
59944307Sbrian
600127094Sdes	str_port[0] = 0;
601127094Sdes	str_server_port[0] = 0;
60244307Sbrian
60344307Sbrian/* Parse command string with state machine */
60444307Sbrian#define STATE_READ_KEYWORD    0
60544307Sbrian#define STATE_READ_TYPE       1
60644307Sbrian#define STATE_READ_PORT       2
60744307Sbrian#define STATE_READ_SERVER     3
60844307Sbrian#define STATE_READ_RULE       4
60944307Sbrian#define STATE_READ_DELETE     5
61044307Sbrian#define STATE_READ_PROTO      6
61144307Sbrian#define STATE_READ_SRC        7
61244307Sbrian#define STATE_READ_DST        8
613127094Sdes	state = STATE_READ_KEYWORD;
614127094Sdes	token = strsep(&res, " \t");
615127094Sdes	token_count = 0;
616127094Sdes	while (token != NULL) {
617127094Sdes		token_count++;
618127094Sdes		switch (state) {
619127094Sdes		case STATE_READ_KEYWORD:
620127094Sdes			if (strcmp(token, "type") == 0)
621127094Sdes				state = STATE_READ_TYPE;
622127094Sdes			else if (strcmp(token, "port") == 0)
623127094Sdes				state = STATE_READ_PORT;
624127094Sdes			else if (strcmp(token, "server") == 0)
625127094Sdes				state = STATE_READ_SERVER;
626127094Sdes			else if (strcmp(token, "rule") == 0)
627127094Sdes				state = STATE_READ_RULE;
628127094Sdes			else if (strcmp(token, "delete") == 0)
629127094Sdes				state = STATE_READ_DELETE;
630127094Sdes			else if (strcmp(token, "proto") == 0)
631127094Sdes				state = STATE_READ_PROTO;
632127094Sdes			else if (strcmp(token, "src") == 0)
633127094Sdes				state = STATE_READ_SRC;
634127094Sdes			else if (strcmp(token, "dst") == 0)
635127094Sdes				state = STATE_READ_DST;
636165243Spiso			else {
637165243Spiso				ret = -1;
638165243Spiso				goto getout;
639165243Spiso			}
640127094Sdes			break;
64144307Sbrian
642127094Sdes		case STATE_READ_TYPE:
643127094Sdes			if (strcmp(token, "encode_ip_hdr") == 0)
644127094Sdes				proxy_type = PROXY_TYPE_ENCODE_IPHDR;
645127094Sdes			else if (strcmp(token, "encode_tcp_stream") == 0)
646127094Sdes				proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
647127094Sdes			else if (strcmp(token, "no_encode") == 0)
648127094Sdes				proxy_type = PROXY_TYPE_ENCODE_NONE;
649165243Spiso			else {
650165243Spiso				ret = -1;
651165243Spiso				goto getout;
652165243Spiso			}
653127094Sdes			state = STATE_READ_KEYWORD;
654127094Sdes			break;
65544307Sbrian
656127094Sdes		case STATE_READ_PORT:
657127094Sdes			strcpy(str_port, token);
658127094Sdes			state = STATE_READ_KEYWORD;
659127094Sdes			break;
66044307Sbrian
661127094Sdes		case STATE_READ_SERVER:
662127094Sdes			{
663127094Sdes				int err;
664127094Sdes				char *p;
665127094Sdes				char s[sizeof(buffer)];
66644307Sbrian
667127094Sdes				p = token;
668127094Sdes				while (*p != ':' && *p != 0)
669127094Sdes					p++;
67044307Sbrian
671127094Sdes				if (*p != ':') {
672127094Sdes					err = IpAddr(token, &server_addr);
673165243Spiso					if (err) {
674165243Spiso						ret = -1;
675165243Spiso						goto getout;
676165243Spiso					}
677127094Sdes				} else {
678127094Sdes					*p = ' ';
67999207Sbrian
680127094Sdes					n = sscanf(token, "%s %s", s, str_server_port);
681165243Spiso					if (n != 2) {
682165243Spiso						ret = -1;
683165243Spiso						goto getout;
684165243Spiso					}
68544307Sbrian
686127094Sdes					err = IpAddr(s, &server_addr);
687165243Spiso					if (err) {
688165243Spiso						ret = -1;
689165243Spiso						goto getout;
690165243Spiso					}
691127094Sdes				}
692127094Sdes			}
693127094Sdes			state = STATE_READ_KEYWORD;
694127094Sdes			break;
69544307Sbrian
696127094Sdes		case STATE_READ_RULE:
697127094Sdes			n = sscanf(token, "%d", &rule_index);
698165243Spiso			if (n != 1 || rule_index < 0) {
699165243Spiso				ret = -1;
700165243Spiso				goto getout;
701165243Spiso			}
702127094Sdes			state = STATE_READ_KEYWORD;
703127094Sdes			break;
70444307Sbrian
705127094Sdes		case STATE_READ_DELETE:
706127094Sdes			{
707127094Sdes				int err;
708127094Sdes				int rule_to_delete;
70944307Sbrian
710165243Spiso				if (token_count != 2) {
711165243Spiso					ret = -1;
712165243Spiso					goto getout;
713165243Spiso				}
71444307Sbrian
715127094Sdes				n = sscanf(token, "%d", &rule_to_delete);
716165243Spiso				if (n != 1) {
717165243Spiso					ret = -1;
718165243Spiso					goto getout;
719165243Spiso				}
720127094Sdes				err = RuleNumberDelete(la, rule_to_delete);
721127094Sdes				if (err)
722165243Spiso					ret = -1;
723165243Spiso				ret = 0;
724165243Spiso				goto getout;
725127094Sdes			}
72644307Sbrian
727127094Sdes		case STATE_READ_PROTO:
728127094Sdes			if (strcmp(token, "tcp") == 0)
729127094Sdes				proto = IPPROTO_TCP;
730127094Sdes			else if (strcmp(token, "udp") == 0)
731127094Sdes				proto = IPPROTO_UDP;
732165243Spiso			else {
733165243Spiso				ret = -1;
734165243Spiso				goto getout;
735165243Spiso			}
736127094Sdes			state = STATE_READ_KEYWORD;
737127094Sdes			break;
73844307Sbrian
739127094Sdes		case STATE_READ_SRC:
740127094Sdes		case STATE_READ_DST:
741127094Sdes			{
742127094Sdes				int err;
743127094Sdes				char *p;
744127094Sdes				struct in_addr mask;
745127094Sdes				struct in_addr addr;
74644307Sbrian
747127094Sdes				p = token;
748127094Sdes				while (*p != '/' && *p != 0)
749127094Sdes					p++;
75044307Sbrian
751127094Sdes				if (*p != '/') {
752127094Sdes					IpMask(32, &mask);
753127094Sdes					err = IpAddr(token, &addr);
754165243Spiso					if (err) {
755165243Spiso						ret = -1;
756165243Spiso						goto getout;
757165243Spiso					}
758127094Sdes				} else {
759127094Sdes					int nbits;
760127094Sdes					char s[sizeof(buffer)];
76144307Sbrian
762127094Sdes					*p = ' ';
763127094Sdes					n = sscanf(token, "%s %d", s, &nbits);
764165243Spiso					if (n != 2) {
765165243Spiso						ret = -1;
766165243Spiso						goto getout;
767165243Spiso					}
76844307Sbrian
769127094Sdes					err = IpAddr(s, &addr);
770165243Spiso					if (err) {
771165243Spiso						ret = -1;
772165243Spiso						goto getout;
773165243Spiso					}
77444307Sbrian
775127094Sdes					err = IpMask(nbits, &mask);
776165243Spiso					if (err) {
777165243Spiso						ret = -1;
778165243Spiso						goto getout;
779165243Spiso					}
780127094Sdes				}
78144307Sbrian
782127094Sdes				if (state == STATE_READ_SRC) {
783127094Sdes					src_addr = addr;
784127094Sdes					src_mask = mask;
785127094Sdes				} else {
786127094Sdes					dst_addr = addr;
787127094Sdes					dst_mask = mask;
788127094Sdes				}
789127094Sdes			}
790127094Sdes			state = STATE_READ_KEYWORD;
791127094Sdes			break;
79244307Sbrian
793127094Sdes		default:
794165243Spiso			ret = -1;
795165243Spiso			goto getout;
796127094Sdes			break;
797127094Sdes		}
79844307Sbrian
799127094Sdes		do {
800127094Sdes			token = strsep(&res, " \t");
801127094Sdes		} while (token != NULL && !*token);
802127094Sdes	}
80344307Sbrian#undef STATE_READ_KEYWORD
80444307Sbrian#undef STATE_READ_TYPE
80544307Sbrian#undef STATE_READ_PORT
80644307Sbrian#undef STATE_READ_SERVER
80744307Sbrian#undef STATE_READ_RULE
80844307Sbrian#undef STATE_READ_DELETE
80944307Sbrian#undef STATE_READ_PROTO
81044307Sbrian#undef STATE_READ_SRC
81144307Sbrian#undef STATE_READ_DST
81244307Sbrian
81344307Sbrian/* Convert port strings to numbers.  This needs to be done after
81444307Sbrian   the string is parsed, because the prototype might not be designated
81544307Sbrian   before the ports (which might be symbolic entries in /etc/services) */
81644307Sbrian
817127094Sdes	if (strlen(str_port) != 0) {
818127094Sdes		int err;
81944307Sbrian
820127094Sdes		err = IpPort(str_port, proto, &proxy_port);
821165243Spiso		if (err) {
822165243Spiso			ret = -1;
823165243Spiso			goto getout;
824165243Spiso		}
825127094Sdes	} else {
826127094Sdes		proxy_port = 0;
827127094Sdes	}
82844307Sbrian
829127094Sdes	if (strlen(str_server_port) != 0) {
830127094Sdes		int err;
83144307Sbrian
832127094Sdes		err = IpPort(str_server_port, proto, &server_port);
833165243Spiso		if (err) {
834165243Spiso			ret = -1;
835165243Spiso			goto getout;
836165243Spiso		}
837127094Sdes	} else {
838127094Sdes		server_port = 0;
839127094Sdes	}
84044307Sbrian
84144307Sbrian/* Check that at least the server address has been defined */
842165243Spiso	if (server_addr.s_addr == 0) {
843165243Spiso		ret = -1;
844165243Spiso		goto getout;
845165243Spiso	}
84644307Sbrian
84744307Sbrian/* Add to linked list */
848127094Sdes	proxy_entry = malloc(sizeof(struct proxy_entry));
849165243Spiso	if (proxy_entry == NULL) {
850165243Spiso		ret = -1;
851165243Spiso		goto getout;
852165243Spiso	}
85344307Sbrian
854127094Sdes	proxy_entry->proxy_type = proxy_type;
855127094Sdes	proxy_entry->rule_index = rule_index;
856127094Sdes	proxy_entry->proto = proto;
857127094Sdes	proxy_entry->proxy_port = htons(proxy_port);
858127094Sdes	proxy_entry->server_port = htons(server_port);
859127094Sdes	proxy_entry->server_addr = server_addr;
860127094Sdes	proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
861127094Sdes	proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
862127094Sdes	proxy_entry->src_mask = src_mask;
863127094Sdes	proxy_entry->dst_mask = dst_mask;
86444307Sbrian
865127094Sdes	RuleAdd(la, proxy_entry);
86644307Sbrian
867165243Spisogetout:
868165243Spiso	LIBALIAS_UNLOCK(la);
869165243Spiso	return (ret);
87044307Sbrian}
871