alias_irc.c revision 147623
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: head/sys/netinet/libalias/alias_irc.c 147623 2005-06-27 07:36:02Z glebius $"); 2984195Sdillon 3026026Sbrian/* Alias_irc.c intercepts packages contain IRC CTCP commands, and 3126026Sbrian changes DCC commands to export a port on the aliasing host instead 3226026Sbrian of an aliased host. 3326026Sbrian 3426026Sbrian For this routine to work, the DCC command must fit entirely into a 3526026Sbrian single TCP packet. This will usually happen, but is not 3626026Sbrian guaranteed. 3726026Sbrian 3826026Sbrian The interception is likely to change the length of the packet. 3926026Sbrian The handling of this is copied more-or-less verbatim from 4026026Sbrian ftp_alias.c 4126026Sbrian 4226026Sbrian Initial version: Eivind Eklund <perhaps@yes.no> (ee) 97-01-29 4326026Sbrian 44131612Sdes Version 2.1: May, 1997 (cjm) 45131612Sdes Very minor changes to conform with 46131612Sdes local/global/function naming conventions 47131612Sdes withing the packet alising module. 4826026Sbrian*/ 4926026Sbrian 5026026Sbrian/* Includes */ 51145921Sglebius#ifdef _KERNEL 52145921Sglebius#include <sys/param.h> 53145921Sglebius#include <sys/libkern.h> 54145921Sglebius#include <sys/ctype.h> 55145921Sglebius#include <sys/limits.h> 56145921Sglebius#else 57145921Sglebius#include <sys/types.h> 5826026Sbrian#include <ctype.h> 5999207Sbrian#include <stdio.h> 6026026Sbrian#include <string.h> 61145921Sglebius#include <limits.h> 62145921Sglebius#endif 63145921Sglebius 6426026Sbrian#include <netinet/in_systm.h> 6526026Sbrian#include <netinet/in.h> 6626026Sbrian#include <netinet/ip.h> 6726026Sbrian#include <netinet/tcp.h> 6826026Sbrian 69145921Sglebius#ifdef _KERNEL 70145932Sglebius#include <netinet/libalias/alias.h> 71145921Sglebius#include <netinet/libalias/alias_local.h> 72145921Sglebius#else 7326026Sbrian#include "alias_local.h" 74145921Sglebius#endif 7526026Sbrian 7626026Sbrian/* Local defines */ 7726026Sbrian#define DBprintf(a) 7826026Sbrian 7926026Sbrian 8026026Sbrianvoid 81127094SdesAliasHandleIrcOut(struct libalias *la, 82127094Sdes struct ip *pip, /* IP packet to examine */ 83131614Sdes struct alias_link *lnk, /* Which link are we on? */ 84127094Sdes int maxsize /* Maximum size of IP packet including 85127094Sdes * headers */ 86127094Sdes) 8799207Sbrian{ 88127094Sdes int hlen, tlen, dlen; 89127094Sdes struct in_addr true_addr; 90127094Sdes u_short true_port; 91127094Sdes char *sptr; 92127094Sdes struct tcphdr *tc; 93127094Sdes int i; /* Iterator through the source */ 9499207Sbrian 9526026Sbrian/* Calculate data length of TCP packet */ 96131699Sdes tc = (struct tcphdr *)ip_next(pip); 97127094Sdes hlen = (pip->ip_hl + tc->th_off) << 2; 98127094Sdes tlen = ntohs(pip->ip_len); 99127094Sdes dlen = tlen - hlen; 10026026Sbrian 101127094Sdes /* 102127094Sdes * Return if data length is too short - assume an entire PRIVMSG in 103127094Sdes * each packet. 104127094Sdes */ 105131614Sdes if (dlen < (int)sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a") - 1) 106127094Sdes return; 10726026Sbrian 10826026Sbrian/* Place string pointer at beginning of data */ 109127094Sdes sptr = (char *)pip; 110127094Sdes sptr += hlen; 111127094Sdes maxsize -= hlen; /* We're interested in maximum size of 112127094Sdes * data, not packet */ 11326026Sbrian 114127094Sdes /* Search for a CTCP command [Note 1] */ 115127094Sdes for (i = 0; i < dlen; i++) { 116127094Sdes if (sptr[i] == '\001') 117127094Sdes goto lFOUND_CTCP; 118127094Sdes } 119127094Sdes return; /* No CTCP commands in */ 120127094Sdes /* Handle CTCP commands - the buffer may have to be copied */ 12126026SbrianlFOUND_CTCP: 122127094Sdes { 123127094Sdes char newpacket[65536]; /* Estimate of maximum packet size 124127094Sdes * :) */ 125131614Sdes unsigned int copyat = i; /* Same */ 126131614Sdes unsigned int iCopy = 0; /* How much data have we written to 127131614Sdes * copy-back string? */ 128127094Sdes unsigned long org_addr; /* Original IP address */ 129127094Sdes unsigned short org_port; /* Original source port 130127094Sdes * address */ 13126026Sbrian 132127094SdeslCTCP_START: 133127094Sdes if (i >= dlen || iCopy >= sizeof(newpacket)) 134127094Sdes goto lPACKET_DONE; 135127094Sdes newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start 136127094Sdes * character */ 137127094Sdes /* Start of a CTCP */ 138127094Sdes if (i + 4 >= dlen) /* Too short for DCC */ 139127094Sdes goto lBAD_CTCP; 140127094Sdes if (sptr[i + 0] != 'D') 141127094Sdes goto lBAD_CTCP; 142127094Sdes if (sptr[i + 1] != 'C') 143127094Sdes goto lBAD_CTCP; 144127094Sdes if (sptr[i + 2] != 'C') 145127094Sdes goto lBAD_CTCP; 146127094Sdes if (sptr[i + 3] != ' ') 147127094Sdes goto lBAD_CTCP; 148127094Sdes /* We have a DCC command - handle it! */ 149127094Sdes i += 4; /* Skip "DCC " */ 150127094Sdes if (iCopy + 4 > sizeof(newpacket)) 151127094Sdes goto lPACKET_DONE; 152127094Sdes newpacket[iCopy++] = 'D'; 153127094Sdes newpacket[iCopy++] = 'C'; 154127094Sdes newpacket[iCopy++] = 'C'; 155127094Sdes newpacket[iCopy++] = ' '; 15626026Sbrian 157127094Sdes DBprintf(("Found DCC\n")); 158127094Sdes /* 159127094Sdes * Skip any extra spaces (should not occur according to 160127094Sdes * protocol, but DCC breaks CTCP protocol anyway 161127094Sdes */ 162127094Sdes while (sptr[i] == ' ') { 163127094Sdes if (++i >= dlen) { 164127094Sdes DBprintf(("DCC packet terminated in just spaces\n")); 165127094Sdes goto lPACKET_DONE; 166127094Sdes } 167127094Sdes } 16826026Sbrian 169127094Sdes DBprintf(("Transferring command...\n")); 170127094Sdes while (sptr[i] != ' ') { 171127094Sdes newpacket[iCopy++] = sptr[i]; 172127094Sdes if (++i >= dlen || iCopy >= sizeof(newpacket)) { 173127094Sdes DBprintf(("DCC packet terminated during command\n")); 174127094Sdes goto lPACKET_DONE; 175127094Sdes } 176127094Sdes } 177127094Sdes /* Copy _one_ space */ 178127094Sdes if (i + 1 < dlen && iCopy < sizeof(newpacket)) 179127094Sdes newpacket[iCopy++] = sptr[i++]; 18026026Sbrian 181127094Sdes DBprintf(("Done command - removing spaces\n")); 182127094Sdes /* 183127094Sdes * Skip any extra spaces (should not occur according to 184127094Sdes * protocol, but DCC breaks CTCP protocol anyway 185127094Sdes */ 186127094Sdes while (sptr[i] == ' ') { 187127094Sdes if (++i >= dlen) { 188127094Sdes DBprintf(("DCC packet terminated in just spaces (post-command)\n")); 189127094Sdes goto lPACKET_DONE; 190127094Sdes } 191127094Sdes } 19226026Sbrian 193127094Sdes DBprintf(("Transferring filename...\n")); 194127094Sdes while (sptr[i] != ' ') { 195127094Sdes newpacket[iCopy++] = sptr[i]; 196127094Sdes if (++i >= dlen || iCopy >= sizeof(newpacket)) { 197127094Sdes DBprintf(("DCC packet terminated during filename\n")); 198127094Sdes goto lPACKET_DONE; 199127094Sdes } 200127094Sdes } 201127094Sdes /* Copy _one_ space */ 202127094Sdes if (i + 1 < dlen && iCopy < sizeof(newpacket)) 203127094Sdes newpacket[iCopy++] = sptr[i++]; 20426026Sbrian 205127094Sdes DBprintf(("Done filename - removing spaces\n")); 206127094Sdes /* 207127094Sdes * Skip any extra spaces (should not occur according to 208127094Sdes * protocol, but DCC breaks CTCP protocol anyway 209127094Sdes */ 210127094Sdes while (sptr[i] == ' ') { 211127094Sdes if (++i >= dlen) { 212127094Sdes DBprintf(("DCC packet terminated in just spaces (post-filename)\n")); 213127094Sdes goto lPACKET_DONE; 214127094Sdes } 215127094Sdes } 21626026Sbrian 217127094Sdes DBprintf(("Fetching IP address\n")); 218127094Sdes /* Fetch IP address */ 219127094Sdes org_addr = 0; 220127094Sdes while (i < dlen && isdigit(sptr[i])) { 221127094Sdes if (org_addr > ULONG_MAX / 10UL) { /* Terminate on overflow */ 222127094Sdes DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i])); 223127094Sdes goto lBAD_CTCP; 224127094Sdes } 225127094Sdes org_addr *= 10; 226127094Sdes org_addr += sptr[i++] - '0'; 227127094Sdes } 228127094Sdes DBprintf(("Skipping space\n")); 229127094Sdes if (i + 1 >= dlen || sptr[i] != ' ') { 230127094Sdes DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i + 1, dlen, sptr[i])); 231127094Sdes goto lBAD_CTCP; 232127094Sdes } 233127094Sdes /* 234127094Sdes * Skip any extra spaces (should not occur according to 235127094Sdes * protocol, but DCC breaks CTCP protocol anyway, so we 236127094Sdes * might as well play it safe 237127094Sdes */ 238127094Sdes while (sptr[i] == ' ') { 239127094Sdes if (++i >= dlen) { 240127094Sdes DBprintf(("Packet failure - space overflow.\n")); 241127094Sdes goto lPACKET_DONE; 242127094Sdes } 243127094Sdes } 244127094Sdes DBprintf(("Fetching port number\n")); 245127094Sdes /* Fetch source port */ 246127094Sdes org_port = 0; 247127094Sdes while (i < dlen && isdigit(sptr[i])) { 248127094Sdes if (org_port > 6554) { /* Terminate on overflow 249127094Sdes * (65536/10 rounded up */ 250127094Sdes DBprintf(("DCC: port number overflow\n")); 251127094Sdes goto lBAD_CTCP; 252127094Sdes } 253127094Sdes org_port *= 10; 254127094Sdes org_port += sptr[i++] - '0'; 255127094Sdes } 256127094Sdes /* Skip illegal addresses (or early termination) */ 257127094Sdes if (i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ')) { 258127094Sdes DBprintf(("Bad port termination\n")); 259127094Sdes goto lBAD_CTCP; 260127094Sdes } 261127094Sdes DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port)); 26226026Sbrian 263127094Sdes /* We've got the address and port - now alias it */ 264127094Sdes { 265131614Sdes struct alias_link *dcc_lnk; 266127094Sdes struct in_addr destaddr; 26799207Sbrian 26826026Sbrian 269127094Sdes true_port = htons(org_port); 270127094Sdes true_addr.s_addr = htonl(org_addr); 271127094Sdes destaddr.s_addr = 0; 27282050Sru 273127094Sdes /* Sanity/Security checking */ 274127094Sdes if (!org_addr || !org_port || 275127094Sdes pip->ip_src.s_addr != true_addr.s_addr || 276127094Sdes org_port < IPPORT_RESERVED) 277127094Sdes goto lBAD_CTCP; 27826026Sbrian 279127094Sdes /* 280127094Sdes * Steal the FTP_DATA_PORT - it doesn't really 281127094Sdes * matter, and this would probably allow it through 282127094Sdes * at least _some_ firewalls. 283127094Sdes */ 284131614Sdes dcc_lnk = FindUdpTcpOut(la, true_addr, destaddr, 285127094Sdes true_port, 0, 286127094Sdes IPPROTO_TCP, 1); 287127094Sdes DBprintf(("Got a DCC link\n")); 288131614Sdes if (dcc_lnk) { 289127094Sdes struct in_addr alias_address; /* Address from aliasing */ 290127094Sdes u_short alias_port; /* Port given by 291127094Sdes * aliasing */ 292127094Sdes int n; 293127094Sdes 29436711Sbrian#ifndef NO_FW_PUNCH 295127094Sdes /* Generate firewall hole as appropriate */ 296131614Sdes PunchFWHole(dcc_lnk); 29736711Sbrian#endif 29832377Seivind 299131614Sdes alias_address = GetAliasAddress(lnk); 300127094Sdes n = snprintf(&newpacket[iCopy], 301127094Sdes sizeof(newpacket) - iCopy, 302127094Sdes "%lu ", (u_long) htonl(alias_address.s_addr)); 303127094Sdes if (n < 0) { 304127094Sdes DBprintf(("DCC packet construct failure.\n")); 305127094Sdes goto lBAD_CTCP; 306127094Sdes } 307127094Sdes if ((iCopy += n) >= sizeof(newpacket)) { /* Truncated/fit exactly 308127094Sdes * - bad news */ 309127094Sdes DBprintf(("DCC constructed packet overflow.\n")); 310127094Sdes goto lBAD_CTCP; 311127094Sdes } 312131614Sdes alias_port = GetAliasPort(dcc_lnk); 313127094Sdes n = snprintf(&newpacket[iCopy], 314127094Sdes sizeof(newpacket) - iCopy, 315127094Sdes "%u", htons(alias_port)); 316127094Sdes if (n < 0) { 317127094Sdes DBprintf(("DCC packet construct failure.\n")); 318127094Sdes goto lBAD_CTCP; 319127094Sdes } 320127094Sdes iCopy += n; 321127094Sdes /* 322127094Sdes * Done - truncated cases will be taken 323127094Sdes * care of by lBAD_CTCP 324127094Sdes */ 325127094Sdes DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port)); 326127094Sdes } 327127094Sdes } 328127094Sdes /* 329127094Sdes * An uninteresting CTCP - state entered right after '\001' 330127094Sdes * has been pushed. Also used to copy the rest of a DCC, 331127094Sdes * after IP address and port has been handled 332127094Sdes */ 333127094SdeslBAD_CTCP: 334127094Sdes for (; i < dlen && iCopy < sizeof(newpacket); i++, iCopy++) { 335127094Sdes newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 336127094Sdes if (sptr[i] == '\001') { 337127094Sdes goto lNORMAL_TEXT; 338127094Sdes } 339127094Sdes } 340127094Sdes goto lPACKET_DONE; 341127094Sdes /* Normal text */ 342127094SdeslNORMAL_TEXT: 343127094Sdes for (; i < dlen && iCopy < sizeof(newpacket); i++, iCopy++) { 344127094Sdes newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 345127094Sdes if (sptr[i] == '\001') { 346127094Sdes goto lCTCP_START; 347127094Sdes } 348127094Sdes } 349127094Sdes /* Handle the end of a packet */ 350127094SdeslPACKET_DONE: 351127094Sdes iCopy = iCopy > maxsize - copyat ? maxsize - copyat : iCopy; 352127094Sdes memcpy(sptr + copyat, newpacket, iCopy); 35326026Sbrian 35426026Sbrian/* Save information regarding modified seq and ack numbers */ 355127094Sdes { 356127094Sdes int delta; 35726026Sbrian 358131614Sdes SetAckModified(lnk); 359131614Sdes delta = GetDeltaSeqOut(pip, lnk); 360131614Sdes AddSeq(pip, lnk, delta + copyat + iCopy - dlen); 361127094Sdes } 36226026Sbrian 363127094Sdes /* Revise IP header */ 364127094Sdes { 365127094Sdes u_short new_len; 36699207Sbrian 367127094Sdes new_len = htons(hlen + iCopy + copyat); 368127094Sdes DifferentialChecksum(&pip->ip_sum, 369127094Sdes &new_len, 370127094Sdes &pip->ip_len, 371127094Sdes 1); 372127094Sdes pip->ip_len = new_len; 373127094Sdes } 37426026Sbrian 375127094Sdes /* Compute TCP checksum for revised packet */ 376127094Sdes tc->th_sum = 0; 377147623Sglebius#ifdef _KERNEL 378147623Sglebius tc->th_x2 = 1; 379147623Sglebius#else 380127094Sdes tc->th_sum = TcpChecksum(pip); 381147623Sglebius#endif 382127094Sdes return; 383127094Sdes } 38426026Sbrian} 38526026Sbrian 38626026Sbrian/* Notes: 38726026Sbrian [Note 1] 38826026Sbrian The initial search will most often fail; it could be replaced with a 32-bit specific search. 38926026Sbrian Such a search would be done for 32-bit unsigned value V: 39026026Sbrian V ^= 0x01010101; (Search is for null bytes) 39126026Sbrian if( ((V-0x01010101)^V) & 0x80808080 ) { 39226026Sbrian (found a null bytes which was a 01 byte) 39326026Sbrian } 39426026Sbrian To assert that the processor is 32-bits, do 39526026Sbrian extern int ircdccar[32]; (32 bits) 39626026Sbrian extern int ircdccar[CHAR_BIT*sizeof(unsigned int)]; 39726026Sbrian which will generate a type-error on all but 32-bit machines. 39826026Sbrian 39926026Sbrian [Note 2] This routine really ought to be replaced with one that 40026026Sbrian creates a transparent proxy on the aliasing host, to allow arbitary 40126026Sbrian changes in the TCP stream. This should not be too difficult given 40226026Sbrian this base; I (ee) will try to do this some time later. 40326026Sbrian */ 404