#include <sys/cdefs.h>
#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"
#include "alias_local.h"
#endif
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;
};
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);
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
{
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;
LIBALIAS_LOCK_ASSERT(la);
entry->la = la;
if (la->proxyList == NULL) {
la->proxyList = entry;
entry->last = NULL;
entry->next = NULL;
return;
}
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;
LIBALIAS_LOCK_ASSERT(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;
LIBALIAS_LOCK_ASSERT(la);
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;
char addrbuf[INET_ADDRSTRLEN];
tc = (struct tcphdr *)ip_next(pip);
if (GetAckModified(lnk))
return;
snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
inet_ntoa_r(GetProxyAddress(lnk), INET_NTOA_BUF(addrbuf)),
(u_int)ntohs(GetProxyPort(lnk)));
slen = strlen(buffer);
switch (slen % 2) {
case 0:
strcat(buffer, " \n");
slen += 2;
break;
case 1:
strcat(buffer, "\n");
slen += 1;
}
if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
return;
{
int dlen;
int hlen;
char *p;
hlen = (pip->ip_hl + tc->th_off) << 2;
dlen = ntohs(pip->ip_len) - hlen;
if (dlen == 0)
return;
p = (char *)pip;
p += hlen;
bcopy(p, p + slen, dlen);
memcpy(p, buffer, slen);
}
{
int delta;
SetAckModified(lnk);
tc = (struct tcphdr *)ip_next(pip);
delta = GetDeltaSeqOut(tc->th_seq, lnk);
AddSeq(lnk, delta + slen, pip->ip_hl, pip->ip_len, tc->th_seq,
tc->th_off);
}
{
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);
}
tc->th_sum = 0;
#ifdef _KERNEL
tcp_set_flags(tc, tcp_get_flags(tc) | TH_RES1);
#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
_Alignas(_Alignof(u_short)) 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;
if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
return;
{
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;
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);
}
{
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
}
int
ProxyCheck(struct libalias *la, struct in_addr *proxy_server_addr,
u_short * proxy_server_port, struct in_addr src_addr,
struct in_addr dst_addr, u_short dst_port, u_char ip_p)
{
struct proxy_entry *ptr;
LIBALIAS_LOCK_ASSERT(la);
ptr = la->proxyList;
while (ptr != NULL) {
u_short proxy_port;
proxy_port = ptr->proxy_port;
if ((dst_port == proxy_port || proxy_port == 0)
&& 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)
{
LIBALIAS_LOCK_ASSERT(la);
(void)la;
switch (proxy_type) {
case PROXY_TYPE_ENCODE_IPHDR:
ProxyEncodeIpHeader(pip, maxpacketsize);
break;
case PROXY_TYPE_ENCODE_TCPSTREAM:
ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
break;
}
}
int
LibAliasProxyRule(struct libalias *la, const char *cmd)
{
int i, n, len, ret;
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;
LIBALIAS_LOCK(la);
ret = 0;
cmd += strspn(cmd, " \t");
cmd_len = strlen(cmd);
if (cmd_len > (int)(sizeof(buffer) - 1)) {
ret = -1;
goto getout;
}
strcpy(buffer, cmd);
len = strlen(buffer);
for (i = 0; i < len; i++)
buffer[i] = tolower((unsigned char)buffer[i]);
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;
#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 {
ret = -1;
goto getout;
}
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 {
ret = -1;
goto getout;
}
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) {
ret = -1;
goto getout;
}
} else {
*p = ' ';
n = sscanf(token, "%s %s", s, str_server_port);
if (n != 2) {
ret = -1;
goto getout;
}
err = IpAddr(s, &server_addr);
if (err) {
ret = -1;
goto getout;
}
}
state = STATE_READ_KEYWORD;
break;
}
case STATE_READ_RULE:
n = sscanf(token, "%d", &rule_index);
if (n != 1 || rule_index < 0) {
ret = -1;
goto getout;
}
state = STATE_READ_KEYWORD;
break;
case STATE_READ_DELETE: {
int err;
int rule_to_delete;
if (token_count != 2) {
ret = -1;
goto getout;
}
n = sscanf(token, "%d", &rule_to_delete);
if (n != 1) {
ret = -1;
goto getout;
}
err = RuleNumberDelete(la, rule_to_delete);
if (err)
ret = -1;
else
ret = 0;
goto getout;
}
case STATE_READ_PROTO:
if (strcmp(token, "tcp") == 0)
proto = IPPROTO_TCP;
else if (strcmp(token, "udp") == 0)
proto = IPPROTO_UDP;
else {
ret = -1;
goto getout;
}
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) {
ret = -1;
goto getout;
}
} else {
int nbits;
char s[sizeof(buffer)];
*p = ' ';
n = sscanf(token, "%s %d", s, &nbits);
if (n != 2) {
ret = -1;
goto getout;
}
err = IpAddr(s, &addr);
if (err) {
ret = -1;
goto getout;
}
err = IpMask(nbits, &mask);
if (err) {
ret = -1;
goto getout;
}
}
if (state == STATE_READ_SRC) {
src_addr = addr;
src_mask = mask;
} else {
dst_addr = addr;
dst_mask = mask;
}
state = STATE_READ_KEYWORD;
break;
}
default:
ret = -1;
goto getout;
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
if (strlen(str_port) != 0) {
int err;
err = IpPort(str_port, proto, &proxy_port);
if (err) {
ret = -1;
goto getout;
}
} else {
proxy_port = 0;
}
if (strlen(str_server_port) != 0) {
int err;
err = IpPort(str_server_port, proto, &server_port);
if (err) {
ret = -1;
goto getout;
}
} else {
server_port = 0;
}
if (server_addr.s_addr == 0) {
ret = -1;
goto getout;
}
proxy_entry = malloc(sizeof(struct proxy_entry));
if (proxy_entry == NULL) {
ret = -1;
goto getout;
}
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);
getout:
LIBALIAS_UNLOCK(la);
return (ret);
}