alias_irc.c revision 127094
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 127094 2004-03-16 21:30:41Z des $"); 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 4426026Sbrian Version 2.1: May, 1997 (cjm) 4526026Sbrian Very minor changes to conform with 4626026Sbrian local/global/function naming conventions 4726026Sbrian withing the packet alising module. 4826026Sbrian*/ 4926026Sbrian 5026026Sbrian/* Includes */ 5126026Sbrian#include <ctype.h> 5299207Sbrian#include <stdio.h> 5326026Sbrian#include <string.h> 5426026Sbrian#include <sys/types.h> 5526026Sbrian#include <netinet/in_systm.h> 5626026Sbrian#include <netinet/in.h> 5726026Sbrian#include <netinet/ip.h> 5826026Sbrian#include <netinet/tcp.h> 5926026Sbrian#include <limits.h> 6026026Sbrian 6126026Sbrian#include "alias_local.h" 6226026Sbrian 6326026Sbrian/* Local defines */ 6426026Sbrian#define DBprintf(a) 6526026Sbrian 6626026Sbrian 6726026Sbrianvoid 68127094SdesAliasHandleIrcOut(struct libalias *la, 69127094Sdes struct ip *pip, /* IP packet to examine */ 70127094Sdes struct alias_link *link, /* Which link are we on? */ 71127094Sdes int maxsize /* Maximum size of IP packet including 72127094Sdes * headers */ 73127094Sdes) 7499207Sbrian{ 75127094Sdes int hlen, tlen, dlen; 76127094Sdes struct in_addr true_addr; 77127094Sdes u_short true_port; 78127094Sdes char *sptr; 79127094Sdes struct tcphdr *tc; 80127094Sdes int i; /* Iterator through the source */ 8199207Sbrian 8226026Sbrian/* Calculate data length of TCP packet */ 83127094Sdes tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2)); 84127094Sdes hlen = (pip->ip_hl + tc->th_off) << 2; 85127094Sdes tlen = ntohs(pip->ip_len); 86127094Sdes dlen = tlen - hlen; 8726026Sbrian 88127094Sdes /* 89127094Sdes * Return if data length is too short - assume an entire PRIVMSG in 90127094Sdes * each packet. 91127094Sdes */ 92127094Sdes if (dlen < sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a") - 1) 93127094Sdes return; 9426026Sbrian 9526026Sbrian/* Place string pointer at beginning of data */ 96127094Sdes sptr = (char *)pip; 97127094Sdes sptr += hlen; 98127094Sdes maxsize -= hlen; /* We're interested in maximum size of 99127094Sdes * data, not packet */ 10026026Sbrian 101127094Sdes /* Search for a CTCP command [Note 1] */ 102127094Sdes for (i = 0; i < dlen; i++) { 103127094Sdes if (sptr[i] == '\001') 104127094Sdes goto lFOUND_CTCP; 105127094Sdes } 106127094Sdes return; /* No CTCP commands in */ 107127094Sdes /* Handle CTCP commands - the buffer may have to be copied */ 10826026SbrianlFOUND_CTCP: 109127094Sdes { 110127094Sdes char newpacket[65536]; /* Estimate of maximum packet size 111127094Sdes * :) */ 112127094Sdes int copyat = i; /* Same */ 113127094Sdes int iCopy = 0; /* How much data have we written to 114127094Sdes * copy-back string? */ 115127094Sdes unsigned long org_addr; /* Original IP address */ 116127094Sdes unsigned short org_port; /* Original source port 117127094Sdes * address */ 11826026Sbrian 119127094SdeslCTCP_START: 120127094Sdes if (i >= dlen || iCopy >= sizeof(newpacket)) 121127094Sdes goto lPACKET_DONE; 122127094Sdes newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start 123127094Sdes * character */ 124127094Sdes /* Start of a CTCP */ 125127094Sdes if (i + 4 >= dlen) /* Too short for DCC */ 126127094Sdes goto lBAD_CTCP; 127127094Sdes if (sptr[i + 0] != 'D') 128127094Sdes goto lBAD_CTCP; 129127094Sdes if (sptr[i + 1] != 'C') 130127094Sdes goto lBAD_CTCP; 131127094Sdes if (sptr[i + 2] != 'C') 132127094Sdes goto lBAD_CTCP; 133127094Sdes if (sptr[i + 3] != ' ') 134127094Sdes goto lBAD_CTCP; 135127094Sdes /* We have a DCC command - handle it! */ 136127094Sdes i += 4; /* Skip "DCC " */ 137127094Sdes if (iCopy + 4 > sizeof(newpacket)) 138127094Sdes goto lPACKET_DONE; 139127094Sdes newpacket[iCopy++] = 'D'; 140127094Sdes newpacket[iCopy++] = 'C'; 141127094Sdes newpacket[iCopy++] = 'C'; 142127094Sdes newpacket[iCopy++] = ' '; 14326026Sbrian 144127094Sdes DBprintf(("Found DCC\n")); 145127094Sdes /* 146127094Sdes * Skip any extra spaces (should not occur according to 147127094Sdes * protocol, but DCC breaks CTCP protocol anyway 148127094Sdes */ 149127094Sdes while (sptr[i] == ' ') { 150127094Sdes if (++i >= dlen) { 151127094Sdes DBprintf(("DCC packet terminated in just spaces\n")); 152127094Sdes goto lPACKET_DONE; 153127094Sdes } 154127094Sdes } 15526026Sbrian 156127094Sdes DBprintf(("Transferring command...\n")); 157127094Sdes while (sptr[i] != ' ') { 158127094Sdes newpacket[iCopy++] = sptr[i]; 159127094Sdes if (++i >= dlen || iCopy >= sizeof(newpacket)) { 160127094Sdes DBprintf(("DCC packet terminated during command\n")); 161127094Sdes goto lPACKET_DONE; 162127094Sdes } 163127094Sdes } 164127094Sdes /* Copy _one_ space */ 165127094Sdes if (i + 1 < dlen && iCopy < sizeof(newpacket)) 166127094Sdes newpacket[iCopy++] = sptr[i++]; 16726026Sbrian 168127094Sdes DBprintf(("Done command - removing spaces\n")); 169127094Sdes /* 170127094Sdes * Skip any extra spaces (should not occur according to 171127094Sdes * protocol, but DCC breaks CTCP protocol anyway 172127094Sdes */ 173127094Sdes while (sptr[i] == ' ') { 174127094Sdes if (++i >= dlen) { 175127094Sdes DBprintf(("DCC packet terminated in just spaces (post-command)\n")); 176127094Sdes goto lPACKET_DONE; 177127094Sdes } 178127094Sdes } 17926026Sbrian 180127094Sdes DBprintf(("Transferring filename...\n")); 181127094Sdes while (sptr[i] != ' ') { 182127094Sdes newpacket[iCopy++] = sptr[i]; 183127094Sdes if (++i >= dlen || iCopy >= sizeof(newpacket)) { 184127094Sdes DBprintf(("DCC packet terminated during filename\n")); 185127094Sdes goto lPACKET_DONE; 186127094Sdes } 187127094Sdes } 188127094Sdes /* Copy _one_ space */ 189127094Sdes if (i + 1 < dlen && iCopy < sizeof(newpacket)) 190127094Sdes newpacket[iCopy++] = sptr[i++]; 19126026Sbrian 192127094Sdes DBprintf(("Done filename - removing spaces\n")); 193127094Sdes /* 194127094Sdes * Skip any extra spaces (should not occur according to 195127094Sdes * protocol, but DCC breaks CTCP protocol anyway 196127094Sdes */ 197127094Sdes while (sptr[i] == ' ') { 198127094Sdes if (++i >= dlen) { 199127094Sdes DBprintf(("DCC packet terminated in just spaces (post-filename)\n")); 200127094Sdes goto lPACKET_DONE; 201127094Sdes } 202127094Sdes } 20326026Sbrian 204127094Sdes DBprintf(("Fetching IP address\n")); 205127094Sdes /* Fetch IP address */ 206127094Sdes org_addr = 0; 207127094Sdes while (i < dlen && isdigit(sptr[i])) { 208127094Sdes if (org_addr > ULONG_MAX / 10UL) { /* Terminate on overflow */ 209127094Sdes DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i])); 210127094Sdes goto lBAD_CTCP; 211127094Sdes } 212127094Sdes org_addr *= 10; 213127094Sdes org_addr += sptr[i++] - '0'; 214127094Sdes } 215127094Sdes DBprintf(("Skipping space\n")); 216127094Sdes if (i + 1 >= dlen || sptr[i] != ' ') { 217127094Sdes DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i + 1, dlen, sptr[i])); 218127094Sdes goto lBAD_CTCP; 219127094Sdes } 220127094Sdes /* 221127094Sdes * Skip any extra spaces (should not occur according to 222127094Sdes * protocol, but DCC breaks CTCP protocol anyway, so we 223127094Sdes * might as well play it safe 224127094Sdes */ 225127094Sdes while (sptr[i] == ' ') { 226127094Sdes if (++i >= dlen) { 227127094Sdes DBprintf(("Packet failure - space overflow.\n")); 228127094Sdes goto lPACKET_DONE; 229127094Sdes } 230127094Sdes } 231127094Sdes DBprintf(("Fetching port number\n")); 232127094Sdes /* Fetch source port */ 233127094Sdes org_port = 0; 234127094Sdes while (i < dlen && isdigit(sptr[i])) { 235127094Sdes if (org_port > 6554) { /* Terminate on overflow 236127094Sdes * (65536/10 rounded up */ 237127094Sdes DBprintf(("DCC: port number overflow\n")); 238127094Sdes goto lBAD_CTCP; 239127094Sdes } 240127094Sdes org_port *= 10; 241127094Sdes org_port += sptr[i++] - '0'; 242127094Sdes } 243127094Sdes /* Skip illegal addresses (or early termination) */ 244127094Sdes if (i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ')) { 245127094Sdes DBprintf(("Bad port termination\n")); 246127094Sdes goto lBAD_CTCP; 247127094Sdes } 248127094Sdes DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port)); 24926026Sbrian 250127094Sdes /* We've got the address and port - now alias it */ 251127094Sdes { 252127094Sdes struct alias_link *dcc_link; 253127094Sdes struct in_addr destaddr; 25499207Sbrian 25526026Sbrian 256127094Sdes true_port = htons(org_port); 257127094Sdes true_addr.s_addr = htonl(org_addr); 258127094Sdes destaddr.s_addr = 0; 25982050Sru 260127094Sdes /* Sanity/Security checking */ 261127094Sdes if (!org_addr || !org_port || 262127094Sdes pip->ip_src.s_addr != true_addr.s_addr || 263127094Sdes org_port < IPPORT_RESERVED) 264127094Sdes goto lBAD_CTCP; 26526026Sbrian 266127094Sdes /* 267127094Sdes * Steal the FTP_DATA_PORT - it doesn't really 268127094Sdes * matter, and this would probably allow it through 269127094Sdes * at least _some_ firewalls. 270127094Sdes */ 271127094Sdes dcc_link = FindUdpTcpOut(la, true_addr, destaddr, 272127094Sdes true_port, 0, 273127094Sdes IPPROTO_TCP, 1); 274127094Sdes DBprintf(("Got a DCC link\n")); 275127094Sdes if (dcc_link) { 276127094Sdes struct in_addr alias_address; /* Address from aliasing */ 277127094Sdes u_short alias_port; /* Port given by 278127094Sdes * aliasing */ 279127094Sdes int n; 280127094Sdes 28136711Sbrian#ifndef NO_FW_PUNCH 282127094Sdes /* Generate firewall hole as appropriate */ 283127094Sdes PunchFWHole(dcc_link); 28436711Sbrian#endif 28532377Seivind 286127094Sdes alias_address = GetAliasAddress(link); 287127094Sdes n = snprintf(&newpacket[iCopy], 288127094Sdes sizeof(newpacket) - iCopy, 289127094Sdes "%lu ", (u_long) htonl(alias_address.s_addr)); 290127094Sdes if (n < 0) { 291127094Sdes DBprintf(("DCC packet construct failure.\n")); 292127094Sdes goto lBAD_CTCP; 293127094Sdes } 294127094Sdes if ((iCopy += n) >= sizeof(newpacket)) { /* Truncated/fit exactly 295127094Sdes * - bad news */ 296127094Sdes DBprintf(("DCC constructed packet overflow.\n")); 297127094Sdes goto lBAD_CTCP; 298127094Sdes } 299127094Sdes alias_port = GetAliasPort(dcc_link); 300127094Sdes n = snprintf(&newpacket[iCopy], 301127094Sdes sizeof(newpacket) - iCopy, 302127094Sdes "%u", htons(alias_port)); 303127094Sdes if (n < 0) { 304127094Sdes DBprintf(("DCC packet construct failure.\n")); 305127094Sdes goto lBAD_CTCP; 306127094Sdes } 307127094Sdes iCopy += n; 308127094Sdes /* 309127094Sdes * Done - truncated cases will be taken 310127094Sdes * care of by lBAD_CTCP 311127094Sdes */ 312127094Sdes DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port)); 313127094Sdes } 314127094Sdes } 315127094Sdes /* 316127094Sdes * An uninteresting CTCP - state entered right after '\001' 317127094Sdes * has been pushed. Also used to copy the rest of a DCC, 318127094Sdes * after IP address and port has been handled 319127094Sdes */ 320127094SdeslBAD_CTCP: 321127094Sdes for (; i < dlen && iCopy < sizeof(newpacket); i++, iCopy++) { 322127094Sdes newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 323127094Sdes if (sptr[i] == '\001') { 324127094Sdes goto lNORMAL_TEXT; 325127094Sdes } 326127094Sdes } 327127094Sdes goto lPACKET_DONE; 328127094Sdes /* Normal text */ 329127094SdeslNORMAL_TEXT: 330127094Sdes for (; i < dlen && iCopy < sizeof(newpacket); i++, iCopy++) { 331127094Sdes newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 332127094Sdes if (sptr[i] == '\001') { 333127094Sdes goto lCTCP_START; 334127094Sdes } 335127094Sdes } 336127094Sdes /* Handle the end of a packet */ 337127094SdeslPACKET_DONE: 338127094Sdes iCopy = iCopy > maxsize - copyat ? maxsize - copyat : iCopy; 339127094Sdes memcpy(sptr + copyat, newpacket, iCopy); 34026026Sbrian 34126026Sbrian/* Save information regarding modified seq and ack numbers */ 342127094Sdes { 343127094Sdes int delta; 34426026Sbrian 345127094Sdes SetAckModified(link); 346127094Sdes delta = GetDeltaSeqOut(pip, link); 347127094Sdes AddSeq(pip, link, delta + copyat + iCopy - dlen); 348127094Sdes } 34926026Sbrian 350127094Sdes /* Revise IP header */ 351127094Sdes { 352127094Sdes u_short new_len; 35399207Sbrian 354127094Sdes new_len = htons(hlen + iCopy + copyat); 355127094Sdes DifferentialChecksum(&pip->ip_sum, 356127094Sdes &new_len, 357127094Sdes &pip->ip_len, 358127094Sdes 1); 359127094Sdes pip->ip_len = new_len; 360127094Sdes } 36126026Sbrian 362127094Sdes /* Compute TCP checksum for revised packet */ 363127094Sdes tc->th_sum = 0; 364127094Sdes tc->th_sum = TcpChecksum(pip); 365127094Sdes return; 366127094Sdes } 36726026Sbrian} 36826026Sbrian 36926026Sbrian/* Notes: 37026026Sbrian [Note 1] 37126026Sbrian The initial search will most often fail; it could be replaced with a 32-bit specific search. 37226026Sbrian Such a search would be done for 32-bit unsigned value V: 37326026Sbrian V ^= 0x01010101; (Search is for null bytes) 37426026Sbrian if( ((V-0x01010101)^V) & 0x80808080 ) { 37526026Sbrian (found a null bytes which was a 01 byte) 37626026Sbrian } 37726026Sbrian To assert that the processor is 32-bits, do 37826026Sbrian extern int ircdccar[32]; (32 bits) 37926026Sbrian extern int ircdccar[CHAR_BIT*sizeof(unsigned int)]; 38026026Sbrian which will generate a type-error on all but 32-bit machines. 38126026Sbrian 38226026Sbrian [Note 2] This routine really ought to be replaced with one that 38326026Sbrian creates a transparent proxy on the aliasing host, to allow arbitary 38426026Sbrian changes in the TCP stream. This should not be too difficult given 38526026Sbrian this base; I (ee) will try to do this some time later. 38626026Sbrian */ 387