alias_irc.c revision 190841
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 190841 2009-04-08 11:56:49Z 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 59187304Spiso#include <ctype.h> 60162674Spiso#include <errno.h> 61145921Sglebius#include <sys/types.h> 6299207Sbrian#include <stdio.h> 63177323Spiso#include <stdlib.h> 64168346Skan#include <string.h> 65145921Sglebius#include <limits.h> 66145921Sglebius#endif 67145921Sglebius 6826026Sbrian#include <netinet/in_systm.h> 6926026Sbrian#include <netinet/in.h> 7026026Sbrian#include <netinet/ip.h> 7126026Sbrian#include <netinet/tcp.h> 7226026Sbrian 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 7826026Sbrian#include "alias_local.h" 79162674Spiso#include "alias_mod.h" 80145921Sglebius#endif 8126026Sbrian 82162674Spiso#define IRC_CONTROL_PORT_NUMBER_1 6667 83162674Spiso#define IRC_CONTROL_PORT_NUMBER_2 6668 84162674Spiso 85177382Spiso#define PKTSIZE (IP_MAXPACKET + 1) 86177323Spisochar *newpacket; 87177323Spiso 8826026Sbrian/* Local defines */ 8926026Sbrian#define DBprintf(a) 9026026Sbrian 91162674Spisostatic void 92162674SpisoAliasHandleIrcOut(struct libalias *, struct ip *, struct alias_link *, 93162674Spiso int maxpacketsize); 9426026Sbrian 95162674Spisostatic int 96190841Spisofingerprint(struct libalias *la, struct alias_data *ah) 97162674Spiso{ 98162674Spiso 99162674Spiso if (ah->dport == NULL || ah->dport == NULL || ah->lnk == NULL || 100162674Spiso ah->maxpktsize == 0) 101162674Spiso return (-1); 102162674Spiso if (ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_1 103162674Spiso || ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_2) 104162674Spiso return (0); 105162674Spiso return (-1); 106162674Spiso} 107162674Spiso 108162674Spisostatic int 109162674Spisoprotohandler(struct libalias *la, struct ip *pip, struct alias_data *ah) 110162674Spiso{ 111177323Spiso 112177382Spiso newpacket = malloc(PKTSIZE); 113177323Spiso if (newpacket) { 114177323Spiso AliasHandleIrcOut(la, pip, ah->lnk, ah->maxpktsize); 115177323Spiso free(newpacket); 116177323Spiso } 117162674Spiso return (0); 118162674Spiso} 119162674Spiso 120162674Spisostruct proto_handler handlers[] = { 121162674Spiso { 122162674Spiso .pri = 90, 123162674Spiso .dir = OUT, 124162674Spiso .proto = TCP, 125162674Spiso .fingerprint = &fingerprint, 126162674Spiso .protohandler = &protohandler 127162674Spiso }, 128162674Spiso { EOH } 129162674Spiso}; 130162674Spiso 131162674Spisostatic int 132162674Spisomod_handler(module_t mod, int type, void *data) 133162674Spiso{ 134162674Spiso int error; 135162674Spiso 136162674Spiso switch (type) { 137162674Spiso case MOD_LOAD: 138162674Spiso error = 0; 139162674Spiso LibAliasAttachHandlers(handlers); 140162674Spiso break; 141162674Spiso case MOD_UNLOAD: 142162674Spiso error = 0; 143162674Spiso LibAliasDetachHandlers(handlers); 144162674Spiso break; 145162674Spiso default: 146162674Spiso error = EINVAL; 147162674Spiso } 148162674Spiso return (error); 149162674Spiso} 150162674Spiso 151162674Spiso#ifdef _KERNEL 152162674Spisostatic 153162674Spiso#endif 154162674Spisomoduledata_t alias_mod = { 155162674Spiso "alias_irc", mod_handler, NULL 156162674Spiso}; 157162674Spiso 158162674Spiso/* Kernel module definition. */ 159162674Spiso#ifdef _KERNEL 160162674SpisoDECLARE_MODULE(alias_irc, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); 161162674SpisoMODULE_VERSION(alias_irc, 1); 162162674SpisoMODULE_DEPEND(alias_irc, libalias, 1, 1, 1); 163162674Spiso#endif 164162674Spiso 165162674Spisostatic void 166127094SdesAliasHandleIrcOut(struct libalias *la, 167127094Sdes struct ip *pip, /* IP packet to examine */ 168131614Sdes struct alias_link *lnk, /* Which link are we on? */ 169127094Sdes int maxsize /* Maximum size of IP packet including 170127094Sdes * headers */ 171127094Sdes) 17299207Sbrian{ 173127094Sdes int hlen, tlen, dlen; 174127094Sdes struct in_addr true_addr; 175127094Sdes u_short true_port; 176127094Sdes char *sptr; 177127094Sdes struct tcphdr *tc; 178127094Sdes int i; /* Iterator through the source */ 17999207Sbrian 18026026Sbrian/* Calculate data length of TCP packet */ 181131699Sdes tc = (struct tcphdr *)ip_next(pip); 182127094Sdes hlen = (pip->ip_hl + tc->th_off) << 2; 183127094Sdes tlen = ntohs(pip->ip_len); 184127094Sdes dlen = tlen - hlen; 18526026Sbrian 186127094Sdes /* 187127094Sdes * Return if data length is too short - assume an entire PRIVMSG in 188127094Sdes * each packet. 189127094Sdes */ 190131614Sdes if (dlen < (int)sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a") - 1) 191127094Sdes return; 19226026Sbrian 19326026Sbrian/* Place string pointer at beginning of data */ 194127094Sdes sptr = (char *)pip; 195127094Sdes sptr += hlen; 196127094Sdes maxsize -= hlen; /* We're interested in maximum size of 197127094Sdes * data, not packet */ 19826026Sbrian 199127094Sdes /* Search for a CTCP command [Note 1] */ 200127094Sdes for (i = 0; i < dlen; i++) { 201127094Sdes if (sptr[i] == '\001') 202127094Sdes goto lFOUND_CTCP; 203127094Sdes } 204127094Sdes return; /* No CTCP commands in */ 205127094Sdes /* Handle CTCP commands - the buffer may have to be copied */ 20626026SbrianlFOUND_CTCP: 207127094Sdes { 208177323Spiso unsigned int copyat = i; 209131614Sdes unsigned int iCopy = 0; /* How much data have we written to 210131614Sdes * copy-back string? */ 211127094Sdes unsigned long org_addr; /* Original IP address */ 212127094Sdes unsigned short org_port; /* Original source port 213127094Sdes * address */ 21426026Sbrian 215127094SdeslCTCP_START: 216177382Spiso if (i >= dlen || iCopy >= PKTSIZE) 217127094Sdes goto lPACKET_DONE; 218127094Sdes newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start 219127094Sdes * character */ 220127094Sdes /* Start of a CTCP */ 221127094Sdes if (i + 4 >= dlen) /* Too short for DCC */ 222127094Sdes goto lBAD_CTCP; 223127094Sdes if (sptr[i + 0] != 'D') 224127094Sdes goto lBAD_CTCP; 225127094Sdes if (sptr[i + 1] != 'C') 226127094Sdes goto lBAD_CTCP; 227127094Sdes if (sptr[i + 2] != 'C') 228127094Sdes goto lBAD_CTCP; 229127094Sdes if (sptr[i + 3] != ' ') 230127094Sdes goto lBAD_CTCP; 231127094Sdes /* We have a DCC command - handle it! */ 232127094Sdes i += 4; /* Skip "DCC " */ 233177382Spiso if (iCopy + 4 > PKTSIZE) 234127094Sdes goto lPACKET_DONE; 235127094Sdes newpacket[iCopy++] = 'D'; 236127094Sdes newpacket[iCopy++] = 'C'; 237127094Sdes newpacket[iCopy++] = 'C'; 238127094Sdes newpacket[iCopy++] = ' '; 23926026Sbrian 240127094Sdes DBprintf(("Found DCC\n")); 241127094Sdes /* 242127094Sdes * Skip any extra spaces (should not occur according to 243127094Sdes * protocol, but DCC breaks CTCP protocol anyway 244127094Sdes */ 245127094Sdes while (sptr[i] == ' ') { 246127094Sdes if (++i >= dlen) { 247127094Sdes DBprintf(("DCC packet terminated in just spaces\n")); 248127094Sdes goto lPACKET_DONE; 249127094Sdes } 250127094Sdes } 25126026Sbrian 252127094Sdes DBprintf(("Transferring command...\n")); 253127094Sdes while (sptr[i] != ' ') { 254127094Sdes newpacket[iCopy++] = sptr[i]; 255177382Spiso if (++i >= dlen || iCopy >= PKTSIZE) { 256127094Sdes DBprintf(("DCC packet terminated during command\n")); 257127094Sdes goto lPACKET_DONE; 258127094Sdes } 259127094Sdes } 260127094Sdes /* Copy _one_ space */ 261177382Spiso if (i + 1 < dlen && iCopy < PKTSIZE) 262127094Sdes newpacket[iCopy++] = sptr[i++]; 26326026Sbrian 264127094Sdes DBprintf(("Done command - removing spaces\n")); 265127094Sdes /* 266127094Sdes * Skip any extra spaces (should not occur according to 267127094Sdes * protocol, but DCC breaks CTCP protocol anyway 268127094Sdes */ 269127094Sdes while (sptr[i] == ' ') { 270127094Sdes if (++i >= dlen) { 271127094Sdes DBprintf(("DCC packet terminated in just spaces (post-command)\n")); 272127094Sdes goto lPACKET_DONE; 273127094Sdes } 274127094Sdes } 27526026Sbrian 276127094Sdes DBprintf(("Transferring filename...\n")); 277127094Sdes while (sptr[i] != ' ') { 278127094Sdes newpacket[iCopy++] = sptr[i]; 279177382Spiso if (++i >= dlen || iCopy >= PKTSIZE) { 280127094Sdes DBprintf(("DCC packet terminated during filename\n")); 281127094Sdes goto lPACKET_DONE; 282127094Sdes } 283127094Sdes } 284127094Sdes /* Copy _one_ space */ 285177382Spiso if (i + 1 < dlen && iCopy < PKTSIZE) 286127094Sdes newpacket[iCopy++] = sptr[i++]; 28726026Sbrian 288127094Sdes DBprintf(("Done filename - removing spaces\n")); 289127094Sdes /* 290127094Sdes * Skip any extra spaces (should not occur according to 291127094Sdes * protocol, but DCC breaks CTCP protocol anyway 292127094Sdes */ 293127094Sdes while (sptr[i] == ' ') { 294127094Sdes if (++i >= dlen) { 295127094Sdes DBprintf(("DCC packet terminated in just spaces (post-filename)\n")); 296127094Sdes goto lPACKET_DONE; 297127094Sdes } 298127094Sdes } 29926026Sbrian 300127094Sdes DBprintf(("Fetching IP address\n")); 301127094Sdes /* Fetch IP address */ 302127094Sdes org_addr = 0; 303127094Sdes while (i < dlen && isdigit(sptr[i])) { 304127094Sdes if (org_addr > ULONG_MAX / 10UL) { /* Terminate on overflow */ 305127094Sdes DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i])); 306127094Sdes goto lBAD_CTCP; 307127094Sdes } 308127094Sdes org_addr *= 10; 309127094Sdes org_addr += sptr[i++] - '0'; 310127094Sdes } 311127094Sdes DBprintf(("Skipping space\n")); 312127094Sdes if (i + 1 >= dlen || sptr[i] != ' ') { 313127094Sdes DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i + 1, dlen, sptr[i])); 314127094Sdes goto lBAD_CTCP; 315127094Sdes } 316127094Sdes /* 317127094Sdes * Skip any extra spaces (should not occur according to 318127094Sdes * protocol, but DCC breaks CTCP protocol anyway, so we 319127094Sdes * might as well play it safe 320127094Sdes */ 321127094Sdes while (sptr[i] == ' ') { 322127094Sdes if (++i >= dlen) { 323127094Sdes DBprintf(("Packet failure - space overflow.\n")); 324127094Sdes goto lPACKET_DONE; 325127094Sdes } 326127094Sdes } 327127094Sdes DBprintf(("Fetching port number\n")); 328127094Sdes /* Fetch source port */ 329127094Sdes org_port = 0; 330127094Sdes while (i < dlen && isdigit(sptr[i])) { 331127094Sdes if (org_port > 6554) { /* Terminate on overflow 332127094Sdes * (65536/10 rounded up */ 333127094Sdes DBprintf(("DCC: port number overflow\n")); 334127094Sdes goto lBAD_CTCP; 335127094Sdes } 336127094Sdes org_port *= 10; 337127094Sdes org_port += sptr[i++] - '0'; 338127094Sdes } 339127094Sdes /* Skip illegal addresses (or early termination) */ 340127094Sdes if (i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ')) { 341127094Sdes DBprintf(("Bad port termination\n")); 342127094Sdes goto lBAD_CTCP; 343127094Sdes } 344127094Sdes DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port)); 34526026Sbrian 346127094Sdes /* We've got the address and port - now alias it */ 347127094Sdes { 348131614Sdes struct alias_link *dcc_lnk; 349127094Sdes struct in_addr destaddr; 35099207Sbrian 35126026Sbrian 352127094Sdes true_port = htons(org_port); 353127094Sdes true_addr.s_addr = htonl(org_addr); 354127094Sdes destaddr.s_addr = 0; 35582050Sru 356127094Sdes /* Sanity/Security checking */ 357127094Sdes if (!org_addr || !org_port || 358127094Sdes pip->ip_src.s_addr != true_addr.s_addr || 359127094Sdes org_port < IPPORT_RESERVED) 360127094Sdes goto lBAD_CTCP; 36126026Sbrian 362127094Sdes /* 363127094Sdes * Steal the FTP_DATA_PORT - it doesn't really 364127094Sdes * matter, and this would probably allow it through 365127094Sdes * at least _some_ firewalls. 366127094Sdes */ 367131614Sdes dcc_lnk = FindUdpTcpOut(la, true_addr, destaddr, 368127094Sdes true_port, 0, 369127094Sdes IPPROTO_TCP, 1); 370127094Sdes DBprintf(("Got a DCC link\n")); 371131614Sdes if (dcc_lnk) { 372127094Sdes struct in_addr alias_address; /* Address from aliasing */ 373127094Sdes u_short alias_port; /* Port given by 374127094Sdes * aliasing */ 375127094Sdes int n; 376127094Sdes 37736711Sbrian#ifndef NO_FW_PUNCH 378127094Sdes /* Generate firewall hole as appropriate */ 379131614Sdes PunchFWHole(dcc_lnk); 38036711Sbrian#endif 38132377Seivind 382131614Sdes alias_address = GetAliasAddress(lnk); 383127094Sdes n = snprintf(&newpacket[iCopy], 384177382Spiso PKTSIZE - iCopy, 385127094Sdes "%lu ", (u_long) htonl(alias_address.s_addr)); 386127094Sdes if (n < 0) { 387127094Sdes DBprintf(("DCC packet construct failure.\n")); 388127094Sdes goto lBAD_CTCP; 389127094Sdes } 390177382Spiso if ((iCopy += n) >= PKTSIZE) { /* Truncated/fit exactly 391127094Sdes * - bad news */ 392127094Sdes DBprintf(("DCC constructed packet overflow.\n")); 393127094Sdes goto lBAD_CTCP; 394127094Sdes } 395131614Sdes alias_port = GetAliasPort(dcc_lnk); 396127094Sdes n = snprintf(&newpacket[iCopy], 397177382Spiso PKTSIZE - iCopy, 398127094Sdes "%u", htons(alias_port)); 399127094Sdes if (n < 0) { 400127094Sdes DBprintf(("DCC packet construct failure.\n")); 401127094Sdes goto lBAD_CTCP; 402127094Sdes } 403127094Sdes iCopy += n; 404127094Sdes /* 405127094Sdes * Done - truncated cases will be taken 406127094Sdes * care of by lBAD_CTCP 407127094Sdes */ 408127094Sdes DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port)); 409127094Sdes } 410127094Sdes } 411127094Sdes /* 412127094Sdes * An uninteresting CTCP - state entered right after '\001' 413127094Sdes * has been pushed. Also used to copy the rest of a DCC, 414127094Sdes * after IP address and port has been handled 415127094Sdes */ 416127094SdeslBAD_CTCP: 417177382Spiso for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) { 418127094Sdes newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 419127094Sdes if (sptr[i] == '\001') { 420127094Sdes goto lNORMAL_TEXT; 421127094Sdes } 422127094Sdes } 423127094Sdes goto lPACKET_DONE; 424127094Sdes /* Normal text */ 425127094SdeslNORMAL_TEXT: 426177382Spiso for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) { 427127094Sdes newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 428127094Sdes if (sptr[i] == '\001') { 429127094Sdes goto lCTCP_START; 430127094Sdes } 431127094Sdes } 432127094Sdes /* Handle the end of a packet */ 433127094SdeslPACKET_DONE: 434127094Sdes iCopy = iCopy > maxsize - copyat ? maxsize - copyat : iCopy; 435127094Sdes memcpy(sptr + copyat, newpacket, iCopy); 43626026Sbrian 43726026Sbrian/* Save information regarding modified seq and ack numbers */ 438127094Sdes { 439127094Sdes int delta; 44026026Sbrian 441131614Sdes SetAckModified(lnk); 442176884Spiso tc = (struct tcphdr *)ip_next(pip); 443176884Spiso delta = GetDeltaSeqOut(tc->th_seq, lnk); 444176884Spiso AddSeq(lnk, delta + copyat + iCopy - dlen, pip->ip_hl, 445176884Spiso pip->ip_len, tc->th_seq, tc->th_off); 446127094Sdes } 44726026Sbrian 448127094Sdes /* Revise IP header */ 449127094Sdes { 450127094Sdes u_short new_len; 45199207Sbrian 452127094Sdes new_len = htons(hlen + iCopy + copyat); 453127094Sdes DifferentialChecksum(&pip->ip_sum, 454127094Sdes &new_len, 455127094Sdes &pip->ip_len, 456127094Sdes 1); 457127094Sdes pip->ip_len = new_len; 458127094Sdes } 45926026Sbrian 460127094Sdes /* Compute TCP checksum for revised packet */ 461127094Sdes tc->th_sum = 0; 462147623Sglebius#ifdef _KERNEL 463147623Sglebius tc->th_x2 = 1; 464147623Sglebius#else 465127094Sdes tc->th_sum = TcpChecksum(pip); 466147623Sglebius#endif 467127094Sdes return; 468127094Sdes } 46926026Sbrian} 47026026Sbrian 47126026Sbrian/* Notes: 47226026Sbrian [Note 1] 47326026Sbrian The initial search will most often fail; it could be replaced with a 32-bit specific search. 47426026Sbrian Such a search would be done for 32-bit unsigned value V: 47526026Sbrian V ^= 0x01010101; (Search is for null bytes) 47626026Sbrian if( ((V-0x01010101)^V) & 0x80808080 ) { 47726026Sbrian (found a null bytes which was a 01 byte) 47826026Sbrian } 47926026Sbrian To assert that the processor is 32-bits, do 48026026Sbrian extern int ircdccar[32]; (32 bits) 48126026Sbrian extern int ircdccar[CHAR_BIT*sizeof(unsigned int)]; 48226026Sbrian which will generate a type-error on all but 32-bit machines. 48326026Sbrian 48426026Sbrian [Note 2] This routine really ought to be replaced with one that 48526026Sbrian creates a transparent proxy on the aliasing host, to allow arbitary 48626026Sbrian changes in the TCP stream. This should not be too difficult given 48726026Sbrian this base; I (ee) will try to do this some time later. 48826026Sbrian */ 489