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_irc.c 336279 2018-07-14 15:32:36Z markj $"); 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 47298995Spfg within 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 95259858Sglebiusstatic int 96190841Spisofingerprint(struct libalias *la, struct alias_data *ah) 97162674Spiso{ 98162674Spiso 99336279Smarkj if (ah->dport == NULL || ah->lnk == NULL || ah->maxpktsize == 0) 100162674Spiso return (-1); 101162674Spiso if (ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_1 102162674Spiso || ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_2) 103162674Spiso return (0); 104162674Spiso return (-1); 105162674Spiso} 106162674Spiso 107259858Sglebiusstatic int 108162674Spisoprotohandler(struct libalias *la, struct ip *pip, struct alias_data *ah) 109162674Spiso{ 110177323Spiso 111177382Spiso newpacket = malloc(PKTSIZE); 112177323Spiso if (newpacket) { 113177323Spiso AliasHandleIrcOut(la, pip, ah->lnk, ah->maxpktsize); 114177323Spiso free(newpacket); 115177323Spiso } 116162674Spiso return (0); 117162674Spiso} 118162674Spiso 119162674Spisostruct proto_handler handlers[] = { 120259858Sglebius { 121259858Sglebius .pri = 90, 122259858Sglebius .dir = OUT, 123259858Sglebius .proto = TCP, 124259858Sglebius .fingerprint = &fingerprint, 125162674Spiso .protohandler = &protohandler 126259858Sglebius }, 127162674Spiso { EOH } 128162674Spiso}; 129162674Spiso 130162674Spisostatic int 131162674Spisomod_handler(module_t mod, int type, void *data) 132162674Spiso{ 133162674Spiso int error; 134162674Spiso 135162674Spiso switch (type) { 136162674Spiso case MOD_LOAD: 137162674Spiso error = 0; 138162674Spiso LibAliasAttachHandlers(handlers); 139162674Spiso break; 140162674Spiso case MOD_UNLOAD: 141162674Spiso error = 0; 142162674Spiso LibAliasDetachHandlers(handlers); 143162674Spiso break; 144162674Spiso default: 145162674Spiso error = EINVAL; 146162674Spiso } 147162674Spiso return (error); 148162674Spiso} 149162674Spiso 150162674Spiso#ifdef _KERNEL 151259858Sglebiusstatic 152162674Spiso#endif 153162674Spisomoduledata_t alias_mod = { 154162674Spiso "alias_irc", mod_handler, NULL 155162674Spiso}; 156162674Spiso 157162674Spiso/* Kernel module definition. */ 158162674Spiso#ifdef _KERNEL 159162674SpisoDECLARE_MODULE(alias_irc, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); 160162674SpisoMODULE_VERSION(alias_irc, 1); 161162674SpisoMODULE_DEPEND(alias_irc, libalias, 1, 1, 1); 162162674Spiso#endif 163162674Spiso 164162674Spisostatic void 165127094SdesAliasHandleIrcOut(struct libalias *la, 166127094Sdes struct ip *pip, /* IP packet to examine */ 167131614Sdes struct alias_link *lnk, /* Which link are we on? */ 168127094Sdes int maxsize /* Maximum size of IP packet including 169127094Sdes * headers */ 170127094Sdes) 17199207Sbrian{ 172127094Sdes int hlen, tlen, dlen; 173127094Sdes struct in_addr true_addr; 174127094Sdes u_short true_port; 175127094Sdes char *sptr; 176127094Sdes struct tcphdr *tc; 177127094Sdes int i; /* Iterator through the source */ 17899207Sbrian 17926026Sbrian/* Calculate data length of TCP packet */ 180131699Sdes tc = (struct tcphdr *)ip_next(pip); 181127094Sdes hlen = (pip->ip_hl + tc->th_off) << 2; 182127094Sdes tlen = ntohs(pip->ip_len); 183127094Sdes dlen = tlen - hlen; 18426026Sbrian 185127094Sdes /* 186127094Sdes * Return if data length is too short - assume an entire PRIVMSG in 187127094Sdes * each packet. 188127094Sdes */ 189131614Sdes if (dlen < (int)sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a") - 1) 190127094Sdes return; 19126026Sbrian 19226026Sbrian/* Place string pointer at beginning of data */ 193127094Sdes sptr = (char *)pip; 194127094Sdes sptr += hlen; 195127094Sdes maxsize -= hlen; /* We're interested in maximum size of 196127094Sdes * data, not packet */ 19726026Sbrian 198127094Sdes /* Search for a CTCP command [Note 1] */ 199127094Sdes for (i = 0; i < dlen; i++) { 200127094Sdes if (sptr[i] == '\001') 201127094Sdes goto lFOUND_CTCP; 202127094Sdes } 203127094Sdes return; /* No CTCP commands in */ 204127094Sdes /* Handle CTCP commands - the buffer may have to be copied */ 20526026SbrianlFOUND_CTCP: 206127094Sdes { 207177323Spiso unsigned int copyat = i; 208131614Sdes unsigned int iCopy = 0; /* How much data have we written to 209131614Sdes * copy-back string? */ 210127094Sdes unsigned long org_addr; /* Original IP address */ 211127094Sdes unsigned short org_port; /* Original source port 212127094Sdes * address */ 21326026Sbrian 214127094SdeslCTCP_START: 215177382Spiso if (i >= dlen || iCopy >= PKTSIZE) 216127094Sdes goto lPACKET_DONE; 217127094Sdes newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start 218127094Sdes * character */ 219127094Sdes /* Start of a CTCP */ 220127094Sdes if (i + 4 >= dlen) /* Too short for DCC */ 221127094Sdes goto lBAD_CTCP; 222127094Sdes if (sptr[i + 0] != 'D') 223127094Sdes goto lBAD_CTCP; 224127094Sdes if (sptr[i + 1] != 'C') 225127094Sdes goto lBAD_CTCP; 226127094Sdes if (sptr[i + 2] != 'C') 227127094Sdes goto lBAD_CTCP; 228127094Sdes if (sptr[i + 3] != ' ') 229127094Sdes goto lBAD_CTCP; 230127094Sdes /* We have a DCC command - handle it! */ 231127094Sdes i += 4; /* Skip "DCC " */ 232177382Spiso if (iCopy + 4 > PKTSIZE) 233127094Sdes goto lPACKET_DONE; 234127094Sdes newpacket[iCopy++] = 'D'; 235127094Sdes newpacket[iCopy++] = 'C'; 236127094Sdes newpacket[iCopy++] = 'C'; 237127094Sdes newpacket[iCopy++] = ' '; 23826026Sbrian 239127094Sdes DBprintf(("Found DCC\n")); 240127094Sdes /* 241127094Sdes * Skip any extra spaces (should not occur according to 242127094Sdes * protocol, but DCC breaks CTCP protocol anyway 243127094Sdes */ 244127094Sdes while (sptr[i] == ' ') { 245127094Sdes if (++i >= dlen) { 246127094Sdes DBprintf(("DCC packet terminated in just spaces\n")); 247127094Sdes goto lPACKET_DONE; 248127094Sdes } 249127094Sdes } 25026026Sbrian 251127094Sdes DBprintf(("Transferring command...\n")); 252127094Sdes while (sptr[i] != ' ') { 253127094Sdes newpacket[iCopy++] = sptr[i]; 254177382Spiso if (++i >= dlen || iCopy >= PKTSIZE) { 255127094Sdes DBprintf(("DCC packet terminated during command\n")); 256127094Sdes goto lPACKET_DONE; 257127094Sdes } 258127094Sdes } 259127094Sdes /* Copy _one_ space */ 260177382Spiso if (i + 1 < dlen && iCopy < PKTSIZE) 261127094Sdes newpacket[iCopy++] = sptr[i++]; 26226026Sbrian 263127094Sdes DBprintf(("Done command - removing spaces\n")); 264127094Sdes /* 265127094Sdes * Skip any extra spaces (should not occur according to 266127094Sdes * protocol, but DCC breaks CTCP protocol anyway 267127094Sdes */ 268127094Sdes while (sptr[i] == ' ') { 269127094Sdes if (++i >= dlen) { 270127094Sdes DBprintf(("DCC packet terminated in just spaces (post-command)\n")); 271127094Sdes goto lPACKET_DONE; 272127094Sdes } 273127094Sdes } 27426026Sbrian 275127094Sdes DBprintf(("Transferring filename...\n")); 276127094Sdes while (sptr[i] != ' ') { 277127094Sdes newpacket[iCopy++] = sptr[i]; 278177382Spiso if (++i >= dlen || iCopy >= PKTSIZE) { 279127094Sdes DBprintf(("DCC packet terminated during filename\n")); 280127094Sdes goto lPACKET_DONE; 281127094Sdes } 282127094Sdes } 283127094Sdes /* Copy _one_ space */ 284177382Spiso if (i + 1 < dlen && iCopy < PKTSIZE) 285127094Sdes newpacket[iCopy++] = sptr[i++]; 28626026Sbrian 287127094Sdes DBprintf(("Done filename - removing spaces\n")); 288127094Sdes /* 289127094Sdes * Skip any extra spaces (should not occur according to 290127094Sdes * protocol, but DCC breaks CTCP protocol anyway 291127094Sdes */ 292127094Sdes while (sptr[i] == ' ') { 293127094Sdes if (++i >= dlen) { 294127094Sdes DBprintf(("DCC packet terminated in just spaces (post-filename)\n")); 295127094Sdes goto lPACKET_DONE; 296127094Sdes } 297127094Sdes } 29826026Sbrian 299127094Sdes DBprintf(("Fetching IP address\n")); 300127094Sdes /* Fetch IP address */ 301127094Sdes org_addr = 0; 302127094Sdes while (i < dlen && isdigit(sptr[i])) { 303127094Sdes if (org_addr > ULONG_MAX / 10UL) { /* Terminate on overflow */ 304127094Sdes DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i])); 305127094Sdes goto lBAD_CTCP; 306127094Sdes } 307127094Sdes org_addr *= 10; 308127094Sdes org_addr += sptr[i++] - '0'; 309127094Sdes } 310127094Sdes DBprintf(("Skipping space\n")); 311127094Sdes if (i + 1 >= dlen || sptr[i] != ' ') { 312127094Sdes DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i + 1, dlen, sptr[i])); 313127094Sdes goto lBAD_CTCP; 314127094Sdes } 315127094Sdes /* 316127094Sdes * Skip any extra spaces (should not occur according to 317127094Sdes * protocol, but DCC breaks CTCP protocol anyway, so we 318127094Sdes * might as well play it safe 319127094Sdes */ 320127094Sdes while (sptr[i] == ' ') { 321127094Sdes if (++i >= dlen) { 322127094Sdes DBprintf(("Packet failure - space overflow.\n")); 323127094Sdes goto lPACKET_DONE; 324127094Sdes } 325127094Sdes } 326127094Sdes DBprintf(("Fetching port number\n")); 327127094Sdes /* Fetch source port */ 328127094Sdes org_port = 0; 329127094Sdes while (i < dlen && isdigit(sptr[i])) { 330127094Sdes if (org_port > 6554) { /* Terminate on overflow 331127094Sdes * (65536/10 rounded up */ 332127094Sdes DBprintf(("DCC: port number overflow\n")); 333127094Sdes goto lBAD_CTCP; 334127094Sdes } 335127094Sdes org_port *= 10; 336127094Sdes org_port += sptr[i++] - '0'; 337127094Sdes } 338127094Sdes /* Skip illegal addresses (or early termination) */ 339127094Sdes if (i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ')) { 340127094Sdes DBprintf(("Bad port termination\n")); 341127094Sdes goto lBAD_CTCP; 342127094Sdes } 343127094Sdes DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port)); 34426026Sbrian 345127094Sdes /* We've got the address and port - now alias it */ 346127094Sdes { 347131614Sdes struct alias_link *dcc_lnk; 348127094Sdes struct in_addr destaddr; 34999207Sbrian 35026026Sbrian 351127094Sdes true_port = htons(org_port); 352127094Sdes true_addr.s_addr = htonl(org_addr); 353127094Sdes destaddr.s_addr = 0; 35482050Sru 355127094Sdes /* Sanity/Security checking */ 356127094Sdes if (!org_addr || !org_port || 357127094Sdes pip->ip_src.s_addr != true_addr.s_addr || 358127094Sdes org_port < IPPORT_RESERVED) 359127094Sdes goto lBAD_CTCP; 36026026Sbrian 361127094Sdes /* 362127094Sdes * Steal the FTP_DATA_PORT - it doesn't really 363127094Sdes * matter, and this would probably allow it through 364127094Sdes * at least _some_ firewalls. 365127094Sdes */ 366131614Sdes dcc_lnk = FindUdpTcpOut(la, true_addr, destaddr, 367127094Sdes true_port, 0, 368127094Sdes IPPROTO_TCP, 1); 369127094Sdes DBprintf(("Got a DCC link\n")); 370131614Sdes if (dcc_lnk) { 371127094Sdes struct in_addr alias_address; /* Address from aliasing */ 372127094Sdes u_short alias_port; /* Port given by 373127094Sdes * aliasing */ 374127094Sdes int n; 375127094Sdes 37636711Sbrian#ifndef NO_FW_PUNCH 377127094Sdes /* Generate firewall hole as appropriate */ 378131614Sdes PunchFWHole(dcc_lnk); 37936711Sbrian#endif 38032377Seivind 381131614Sdes alias_address = GetAliasAddress(lnk); 382127094Sdes n = snprintf(&newpacket[iCopy], 383177382Spiso PKTSIZE - iCopy, 384127094Sdes "%lu ", (u_long) htonl(alias_address.s_addr)); 385127094Sdes if (n < 0) { 386127094Sdes DBprintf(("DCC packet construct failure.\n")); 387127094Sdes goto lBAD_CTCP; 388127094Sdes } 389177382Spiso if ((iCopy += n) >= PKTSIZE) { /* Truncated/fit exactly 390127094Sdes * - bad news */ 391127094Sdes DBprintf(("DCC constructed packet overflow.\n")); 392127094Sdes goto lBAD_CTCP; 393127094Sdes } 394131614Sdes alias_port = GetAliasPort(dcc_lnk); 395127094Sdes n = snprintf(&newpacket[iCopy], 396177382Spiso PKTSIZE - iCopy, 397127094Sdes "%u", htons(alias_port)); 398127094Sdes if (n < 0) { 399127094Sdes DBprintf(("DCC packet construct failure.\n")); 400127094Sdes goto lBAD_CTCP; 401127094Sdes } 402127094Sdes iCopy += n; 403127094Sdes /* 404127094Sdes * Done - truncated cases will be taken 405127094Sdes * care of by lBAD_CTCP 406127094Sdes */ 407127094Sdes DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port)); 408127094Sdes } 409127094Sdes } 410127094Sdes /* 411127094Sdes * An uninteresting CTCP - state entered right after '\001' 412127094Sdes * has been pushed. Also used to copy the rest of a DCC, 413127094Sdes * after IP address and port has been handled 414127094Sdes */ 415127094SdeslBAD_CTCP: 416177382Spiso for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) { 417127094Sdes newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 418127094Sdes if (sptr[i] == '\001') { 419127094Sdes goto lNORMAL_TEXT; 420127094Sdes } 421127094Sdes } 422127094Sdes goto lPACKET_DONE; 423127094Sdes /* Normal text */ 424127094SdeslNORMAL_TEXT: 425177382Spiso for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) { 426127094Sdes newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 427127094Sdes if (sptr[i] == '\001') { 428127094Sdes goto lCTCP_START; 429127094Sdes } 430127094Sdes } 431127094Sdes /* Handle the end of a packet */ 432127094SdeslPACKET_DONE: 433127094Sdes iCopy = iCopy > maxsize - copyat ? maxsize - copyat : iCopy; 434127094Sdes memcpy(sptr + copyat, newpacket, iCopy); 43526026Sbrian 43626026Sbrian/* Save information regarding modified seq and ack numbers */ 437127094Sdes { 438127094Sdes int delta; 43926026Sbrian 440131614Sdes SetAckModified(lnk); 441176884Spiso tc = (struct tcphdr *)ip_next(pip); 442176884Spiso delta = GetDeltaSeqOut(tc->th_seq, lnk); 443176884Spiso AddSeq(lnk, delta + copyat + iCopy - dlen, pip->ip_hl, 444176884Spiso pip->ip_len, tc->th_seq, tc->th_off); 445127094Sdes } 44626026Sbrian 447127094Sdes /* Revise IP header */ 448127094Sdes { 449127094Sdes u_short new_len; 45099207Sbrian 451127094Sdes new_len = htons(hlen + iCopy + copyat); 452127094Sdes DifferentialChecksum(&pip->ip_sum, 453127094Sdes &new_len, 454127094Sdes &pip->ip_len, 455127094Sdes 1); 456127094Sdes pip->ip_len = new_len; 457127094Sdes } 45826026Sbrian 459127094Sdes /* Compute TCP checksum for revised packet */ 460127094Sdes tc->th_sum = 0; 461147623Sglebius#ifdef _KERNEL 462147623Sglebius tc->th_x2 = 1; 463147623Sglebius#else 464127094Sdes tc->th_sum = TcpChecksum(pip); 465147623Sglebius#endif 466127094Sdes return; 467127094Sdes } 46826026Sbrian} 46926026Sbrian 47026026Sbrian/* Notes: 47126026Sbrian [Note 1] 47226026Sbrian The initial search will most often fail; it could be replaced with a 32-bit specific search. 47326026Sbrian Such a search would be done for 32-bit unsigned value V: 47426026Sbrian V ^= 0x01010101; (Search is for null bytes) 47526026Sbrian if( ((V-0x01010101)^V) & 0x80808080 ) { 47626026Sbrian (found a null bytes which was a 01 byte) 47726026Sbrian } 47826026Sbrian To assert that the processor is 32-bits, do 47926026Sbrian extern int ircdccar[32]; (32 bits) 48026026Sbrian extern int ircdccar[CHAR_BIT*sizeof(unsigned int)]; 48126026Sbrian which will generate a type-error on all but 32-bit machines. 48226026Sbrian 48326026Sbrian [Note 2] This routine really ought to be replaced with one that 484298995Spfg creates a transparent proxy on the aliasing host, to allow arbitrary 48526026Sbrian changes in the TCP stream. This should not be too difficult given 48626026Sbrian this base; I (ee) will try to do this some time later. 48726026Sbrian */ 488