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