alias_irc.c revision 82050
177701Sbrian/*- 277701Sbrian * Copyright (c) 2001 Charles Mott <cmott@scientech.com> 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 * $FreeBSD: head/sys/netinet/libalias/alias_irc.c 82050 2001-08-21 11:21:08Z ru $ 2777701Sbrian */ 2877701Sbrian 2926026Sbrian/* Alias_irc.c intercepts packages contain IRC CTCP commands, and 3026026Sbrian changes DCC commands to export a port on the aliasing host instead 3126026Sbrian of an aliased host. 3226026Sbrian 3326026Sbrian For this routine to work, the DCC command must fit entirely into a 3426026Sbrian single TCP packet. This will usually happen, but is not 3526026Sbrian guaranteed. 3626026Sbrian 3726026Sbrian The interception is likely to change the length of the packet. 3826026Sbrian The handling of this is copied more-or-less verbatim from 3926026Sbrian ftp_alias.c 4026026Sbrian 4126026Sbrian Initial version: Eivind Eklund <perhaps@yes.no> (ee) 97-01-29 4226026Sbrian 4326026Sbrian Version 2.1: May, 1997 (cjm) 4426026Sbrian Very minor changes to conform with 4526026Sbrian local/global/function naming conventions 4626026Sbrian withing the packet alising module. 4726026Sbrian*/ 4826026Sbrian 4926026Sbrian/* Includes */ 5026026Sbrian#include <ctype.h> 5126026Sbrian#include <stdio.h> 5226026Sbrian#include <string.h> 5326026Sbrian#include <sys/types.h> 5426026Sbrian#include <netinet/in_systm.h> 5526026Sbrian#include <netinet/in.h> 5626026Sbrian#include <netinet/ip.h> 5726026Sbrian#include <netinet/tcp.h> 5826026Sbrian#include <limits.h> 5926026Sbrian 6026026Sbrian#include "alias_local.h" 6126026Sbrian 6226026Sbrian/* Local defines */ 6326026Sbrian#define DBprintf(a) 6426026Sbrian 6526026Sbrian 6626026Sbrianvoid 6726026SbrianAliasHandleIrcOut(struct ip *pip, /* IP packet to examine */ 6826026Sbrian struct alias_link *link, /* Which link are we on? */ 6926026Sbrian int maxsize /* Maximum size of IP packet including headers */ 7026026Sbrian ) 7126026Sbrian{ 7226026Sbrian int hlen, tlen, dlen; 7326026Sbrian struct in_addr true_addr; 7426026Sbrian u_short true_port; 7526026Sbrian char *sptr; 7626026Sbrian struct tcphdr *tc; 7726026Sbrian int i; /* Iterator through the source */ 7826026Sbrian 7926026Sbrian/* Calculate data length of TCP packet */ 8026026Sbrian tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); 8126026Sbrian hlen = (pip->ip_hl + tc->th_off) << 2; 8226026Sbrian tlen = ntohs(pip->ip_len); 8326026Sbrian dlen = tlen - hlen; 8426026Sbrian 8526026Sbrian /* Return if data length is too short - assume an entire PRIVMSG in each packet. */ 8626026Sbrian if (dlen<sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a")-1) 8726026Sbrian return; 8826026Sbrian 8926026Sbrian/* Place string pointer at beginning of data */ 9026026Sbrian sptr = (char *) pip; 9126026Sbrian sptr += hlen; 9226026Sbrian maxsize -= hlen; /* We're interested in maximum size of data, not packet */ 9326026Sbrian 9426026Sbrian /* Search for a CTCP command [Note 1] */ 9526026Sbrian for( i=0; i<dlen; i++ ) { 9626026Sbrian if(sptr[i]=='\001') 9726026Sbrian goto lFOUND_CTCP; 9826026Sbrian } 9926026Sbrian return; /* No CTCP commands in */ 10026026Sbrian /* Handle CTCP commands - the buffer may have to be copied */ 10126026SbrianlFOUND_CTCP: 10226026Sbrian { 10326026Sbrian char newpacket[65536]; /* Estimate of maximum packet size :) */ 10426026Sbrian int copyat = i; /* Same */ 10526026Sbrian int iCopy = 0; /* How much data have we written to copy-back string? */ 10626026Sbrian unsigned long org_addr; /* Original IP address */ 10726026Sbrian unsigned short org_port; /* Original source port address */ 10826026Sbrian lCTCP_START: 10926026Sbrian if( i >= dlen || iCopy >= sizeof(newpacket) ) 11026026Sbrian goto lPACKET_DONE; 11126026Sbrian newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start character */ 11226026Sbrian /* Start of a CTCP */ 11326026Sbrian if( i+4 >= dlen ) /* Too short for DCC */ 11426026Sbrian goto lBAD_CTCP; 11526026Sbrian if( sptr[i+0] != 'D' ) 11626026Sbrian goto lBAD_CTCP; 11726026Sbrian if( sptr[i+1] != 'C' ) 11826026Sbrian goto lBAD_CTCP; 11926026Sbrian if( sptr[i+2] != 'C' ) 12026026Sbrian goto lBAD_CTCP; 12126026Sbrian if( sptr[i+3] != ' ' ) 12226026Sbrian goto lBAD_CTCP; 12326026Sbrian /* We have a DCC command - handle it! */ 12426026Sbrian i+= 4; /* Skip "DCC " */ 12526026Sbrian if( iCopy+4 > sizeof(newpacket) ) 12626026Sbrian goto lPACKET_DONE; 12726026Sbrian newpacket[iCopy++] = 'D'; 12826026Sbrian newpacket[iCopy++] = 'C'; 12926026Sbrian newpacket[iCopy++] = 'C'; 13026026Sbrian newpacket[iCopy++] = ' '; 13126026Sbrian 13226026Sbrian DBprintf(("Found DCC\n")); 13326026Sbrian /* Skip any extra spaces (should not occur according to 13426026Sbrian protocol, but DCC breaks CTCP protocol anyway */ 13526026Sbrian while(sptr[i] == ' ') { 13626026Sbrian if( ++i >= dlen) { 13726026Sbrian DBprintf(("DCC packet terminated in just spaces\n")); 13826026Sbrian goto lPACKET_DONE; 13926026Sbrian } 14026026Sbrian } 14126026Sbrian 14226026Sbrian DBprintf(("Transferring command...\n")); 14326026Sbrian while(sptr[i] != ' ') { 14426026Sbrian newpacket[iCopy++] = sptr[i]; 14526026Sbrian if( ++i >= dlen || iCopy >= sizeof(newpacket) ) { 14626026Sbrian DBprintf(("DCC packet terminated during command\n")); 14726026Sbrian goto lPACKET_DONE; 14826026Sbrian } 14926026Sbrian } 15026026Sbrian /* Copy _one_ space */ 15126026Sbrian if( i+1 < dlen && iCopy < sizeof(newpacket) ) 15226026Sbrian newpacket[iCopy++] = sptr[i++]; 15326026Sbrian 15426026Sbrian DBprintf(("Done command - removing spaces\n")); 15526026Sbrian /* Skip any extra spaces (should not occur according to 15626026Sbrian protocol, but DCC breaks CTCP protocol anyway */ 15726026Sbrian while(sptr[i] == ' ') { 15826026Sbrian if( ++i >= dlen ) { 15926026Sbrian DBprintf(("DCC packet terminated in just spaces (post-command)\n")); 16026026Sbrian goto lPACKET_DONE; 16126026Sbrian } 16226026Sbrian } 16326026Sbrian 16426026Sbrian DBprintf(("Transferring filename...\n")); 16526026Sbrian while(sptr[i] != ' ') { 16626026Sbrian newpacket[iCopy++] = sptr[i]; 16726026Sbrian if( ++i >= dlen || iCopy >= sizeof(newpacket) ) { 16826026Sbrian DBprintf(("DCC packet terminated during filename\n")); 16926026Sbrian goto lPACKET_DONE; 17026026Sbrian } 17126026Sbrian } 17226026Sbrian /* Copy _one_ space */ 17326026Sbrian if( i+1 < dlen && iCopy < sizeof(newpacket) ) 17426026Sbrian newpacket[iCopy++] = sptr[i++]; 17526026Sbrian 17626026Sbrian DBprintf(("Done filename - removing spaces\n")); 17726026Sbrian /* Skip any extra spaces (should not occur according to 17826026Sbrian protocol, but DCC breaks CTCP protocol anyway */ 17926026Sbrian while(sptr[i] == ' ') { 18026026Sbrian if( ++i >= dlen ) { 18126026Sbrian DBprintf(("DCC packet terminated in just spaces (post-filename)\n")); 18226026Sbrian goto lPACKET_DONE; 18326026Sbrian } 18426026Sbrian } 18526026Sbrian 18626026Sbrian DBprintf(("Fetching IP address\n")); 18726026Sbrian /* Fetch IP address */ 18826026Sbrian org_addr = 0; 18926026Sbrian while(i<dlen && isdigit(sptr[i])) { 19026026Sbrian if( org_addr > ULONG_MAX/10UL ) { /* Terminate on overflow */ 19126026Sbrian DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i])); 19226026Sbrian goto lBAD_CTCP; 19326026Sbrian } 19426026Sbrian org_addr *= 10; 19526026Sbrian org_addr += sptr[i++]-'0'; 19626026Sbrian } 19726026Sbrian DBprintf(("Skipping space\n")); 19826026Sbrian if( i+1 >= dlen || sptr[i] != ' ' ) { 19926026Sbrian DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i+1, dlen, sptr[i])); 20026026Sbrian goto lBAD_CTCP; 20126026Sbrian } 20226026Sbrian /* Skip any extra spaces (should not occur according to 20326026Sbrian protocol, but DCC breaks CTCP protocol anyway, so we might 20426026Sbrian as well play it safe */ 20526026Sbrian while(sptr[i] == ' ') { 20626026Sbrian if( ++i >= dlen ) { 20726026Sbrian DBprintf(("Packet failure - space overflow.\n")); 20826026Sbrian goto lPACKET_DONE; 20926026Sbrian } 21026026Sbrian } 21126026Sbrian DBprintf(("Fetching port number\n")); 21226026Sbrian /* Fetch source port */ 21326026Sbrian org_port = 0; 21426026Sbrian while(i<dlen && isdigit(sptr[i])) { 21526026Sbrian if( org_port > 6554 ) { /* Terminate on overflow (65536/10 rounded up*/ 21626026Sbrian DBprintf(("DCC: port number overflow\n")); 21726026Sbrian goto lBAD_CTCP; 21826026Sbrian } 21926026Sbrian org_port *= 10; 22026026Sbrian org_port += sptr[i++]-'0'; 22126026Sbrian } 22226026Sbrian /* Skip illegal addresses (or early termination) */ 22326026Sbrian if( i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ') ) { 22426026Sbrian DBprintf(("Bad port termination\n")); 22526026Sbrian goto lBAD_CTCP; 22626026Sbrian } 22726026Sbrian DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port)); 22826026Sbrian 22926026Sbrian /* We've got the address and port - now alias it */ 23026026Sbrian { 23126026Sbrian struct alias_link *dcc_link; 23226026Sbrian struct in_addr destaddr; 23326026Sbrian 23426026Sbrian 23526026Sbrian true_port = htons(org_port); 23626026Sbrian true_addr.s_addr = htonl(org_addr); 23726026Sbrian destaddr.s_addr = 0; 23826026Sbrian 23982050Sru /* Sanity/Security checking */ 24082050Sru if (!org_addr || !org_port || 24182050Sru pip->ip_src.s_addr != true_addr.s_addr || 24282050Sru org_port < IPPORT_RESERVED) 24382050Sru goto lBAD_CTCP; 24482050Sru 24526026Sbrian /* Steal the FTP_DATA_PORT - it doesn't really matter, and this 24626026Sbrian would probably allow it through at least _some_ 24726026Sbrian firewalls. */ 24867980Sru dcc_link = FindUdpTcpOut(true_addr, destaddr, 24967980Sru true_port, 0, 25067980Sru IPPROTO_TCP, 1); 25126026Sbrian DBprintf(("Got a DCC link\n")); 25226026Sbrian if ( dcc_link ) { 25326026Sbrian struct in_addr alias_address; /* Address from aliasing */ 25426026Sbrian u_short alias_port; /* Port given by aliasing */ 25581962Sbrian int n; 25626026Sbrian 25736711Sbrian#ifndef NO_FW_PUNCH 25832377Seivind /* Generate firewall hole as appropriate */ 25932377Seivind PunchFWHole(dcc_link); 26036711Sbrian#endif 26132377Seivind 26226026Sbrian alias_address = GetAliasAddress(link); 26381962Sbrian n = snprintf(&newpacket[iCopy], 26426026Sbrian sizeof(newpacket)-iCopy, 26535314Sbrian "%lu ", (u_long)htonl(alias_address.s_addr)); 26681962Sbrian if( n < 0 ) { 26781962Sbrian DBprintf(("DCC packet construct failure.\n")); 26881962Sbrian goto lBAD_CTCP; 26981962Sbrian } 27081962Sbrian if( (iCopy += n) >= sizeof(newpacket) ) { /* Truncated/fit exactly - bad news */ 27126026Sbrian DBprintf(("DCC constructed packet overflow.\n")); 27226026Sbrian goto lBAD_CTCP; 27326026Sbrian } 27426026Sbrian alias_port = GetAliasPort(dcc_link); 27581962Sbrian n = snprintf(&newpacket[iCopy], 27626026Sbrian sizeof(newpacket)-iCopy, 27726026Sbrian "%u", htons(alias_port) ); 27881962Sbrian if( n < 0 ) { 27981962Sbrian DBprintf(("DCC packet construct failure.\n")); 28081962Sbrian goto lBAD_CTCP; 28181962Sbrian } 28281962Sbrian iCopy += n; 28326026Sbrian /* Done - truncated cases will be taken care of by lBAD_CTCP */ 28426026Sbrian DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port)); 28526026Sbrian } 28626026Sbrian } 28726026Sbrian /* An uninteresting CTCP - state entered right after '\001' has 28826026Sbrian been pushed. Also used to copy the rest of a DCC, after IP 28926026Sbrian address and port has been handled */ 29026026Sbrian lBAD_CTCP: 29126026Sbrian for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) { 29226026Sbrian newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 29326026Sbrian if(sptr[i] == '\001') { 29426026Sbrian goto lNORMAL_TEXT; 29526026Sbrian } 29626026Sbrian } 29726026Sbrian goto lPACKET_DONE; 29826026Sbrian /* Normal text */ 29926026Sbrian lNORMAL_TEXT: 30026026Sbrian for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) { 30126026Sbrian newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 30226026Sbrian if(sptr[i] == '\001') { 30326026Sbrian goto lCTCP_START; 30426026Sbrian } 30526026Sbrian } 30626026Sbrian /* Handle the end of a packet */ 30726026Sbrian lPACKET_DONE: 30826026Sbrian iCopy = iCopy > maxsize-copyat ? maxsize-copyat : iCopy; 30926026Sbrian memcpy(sptr+copyat, newpacket, iCopy); 31026026Sbrian 31126026Sbrian/* Save information regarding modified seq and ack numbers */ 31226026Sbrian { 31326026Sbrian int delta; 31426026Sbrian 31526026Sbrian SetAckModified(link); 31626026Sbrian delta = GetDeltaSeqOut(pip, link); 31726026Sbrian AddSeq(pip, link, delta+copyat+iCopy-dlen); 31826026Sbrian } 31926026Sbrian 32026026Sbrian /* Revise IP header */ 32126026Sbrian { 32226026Sbrian u_short new_len; 32326026Sbrian 32426026Sbrian new_len = htons(hlen + iCopy + copyat); 32526026Sbrian DifferentialChecksum(&pip->ip_sum, 32626026Sbrian &new_len, 32726026Sbrian &pip->ip_len, 32826026Sbrian 1); 32926026Sbrian pip->ip_len = new_len; 33026026Sbrian } 33126026Sbrian 33226026Sbrian /* Compute TCP checksum for revised packet */ 33326026Sbrian tc->th_sum = 0; 33426026Sbrian tc->th_sum = TcpChecksum(pip); 33526026Sbrian return; 33626026Sbrian } 33726026Sbrian} 33826026Sbrian 33926026Sbrian/* Notes: 34026026Sbrian [Note 1] 34126026Sbrian The initial search will most often fail; it could be replaced with a 32-bit specific search. 34226026Sbrian Such a search would be done for 32-bit unsigned value V: 34326026Sbrian V ^= 0x01010101; (Search is for null bytes) 34426026Sbrian if( ((V-0x01010101)^V) & 0x80808080 ) { 34526026Sbrian (found a null bytes which was a 01 byte) 34626026Sbrian } 34726026Sbrian To assert that the processor is 32-bits, do 34826026Sbrian extern int ircdccar[32]; (32 bits) 34926026Sbrian extern int ircdccar[CHAR_BIT*sizeof(unsigned int)]; 35026026Sbrian which will generate a type-error on all but 32-bit machines. 35126026Sbrian 35226026Sbrian [Note 2] This routine really ought to be replaced with one that 35326026Sbrian creates a transparent proxy on the aliasing host, to allow arbitary 35426026Sbrian changes in the TCP stream. This should not be too difficult given 35526026Sbrian this base; I (ee) will try to do this some time later. 35626026Sbrian */ 357