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