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