be4f3cd0d9
With the first part of my previous Summer of Code work, we get: -made libalias modular: -support for 'particular' protocols (like ftp/irc/etcetc) is no more hardcoded inside libalias, but it's available through external modules loadable at runtime -modules are available both in kernel (/boot/kernel/alias_*.ko) and user land (/lib/libalias_*) -protocols/applications modularized are: cuseeme, ftp, irc, nbt, pptp, skinny and smedia -added logging support for kernel side -cleanup After a buildworld, do a 'mergemaster -i' to install the file libalias.conf in /etc or manually copy it. During startup (and after every HUP signal) user land applications running the new libalias will try to read a file in /etc called libalias.conf: that file contains the list of modules to load. User land applications affected by this commit are ppp and natd: if libalias.conf is present in /etc you won't notice any difference. The only kernel land bit affected by this commit is ng_nat: if you are using ng_nat, and it doesn't correctly handle ftp/irc/etcetc sessions anymore, remember to kldload the correspondent module (i.e. kldload alias_ftp). General information and details about the inner working are available in the libalias man page under the section 'MODULAR ARCHITECTURE (AND ipfw(4) SUPPORT)'. NOTA BENE: this commit affects _ONLY_ libalias, ipfw in-kernel nat support will be part of the next libalias-related commit. Approved by: glebius Reviewed by: glebius, ru
930 lines
20 KiB
C
930 lines
20 KiB
C
/*-
|
|
* Copyright (c) 2001 Charles Mott <cm@linktel.net>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
/* file: alias_proxy.c
|
|
|
|
This file encapsulates special operations related to transparent
|
|
proxy redirection. This is where packets with a particular destination,
|
|
usually tcp port 80, are redirected to a proxy server.
|
|
|
|
When packets are proxied, the destination address and port are
|
|
modified. In certain cases, it is necessary to somehow encode
|
|
the original address/port info into the packet. Two methods are
|
|
presently supported: addition of a [DEST addr port] string at the
|
|
beginning of a tcp stream, or inclusion of an optional field
|
|
in the IP header.
|
|
|
|
There is one public API function:
|
|
|
|
PacketAliasProxyRule() -- Adds and deletes proxy
|
|
rules.
|
|
|
|
Rules are stored in a linear linked list, so lookup efficiency
|
|
won't be too good for large lists.
|
|
|
|
|
|
Initial development: April, 1998 (cjm)
|
|
*/
|
|
|
|
|
|
/* System includes */
|
|
#ifdef _KERNEL
|
|
#include <sys/param.h>
|
|
#include <sys/ctype.h>
|
|
#include <sys/libkern.h>
|
|
#include <sys/limits.h>
|
|
#else
|
|
#include <sys/types.h>
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <netdb.h>
|
|
#include <string.h>
|
|
#endif
|
|
|
|
#include <netinet/tcp.h>
|
|
|
|
#ifdef _KERNEL
|
|
#include <netinet/libalias/alias.h>
|
|
#include <netinet/libalias/alias_local.h>
|
|
#include <netinet/libalias/alias_mod.h>
|
|
#else
|
|
#include <arpa/inet.h>
|
|
#include "alias.h" /* Public API functions for libalias */
|
|
#include "alias_local.h" /* Functions used by alias*.c */
|
|
#endif
|
|
|
|
/*
|
|
Data structures
|
|
*/
|
|
|
|
/*
|
|
* A linked list of arbitrary length, based on struct proxy_entry is
|
|
* used to store proxy rules.
|
|
*/
|
|
struct proxy_entry {
|
|
struct libalias *la;
|
|
#define PROXY_TYPE_ENCODE_NONE 1
|
|
#define PROXY_TYPE_ENCODE_TCPSTREAM 2
|
|
#define PROXY_TYPE_ENCODE_IPHDR 3
|
|
int rule_index;
|
|
int proxy_type;
|
|
u_char proto;
|
|
u_short proxy_port;
|
|
u_short server_port;
|
|
|
|
struct in_addr server_addr;
|
|
|
|
struct in_addr src_addr;
|
|
struct in_addr src_mask;
|
|
|
|
struct in_addr dst_addr;
|
|
struct in_addr dst_mask;
|
|
|
|
struct proxy_entry *next;
|
|
struct proxy_entry *last;
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
File scope variables
|
|
*/
|
|
|
|
|
|
|
|
/* Local (static) functions:
|
|
|
|
IpMask() -- Utility function for creating IP
|
|
masks from integer (1-32) specification.
|
|
IpAddr() -- Utility function for converting string
|
|
to IP address
|
|
IpPort() -- Utility function for converting string
|
|
to port number
|
|
RuleAdd() -- Adds an element to the rule list.
|
|
RuleDelete() -- Removes an element from the rule list.
|
|
RuleNumberDelete() -- Removes all elements from the rule list
|
|
having a certain rule number.
|
|
ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning
|
|
of a TCP stream.
|
|
ProxyEncodeIpHeader() -- Adds an IP option indicating the true
|
|
destination of a proxied IP packet
|
|
*/
|
|
|
|
#ifdef _KERNEL /* XXX: can it be moved to libkern? */
|
|
static int inet_aton(const char *cp, struct in_addr *addr);
|
|
#endif
|
|
static int IpMask(int, struct in_addr *);
|
|
static int IpAddr(char *, struct in_addr *);
|
|
static int IpPort(char *, int, int *);
|
|
static void RuleAdd(struct libalias *la, struct proxy_entry *);
|
|
static void RuleDelete(struct proxy_entry *);
|
|
static int RuleNumberDelete(struct libalias *la, int);
|
|
static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
|
|
static void ProxyEncodeIpHeader(struct ip *, int);
|
|
|
|
#ifdef _KERNEL
|
|
static int
|
|
inet_aton(cp, addr)
|
|
const char *cp;
|
|
struct in_addr *addr;
|
|
{
|
|
u_long parts[4];
|
|
in_addr_t val;
|
|
const char *c;
|
|
char *endptr;
|
|
int gotend, n;
|
|
|
|
c = (const char *)cp;
|
|
n = 0;
|
|
/*
|
|
* Run through the string, grabbing numbers until
|
|
* the end of the string, or some error
|
|
*/
|
|
gotend = 0;
|
|
while (!gotend) {
|
|
unsigned long l;
|
|
|
|
l = strtoul(c, &endptr, 0);
|
|
|
|
if (l == ULONG_MAX || l == 0)
|
|
return (0);
|
|
|
|
val = (in_addr_t)l;
|
|
/*
|
|
* If the whole string is invalid, endptr will equal
|
|
* c.. this way we can make sure someone hasn't
|
|
* gone '.12' or something which would get past
|
|
* the next check.
|
|
*/
|
|
if (endptr == c)
|
|
return (0);
|
|
parts[n] = val;
|
|
c = endptr;
|
|
|
|
/* Check the next character past the previous number's end */
|
|
switch (*c) {
|
|
case '.' :
|
|
/* Make sure we only do 3 dots .. */
|
|
if (n == 3) /* Whoops. Quit. */
|
|
return (0);
|
|
n++;
|
|
c++;
|
|
break;
|
|
|
|
case '\0':
|
|
gotend = 1;
|
|
break;
|
|
|
|
default:
|
|
if (isspace((unsigned char)*c)) {
|
|
gotend = 1;
|
|
break;
|
|
} else
|
|
return (0); /* Invalid character, so fail */
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Concoct the address according to
|
|
* the number of parts specified.
|
|
*/
|
|
|
|
switch (n) {
|
|
case 0: /* a -- 32 bits */
|
|
/*
|
|
* Nothing is necessary here. Overflow checking was
|
|
* already done in strtoul().
|
|
*/
|
|
break;
|
|
case 1: /* a.b -- 8.24 bits */
|
|
if (val > 0xffffff || parts[0] > 0xff)
|
|
return (0);
|
|
val |= parts[0] << 24;
|
|
break;
|
|
|
|
case 2: /* a.b.c -- 8.8.16 bits */
|
|
if (val > 0xffff || parts[0] > 0xff || parts[1] > 0xff)
|
|
return (0);
|
|
val |= (parts[0] << 24) | (parts[1] << 16);
|
|
break;
|
|
|
|
case 3: /* a.b.c.d -- 8.8.8.8 bits */
|
|
if (val > 0xff || parts[0] > 0xff || parts[1] > 0xff ||
|
|
parts[2] > 0xff)
|
|
return (0);
|
|
val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
|
|
break;
|
|
}
|
|
|
|
if (addr != NULL)
|
|
addr->s_addr = htonl(val);
|
|
return (1);
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
IpMask(int nbits, struct in_addr *mask)
|
|
{
|
|
int i;
|
|
u_int imask;
|
|
|
|
if (nbits < 0 || nbits > 32)
|
|
return (-1);
|
|
|
|
imask = 0;
|
|
for (i = 0; i < nbits; i++)
|
|
imask = (imask >> 1) + 0x80000000;
|
|
mask->s_addr = htonl(imask);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
IpAddr(char *s, struct in_addr *addr)
|
|
{
|
|
if (inet_aton(s, addr) == 0)
|
|
return (-1);
|
|
else
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
IpPort(char *s, int proto, int *port)
|
|
{
|
|
int n;
|
|
|
|
n = sscanf(s, "%d", port);
|
|
if (n != 1)
|
|
#ifndef _KERNEL /* XXX: we accept only numeric ports in kernel */
|
|
{
|
|
struct servent *se;
|
|
|
|
if (proto == IPPROTO_TCP)
|
|
se = getservbyname(s, "tcp");
|
|
else if (proto == IPPROTO_UDP)
|
|
se = getservbyname(s, "udp");
|
|
else
|
|
return (-1);
|
|
|
|
if (se == NULL)
|
|
return (-1);
|
|
|
|
*port = (u_int) ntohs(se->s_port);
|
|
}
|
|
#else
|
|
return (-1);
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
RuleAdd(struct libalias *la, struct proxy_entry *entry)
|
|
{
|
|
int rule_index;
|
|
struct proxy_entry *ptr;
|
|
struct proxy_entry *ptr_last;
|
|
|
|
if (la->proxyList == NULL) {
|
|
la->proxyList = entry;
|
|
entry->last = NULL;
|
|
entry->next = NULL;
|
|
return;
|
|
}
|
|
entry->la = la;
|
|
|
|
rule_index = entry->rule_index;
|
|
ptr = la->proxyList;
|
|
ptr_last = NULL;
|
|
while (ptr != NULL) {
|
|
if (ptr->rule_index >= rule_index) {
|
|
if (ptr_last == NULL) {
|
|
entry->next = la->proxyList;
|
|
entry->last = NULL;
|
|
la->proxyList->last = entry;
|
|
la->proxyList = entry;
|
|
return;
|
|
}
|
|
ptr_last->next = entry;
|
|
ptr->last = entry;
|
|
entry->last = ptr->last;
|
|
entry->next = ptr;
|
|
return;
|
|
}
|
|
ptr_last = ptr;
|
|
ptr = ptr->next;
|
|
}
|
|
|
|
ptr_last->next = entry;
|
|
entry->last = ptr_last;
|
|
entry->next = NULL;
|
|
}
|
|
|
|
static void
|
|
RuleDelete(struct proxy_entry *entry)
|
|
{
|
|
struct libalias *la;
|
|
|
|
la = entry->la;
|
|
if (entry->last != NULL)
|
|
entry->last->next = entry->next;
|
|
else
|
|
la->proxyList = entry->next;
|
|
|
|
if (entry->next != NULL)
|
|
entry->next->last = entry->last;
|
|
|
|
free(entry);
|
|
}
|
|
|
|
static int
|
|
RuleNumberDelete(struct libalias *la, int rule_index)
|
|
{
|
|
int err;
|
|
struct proxy_entry *ptr;
|
|
|
|
err = -1;
|
|
ptr = la->proxyList;
|
|
while (ptr != NULL) {
|
|
struct proxy_entry *ptr_next;
|
|
|
|
ptr_next = ptr->next;
|
|
if (ptr->rule_index == rule_index) {
|
|
err = 0;
|
|
RuleDelete(ptr);
|
|
}
|
|
ptr = ptr_next;
|
|
}
|
|
|
|
return (err);
|
|
}
|
|
|
|
static void
|
|
ProxyEncodeTcpStream(struct alias_link *lnk,
|
|
struct ip *pip,
|
|
int maxpacketsize)
|
|
{
|
|
int slen;
|
|
char buffer[40];
|
|
struct tcphdr *tc;
|
|
|
|
/* Compute pointer to tcp header */
|
|
tc = (struct tcphdr *)ip_next(pip);
|
|
|
|
/* Don't modify if once already modified */
|
|
|
|
if (GetAckModified(lnk))
|
|
return;
|
|
|
|
/* Translate destination address and port to string form */
|
|
snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
|
|
inet_ntoa(GetProxyAddress(lnk)), (u_int) ntohs(GetProxyPort(lnk)));
|
|
|
|
/* Pad string out to a multiple of two in length */
|
|
slen = strlen(buffer);
|
|
switch (slen % 2) {
|
|
case 0:
|
|
strcat(buffer, " \n");
|
|
slen += 2;
|
|
break;
|
|
case 1:
|
|
strcat(buffer, "\n");
|
|
slen += 1;
|
|
}
|
|
|
|
/* Check for packet overflow */
|
|
if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
|
|
return;
|
|
|
|
/* Shift existing TCP data and insert destination string */
|
|
{
|
|
int dlen;
|
|
int hlen;
|
|
u_char *p;
|
|
|
|
hlen = (pip->ip_hl + tc->th_off) << 2;
|
|
dlen = ntohs(pip->ip_len) - hlen;
|
|
|
|
/* Modify first packet that has data in it */
|
|
|
|
if (dlen == 0)
|
|
return;
|
|
|
|
p = (char *)pip;
|
|
p += hlen;
|
|
|
|
bcopy(p, p + slen, dlen);
|
|
memcpy(p, buffer, slen);
|
|
}
|
|
|
|
/* Save information about modfied sequence number */
|
|
{
|
|
int delta;
|
|
|
|
SetAckModified(lnk);
|
|
delta = GetDeltaSeqOut(pip, lnk);
|
|
AddSeq(pip, lnk, delta + slen);
|
|
}
|
|
|
|
/* Update IP header packet length and checksum */
|
|
{
|
|
int accumulate;
|
|
|
|
accumulate = pip->ip_len;
|
|
pip->ip_len = htons(ntohs(pip->ip_len) + slen);
|
|
accumulate -= pip->ip_len;
|
|
|
|
ADJUST_CHECKSUM(accumulate, pip->ip_sum);
|
|
}
|
|
|
|
/* Update TCP checksum, Use TcpChecksum since so many things have
|
|
already changed. */
|
|
|
|
tc->th_sum = 0;
|
|
#ifdef _KERNEL
|
|
tc->th_x2 = 1;
|
|
#else
|
|
tc->th_sum = TcpChecksum(pip);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
ProxyEncodeIpHeader(struct ip *pip,
|
|
int maxpacketsize)
|
|
{
|
|
#define OPTION_LEN_BYTES 8
|
|
#define OPTION_LEN_INT16 4
|
|
#define OPTION_LEN_INT32 2
|
|
u_char option[OPTION_LEN_BYTES];
|
|
|
|
#ifdef LIBALIAS_DEBUG
|
|
fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
|
|
fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
|
|
#endif
|
|
|
|
(void)maxpacketsize;
|
|
|
|
/* Check to see that there is room to add an IP option */
|
|
if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
|
|
return;
|
|
|
|
/* Build option and copy into packet */
|
|
{
|
|
u_char *ptr;
|
|
struct tcphdr *tc;
|
|
|
|
ptr = (u_char *) pip;
|
|
ptr += 20;
|
|
memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
|
|
|
|
option[0] = 0x64; /* class: 3 (reserved), option 4 */
|
|
option[1] = OPTION_LEN_BYTES;
|
|
|
|
memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
|
|
|
|
tc = (struct tcphdr *)ip_next(pip);
|
|
memcpy(&option[6], (u_char *) & tc->th_sport, 2);
|
|
|
|
memcpy(ptr, option, 8);
|
|
}
|
|
|
|
/* Update checksum, header length and packet length */
|
|
{
|
|
int i;
|
|
int accumulate;
|
|
u_short *sptr;
|
|
|
|
sptr = (u_short *) option;
|
|
accumulate = 0;
|
|
for (i = 0; i < OPTION_LEN_INT16; i++)
|
|
accumulate -= *(sptr++);
|
|
|
|
sptr = (u_short *) pip;
|
|
accumulate += *sptr;
|
|
pip->ip_hl += OPTION_LEN_INT32;
|
|
accumulate -= *sptr;
|
|
|
|
accumulate += pip->ip_len;
|
|
pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
|
|
accumulate -= pip->ip_len;
|
|
|
|
ADJUST_CHECKSUM(accumulate, pip->ip_sum);
|
|
}
|
|
#undef OPTION_LEN_BYTES
|
|
#undef OPTION_LEN_INT16
|
|
#undef OPTION_LEN_INT32
|
|
#ifdef LIBALIAS_DEBUG
|
|
fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
|
|
fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Functions by other packet alias source files
|
|
|
|
ProxyCheck() -- Checks whether an outgoing packet should
|
|
be proxied.
|
|
ProxyModify() -- Encodes the original destination address/port
|
|
for a packet which is to be redirected to
|
|
a proxy server.
|
|
*/
|
|
|
|
int
|
|
ProxyCheck(struct libalias *la, struct ip *pip,
|
|
struct in_addr *proxy_server_addr,
|
|
u_short * proxy_server_port)
|
|
{
|
|
u_short dst_port;
|
|
struct in_addr src_addr;
|
|
struct in_addr dst_addr;
|
|
struct proxy_entry *ptr;
|
|
|
|
src_addr = pip->ip_src;
|
|
dst_addr = pip->ip_dst;
|
|
dst_port = ((struct tcphdr *)ip_next(pip))
|
|
->th_dport;
|
|
|
|
ptr = la->proxyList;
|
|
while (ptr != NULL) {
|
|
u_short proxy_port;
|
|
|
|
proxy_port = ptr->proxy_port;
|
|
if ((dst_port == proxy_port || proxy_port == 0)
|
|
&& pip->ip_p == ptr->proto
|
|
&& src_addr.s_addr != ptr->server_addr.s_addr) {
|
|
struct in_addr src_addr_masked;
|
|
struct in_addr dst_addr_masked;
|
|
|
|
src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
|
|
dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
|
|
|
|
if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
|
|
&& (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
|
|
if ((*proxy_server_port = ptr->server_port) == 0)
|
|
*proxy_server_port = dst_port;
|
|
*proxy_server_addr = ptr->server_addr;
|
|
return (ptr->proxy_type);
|
|
}
|
|
}
|
|
ptr = ptr->next;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
ProxyModify(struct libalias *la, struct alias_link *lnk,
|
|
struct ip *pip,
|
|
int maxpacketsize,
|
|
int proxy_type)
|
|
{
|
|
|
|
(void)la;
|
|
|
|
switch (proxy_type) {
|
|
case PROXY_TYPE_ENCODE_IPHDR:
|
|
ProxyEncodeIpHeader(pip, maxpacketsize);
|
|
break;
|
|
|
|
case PROXY_TYPE_ENCODE_TCPSTREAM:
|
|
ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
Public API functions
|
|
*/
|
|
|
|
int
|
|
LibAliasProxyRule(struct libalias *la, const char *cmd)
|
|
{
|
|
/*
|
|
* This function takes command strings of the form:
|
|
*
|
|
* server <addr>[:<port>]
|
|
* [port <port>]
|
|
* [rule n]
|
|
* [proto tcp|udp]
|
|
* [src <addr>[/n]]
|
|
* [dst <addr>[/n]]
|
|
* [type encode_tcp_stream|encode_ip_hdr|no_encode]
|
|
*
|
|
* delete <rule number>
|
|
*
|
|
* Subfields can be in arbitrary order. Port numbers and addresses
|
|
* must be in either numeric or symbolic form. An optional rule number
|
|
* is used to control the order in which rules are searched. If two
|
|
* rules have the same number, then search order cannot be guaranteed,
|
|
* and the rules should be disjoint. If no rule number is specified,
|
|
* then 0 is used, and group 0 rules are always checked before any
|
|
* others.
|
|
*/
|
|
int i, n, len;
|
|
int cmd_len;
|
|
int token_count;
|
|
int state;
|
|
char *token;
|
|
char buffer[256];
|
|
char str_port[sizeof(buffer)];
|
|
char str_server_port[sizeof(buffer)];
|
|
char *res = buffer;
|
|
|
|
int rule_index;
|
|
int proto;
|
|
int proxy_type;
|
|
int proxy_port;
|
|
int server_port;
|
|
struct in_addr server_addr;
|
|
struct in_addr src_addr, src_mask;
|
|
struct in_addr dst_addr, dst_mask;
|
|
struct proxy_entry *proxy_entry;
|
|
|
|
/* Copy command line into a buffer */
|
|
cmd += strspn(cmd, " \t");
|
|
cmd_len = strlen(cmd);
|
|
if (cmd_len > (int)(sizeof(buffer) - 1))
|
|
return (-1);
|
|
strcpy(buffer, cmd);
|
|
|
|
/* Convert to lower case */
|
|
len = strlen(buffer);
|
|
for (i = 0; i < len; i++)
|
|
buffer[i] = tolower((unsigned char)buffer[i]);
|
|
|
|
/* Set default proxy type */
|
|
|
|
/* Set up default values */
|
|
rule_index = 0;
|
|
proxy_type = PROXY_TYPE_ENCODE_NONE;
|
|
proto = IPPROTO_TCP;
|
|
proxy_port = 0;
|
|
server_addr.s_addr = 0;
|
|
server_port = 0;
|
|
src_addr.s_addr = 0;
|
|
IpMask(0, &src_mask);
|
|
dst_addr.s_addr = 0;
|
|
IpMask(0, &dst_mask);
|
|
|
|
str_port[0] = 0;
|
|
str_server_port[0] = 0;
|
|
|
|
/* Parse command string with state machine */
|
|
#define STATE_READ_KEYWORD 0
|
|
#define STATE_READ_TYPE 1
|
|
#define STATE_READ_PORT 2
|
|
#define STATE_READ_SERVER 3
|
|
#define STATE_READ_RULE 4
|
|
#define STATE_READ_DELETE 5
|
|
#define STATE_READ_PROTO 6
|
|
#define STATE_READ_SRC 7
|
|
#define STATE_READ_DST 8
|
|
state = STATE_READ_KEYWORD;
|
|
token = strsep(&res, " \t");
|
|
token_count = 0;
|
|
while (token != NULL) {
|
|
token_count++;
|
|
switch (state) {
|
|
case STATE_READ_KEYWORD:
|
|
if (strcmp(token, "type") == 0)
|
|
state = STATE_READ_TYPE;
|
|
else if (strcmp(token, "port") == 0)
|
|
state = STATE_READ_PORT;
|
|
else if (strcmp(token, "server") == 0)
|
|
state = STATE_READ_SERVER;
|
|
else if (strcmp(token, "rule") == 0)
|
|
state = STATE_READ_RULE;
|
|
else if (strcmp(token, "delete") == 0)
|
|
state = STATE_READ_DELETE;
|
|
else if (strcmp(token, "proto") == 0)
|
|
state = STATE_READ_PROTO;
|
|
else if (strcmp(token, "src") == 0)
|
|
state = STATE_READ_SRC;
|
|
else if (strcmp(token, "dst") == 0)
|
|
state = STATE_READ_DST;
|
|
else
|
|
return (-1);
|
|
break;
|
|
|
|
case STATE_READ_TYPE:
|
|
if (strcmp(token, "encode_ip_hdr") == 0)
|
|
proxy_type = PROXY_TYPE_ENCODE_IPHDR;
|
|
else if (strcmp(token, "encode_tcp_stream") == 0)
|
|
proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
|
|
else if (strcmp(token, "no_encode") == 0)
|
|
proxy_type = PROXY_TYPE_ENCODE_NONE;
|
|
else
|
|
return (-1);
|
|
state = STATE_READ_KEYWORD;
|
|
break;
|
|
|
|
case STATE_READ_PORT:
|
|
strcpy(str_port, token);
|
|
state = STATE_READ_KEYWORD;
|
|
break;
|
|
|
|
case STATE_READ_SERVER:
|
|
{
|
|
int err;
|
|
char *p;
|
|
char s[sizeof(buffer)];
|
|
|
|
p = token;
|
|
while (*p != ':' && *p != 0)
|
|
p++;
|
|
|
|
if (*p != ':') {
|
|
err = IpAddr(token, &server_addr);
|
|
if (err)
|
|
return (-1);
|
|
} else {
|
|
*p = ' ';
|
|
|
|
n = sscanf(token, "%s %s", s, str_server_port);
|
|
if (n != 2)
|
|
return (-1);
|
|
|
|
err = IpAddr(s, &server_addr);
|
|
if (err)
|
|
return (-1);
|
|
}
|
|
}
|
|
state = STATE_READ_KEYWORD;
|
|
break;
|
|
|
|
case STATE_READ_RULE:
|
|
n = sscanf(token, "%d", &rule_index);
|
|
if (n != 1 || rule_index < 0)
|
|
return (-1);
|
|
state = STATE_READ_KEYWORD;
|
|
break;
|
|
|
|
case STATE_READ_DELETE:
|
|
{
|
|
int err;
|
|
int rule_to_delete;
|
|
|
|
if (token_count != 2)
|
|
return (-1);
|
|
|
|
n = sscanf(token, "%d", &rule_to_delete);
|
|
if (n != 1)
|
|
return (-1);
|
|
err = RuleNumberDelete(la, rule_to_delete);
|
|
if (err)
|
|
return (-1);
|
|
return (0);
|
|
}
|
|
|
|
case STATE_READ_PROTO:
|
|
if (strcmp(token, "tcp") == 0)
|
|
proto = IPPROTO_TCP;
|
|
else if (strcmp(token, "udp") == 0)
|
|
proto = IPPROTO_UDP;
|
|
else
|
|
return (-1);
|
|
state = STATE_READ_KEYWORD;
|
|
break;
|
|
|
|
case STATE_READ_SRC:
|
|
case STATE_READ_DST:
|
|
{
|
|
int err;
|
|
char *p;
|
|
struct in_addr mask;
|
|
struct in_addr addr;
|
|
|
|
p = token;
|
|
while (*p != '/' && *p != 0)
|
|
p++;
|
|
|
|
if (*p != '/') {
|
|
IpMask(32, &mask);
|
|
err = IpAddr(token, &addr);
|
|
if (err)
|
|
return (-1);
|
|
} else {
|
|
int nbits;
|
|
char s[sizeof(buffer)];
|
|
|
|
*p = ' ';
|
|
n = sscanf(token, "%s %d", s, &nbits);
|
|
if (n != 2)
|
|
return (-1);
|
|
|
|
err = IpAddr(s, &addr);
|
|
if (err)
|
|
return (-1);
|
|
|
|
err = IpMask(nbits, &mask);
|
|
if (err)
|
|
return (-1);
|
|
}
|
|
|
|
if (state == STATE_READ_SRC) {
|
|
src_addr = addr;
|
|
src_mask = mask;
|
|
} else {
|
|
dst_addr = addr;
|
|
dst_mask = mask;
|
|
}
|
|
}
|
|
state = STATE_READ_KEYWORD;
|
|
break;
|
|
|
|
default:
|
|
return (-1);
|
|
break;
|
|
}
|
|
|
|
do {
|
|
token = strsep(&res, " \t");
|
|
} while (token != NULL && !*token);
|
|
}
|
|
#undef STATE_READ_KEYWORD
|
|
#undef STATE_READ_TYPE
|
|
#undef STATE_READ_PORT
|
|
#undef STATE_READ_SERVER
|
|
#undef STATE_READ_RULE
|
|
#undef STATE_READ_DELETE
|
|
#undef STATE_READ_PROTO
|
|
#undef STATE_READ_SRC
|
|
#undef STATE_READ_DST
|
|
|
|
/* Convert port strings to numbers. This needs to be done after
|
|
the string is parsed, because the prototype might not be designated
|
|
before the ports (which might be symbolic entries in /etc/services) */
|
|
|
|
if (strlen(str_port) != 0) {
|
|
int err;
|
|
|
|
err = IpPort(str_port, proto, &proxy_port);
|
|
if (err)
|
|
return (-1);
|
|
} else {
|
|
proxy_port = 0;
|
|
}
|
|
|
|
if (strlen(str_server_port) != 0) {
|
|
int err;
|
|
|
|
err = IpPort(str_server_port, proto, &server_port);
|
|
if (err)
|
|
return (-1);
|
|
} else {
|
|
server_port = 0;
|
|
}
|
|
|
|
/* Check that at least the server address has been defined */
|
|
if (server_addr.s_addr == 0)
|
|
return (-1);
|
|
|
|
/* Add to linked list */
|
|
proxy_entry = malloc(sizeof(struct proxy_entry));
|
|
if (proxy_entry == NULL)
|
|
return (-1);
|
|
|
|
proxy_entry->proxy_type = proxy_type;
|
|
proxy_entry->rule_index = rule_index;
|
|
proxy_entry->proto = proto;
|
|
proxy_entry->proxy_port = htons(proxy_port);
|
|
proxy_entry->server_port = htons(server_port);
|
|
proxy_entry->server_addr = server_addr;
|
|
proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
|
|
proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
|
|
proxy_entry->src_mask = src_mask;
|
|
proxy_entry->dst_mask = dst_mask;
|
|
|
|
RuleAdd(la, proxy_entry);
|
|
|
|
return (0);
|
|
}
|