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