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