alias_irc.c revision 77701
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 77701 2001-06-04 15:09:51Z brian $
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    This software is placed into the public domain with no restrictions
42    on its distribution.
43
44	 Initial version: Eivind Eklund <perhaps@yes.no> (ee) 97-01-29
45
46         Version 2.1:  May, 1997 (cjm)
47             Very minor changes to conform with
48             local/global/function naming conventions
49             withing the packet alising module.
50*/
51
52/* Includes */
53#include <ctype.h>
54#include <stdio.h>
55#include <string.h>
56#include <sys/types.h>
57#include <netinet/in_systm.h>
58#include <netinet/in.h>
59#include <netinet/ip.h>
60#include <netinet/tcp.h>
61#include <limits.h>
62
63#include "alias_local.h"
64
65/* Local defines */
66#define DBprintf(a)
67
68
69void
70AliasHandleIrcOut(struct ip *pip, /* IP packet to examine */
71				 struct alias_link *link,		  /* Which link are we on? */
72				 int maxsize		  /* Maximum size of IP packet including headers */
73				 )
74{
75    int hlen, tlen, dlen;
76    struct in_addr true_addr;
77    u_short true_port;
78    char *sptr;
79    struct tcphdr *tc;
80	 int i;							  /* Iterator through the source */
81
82/* Calculate data length of TCP packet */
83    tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
84    hlen = (pip->ip_hl + tc->th_off) << 2;
85    tlen = ntohs(pip->ip_len);
86    dlen = tlen - hlen;
87
88	 /* Return if data length is too short - assume an entire PRIVMSG in each packet. */
89    if (dlen<sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a")-1)
90        return;
91
92/* Place string pointer at beginning of data */
93    sptr = (char *) pip;
94    sptr += hlen;
95	 maxsize -= hlen;				  /* We're interested in maximum size of data, not packet */
96
97	 /* Search for a CTCP command [Note 1] */
98	 for(	i=0; i<dlen; i++ ) {
99		 if(sptr[i]=='\001')
100			 goto lFOUND_CTCP;
101	 }
102	 return;					  /* No CTCP commands in  */
103	 /* Handle CTCP commands - the buffer may have to be copied */
104lFOUND_CTCP:
105	 {
106		 char newpacket[65536];	  /* Estimate of maximum packet size :) */
107		 int  copyat = i;			  /* Same */
108		 int  iCopy = 0;			  /* How much data have we written to copy-back string? */
109		 unsigned long org_addr;  /* Original IP address */
110		 unsigned short org_port; /* Original source port address */
111	 lCTCP_START:
112		 if( i >= dlen || iCopy >= sizeof(newpacket) )
113			 goto lPACKET_DONE;
114		 newpacket[iCopy++] = sptr[i++];	/* Copy the CTCP start character */
115		 /* Start of a CTCP */
116		 if( i+4 >= dlen )		  /* Too short for DCC */
117			 goto lBAD_CTCP;
118		 if( sptr[i+0] != 'D' )
119			 goto lBAD_CTCP;
120		 if( sptr[i+1] != 'C' )
121			 goto lBAD_CTCP;
122		 if( sptr[i+2] != 'C' )
123			 goto lBAD_CTCP;
124		 if( sptr[i+3] != ' ' )
125			 goto lBAD_CTCP;
126		 /* We have a DCC command - handle it! */
127		 i+= 4;						  /* Skip "DCC " */
128		 if( iCopy+4 > sizeof(newpacket) )
129			 goto lPACKET_DONE;
130		 newpacket[iCopy++] = 'D';
131		 newpacket[iCopy++] = 'C';
132		 newpacket[iCopy++] = 'C';
133		 newpacket[iCopy++] = ' ';
134
135		 DBprintf(("Found DCC\n"));
136		 /* Skip any extra spaces (should not occur according to
137          protocol, but DCC breaks CTCP protocol anyway */
138		 while(sptr[i] == ' ') {
139			 if( ++i >= dlen) {
140				 DBprintf(("DCC packet terminated in just spaces\n"));
141				 goto lPACKET_DONE;
142			 }
143		 }
144
145		 DBprintf(("Transferring command...\n"));
146		 while(sptr[i] != ' ') {
147			 newpacket[iCopy++] = sptr[i];
148			 if( ++i >= dlen || iCopy >= sizeof(newpacket) ) {
149				 DBprintf(("DCC packet terminated during command\n"));
150				 goto lPACKET_DONE;
151			 }
152		 }
153		 /* Copy _one_ space */
154		 if( i+1 < dlen && iCopy < sizeof(newpacket) )
155			 newpacket[iCopy++] = sptr[i++];
156
157		 DBprintf(("Done command - removing spaces\n"));
158		 /* Skip any extra spaces (should not occur according to
159          protocol, but DCC breaks CTCP protocol anyway */
160		 while(sptr[i] == ' ') {
161			 if( ++i >= dlen ) {
162				 DBprintf(("DCC packet terminated in just spaces (post-command)\n"));
163				 goto lPACKET_DONE;
164			 }
165		 }
166
167		 DBprintf(("Transferring filename...\n"));
168		 while(sptr[i] != ' ') {
169			 newpacket[iCopy++] = sptr[i];
170			 if( ++i >= dlen || iCopy >= sizeof(newpacket) ) {
171				 DBprintf(("DCC packet terminated during filename\n"));
172				 goto lPACKET_DONE;
173			 }
174		 }
175		 /* Copy _one_ space */
176		 if( i+1 < dlen && iCopy < sizeof(newpacket) )
177			 newpacket[iCopy++] = sptr[i++];
178
179		 DBprintf(("Done filename - removing spaces\n"));
180		 /* Skip any extra spaces (should not occur according to
181          protocol, but DCC breaks CTCP protocol anyway */
182		 while(sptr[i] == ' ') {
183			 if( ++i >= dlen ) {
184				 DBprintf(("DCC packet terminated in just spaces (post-filename)\n"));
185				 goto lPACKET_DONE;
186			 }
187		 }
188
189		 DBprintf(("Fetching IP address\n"));
190		 /* Fetch IP address */
191		 org_addr = 0;
192		 while(i<dlen && isdigit(sptr[i])) {
193			 if( org_addr > ULONG_MAX/10UL )	{ /* Terminate on overflow */
194				 DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i]));
195				 goto lBAD_CTCP;
196			 }
197			 org_addr *= 10;
198			 org_addr += sptr[i++]-'0';
199		 }
200		 DBprintf(("Skipping space\n"));
201		 if( i+1 >= dlen || sptr[i] != ' ' ) {
202			 DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i+1, dlen, sptr[i]));
203			 goto lBAD_CTCP;
204		 }
205		 /* Skip any extra spaces (should not occur according to
206          protocol, but DCC breaks CTCP protocol anyway, so we might
207          as well play it safe */
208		 while(sptr[i] == ' ') {
209			 if( ++i >= dlen ) {
210				 DBprintf(("Packet failure - space overflow.\n"));
211				 goto lPACKET_DONE;
212			 }
213		 }
214		 DBprintf(("Fetching port number\n"));
215		 /* Fetch source port */
216		 org_port = 0;
217		 while(i<dlen && isdigit(sptr[i])) {
218			 if( org_port > 6554 )	{ /* Terminate on overflow (65536/10 rounded up*/
219				 DBprintf(("DCC: port number overflow\n"));
220				 goto lBAD_CTCP;
221			 }
222			 org_port *= 10;
223			 org_port += sptr[i++]-'0';
224		 }
225		 /* Skip illegal addresses (or early termination) */
226		 if( i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ') ) {
227			 DBprintf(("Bad port termination\n"));
228			 goto lBAD_CTCP;
229		 }
230		 DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port));
231
232		 /* We've got the address and port - now alias it */
233		 {
234			 struct alias_link *dcc_link;
235			 struct in_addr destaddr;
236
237
238			 true_port = htons(org_port);
239			 true_addr.s_addr = htonl(org_addr);
240			 destaddr.s_addr = 0;
241
242			 /* Steal the FTP_DATA_PORT - it doesn't really matter, and this
243				 would probably allow it through at least _some_
244				 firewalls. */
245			 dcc_link = FindUdpTcpOut(true_addr, destaddr,
246						  true_port, 0,
247						  IPPROTO_TCP, 1);
248			 DBprintf(("Got a DCC link\n"));
249			 if ( dcc_link ) {
250				 struct in_addr alias_address;	/* Address from aliasing */
251				 u_short alias_port;	/* Port given by aliasing */
252
253#ifndef NO_FW_PUNCH
254				 /* Generate firewall hole as appropriate */
255				 PunchFWHole(dcc_link);
256#endif
257
258				 alias_address = GetAliasAddress(link);
259				 iCopy += snprintf(&newpacket[iCopy],
260										 sizeof(newpacket)-iCopy,
261										 "%lu ", (u_long)htonl(alias_address.s_addr));
262				 if( iCopy >= sizeof(newpacket) ) { /* Truncated/fit exactly - bad news */
263					 DBprintf(("DCC constructed packet overflow.\n"));
264					 goto lBAD_CTCP;
265				 }
266				 alias_port = GetAliasPort(dcc_link);
267				 iCopy += snprintf(&newpacket[iCopy],
268										 sizeof(newpacket)-iCopy,
269										 "%u", htons(alias_port) );
270				 /* Done - truncated cases will be taken care of by lBAD_CTCP */
271				 DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port));
272			 }
273		 }
274		 /* An uninteresting CTCP - state entered right after '\001' has
275          been pushed.  Also used to copy the rest of a DCC, after IP
276          address and port has been handled */
277	 lBAD_CTCP:
278		 for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) {
279			 newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */
280			 if(sptr[i] == '\001') {
281				 goto lNORMAL_TEXT;
282			 }
283		 }
284		 goto lPACKET_DONE;
285		 /* Normal text */
286	 lNORMAL_TEXT:
287		 for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) {
288			 newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */
289			 if(sptr[i] == '\001') {
290				 goto lCTCP_START;
291			 }
292		 }
293		 /* Handle the end of a packet */
294	 lPACKET_DONE:
295		 iCopy = iCopy > maxsize-copyat ? maxsize-copyat : iCopy;
296		 memcpy(sptr+copyat, newpacket, iCopy);
297
298/* Save information regarding modified seq and ack numbers */
299        {
300            int delta;
301
302            SetAckModified(link);
303            delta = GetDeltaSeqOut(pip, link);
304            AddSeq(pip, link, delta+copyat+iCopy-dlen);
305        }
306
307		  /* Revise IP header */
308        {
309			  u_short new_len;
310
311			  new_len = htons(hlen + iCopy + copyat);
312			  DifferentialChecksum(&pip->ip_sum,
313										  &new_len,
314										  &pip->ip_len,
315										  1);
316			  pip->ip_len = new_len;
317        }
318
319		  /* Compute TCP checksum for revised packet */
320        tc->th_sum = 0;
321        tc->th_sum = TcpChecksum(pip);
322		  return;
323	 }
324}
325
326/* Notes:
327	[Note 1]
328	The initial search will most often fail; it could be replaced with a 32-bit specific search.
329	Such a search would be done for 32-bit unsigned value V:
330	V ^= 0x01010101;				  (Search is for null bytes)
331	if( ((V-0x01010101)^V) & 0x80808080 ) {
332     (found a null bytes which was a 01 byte)
333	}
334   To assert that the processor is 32-bits, do
335   extern int ircdccar[32];        (32 bits)
336   extern int ircdccar[CHAR_BIT*sizeof(unsigned int)];
337   which will generate a type-error on all but 32-bit machines.
338
339	[Note 2] This routine really ought to be replaced with one that
340	creates a transparent proxy on the aliasing host, to allow arbitary
341	changes in the TCP stream.  This should not be too difficult given
342	this base;  I (ee) will try to do this some time later.
343	*/
344