1274955Ssvnmir/*-
2274955Ssvnmir * Copyright (c) 2001 Charles Mott <cm@linktel.net>
3353358Sdim * All rights reserved.
4353358Sdim *
5353358Sdim * Redistribution and use in source and binary forms, with or without
6274955Ssvnmir * modification, are permitted provided that the following conditions
7274955Ssvnmir * are met:
8274955Ssvnmir * 1. Redistributions of source code must retain the above copyright
9274955Ssvnmir *    notice, this list of conditions and the following disclaimer.
10274955Ssvnmir * 2. Redistributions in binary form must reproduce the above copyright
11274955Ssvnmir *    notice, this list of conditions and the following disclaimer in the
12274955Ssvnmir *    documentation and/or other materials provided with the distribution.
13274955Ssvnmir *
14274955Ssvnmir * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15321369Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16274955Ssvnmir * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17274955Ssvnmir * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18274955Ssvnmir * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19274955Ssvnmir * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20274955Ssvnmir * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21274955Ssvnmir * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22274955Ssvnmir * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23274955Ssvnmir * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24274955Ssvnmir * SUCH DAMAGE.
25274955Ssvnmir */
26274955Ssvnmir
27309124Sdim#include <sys/cdefs.h>
28327952Sdim__FBSDID("$FreeBSD: releng/10.3/sys/netinet/libalias/alias_proxy.c 241648 2012-10-17 20:23:07Z emaste $");
29363496Sdim
30363496Sdim/* file: alias_proxy.c
31274955Ssvnmir
32274955Ssvnmir    This file encapsulates special operations related to transparent
33274955Ssvnmir    proxy redirection.  This is where packets with a particular destination,
34274955Ssvnmir    usually tcp port 80, are redirected to a proxy server.
35274955Ssvnmir
36274955Ssvnmir    When packets are proxied, the destination address and port are
37274955Ssvnmir    modified.  In certain cases, it is necessary to somehow encode
38274955Ssvnmir    the original address/port info into the packet.  Two methods are
39274955Ssvnmir    presently supported: addition of a [DEST addr port] string at the
40274955Ssvnmir    beginning of a tcp stream, or inclusion of an optional field
41274955Ssvnmir    in the IP header.
42274955Ssvnmir
43274955Ssvnmir    There is one public API function:
44274955Ssvnmir
45274955Ssvnmir	PacketAliasProxyRule()    -- Adds and deletes proxy
46274955Ssvnmir				     rules.
47274955Ssvnmir
48274955Ssvnmir    Rules are stored in a linear linked list, so lookup efficiency
49274955Ssvnmir    won't be too good for large lists.
50274955Ssvnmir
51274955Ssvnmir
52274955Ssvnmir    Initial development: April, 1998 (cjm)
53274955Ssvnmir*/
54274955Ssvnmir
55274955Ssvnmir
56274955Ssvnmir/* System includes */
57274955Ssvnmir#ifdef _KERNEL
58274955Ssvnmir#include <sys/param.h>
59274955Ssvnmir#include <sys/ctype.h>
60274955Ssvnmir#include <sys/libkern.h>
61274955Ssvnmir#include <sys/limits.h>
62274955Ssvnmir#else
63274955Ssvnmir#include <sys/types.h>
64274955Ssvnmir#include <ctype.h>
65274955Ssvnmir#include <stdio.h>
66274955Ssvnmir#include <stdlib.h>
67274955Ssvnmir#include <netdb.h>
68274955Ssvnmir#include <string.h>
69274955Ssvnmir#endif
70274955Ssvnmir
71274955Ssvnmir#include <netinet/tcp.h>
72274955Ssvnmir
73274955Ssvnmir#ifdef _KERNEL
74274955Ssvnmir#include <netinet/libalias/alias.h>
75274955Ssvnmir#include <netinet/libalias/alias_local.h>
76274955Ssvnmir#include <netinet/libalias/alias_mod.h>
77274955Ssvnmir#else
78274955Ssvnmir#include <arpa/inet.h>
79274955Ssvnmir#include "alias.h"		/* Public API functions for libalias */
80274955Ssvnmir#include "alias_local.h"	/* Functions used by alias*.c */
81274955Ssvnmir#endif
82274955Ssvnmir
83274955Ssvnmir/*
84274955Ssvnmir    Data structures
85274955Ssvnmir */
86274955Ssvnmir
87274955Ssvnmir/*
88274955Ssvnmir * A linked list of arbitrary length, based on struct proxy_entry is
89274955Ssvnmir * used to store proxy rules.
90274955Ssvnmir */
91274955Ssvnmirstruct proxy_entry {
92274955Ssvnmir	struct libalias *la;
93274955Ssvnmir#define PROXY_TYPE_ENCODE_NONE      1
94274955Ssvnmir#define PROXY_TYPE_ENCODE_TCPSTREAM 2
95274955Ssvnmir#define PROXY_TYPE_ENCODE_IPHDR     3
96274955Ssvnmir	int		rule_index;
97274955Ssvnmir	int		proxy_type;
98274955Ssvnmir	u_char		proto;
99274955Ssvnmir	u_short		proxy_port;
100274955Ssvnmir	u_short		server_port;
101274955Ssvnmir
102274955Ssvnmir	struct in_addr	server_addr;
103274955Ssvnmir
104274955Ssvnmir	struct in_addr	src_addr;
105274955Ssvnmir	struct in_addr	src_mask;
106274955Ssvnmir
107274955Ssvnmir	struct in_addr	dst_addr;
108274955Ssvnmir	struct in_addr	dst_mask;
109274955Ssvnmir
110274955Ssvnmir	struct proxy_entry *next;
111274955Ssvnmir	struct proxy_entry *last;
112274955Ssvnmir};
113274955Ssvnmir
114309124Sdim
115309124Sdim
116274955Ssvnmir/*
117274955Ssvnmir    File scope variables
118274955Ssvnmir*/
119274955Ssvnmir
120274955Ssvnmir
121274955Ssvnmir
122274955Ssvnmir/* Local (static) functions:
123274955Ssvnmir
124274955Ssvnmir    IpMask()                 -- Utility function for creating IP
125274955Ssvnmir				masks from integer (1-32) specification.
126274955Ssvnmir    IpAddr()                 -- Utility function for converting string
127274955Ssvnmir				to IP address
128274955Ssvnmir    IpPort()                 -- Utility function for converting string
129274955Ssvnmir				to port number
130274955Ssvnmir    RuleAdd()                -- Adds an element to the rule list.
131274955Ssvnmir    RuleDelete()             -- Removes an element from the rule list.
132274955Ssvnmir    RuleNumberDelete()       -- Removes all elements from the rule list
133274955Ssvnmir				having a certain rule number.
134274955Ssvnmir    ProxyEncodeTcpStream()   -- Adds [DEST x.x.x.x xxxx] to the beginning
135274955Ssvnmir				of a TCP stream.
136274955Ssvnmir    ProxyEncodeIpHeader()    -- Adds an IP option indicating the true
137274955Ssvnmir				destination of a proxied IP packet
138274955Ssvnmir*/
139274955Ssvnmir
140274955Ssvnmirstatic int	IpMask(int, struct in_addr *);
141274955Ssvnmirstatic int	IpAddr(char *, struct in_addr *);
142274955Ssvnmirstatic int	IpPort(char *, int, int *);
143274955Ssvnmirstatic void	RuleAdd(struct libalias *la, struct proxy_entry *);
144274955Ssvnmirstatic void	RuleDelete(struct proxy_entry *);
145274955Ssvnmirstatic int	RuleNumberDelete(struct libalias *la, int);
146274955Ssvnmirstatic void	ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
147274955Ssvnmirstatic void	ProxyEncodeIpHeader(struct ip *, int);
148274955Ssvnmir
149274955Ssvnmirstatic int
150274955SsvnmirIpMask(int nbits, struct in_addr *mask)
151274955Ssvnmir{
152274955Ssvnmir	int i;
153274955Ssvnmir	u_int imask;
154274955Ssvnmir
155274955Ssvnmir	if (nbits < 0 || nbits > 32)
156274955Ssvnmir		return (-1);
157274955Ssvnmir
158274955Ssvnmir	imask = 0;
159274955Ssvnmir	for (i = 0; i < nbits; i++)
160274955Ssvnmir		imask = (imask >> 1) + 0x80000000;
161274955Ssvnmir	mask->s_addr = htonl(imask);
162274955Ssvnmir
163274955Ssvnmir	return (0);
164274955Ssvnmir}
165274955Ssvnmir
166274955Ssvnmirstatic int
167274955SsvnmirIpAddr(char *s, struct in_addr *addr)
168274955Ssvnmir{
169274955Ssvnmir	if (inet_aton(s, addr) == 0)
170274955Ssvnmir		return (-1);
171274955Ssvnmir	else
172274955Ssvnmir		return (0);
173274955Ssvnmir}
174274955Ssvnmir
175274955Ssvnmirstatic int
176274955SsvnmirIpPort(char *s, int proto, int *port)
177274955Ssvnmir{
178274955Ssvnmir	int n;
179274955Ssvnmir
180274955Ssvnmir	n = sscanf(s, "%d", port);
181274955Ssvnmir	if (n != 1)
182274955Ssvnmir#ifndef _KERNEL	/* XXX: we accept only numeric ports in kernel */
183274955Ssvnmir	{
184274955Ssvnmir		struct servent *se;
185274955Ssvnmir
186274955Ssvnmir		if (proto == IPPROTO_TCP)
187274955Ssvnmir			se = getservbyname(s, "tcp");
188274955Ssvnmir		else if (proto == IPPROTO_UDP)
189274955Ssvnmir			se = getservbyname(s, "udp");
190274955Ssvnmir		else
191274955Ssvnmir			return (-1);
192274955Ssvnmir
193274955Ssvnmir		if (se == NULL)
194274955Ssvnmir			return (-1);
195274955Ssvnmir
196274955Ssvnmir		*port = (u_int) ntohs(se->s_port);
197274955Ssvnmir	}
198274955Ssvnmir#else
199274955Ssvnmir		return (-1);
200274955Ssvnmir#endif
201274955Ssvnmir	return (0);
202274955Ssvnmir}
203274955Ssvnmir
204274955Ssvnmirvoid
205274955SsvnmirRuleAdd(struct libalias *la, struct proxy_entry *entry)
206274955Ssvnmir{
207274955Ssvnmir	int rule_index;
208274955Ssvnmir	struct proxy_entry *ptr;
209274955Ssvnmir	struct proxy_entry *ptr_last;
210274955Ssvnmir
211274955Ssvnmir	LIBALIAS_LOCK_ASSERT(la);
212274955Ssvnmir
213274955Ssvnmir	entry->la = la;
214274955Ssvnmir	if (la->proxyList == NULL) {
215274955Ssvnmir		la->proxyList = entry;
216274955Ssvnmir		entry->last = NULL;
217274955Ssvnmir		entry->next = NULL;
218274955Ssvnmir		return;
219274955Ssvnmir	}
220274955Ssvnmir
221274955Ssvnmir	rule_index = entry->rule_index;
222274955Ssvnmir	ptr = la->proxyList;
223274955Ssvnmir	ptr_last = NULL;
224274955Ssvnmir	while (ptr != NULL) {
225341825Sdim		if (ptr->rule_index >= rule_index) {
226341825Sdim			if (ptr_last == NULL) {
227341825Sdim				entry->next = la->proxyList;
228341825Sdim				entry->last = NULL;
229274955Ssvnmir				la->proxyList->last = entry;
230274955Ssvnmir				la->proxyList = entry;
231274955Ssvnmir				return;
232274955Ssvnmir			}
233341825Sdim			ptr_last->next = entry;
234274955Ssvnmir			ptr->last = entry;
235274955Ssvnmir			entry->last = ptr->last;
236274955Ssvnmir			entry->next = ptr;
237341825Sdim			return;
238274955Ssvnmir		}
239274955Ssvnmir		ptr_last = ptr;
240274955Ssvnmir		ptr = ptr->next;
241274955Ssvnmir	}
242274955Ssvnmir
243274955Ssvnmir	ptr_last->next = entry;
244274955Ssvnmir	entry->last = ptr_last;
245274955Ssvnmir	entry->next = NULL;
246274955Ssvnmir}
247274955Ssvnmir
248274955Ssvnmirstatic void
249274955SsvnmirRuleDelete(struct proxy_entry *entry)
250274955Ssvnmir{
251274955Ssvnmir	struct libalias *la;
252274955Ssvnmir
253274955Ssvnmir	la = entry->la;
254274955Ssvnmir	LIBALIAS_LOCK_ASSERT(la);
255274955Ssvnmir	if (entry->last != NULL)
256274955Ssvnmir		entry->last->next = entry->next;
257274955Ssvnmir	else
258274955Ssvnmir		la->proxyList = entry->next;
259274955Ssvnmir
260274955Ssvnmir	if (entry->next != NULL)
261274955Ssvnmir		entry->next->last = entry->last;
262274955Ssvnmir
263274955Ssvnmir	free(entry);
264274955Ssvnmir}
265274955Ssvnmir
266274955Ssvnmirstatic int
267274955SsvnmirRuleNumberDelete(struct libalias *la, int rule_index)
268274955Ssvnmir{
269274955Ssvnmir	int err;
270274955Ssvnmir	struct proxy_entry *ptr;
271274955Ssvnmir
272274955Ssvnmir	LIBALIAS_LOCK_ASSERT(la);
273274955Ssvnmir	err = -1;
274274955Ssvnmir	ptr = la->proxyList;
275274955Ssvnmir	while (ptr != NULL) {
276274955Ssvnmir		struct proxy_entry *ptr_next;
277274955Ssvnmir
278274955Ssvnmir		ptr_next = ptr->next;
279274955Ssvnmir		if (ptr->rule_index == rule_index) {
280274955Ssvnmir			err = 0;
281274955Ssvnmir			RuleDelete(ptr);
282274955Ssvnmir		}
283274955Ssvnmir		ptr = ptr_next;
284274955Ssvnmir	}
285274955Ssvnmir
286274955Ssvnmir	return (err);
287274955Ssvnmir}
288274955Ssvnmir
289274955Ssvnmirstatic void
290274955SsvnmirProxyEncodeTcpStream(struct alias_link *lnk,
291274955Ssvnmir    struct ip *pip,
292274955Ssvnmir    int maxpacketsize)
293274955Ssvnmir{
294274955Ssvnmir	int slen;
295274955Ssvnmir	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