alias_irc.c revision 162674
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 162674 2006-09-26 23:26:53Z piso $"); 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/ctype.h> 54145921Sglebius#include <sys/limits.h> 55162674Spiso#include <sys/systm.h> 56162674Spiso#include <sys/kernel.h> 57162674Spiso#include <sys/module.h> 58145921Sglebius#else 59162674Spiso#include <errno.h> 60145921Sglebius#include <sys/types.h> 6199207Sbrian#include <stdio.h> 62145921Sglebius#include <limits.h> 63145921Sglebius#endif 64145921Sglebius 6526026Sbrian#include <netinet/in_systm.h> 6626026Sbrian#include <netinet/in.h> 6726026Sbrian#include <netinet/ip.h> 6826026Sbrian#include <netinet/tcp.h> 6926026Sbrian 70145921Sglebius#ifdef _KERNEL 71145932Sglebius#include <netinet/libalias/alias.h> 72145921Sglebius#include <netinet/libalias/alias_local.h> 73162674Spiso#include <netinet/libalias/alias_mod.h> 74145921Sglebius#else 7526026Sbrian#include "alias_local.h" 76162674Spiso#include "alias_mod.h" 77145921Sglebius#endif 7826026Sbrian 79162674Spiso#define IRC_CONTROL_PORT_NUMBER_1 6667 80162674Spiso#define IRC_CONTROL_PORT_NUMBER_2 6668 81162674Spiso 8226026Sbrian/* Local defines */ 8326026Sbrian#define DBprintf(a) 8426026Sbrian 85162674Spisostatic void 86162674SpisoAliasHandleIrcOut(struct libalias *, struct ip *, struct alias_link *, 87162674Spiso int maxpacketsize); 8826026Sbrian 89162674Spisostatic int 90162674Spisofingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah) 91162674Spiso{ 92162674Spiso 93162674Spiso if (ah->dport == NULL || ah->dport == NULL || ah->lnk == NULL || 94162674Spiso ah->maxpktsize == 0) 95162674Spiso return (-1); 96162674Spiso if (ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_1 97162674Spiso || ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_2) 98162674Spiso return (0); 99162674Spiso return (-1); 100162674Spiso} 101162674Spiso 102162674Spisostatic int 103162674Spisoprotohandler(struct libalias *la, struct ip *pip, struct alias_data *ah) 104162674Spiso{ 105162674Spiso 106162674Spiso AliasHandleIrcOut(la, pip, ah->lnk, ah->maxpktsize); 107162674Spiso return (0); 108162674Spiso} 109162674Spiso 110162674Spisostruct proto_handler handlers[] = { 111162674Spiso { 112162674Spiso .pri = 90, 113162674Spiso .dir = OUT, 114162674Spiso .proto = TCP, 115162674Spiso .fingerprint = &fingerprint, 116162674Spiso .protohandler = &protohandler 117162674Spiso }, 118162674Spiso { EOH } 119162674Spiso}; 120162674Spiso 121162674Spisostatic int 122162674Spisomod_handler(module_t mod, int type, void *data) 123162674Spiso{ 124162674Spiso int error; 125162674Spiso 126162674Spiso switch (type) { 127162674Spiso case MOD_LOAD: 128162674Spiso error = 0; 129162674Spiso LibAliasAttachHandlers(handlers); 130162674Spiso break; 131162674Spiso case MOD_UNLOAD: 132162674Spiso error = 0; 133162674Spiso LibAliasDetachHandlers(handlers); 134162674Spiso break; 135162674Spiso default: 136162674Spiso error = EINVAL; 137162674Spiso } 138162674Spiso return (error); 139162674Spiso} 140162674Spiso 141162674Spiso#ifdef _KERNEL 142162674Spisostatic 143162674Spiso#endif 144162674Spisomoduledata_t alias_mod = { 145162674Spiso "alias_irc", mod_handler, NULL 146162674Spiso}; 147162674Spiso 148162674Spiso/* Kernel module definition. */ 149162674Spiso#ifdef _KERNEL 150162674SpisoDECLARE_MODULE(alias_irc, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); 151162674SpisoMODULE_VERSION(alias_irc, 1); 152162674SpisoMODULE_DEPEND(alias_irc, libalias, 1, 1, 1); 153162674Spiso#endif 154162674Spiso 155162674Spisostatic void 156127094SdesAliasHandleIrcOut(struct libalias *la, 157127094Sdes struct ip *pip, /* IP packet to examine */ 158131614Sdes struct alias_link *lnk, /* Which link are we on? */ 159127094Sdes int maxsize /* Maximum size of IP packet including 160127094Sdes * headers */ 161127094Sdes) 16299207Sbrian{ 163127094Sdes int hlen, tlen, dlen; 164127094Sdes struct in_addr true_addr; 165127094Sdes u_short true_port; 166127094Sdes char *sptr; 167127094Sdes struct tcphdr *tc; 168127094Sdes int i; /* Iterator through the source */ 16999207Sbrian 17026026Sbrian/* Calculate data length of TCP packet */ 171131699Sdes tc = (struct tcphdr *)ip_next(pip); 172127094Sdes hlen = (pip->ip_hl + tc->th_off) << 2; 173127094Sdes tlen = ntohs(pip->ip_len); 174127094Sdes dlen = tlen - hlen; 17526026Sbrian 176127094Sdes /* 177127094Sdes * Return if data length is too short - assume an entire PRIVMSG in 178127094Sdes * each packet. 179127094Sdes */ 180131614Sdes if (dlen < (int)sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a") - 1) 181127094Sdes return; 18226026Sbrian 18326026Sbrian/* Place string pointer at beginning of data */ 184127094Sdes sptr = (char *)pip; 185127094Sdes sptr += hlen; 186127094Sdes maxsize -= hlen; /* We're interested in maximum size of 187127094Sdes * data, not packet */ 18826026Sbrian 189127094Sdes /* Search for a CTCP command [Note 1] */ 190127094Sdes for (i = 0; i < dlen; i++) { 191127094Sdes if (sptr[i] == '\001') 192127094Sdes goto lFOUND_CTCP; 193127094Sdes } 194127094Sdes return; /* No CTCP commands in */ 195127094Sdes /* Handle CTCP commands - the buffer may have to be copied */ 19626026SbrianlFOUND_CTCP: 197127094Sdes { 198127094Sdes char newpacket[65536]; /* Estimate of maximum packet size 199127094Sdes * :) */ 200131614Sdes unsigned int copyat = i; /* Same */ 201131614Sdes unsigned int iCopy = 0; /* How much data have we written to 202131614Sdes * copy-back string? */ 203127094Sdes unsigned long org_addr; /* Original IP address */ 204127094Sdes unsigned short org_port; /* Original source port 205127094Sdes * address */ 20626026Sbrian 207127094SdeslCTCP_START: 208127094Sdes if (i >= dlen || iCopy >= sizeof(newpacket)) 209127094Sdes goto lPACKET_DONE; 210127094Sdes newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start 211127094Sdes * character */ 212127094Sdes /* Start of a CTCP */ 213127094Sdes if (i + 4 >= dlen) /* Too short for DCC */ 214127094Sdes goto lBAD_CTCP; 215127094Sdes if (sptr[i + 0] != 'D') 216127094Sdes goto lBAD_CTCP; 217127094Sdes if (sptr[i + 1] != 'C') 218127094Sdes goto lBAD_CTCP; 219127094Sdes if (sptr[i + 2] != 'C') 220127094Sdes goto lBAD_CTCP; 221127094Sdes if (sptr[i + 3] != ' ') 222127094Sdes goto lBAD_CTCP; 223127094Sdes /* We have a DCC command - handle it! */ 224127094Sdes i += 4; /* Skip "DCC " */ 225127094Sdes if (iCopy + 4 > sizeof(newpacket)) 226127094Sdes goto lPACKET_DONE; 227127094Sdes newpacket[iCopy++] = 'D'; 228127094Sdes newpacket[iCopy++] = 'C'; 229127094Sdes newpacket[iCopy++] = 'C'; 230127094Sdes newpacket[iCopy++] = ' '; 23126026Sbrian 232127094Sdes DBprintf(("Found DCC\n")); 233127094Sdes /* 234127094Sdes * Skip any extra spaces (should not occur according to 235127094Sdes * protocol, but DCC breaks CTCP protocol anyway 236127094Sdes */ 237127094Sdes while (sptr[i] == ' ') { 238127094Sdes if (++i >= dlen) { 239127094Sdes DBprintf(("DCC packet terminated in just spaces\n")); 240127094Sdes goto lPACKET_DONE; 241127094Sdes } 242127094Sdes } 24326026Sbrian 244127094Sdes DBprintf(("Transferring command...\n")); 245127094Sdes while (sptr[i] != ' ') { 246127094Sdes newpacket[iCopy++] = sptr[i]; 247127094Sdes if (++i >= dlen || iCopy >= sizeof(newpacket)) { 248127094Sdes DBprintf(("DCC packet terminated during command\n")); 249127094Sdes goto lPACKET_DONE; 250127094Sdes } 251127094Sdes } 252127094Sdes /* Copy _one_ space */ 253127094Sdes if (i + 1 < dlen && iCopy < sizeof(newpacket)) 254127094Sdes newpacket[iCopy++] = sptr[i++]; 25526026Sbrian 256127094Sdes DBprintf(("Done command - removing spaces\n")); 257127094Sdes /* 258127094Sdes * Skip any extra spaces (should not occur according to 259127094Sdes * protocol, but DCC breaks CTCP protocol anyway 260127094Sdes */ 261127094Sdes while (sptr[i] == ' ') { 262127094Sdes if (++i >= dlen) { 263127094Sdes DBprintf(("DCC packet terminated in just spaces (post-command)\n")); 264127094Sdes goto lPACKET_DONE; 265127094Sdes } 266127094Sdes } 26726026Sbrian 268127094Sdes DBprintf(("Transferring filename...\n")); 269127094Sdes while (sptr[i] != ' ') { 270127094Sdes newpacket[iCopy++] = sptr[i]; 271127094Sdes if (++i >= dlen || iCopy >= sizeof(newpacket)) { 272127094Sdes DBprintf(("DCC packet terminated during filename\n")); 273127094Sdes goto lPACKET_DONE; 274127094Sdes } 275127094Sdes } 276127094Sdes /* Copy _one_ space */ 277127094Sdes if (i + 1 < dlen && iCopy < sizeof(newpacket)) 278127094Sdes newpacket[iCopy++] = sptr[i++]; 27926026Sbrian 280127094Sdes DBprintf(("Done filename - removing spaces\n")); 281127094Sdes /* 282127094Sdes * Skip any extra spaces (should not occur according to 283127094Sdes * protocol, but DCC breaks CTCP protocol anyway 284127094Sdes */ 285127094Sdes while (sptr[i] == ' ') { 286127094Sdes if (++i >= dlen) { 287127094Sdes DBprintf(("DCC packet terminated in just spaces (post-filename)\n")); 288127094Sdes goto lPACKET_DONE; 289127094Sdes } 290127094Sdes } 29126026Sbrian 292127094Sdes DBprintf(("Fetching IP address\n")); 293127094Sdes /* Fetch IP address */ 294127094Sdes org_addr = 0; 295127094Sdes while (i < dlen && isdigit(sptr[i])) { 296127094Sdes if (org_addr > ULONG_MAX / 10UL) { /* Terminate on overflow */ 297127094Sdes DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i])); 298127094Sdes goto lBAD_CTCP; 299127094Sdes } 300127094Sdes org_addr *= 10; 301127094Sdes org_addr += sptr[i++] - '0'; 302127094Sdes } 303127094Sdes DBprintf(("Skipping space\n")); 304127094Sdes if (i + 1 >= dlen || sptr[i] != ' ') { 305127094Sdes DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i + 1, dlen, sptr[i])); 306127094Sdes goto lBAD_CTCP; 307127094Sdes } 308127094Sdes /* 309127094Sdes * Skip any extra spaces (should not occur according to 310127094Sdes * protocol, but DCC breaks CTCP protocol anyway, so we 311127094Sdes * might as well play it safe 312127094Sdes */ 313127094Sdes while (sptr[i] == ' ') { 314127094Sdes if (++i >= dlen) { 315127094Sdes DBprintf(("Packet failure - space overflow.\n")); 316127094Sdes goto lPACKET_DONE; 317127094Sdes } 318127094Sdes } 319127094Sdes DBprintf(("Fetching port number\n")); 320127094Sdes /* Fetch source port */ 321127094Sdes org_port = 0; 322127094Sdes while (i < dlen && isdigit(sptr[i])) { 323127094Sdes if (org_port > 6554) { /* Terminate on overflow 324127094Sdes * (65536/10 rounded up */ 325127094Sdes DBprintf(("DCC: port number overflow\n")); 326127094Sdes goto lBAD_CTCP; 327127094Sdes } 328127094Sdes org_port *= 10; 329127094Sdes org_port += sptr[i++] - '0'; 330127094Sdes } 331127094Sdes /* Skip illegal addresses (or early termination) */ 332127094Sdes if (i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ')) { 333127094Sdes DBprintf(("Bad port termination\n")); 334127094Sdes goto lBAD_CTCP; 335127094Sdes } 336127094Sdes DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port)); 33726026Sbrian 338127094Sdes /* We've got the address and port - now alias it */ 339127094Sdes { 340131614Sdes struct alias_link *dcc_lnk; 341127094Sdes struct in_addr destaddr; 34299207Sbrian 34326026Sbrian 344127094Sdes true_port = htons(org_port); 345127094Sdes true_addr.s_addr = htonl(org_addr); 346127094Sdes destaddr.s_addr = 0; 34782050Sru 348127094Sdes /* Sanity/Security checking */ 349127094Sdes if (!org_addr || !org_port || 350127094Sdes pip->ip_src.s_addr != true_addr.s_addr || 351127094Sdes org_port < IPPORT_RESERVED) 352127094Sdes goto lBAD_CTCP; 35326026Sbrian 354127094Sdes /* 355127094Sdes * Steal the FTP_DATA_PORT - it doesn't really 356127094Sdes * matter, and this would probably allow it through 357127094Sdes * at least _some_ firewalls. 358127094Sdes */ 359131614Sdes dcc_lnk = FindUdpTcpOut(la, true_addr, destaddr, 360127094Sdes true_port, 0, 361127094Sdes IPPROTO_TCP, 1); 362127094Sdes DBprintf(("Got a DCC link\n")); 363131614Sdes if (dcc_lnk) { 364127094Sdes struct in_addr alias_address; /* Address from aliasing */ 365127094Sdes u_short alias_port; /* Port given by 366127094Sdes * aliasing */ 367127094Sdes int n; 368127094Sdes 36936711Sbrian#ifndef NO_FW_PUNCH 370127094Sdes /* Generate firewall hole as appropriate */ 371131614Sdes PunchFWHole(dcc_lnk); 37236711Sbrian#endif 37332377Seivind 374131614Sdes alias_address = GetAliasAddress(lnk); 375127094Sdes n = snprintf(&newpacket[iCopy], 376127094Sdes sizeof(newpacket) - iCopy, 377127094Sdes "%lu ", (u_long) htonl(alias_address.s_addr)); 378127094Sdes if (n < 0) { 379127094Sdes DBprintf(("DCC packet construct failure.\n")); 380127094Sdes goto lBAD_CTCP; 381127094Sdes } 382127094Sdes if ((iCopy += n) >= sizeof(newpacket)) { /* Truncated/fit exactly 383127094Sdes * - bad news */ 384127094Sdes DBprintf(("DCC constructed packet overflow.\n")); 385127094Sdes goto lBAD_CTCP; 386127094Sdes } 387131614Sdes alias_port = GetAliasPort(dcc_lnk); 388127094Sdes n = snprintf(&newpacket[iCopy], 389127094Sdes sizeof(newpacket) - iCopy, 390127094Sdes "%u", htons(alias_port)); 391127094Sdes if (n < 0) { 392127094Sdes DBprintf(("DCC packet construct failure.\n")); 393127094Sdes goto lBAD_CTCP; 394127094Sdes } 395127094Sdes iCopy += n; 396127094Sdes /* 397127094Sdes * Done - truncated cases will be taken 398127094Sdes * care of by lBAD_CTCP 399127094Sdes */ 400127094Sdes DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port)); 401127094Sdes } 402127094Sdes } 403127094Sdes /* 404127094Sdes * An uninteresting CTCP - state entered right after '\001' 405127094Sdes * has been pushed. Also used to copy the rest of a DCC, 406127094Sdes * after IP address and port has been handled 407127094Sdes */ 408127094SdeslBAD_CTCP: 409127094Sdes for (; i < dlen && iCopy < sizeof(newpacket); i++, iCopy++) { 410127094Sdes newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 411127094Sdes if (sptr[i] == '\001') { 412127094Sdes goto lNORMAL_TEXT; 413127094Sdes } 414127094Sdes } 415127094Sdes goto lPACKET_DONE; 416127094Sdes /* Normal text */ 417127094SdeslNORMAL_TEXT: 418127094Sdes for (; i < dlen && iCopy < sizeof(newpacket); i++, iCopy++) { 419127094Sdes newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 420127094Sdes if (sptr[i] == '\001') { 421127094Sdes goto lCTCP_START; 422127094Sdes } 423127094Sdes } 424127094Sdes /* Handle the end of a packet */ 425127094SdeslPACKET_DONE: 426127094Sdes iCopy = iCopy > maxsize - copyat ? maxsize - copyat : iCopy; 427127094Sdes memcpy(sptr + copyat, newpacket, iCopy); 42826026Sbrian 42926026Sbrian/* Save information regarding modified seq and ack numbers */ 430127094Sdes { 431127094Sdes int delta; 43226026Sbrian 433131614Sdes SetAckModified(lnk); 434131614Sdes delta = GetDeltaSeqOut(pip, lnk); 435131614Sdes AddSeq(pip, lnk, delta + copyat + iCopy - dlen); 436127094Sdes } 43726026Sbrian 438127094Sdes /* Revise IP header */ 439127094Sdes { 440127094Sdes u_short new_len; 44199207Sbrian 442127094Sdes new_len = htons(hlen + iCopy + copyat); 443127094Sdes DifferentialChecksum(&pip->ip_sum, 444127094Sdes &new_len, 445127094Sdes &pip->ip_len, 446127094Sdes 1); 447127094Sdes pip->ip_len = new_len; 448127094Sdes } 44926026Sbrian 450127094Sdes /* Compute TCP checksum for revised packet */ 451127094Sdes tc->th_sum = 0; 452147623Sglebius#ifdef _KERNEL 453147623Sglebius tc->th_x2 = 1; 454147623Sglebius#else 455127094Sdes tc->th_sum = TcpChecksum(pip); 456147623Sglebius#endif 457127094Sdes return; 458127094Sdes } 45926026Sbrian} 46026026Sbrian 46126026Sbrian/* Notes: 46226026Sbrian [Note 1] 46326026Sbrian The initial search will most often fail; it could be replaced with a 32-bit specific search. 46426026Sbrian Such a search would be done for 32-bit unsigned value V: 46526026Sbrian V ^= 0x01010101; (Search is for null bytes) 46626026Sbrian if( ((V-0x01010101)^V) & 0x80808080 ) { 46726026Sbrian (found a null bytes which was a 01 byte) 46826026Sbrian } 46926026Sbrian To assert that the processor is 32-bits, do 47026026Sbrian extern int ircdccar[32]; (32 bits) 47126026Sbrian extern int ircdccar[CHAR_BIT*sizeof(unsigned int)]; 47226026Sbrian which will generate a type-error on all but 32-bit machines. 47326026Sbrian 47426026Sbrian [Note 2] This routine really ought to be replaced with one that 47526026Sbrian creates a transparent proxy on the aliasing host, to allow arbitary 47626026Sbrian changes in the TCP stream. This should not be too difficult given 47726026Sbrian this base; I (ee) will try to do this some time later. 47826026Sbrian */ 479