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