alias_proxy.c revision fcef7765190c983178ce948a1d0bfc0a0c3cd1ba
/*-
* 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
*
* 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.
*/
#ifndef VBOX
__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_proxy.c,v 1.31.8.1 2009/04/15 03:14:26 kensmith Exp $");
/* 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
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
#else
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <string.h>
#endif
#ifdef _KERNEL
#else
#include "alias.h" /* Public API functions for libalias */
#include "alias_local.h" /* Functions used by alias*.c */
#endif
#else /* VBOX */
# include <slirp.h>
# include "alias.h" /* Public API functions for libalias */
# include "alias_local.h" /* Functions used by alias*.c */
#endif /* VBOX */
/*
Data structures
*/
/*
* A linked list of arbitrary length, based on struct proxy_entry is
* used to store proxy rules.
*/
struct proxy_entry {
#define PROXY_TYPE_ENCODE_NONE 1
#define PROXY_TYPE_ENCODE_TCPSTREAM 2
#define PROXY_TYPE_ENCODE_IPHDR 3
int rule_index;
int proxy_type;
struct in_addr server_addr;
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? */
#endif
static int IpPort(char *, int, int *);
static void RuleDelete(struct proxy_entry *);
static void ProxyEncodeIpHeader(struct ip *, int);
#ifdef _KERNEL
static int
const char *cp;
{
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;
return (0);
/*
* 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);
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 */
return (0);
break;
case 2: /* a.b.c -- 8.8.16 bits */
return (0);
break;
case 3: /* a.b.c.d -- 8.8.8.8 bits */
return (0);
break;
}
return (1);
}
#endif
static int
{
int i;
return (-1);
imask = 0;
for (i = 0; i < nbits; i++)
return (0);
}
static int
{
return (-1);
else
return (0);
}
static int
{
int n;
if (n != 1)
#ifndef _KERNEL /* XXX: we accept only numeric ports in kernel */
{
if (proto == IPPROTO_TCP)
else if (proto == IPPROTO_UDP)
else
return (-1);
return (-1);
}
#else
return (-1);
#endif
return (0);
}
void
{
int rule_index;
struct proxy_entry *ptr;
struct proxy_entry *ptr_last;
return;
}
return;
}
return;
}
}
}
static void
{
else
}
static int
{
int err;
struct proxy_entry *ptr;
err = -1;
struct proxy_entry *ptr_next;
err = 0;
}
}
return (err);
}
static void
int maxpacketsize)
{
int slen;
char buffer[40];
/* Compute pointer to tcp header */
/* Don't modify if once already modified */
if (GetAckModified(lnk))
return;
/* Translate destination address and port to string form */
#ifndef VBOX
#else
#endif
/* Pad string out to a multiple of two in length */
switch (slen % 2) {
case 0:
slen += 2;
break;
case 1:
slen += 1;
}
/* Check for packet overflow */
return;
/* Shift existing TCP data and insert destination string */
{
int dlen;
int hlen;
char *p;
/* Modify first packet that has data in it */
if (dlen == 0)
return;
p = (char *)pip;
p += hlen;
}
/* Save information about modfied sequence number */
{
int delta;
}
/* Update IP header packet length and checksum */
{
int accumulate;
}
/* Update TCP checksum, Use TcpChecksum since so many things have
already changed. */
#ifdef _KERNEL
#else
#endif
}
static void
int maxpacketsize)
{
#define OPTION_LEN_BYTES 8
#define OPTION_LEN_INT16 4
#define OPTION_LEN_INT32 2
#ifdef LIBALIAS_DEBUG
#endif
(void)maxpacketsize;
/* Check to see that there is room to add an IP option */
return;
/* Build option and copy into packet */
{
ptr += 20;
}
/* Update checksum, header length and packet length */
{
int i;
int accumulate;
accumulate = 0;
for (i = 0; i < OPTION_LEN_INT16; i++)
accumulate -= *(sptr++);
accumulate += *sptr;
accumulate -= *sptr;
}
#ifdef LIBALIAS_DEBUG
#endif
}
/* Functions by other packet alias source files
ProxyCheck() -- Checks whether an outgoing packet should
be proxied.
for a packet which is to be redirected to
a proxy server.
*/
int
struct in_addr *proxy_server_addr,
{
struct proxy_entry *ptr;
->th_dport;
struct in_addr src_addr_masked;
struct in_addr dst_addr_masked;
return (ptr->proxy_type);
}
}
}
return (0);
}
void
int maxpacketsize,
int proxy_type)
{
(void)la;
switch (proxy_type) {
case PROXY_TYPE_ENCODE_IPHDR:
break;
break;
}
}
/*
Public API functions
*/
int
{
/*
* 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 cmd_len;
int token_count;
int state;
char *token;
char buffer[256];
char str_server_port[sizeof(buffer)];
int rule_index;
int proto;
int proxy_type;
int proxy_port;
int server_port;
struct in_addr server_addr;
struct proxy_entry *proxy_entry;
ret = 0;
/* Copy command line into a buffer */
ret = -1;
goto getout;
}
/* Convert to lower case */
for (i = 0; i < len; i++)
/* Set default proxy type */
/* Set up default values */
rule_index = 0;
proto = IPPROTO_TCP;
proxy_port = 0;
server_addr.s_addr = 0;
server_port = 0;
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
#ifndef VBOX
#else
#endif
token_count = 0;
token_count++;
switch (state) {
case STATE_READ_KEYWORD:
else {
ret = -1;
goto getout;
}
break;
case STATE_READ_TYPE:
else {
ret = -1;
goto getout;
}
break;
case STATE_READ_PORT:
break;
case STATE_READ_SERVER:
{
int err;
char *p;
char s[sizeof(buffer)];
p = token;
while (*p != ':' && *p != 0)
p++;
if (*p != ':') {
if (err) {
ret = -1;
goto getout;
}
} else {
*p = ' ';
if (n != 2) {
ret = -1;
goto getout;
}
if (err) {
ret = -1;
goto getout;
}
}
}
break;
case STATE_READ_RULE:
if (n != 1 || rule_index < 0) {
ret = -1;
goto getout;
}
break;
case STATE_READ_DELETE:
{
int err;
int rule_to_delete;
if (token_count != 2) {
ret = -1;
goto getout;
}
if (n != 1) {
ret = -1;
goto getout;
}
if (err)
ret = -1;
ret = 0;
goto getout;
}
case STATE_READ_PROTO:
proto = IPPROTO_TCP;
proto = IPPROTO_UDP;
else {
ret = -1;
goto getout;
}
break;
case STATE_READ_SRC:
case STATE_READ_DST:
{
int err;
char *p;
p = token;
while (*p != '/' && *p != 0)
p++;
if (*p != '/') {
if (err) {
ret = -1;
goto getout;
}
} else {
int nbits;
char s[sizeof(buffer)];
*p = ' ';
if (n != 2) {
ret = -1;
goto getout;
}
if (err) {
ret = -1;
goto getout;
}
if (err) {
ret = -1;
goto getout;
}
}
if (state == STATE_READ_SRC) {
} else {
}
}
break;
default:
ret = -1;
goto getout;
break;
}
do {
#ifndef VBOX
#else
#endif
}
/* Convert port strings to numbers. This needs to be done after
the string is parsed, because the prototype might not be designated
int err;
if (err) {
ret = -1;
goto getout;
}
} else {
proxy_port = 0;
}
if (strlen(str_server_port) != 0) {
int err;
if (err) {
ret = -1;
goto getout;
}
} else {
server_port = 0;
}
/* Check that at least the server address has been defined */
if (server_addr.s_addr == 0) {
ret = -1;
goto getout;
}
/* Add to linked list */
if (proxy_entry == NULL) {
ret = -1;
goto getout;
}
return (ret);
}