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