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