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