alias_irc.c revision 131699
1102782Skan/*-
2102782Skan * Copyright (c) 2001 Charles Mott <cm@linktel.net>
3102782Skan * All rights reserved.
4102782Skan *
5102782Skan * Redistribution and use in source and binary forms, with or without
6102782Skan * modification, are permitted provided that the following conditions
7102782Skan * are met:
8102782Skan * 1. Redistributions of source code must retain the above copyright
9102782Skan *    notice, this list of conditions and the following disclaimer.
10102782Skan * 2. Redistributions in binary form must reproduce the above copyright
11102782Skan *    notice, this list of conditions and the following disclaimer in the
12102782Skan *    documentation and/or other materials provided with the distribution.
13102782Skan *
14102782Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15102782Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16102782Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17102782Skan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18169691Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19102782Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20102782Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21102782Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22102782Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23102782Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24102782Skan * SUCH DAMAGE.
25102782Skan */
26102782Skan
27102782Skan#include <sys/cdefs.h>
28102782Skan__FBSDID("$FreeBSD: head/sys/netinet/libalias/alias_irc.c 131699 2004-07-06 12:13:28Z des $");
29102782Skan
30132720Skan/* Alias_irc.c intercepts packages contain IRC CTCP commands, and
31132720Skan	changes DCC commands to export a port on the aliasing host instead
32102782Skan	of an aliased host.
33102782Skan
34102782Skan    For this routine to work, the DCC command must fit entirely into a
35132720Skan    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 *lnk,	/* Which link are we on? */
71    int maxsize			/* Maximum size of IP packet including
72				 * 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 *)ip_next(pip);
84	hlen = (pip->ip_hl + tc->th_off) << 2;
85	tlen = ntohs(pip->ip_len);
86	dlen = tlen - hlen;
87
88	/*
89	 * Return if data length is too short - assume an entire PRIVMSG in
90	 * each packet.
91	 */
92	if (dlen < (int)sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a") - 1)
93		return;
94
95/* Place string pointer at beginning of data */
96	sptr = (char *)pip;
97	sptr += hlen;
98	maxsize -= hlen;	/* We're interested in maximum size of
99				 * data, not packet */
100
101	/* Search for a CTCP command [Note 1] */
102	for (i = 0; i < dlen; i++) {
103		if (sptr[i] == '\001')
104			goto lFOUND_CTCP;
105	}
106	return;			/* No CTCP commands in  */
107	/* Handle CTCP commands - the buffer may have to be copied */
108lFOUND_CTCP:
109	{
110		char newpacket[65536];	/* Estimate of maximum packet size
111					 * :) */
112		unsigned int copyat = i;	/* Same */
113		unsigned int iCopy = 0;	/* How much data have we written to
114					 * copy-back string? */
115		unsigned long org_addr;	/* Original IP address */
116		unsigned short org_port;	/* Original source port
117						 * address */
118
119lCTCP_START:
120		if (i >= dlen || iCopy >= sizeof(newpacket))
121			goto lPACKET_DONE;
122		newpacket[iCopy++] = sptr[i++];	/* Copy the CTCP start
123						 * character */
124		/* Start of a CTCP */
125		if (i + 4 >= dlen)	/* Too short for DCC */
126			goto lBAD_CTCP;
127		if (sptr[i + 0] != 'D')
128			goto lBAD_CTCP;
129		if (sptr[i + 1] != 'C')
130			goto lBAD_CTCP;
131		if (sptr[i + 2] != 'C')
132			goto lBAD_CTCP;
133		if (sptr[i + 3] != ' ')
134			goto lBAD_CTCP;
135		/* We have a DCC command - handle it! */
136		i += 4;		/* Skip "DCC " */
137		if (iCopy + 4 > sizeof(newpacket))
138			goto lPACKET_DONE;
139		newpacket[iCopy++] = 'D';
140		newpacket[iCopy++] = 'C';
141		newpacket[iCopy++] = 'C';
142		newpacket[iCopy++] = ' ';
143
144		DBprintf(("Found DCC\n"));
145		/*
146		 * Skip any extra spaces (should not occur according to
147		 * protocol, but DCC breaks CTCP protocol anyway
148		 */
149		while (sptr[i] == ' ') {
150			if (++i >= dlen) {
151				DBprintf(("DCC packet terminated in just spaces\n"));
152				goto lPACKET_DONE;
153			}
154		}
155
156		DBprintf(("Transferring command...\n"));
157		while (sptr[i] != ' ') {
158			newpacket[iCopy++] = sptr[i];
159			if (++i >= dlen || iCopy >= sizeof(newpacket)) {
160				DBprintf(("DCC packet terminated during command\n"));
161				goto lPACKET_DONE;
162			}
163		}
164		/* Copy _one_ space */
165		if (i + 1 < dlen && iCopy < sizeof(newpacket))
166			newpacket[iCopy++] = sptr[i++];
167
168		DBprintf(("Done command - removing spaces\n"));
169		/*
170		 * Skip any extra spaces (should not occur according to
171		 * protocol, but DCC breaks CTCP protocol anyway
172		 */
173		while (sptr[i] == ' ') {
174			if (++i >= dlen) {
175				DBprintf(("DCC packet terminated in just spaces (post-command)\n"));
176				goto lPACKET_DONE;
177			}
178		}
179
180		DBprintf(("Transferring filename...\n"));
181		while (sptr[i] != ' ') {
182			newpacket[iCopy++] = sptr[i];
183			if (++i >= dlen || iCopy >= sizeof(newpacket)) {
184				DBprintf(("DCC packet terminated during filename\n"));
185				goto lPACKET_DONE;
186			}
187		}
188		/* Copy _one_ space */
189		if (i + 1 < dlen && iCopy < sizeof(newpacket))
190			newpacket[iCopy++] = sptr[i++];
191
192		DBprintf(("Done filename - removing spaces\n"));
193		/*
194		 * Skip any extra spaces (should not occur according to
195		 * protocol, but DCC breaks CTCP protocol anyway
196		 */
197		while (sptr[i] == ' ') {
198			if (++i >= dlen) {
199				DBprintf(("DCC packet terminated in just spaces (post-filename)\n"));
200				goto lPACKET_DONE;
201			}
202		}
203
204		DBprintf(("Fetching IP address\n"));
205		/* Fetch IP address */
206		org_addr = 0;
207		while (i < dlen && isdigit(sptr[i])) {
208			if (org_addr > ULONG_MAX / 10UL) {	/* Terminate on overflow */
209				DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i]));
210				goto lBAD_CTCP;
211			}
212			org_addr *= 10;
213			org_addr += sptr[i++] - '0';
214		}
215		DBprintf(("Skipping space\n"));
216		if (i + 1 >= dlen || sptr[i] != ' ') {
217			DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i + 1, dlen, sptr[i]));
218			goto lBAD_CTCP;
219		}
220		/*
221		 * Skip any extra spaces (should not occur according to
222		 * protocol, but DCC breaks CTCP protocol anyway, so we
223		 * might as well play it safe
224		 */
225		while (sptr[i] == ' ') {
226			if (++i >= dlen) {
227				DBprintf(("Packet failure - space overflow.\n"));
228				goto lPACKET_DONE;
229			}
230		}
231		DBprintf(("Fetching port number\n"));
232		/* Fetch source port */
233		org_port = 0;
234		while (i < dlen && isdigit(sptr[i])) {
235			if (org_port > 6554) {	/* Terminate on overflow
236						 * (65536/10 rounded up */
237				DBprintf(("DCC: port number overflow\n"));
238				goto lBAD_CTCP;
239			}
240			org_port *= 10;
241			org_port += sptr[i++] - '0';
242		}
243		/* Skip illegal addresses (or early termination) */
244		if (i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ')) {
245			DBprintf(("Bad port termination\n"));
246			goto lBAD_CTCP;
247		}
248		DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port));
249
250		/* We've got the address and port - now alias it */
251		{
252			struct alias_link *dcc_lnk;
253			struct in_addr destaddr;
254
255
256			true_port = htons(org_port);
257			true_addr.s_addr = htonl(org_addr);
258			destaddr.s_addr = 0;
259
260			/* Sanity/Security checking */
261			if (!org_addr || !org_port ||
262			    pip->ip_src.s_addr != true_addr.s_addr ||
263			    org_port < IPPORT_RESERVED)
264				goto lBAD_CTCP;
265
266			/*
267			 * Steal the FTP_DATA_PORT - it doesn't really
268			 * matter, and this would probably allow it through
269			 * at least _some_ firewalls.
270			 */
271			dcc_lnk = FindUdpTcpOut(la, true_addr, destaddr,
272			    true_port, 0,
273			    IPPROTO_TCP, 1);
274			DBprintf(("Got a DCC link\n"));
275			if (dcc_lnk) {
276				struct in_addr alias_address;	/* Address from aliasing */
277				u_short alias_port;	/* Port given by
278							 * aliasing */
279				int n;
280
281#ifndef NO_FW_PUNCH
282				/* Generate firewall hole as appropriate */
283				PunchFWHole(dcc_lnk);
284#endif
285
286				alias_address = GetAliasAddress(lnk);
287				n = snprintf(&newpacket[iCopy],
288				    sizeof(newpacket) - iCopy,
289				    "%lu ", (u_long) htonl(alias_address.s_addr));
290				if (n < 0) {
291					DBprintf(("DCC packet construct failure.\n"));
292					goto lBAD_CTCP;
293				}
294				if ((iCopy += n) >= sizeof(newpacket)) {	/* Truncated/fit exactly
295										 * - bad news */
296					DBprintf(("DCC constructed packet overflow.\n"));
297					goto lBAD_CTCP;
298				}
299				alias_port = GetAliasPort(dcc_lnk);
300				n = snprintf(&newpacket[iCopy],
301				    sizeof(newpacket) - iCopy,
302				    "%u", htons(alias_port));
303				if (n < 0) {
304					DBprintf(("DCC packet construct failure.\n"));
305					goto lBAD_CTCP;
306				}
307				iCopy += n;
308				/*
309				 * Done - truncated cases will be taken
310				 * care of by lBAD_CTCP
311				 */
312				DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port));
313			}
314		}
315		/*
316		 * An uninteresting CTCP - state entered right after '\001'
317		 * has been pushed.  Also used to copy the rest of a DCC,
318		 * after IP address and port has been handled
319		 */
320lBAD_CTCP:
321		for (; i < dlen && iCopy < sizeof(newpacket); i++, iCopy++) {
322			newpacket[iCopy] = sptr[i];	/* Copy CTCP unchanged */
323			if (sptr[i] == '\001') {
324				goto lNORMAL_TEXT;
325			}
326		}
327		goto lPACKET_DONE;
328		/* Normal text */
329lNORMAL_TEXT:
330		for (; i < dlen && iCopy < sizeof(newpacket); i++, iCopy++) {
331			newpacket[iCopy] = sptr[i];	/* Copy CTCP unchanged */
332			if (sptr[i] == '\001') {
333				goto lCTCP_START;
334			}
335		}
336		/* Handle the end of a packet */
337lPACKET_DONE:
338		iCopy = iCopy > maxsize - copyat ? maxsize - copyat : iCopy;
339		memcpy(sptr + copyat, newpacket, iCopy);
340
341/* Save information regarding modified seq and ack numbers */
342		{
343			int delta;
344
345			SetAckModified(lnk);
346			delta = GetDeltaSeqOut(pip, lnk);
347			AddSeq(pip, lnk, delta + copyat + iCopy - dlen);
348		}
349
350		/* Revise IP header */
351		{
352			u_short new_len;
353
354			new_len = htons(hlen + iCopy + copyat);
355			DifferentialChecksum(&pip->ip_sum,
356			    &new_len,
357			    &pip->ip_len,
358			    1);
359			pip->ip_len = new_len;
360		}
361
362		/* Compute TCP checksum for revised packet */
363		tc->th_sum = 0;
364		tc->th_sum = TcpChecksum(pip);
365		return;
366	}
367}
368
369/* Notes:
370	[Note 1]
371	The initial search will most often fail; it could be replaced with a 32-bit specific search.
372	Such a search would be done for 32-bit unsigned value V:
373	V ^= 0x01010101;				  (Search is for null bytes)
374	if( ((V-0x01010101)^V) & 0x80808080 ) {
375     (found a null bytes which was a 01 byte)
376	}
377   To assert that the processor is 32-bits, do
378   extern int ircdccar[32];        (32 bits)
379   extern int ircdccar[CHAR_BIT*sizeof(unsigned int)];
380   which will generate a type-error on all but 32-bit machines.
381
382	[Note 2] This routine really ought to be replaced with one that
383	creates a transparent proxy on the aliasing host, to allow arbitary
384	changes in the TCP stream.  This should not be too difficult given
385	this base;  I (ee) will try to do this some time later.
386	*/
387