alias_irc.c revision 82050
1/*-
2 * Copyright (c) 2001 Charles Mott <cmott@scientech.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/netinet/libalias/alias_irc.c 82050 2001-08-21 11:21:08Z ru $
27 */
28
29/* Alias_irc.c intercepts packages contain IRC CTCP commands, and
30	changes DCC commands to export a port on the aliasing host instead
31	of an aliased host.
32
33    For this routine to work, the DCC command must fit entirely into a
34    single TCP packet.  This will usually happen, but is not
35    guaranteed.
36
37	 The interception is likely to change the length of the packet.
38	 The handling of this is copied more-or-less verbatim from
39	 ftp_alias.c
40
41	 Initial version: Eivind Eklund <perhaps@yes.no> (ee) 97-01-29
42
43         Version 2.1:  May, 1997 (cjm)
44             Very minor changes to conform with
45             local/global/function naming conventions
46             withing the packet alising module.
47*/
48
49/* Includes */
50#include <ctype.h>
51#include <stdio.h>
52#include <string.h>
53#include <sys/types.h>
54#include <netinet/in_systm.h>
55#include <netinet/in.h>
56#include <netinet/ip.h>
57#include <netinet/tcp.h>
58#include <limits.h>
59
60#include "alias_local.h"
61
62/* Local defines */
63#define DBprintf(a)
64
65
66void
67AliasHandleIrcOut(struct ip *pip, /* IP packet to examine */
68				 struct alias_link *link,		  /* Which link are we on? */
69				 int maxsize		  /* Maximum size of IP packet including headers */
70				 )
71{
72    int hlen, tlen, dlen;
73    struct in_addr true_addr;
74    u_short true_port;
75    char *sptr;
76    struct tcphdr *tc;
77	 int i;							  /* Iterator through the source */
78
79/* Calculate data length of TCP packet */
80    tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
81    hlen = (pip->ip_hl + tc->th_off) << 2;
82    tlen = ntohs(pip->ip_len);
83    dlen = tlen - hlen;
84
85	 /* Return if data length is too short - assume an entire PRIVMSG in each packet. */
86    if (dlen<sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a")-1)
87        return;
88
89/* Place string pointer at beginning of data */
90    sptr = (char *) pip;
91    sptr += hlen;
92	 maxsize -= hlen;				  /* We're interested in maximum size of data, not packet */
93
94	 /* Search for a CTCP command [Note 1] */
95	 for(	i=0; i<dlen; i++ ) {
96		 if(sptr[i]=='\001')
97			 goto lFOUND_CTCP;
98	 }
99	 return;					  /* No CTCP commands in  */
100	 /* Handle CTCP commands - the buffer may have to be copied */
101lFOUND_CTCP:
102	 {
103		 char newpacket[65536];	  /* Estimate of maximum packet size :) */
104		 int  copyat = i;			  /* Same */
105		 int  iCopy = 0;			  /* How much data have we written to copy-back string? */
106		 unsigned long org_addr;  /* Original IP address */
107		 unsigned short org_port; /* Original source port address */
108	 lCTCP_START:
109		 if( i >= dlen || iCopy >= sizeof(newpacket) )
110			 goto lPACKET_DONE;
111		 newpacket[iCopy++] = sptr[i++];	/* Copy the CTCP start character */
112		 /* Start of a CTCP */
113		 if( i+4 >= dlen )		  /* Too short for DCC */
114			 goto lBAD_CTCP;
115		 if( sptr[i+0] != 'D' )
116			 goto lBAD_CTCP;
117		 if( sptr[i+1] != 'C' )
118			 goto lBAD_CTCP;
119		 if( sptr[i+2] != 'C' )
120			 goto lBAD_CTCP;
121		 if( sptr[i+3] != ' ' )
122			 goto lBAD_CTCP;
123		 /* We have a DCC command - handle it! */
124		 i+= 4;						  /* Skip "DCC " */
125		 if( iCopy+4 > sizeof(newpacket) )
126			 goto lPACKET_DONE;
127		 newpacket[iCopy++] = 'D';
128		 newpacket[iCopy++] = 'C';
129		 newpacket[iCopy++] = 'C';
130		 newpacket[iCopy++] = ' ';
131
132		 DBprintf(("Found DCC\n"));
133		 /* Skip any extra spaces (should not occur according to
134          protocol, but DCC breaks CTCP protocol anyway */
135		 while(sptr[i] == ' ') {
136			 if( ++i >= dlen) {
137				 DBprintf(("DCC packet terminated in just spaces\n"));
138				 goto lPACKET_DONE;
139			 }
140		 }
141
142		 DBprintf(("Transferring command...\n"));
143		 while(sptr[i] != ' ') {
144			 newpacket[iCopy++] = sptr[i];
145			 if( ++i >= dlen || iCopy >= sizeof(newpacket) ) {
146				 DBprintf(("DCC packet terminated during command\n"));
147				 goto lPACKET_DONE;
148			 }
149		 }
150		 /* Copy _one_ space */
151		 if( i+1 < dlen && iCopy < sizeof(newpacket) )
152			 newpacket[iCopy++] = sptr[i++];
153
154		 DBprintf(("Done command - removing spaces\n"));
155		 /* Skip any extra spaces (should not occur according to
156          protocol, but DCC breaks CTCP protocol anyway */
157		 while(sptr[i] == ' ') {
158			 if( ++i >= dlen ) {
159				 DBprintf(("DCC packet terminated in just spaces (post-command)\n"));
160				 goto lPACKET_DONE;
161			 }
162		 }
163
164		 DBprintf(("Transferring filename...\n"));
165		 while(sptr[i] != ' ') {
166			 newpacket[iCopy++] = sptr[i];
167			 if( ++i >= dlen || iCopy >= sizeof(newpacket) ) {
168				 DBprintf(("DCC packet terminated during filename\n"));
169				 goto lPACKET_DONE;
170			 }
171		 }
172		 /* Copy _one_ space */
173		 if( i+1 < dlen && iCopy < sizeof(newpacket) )
174			 newpacket[iCopy++] = sptr[i++];
175
176		 DBprintf(("Done filename - removing spaces\n"));
177		 /* Skip any extra spaces (should not occur according to
178          protocol, but DCC breaks CTCP protocol anyway */
179		 while(sptr[i] == ' ') {
180			 if( ++i >= dlen ) {
181				 DBprintf(("DCC packet terminated in just spaces (post-filename)\n"));
182				 goto lPACKET_DONE;
183			 }
184		 }
185
186		 DBprintf(("Fetching IP address\n"));
187		 /* Fetch IP address */
188		 org_addr = 0;
189		 while(i<dlen && isdigit(sptr[i])) {
190			 if( org_addr > ULONG_MAX/10UL )	{ /* Terminate on overflow */
191				 DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i]));
192				 goto lBAD_CTCP;
193			 }
194			 org_addr *= 10;
195			 org_addr += sptr[i++]-'0';
196		 }
197		 DBprintf(("Skipping space\n"));
198		 if( i+1 >= dlen || sptr[i] != ' ' ) {
199			 DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i+1, dlen, sptr[i]));
200			 goto lBAD_CTCP;
201		 }
202		 /* Skip any extra spaces (should not occur according to
203          protocol, but DCC breaks CTCP protocol anyway, so we might
204          as well play it safe */
205		 while(sptr[i] == ' ') {
206			 if( ++i >= dlen ) {
207				 DBprintf(("Packet failure - space overflow.\n"));
208				 goto lPACKET_DONE;
209			 }
210		 }
211		 DBprintf(("Fetching port number\n"));
212		 /* Fetch source port */
213		 org_port = 0;
214		 while(i<dlen && isdigit(sptr[i])) {
215			 if( org_port > 6554 )	{ /* Terminate on overflow (65536/10 rounded up*/
216				 DBprintf(("DCC: port number overflow\n"));
217				 goto lBAD_CTCP;
218			 }
219			 org_port *= 10;
220			 org_port += sptr[i++]-'0';
221		 }
222		 /* Skip illegal addresses (or early termination) */
223		 if( i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ') ) {
224			 DBprintf(("Bad port termination\n"));
225			 goto lBAD_CTCP;
226		 }
227		 DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port));
228
229		 /* We've got the address and port - now alias it */
230		 {
231			 struct alias_link *dcc_link;
232			 struct in_addr destaddr;
233
234
235			 true_port = htons(org_port);
236			 true_addr.s_addr = htonl(org_addr);
237			 destaddr.s_addr = 0;
238
239			 /* Sanity/Security checking */
240			 if (!org_addr || !org_port ||
241			     pip->ip_src.s_addr != true_addr.s_addr ||
242			     org_port < IPPORT_RESERVED)
243				 goto lBAD_CTCP;
244
245			 /* Steal the FTP_DATA_PORT - it doesn't really matter, and this
246				 would probably allow it through at least _some_
247				 firewalls. */
248			 dcc_link = FindUdpTcpOut(true_addr, destaddr,
249						  true_port, 0,
250						  IPPROTO_TCP, 1);
251			 DBprintf(("Got a DCC link\n"));
252			 if ( dcc_link ) {
253				 struct in_addr alias_address;	/* Address from aliasing */
254				 u_short alias_port;	/* Port given by aliasing */
255				 int n;
256
257#ifndef NO_FW_PUNCH
258				 /* Generate firewall hole as appropriate */
259				 PunchFWHole(dcc_link);
260#endif
261
262				 alias_address = GetAliasAddress(link);
263				 n = snprintf(&newpacket[iCopy],
264										 sizeof(newpacket)-iCopy,
265										 "%lu ", (u_long)htonl(alias_address.s_addr));
266				 if( n < 0 ) {
267					 DBprintf(("DCC packet construct failure.\n"));
268					 goto lBAD_CTCP;
269				 }
270				 if( (iCopy += n) >= sizeof(newpacket) ) { /* Truncated/fit exactly - bad news */
271					 DBprintf(("DCC constructed packet overflow.\n"));
272					 goto lBAD_CTCP;
273				 }
274				 alias_port = GetAliasPort(dcc_link);
275				 n = snprintf(&newpacket[iCopy],
276										 sizeof(newpacket)-iCopy,
277										 "%u", htons(alias_port) );
278				 if( n < 0 ) {
279					 DBprintf(("DCC packet construct failure.\n"));
280					 goto lBAD_CTCP;
281				 }
282				 iCopy += n;
283				 /* Done - truncated cases will be taken care of by lBAD_CTCP */
284				 DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port));
285			 }
286		 }
287		 /* An uninteresting CTCP - state entered right after '\001' has
288          been pushed.  Also used to copy the rest of a DCC, after IP
289          address and port has been handled */
290	 lBAD_CTCP:
291		 for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) {
292			 newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */
293			 if(sptr[i] == '\001') {
294				 goto lNORMAL_TEXT;
295			 }
296		 }
297		 goto lPACKET_DONE;
298		 /* Normal text */
299	 lNORMAL_TEXT:
300		 for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) {
301			 newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */
302			 if(sptr[i] == '\001') {
303				 goto lCTCP_START;
304			 }
305		 }
306		 /* Handle the end of a packet */
307	 lPACKET_DONE:
308		 iCopy = iCopy > maxsize-copyat ? maxsize-copyat : iCopy;
309		 memcpy(sptr+copyat, newpacket, iCopy);
310
311/* Save information regarding modified seq and ack numbers */
312        {
313            int delta;
314
315            SetAckModified(link);
316            delta = GetDeltaSeqOut(pip, link);
317            AddSeq(pip, link, delta+copyat+iCopy-dlen);
318        }
319
320		  /* Revise IP header */
321        {
322			  u_short new_len;
323
324			  new_len = htons(hlen + iCopy + copyat);
325			  DifferentialChecksum(&pip->ip_sum,
326										  &new_len,
327										  &pip->ip_len,
328										  1);
329			  pip->ip_len = new_len;
330        }
331
332		  /* Compute TCP checksum for revised packet */
333        tc->th_sum = 0;
334        tc->th_sum = TcpChecksum(pip);
335		  return;
336	 }
337}
338
339/* Notes:
340	[Note 1]
341	The initial search will most often fail; it could be replaced with a 32-bit specific search.
342	Such a search would be done for 32-bit unsigned value V:
343	V ^= 0x01010101;				  (Search is for null bytes)
344	if( ((V-0x01010101)^V) & 0x80808080 ) {
345     (found a null bytes which was a 01 byte)
346	}
347   To assert that the processor is 32-bits, do
348   extern int ircdccar[32];        (32 bits)
349   extern int ircdccar[CHAR_BIT*sizeof(unsigned int)];
350   which will generate a type-error on all but 32-bit machines.
351
352	[Note 2] This routine really ought to be replaced with one that
353	creates a transparent proxy on the aliasing host, to allow arbitary
354	changes in the TCP stream.  This should not be too difficult given
355	this base;  I (ee) will try to do this some time later.
356	*/
357