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