alias_irc.c revision 127094
177701Sbrian/*-
285964Sbrian * Copyright (c) 2001 Charles Mott <cm@linktel.net>
377701Sbrian * All rights reserved.
477701Sbrian *
577701Sbrian * Redistribution and use in source and binary forms, with or without
677701Sbrian * modification, are permitted provided that the following conditions
777701Sbrian * are met:
877701Sbrian * 1. Redistributions of source code must retain the above copyright
977701Sbrian *    notice, this list of conditions and the following disclaimer.
1077701Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1177701Sbrian *    notice, this list of conditions and the following disclaimer in the
1277701Sbrian *    documentation and/or other materials provided with the distribution.
1377701Sbrian *
1477701Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1577701Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1677701Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1777701Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1877701Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1977701Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2077701Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2177701Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2277701Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2377701Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2477701Sbrian * SUCH DAMAGE.
2577701Sbrian */
2677701Sbrian
2784195Sdillon#include <sys/cdefs.h>
2884195Sdillon__FBSDID("$FreeBSD: head/sys/netinet/libalias/alias_irc.c 127094 2004-03-16 21:30:41Z des $");
2984195Sdillon
3026026Sbrian/* Alias_irc.c intercepts packages contain IRC CTCP commands, and
3126026Sbrian	changes DCC commands to export a port on the aliasing host instead
3226026Sbrian	of an aliased host.
3326026Sbrian
3426026Sbrian    For this routine to work, the DCC command must fit entirely into a
3526026Sbrian    single TCP packet.  This will usually happen, but is not
3626026Sbrian    guaranteed.
3726026Sbrian
3826026Sbrian	 The interception is likely to change the length of the packet.
3926026Sbrian	 The handling of this is copied more-or-less verbatim from
4026026Sbrian	 ftp_alias.c
4126026Sbrian
4226026Sbrian	 Initial version: Eivind Eklund <perhaps@yes.no> (ee) 97-01-29
4326026Sbrian
4426026Sbrian         Version 2.1:  May, 1997 (cjm)
4526026Sbrian             Very minor changes to conform with
4626026Sbrian             local/global/function naming conventions
4726026Sbrian             withing the packet alising module.
4826026Sbrian*/
4926026Sbrian
5026026Sbrian/* Includes */
5126026Sbrian#include <ctype.h>
5299207Sbrian#include <stdio.h>
5326026Sbrian#include <string.h>
5426026Sbrian#include <sys/types.h>
5526026Sbrian#include <netinet/in_systm.h>
5626026Sbrian#include <netinet/in.h>
5726026Sbrian#include <netinet/ip.h>
5826026Sbrian#include <netinet/tcp.h>
5926026Sbrian#include <limits.h>
6026026Sbrian
6126026Sbrian#include "alias_local.h"
6226026Sbrian
6326026Sbrian/* Local defines */
6426026Sbrian#define DBprintf(a)
6526026Sbrian
6626026Sbrian
6726026Sbrianvoid
68127094SdesAliasHandleIrcOut(struct libalias *la,
69127094Sdes    struct ip *pip,		/* IP packet to examine */
70127094Sdes    struct alias_link *link,	/* Which link are we on? */
71127094Sdes    int maxsize			/* Maximum size of IP packet including
72127094Sdes				 * headers */
73127094Sdes)
7499207Sbrian{
75127094Sdes	int hlen, tlen, dlen;
76127094Sdes	struct in_addr true_addr;
77127094Sdes	u_short true_port;
78127094Sdes	char *sptr;
79127094Sdes	struct tcphdr *tc;
80127094Sdes	int i;			/* Iterator through the source */
8199207Sbrian
8226026Sbrian/* Calculate data length of TCP packet */
83127094Sdes	tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2));
84127094Sdes	hlen = (pip->ip_hl + tc->th_off) << 2;
85127094Sdes	tlen = ntohs(pip->ip_len);
86127094Sdes	dlen = tlen - hlen;
8726026Sbrian
88127094Sdes	/*
89127094Sdes	 * Return if data length is too short - assume an entire PRIVMSG in
90127094Sdes	 * each packet.
91127094Sdes	 */
92127094Sdes	if (dlen < sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a") - 1)
93127094Sdes		return;
9426026Sbrian
9526026Sbrian/* Place string pointer at beginning of data */
96127094Sdes	sptr = (char *)pip;
97127094Sdes	sptr += hlen;
98127094Sdes	maxsize -= hlen;	/* We're interested in maximum size of
99127094Sdes				 * data, not packet */
10026026Sbrian
101127094Sdes	/* Search for a CTCP command [Note 1] */
102127094Sdes	for (i = 0; i < dlen; i++) {
103127094Sdes		if (sptr[i] == '\001')
104127094Sdes			goto lFOUND_CTCP;
105127094Sdes	}
106127094Sdes	return;			/* No CTCP commands in  */
107127094Sdes	/* Handle CTCP commands - the buffer may have to be copied */
10826026SbrianlFOUND_CTCP:
109127094Sdes	{
110127094Sdes		char newpacket[65536];	/* Estimate of maximum packet size
111127094Sdes					 * :) */
112127094Sdes		int copyat = i;	/* Same */
113127094Sdes		int iCopy = 0;	/* How much data have we written to
114127094Sdes				 * copy-back string? */
115127094Sdes		unsigned long org_addr;	/* Original IP address */
116127094Sdes		unsigned short org_port;	/* Original source port
117127094Sdes						 * address */
11826026Sbrian
119127094SdeslCTCP_START:
120127094Sdes		if (i >= dlen || iCopy >= sizeof(newpacket))
121127094Sdes			goto lPACKET_DONE;
122127094Sdes		newpacket[iCopy++] = sptr[i++];	/* Copy the CTCP start
123127094Sdes						 * character */
124127094Sdes		/* Start of a CTCP */
125127094Sdes		if (i + 4 >= dlen)	/* Too short for DCC */
126127094Sdes			goto lBAD_CTCP;
127127094Sdes		if (sptr[i + 0] != 'D')
128127094Sdes			goto lBAD_CTCP;
129127094Sdes		if (sptr[i + 1] != 'C')
130127094Sdes			goto lBAD_CTCP;
131127094Sdes		if (sptr[i + 2] != 'C')
132127094Sdes			goto lBAD_CTCP;
133127094Sdes		if (sptr[i + 3] != ' ')
134127094Sdes			goto lBAD_CTCP;
135127094Sdes		/* We have a DCC command - handle it! */
136127094Sdes		i += 4;		/* Skip "DCC " */
137127094Sdes		if (iCopy + 4 > sizeof(newpacket))
138127094Sdes			goto lPACKET_DONE;
139127094Sdes		newpacket[iCopy++] = 'D';
140127094Sdes		newpacket[iCopy++] = 'C';
141127094Sdes		newpacket[iCopy++] = 'C';
142127094Sdes		newpacket[iCopy++] = ' ';
14326026Sbrian
144127094Sdes		DBprintf(("Found DCC\n"));
145127094Sdes		/*
146127094Sdes		 * Skip any extra spaces (should not occur according to
147127094Sdes		 * protocol, but DCC breaks CTCP protocol anyway
148127094Sdes		 */
149127094Sdes		while (sptr[i] == ' ') {
150127094Sdes			if (++i >= dlen) {
151127094Sdes				DBprintf(("DCC packet terminated in just spaces\n"));
152127094Sdes				goto lPACKET_DONE;
153127094Sdes			}
154127094Sdes		}
15526026Sbrian
156127094Sdes		DBprintf(("Transferring command...\n"));
157127094Sdes		while (sptr[i] != ' ') {
158127094Sdes			newpacket[iCopy++] = sptr[i];
159127094Sdes			if (++i >= dlen || iCopy >= sizeof(newpacket)) {
160127094Sdes				DBprintf(("DCC packet terminated during command\n"));
161127094Sdes				goto lPACKET_DONE;
162127094Sdes			}
163127094Sdes		}
164127094Sdes		/* Copy _one_ space */
165127094Sdes		if (i + 1 < dlen && iCopy < sizeof(newpacket))
166127094Sdes			newpacket[iCopy++] = sptr[i++];
16726026Sbrian
168127094Sdes		DBprintf(("Done command - removing spaces\n"));
169127094Sdes		/*
170127094Sdes		 * Skip any extra spaces (should not occur according to
171127094Sdes		 * protocol, but DCC breaks CTCP protocol anyway
172127094Sdes		 */
173127094Sdes		while (sptr[i] == ' ') {
174127094Sdes			if (++i >= dlen) {
175127094Sdes				DBprintf(("DCC packet terminated in just spaces (post-command)\n"));
176127094Sdes				goto lPACKET_DONE;
177127094Sdes			}
178127094Sdes		}
17926026Sbrian
180127094Sdes		DBprintf(("Transferring filename...\n"));
181127094Sdes		while (sptr[i] != ' ') {
182127094Sdes			newpacket[iCopy++] = sptr[i];
183127094Sdes			if (++i >= dlen || iCopy >= sizeof(newpacket)) {
184127094Sdes				DBprintf(("DCC packet terminated during filename\n"));
185127094Sdes				goto lPACKET_DONE;
186127094Sdes			}
187127094Sdes		}
188127094Sdes		/* Copy _one_ space */
189127094Sdes		if (i + 1 < dlen && iCopy < sizeof(newpacket))
190127094Sdes			newpacket[iCopy++] = sptr[i++];
19126026Sbrian
192127094Sdes		DBprintf(("Done filename - removing spaces\n"));
193127094Sdes		/*
194127094Sdes		 * Skip any extra spaces (should not occur according to
195127094Sdes		 * protocol, but DCC breaks CTCP protocol anyway
196127094Sdes		 */
197127094Sdes		while (sptr[i] == ' ') {
198127094Sdes			if (++i >= dlen) {
199127094Sdes				DBprintf(("DCC packet terminated in just spaces (post-filename)\n"));
200127094Sdes				goto lPACKET_DONE;
201127094Sdes			}
202127094Sdes		}
20326026Sbrian
204127094Sdes		DBprintf(("Fetching IP address\n"));
205127094Sdes		/* Fetch IP address */
206127094Sdes		org_addr = 0;
207127094Sdes		while (i < dlen && isdigit(sptr[i])) {
208127094Sdes			if (org_addr > ULONG_MAX / 10UL) {	/* Terminate on overflow */
209127094Sdes				DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i]));
210127094Sdes				goto lBAD_CTCP;
211127094Sdes			}
212127094Sdes			org_addr *= 10;
213127094Sdes			org_addr += sptr[i++] - '0';
214127094Sdes		}
215127094Sdes		DBprintf(("Skipping space\n"));
216127094Sdes		if (i + 1 >= dlen || sptr[i] != ' ') {
217127094Sdes			DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i + 1, dlen, sptr[i]));
218127094Sdes			goto lBAD_CTCP;
219127094Sdes		}
220127094Sdes		/*
221127094Sdes		 * Skip any extra spaces (should not occur according to
222127094Sdes		 * protocol, but DCC breaks CTCP protocol anyway, so we
223127094Sdes		 * might as well play it safe
224127094Sdes		 */
225127094Sdes		while (sptr[i] == ' ') {
226127094Sdes			if (++i >= dlen) {
227127094Sdes				DBprintf(("Packet failure - space overflow.\n"));
228127094Sdes				goto lPACKET_DONE;
229127094Sdes			}
230127094Sdes		}
231127094Sdes		DBprintf(("Fetching port number\n"));
232127094Sdes		/* Fetch source port */
233127094Sdes		org_port = 0;
234127094Sdes		while (i < dlen && isdigit(sptr[i])) {
235127094Sdes			if (org_port > 6554) {	/* Terminate on overflow
236127094Sdes						 * (65536/10 rounded up */
237127094Sdes				DBprintf(("DCC: port number overflow\n"));
238127094Sdes				goto lBAD_CTCP;
239127094Sdes			}
240127094Sdes			org_port *= 10;
241127094Sdes			org_port += sptr[i++] - '0';
242127094Sdes		}
243127094Sdes		/* Skip illegal addresses (or early termination) */
244127094Sdes		if (i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ')) {
245127094Sdes			DBprintf(("Bad port termination\n"));
246127094Sdes			goto lBAD_CTCP;
247127094Sdes		}
248127094Sdes		DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port));
24926026Sbrian
250127094Sdes		/* We've got the address and port - now alias it */
251127094Sdes		{
252127094Sdes			struct alias_link *dcc_link;
253127094Sdes			struct in_addr destaddr;
25499207Sbrian
25526026Sbrian
256127094Sdes			true_port = htons(org_port);
257127094Sdes			true_addr.s_addr = htonl(org_addr);
258127094Sdes			destaddr.s_addr = 0;
25982050Sru
260127094Sdes			/* Sanity/Security checking */
261127094Sdes			if (!org_addr || !org_port ||
262127094Sdes			    pip->ip_src.s_addr != true_addr.s_addr ||
263127094Sdes			    org_port < IPPORT_RESERVED)
264127094Sdes				goto lBAD_CTCP;
26526026Sbrian
266127094Sdes			/*
267127094Sdes			 * Steal the FTP_DATA_PORT - it doesn't really
268127094Sdes			 * matter, and this would probably allow it through
269127094Sdes			 * at least _some_ firewalls.
270127094Sdes			 */
271127094Sdes			dcc_link = FindUdpTcpOut(la, true_addr, destaddr,
272127094Sdes			    true_port, 0,
273127094Sdes			    IPPROTO_TCP, 1);
274127094Sdes			DBprintf(("Got a DCC link\n"));
275127094Sdes			if (dcc_link) {
276127094Sdes				struct in_addr alias_address;	/* Address from aliasing */
277127094Sdes				u_short alias_port;	/* Port given by
278127094Sdes							 * aliasing */
279127094Sdes				int n;
280127094Sdes
28136711Sbrian#ifndef NO_FW_PUNCH
282127094Sdes				/* Generate firewall hole as appropriate */
283127094Sdes				PunchFWHole(dcc_link);
28436711Sbrian#endif
28532377Seivind
286127094Sdes				alias_address = GetAliasAddress(link);
287127094Sdes				n = snprintf(&newpacket[iCopy],
288127094Sdes				    sizeof(newpacket) - iCopy,
289127094Sdes				    "%lu ", (u_long) htonl(alias_address.s_addr));
290127094Sdes				if (n < 0) {
291127094Sdes					DBprintf(("DCC packet construct failure.\n"));
292127094Sdes					goto lBAD_CTCP;
293127094Sdes				}
294127094Sdes				if ((iCopy += n) >= sizeof(newpacket)) {	/* Truncated/fit exactly
295127094Sdes										 * - bad news */
296127094Sdes					DBprintf(("DCC constructed packet overflow.\n"));
297127094Sdes					goto lBAD_CTCP;
298127094Sdes				}
299127094Sdes				alias_port = GetAliasPort(dcc_link);
300127094Sdes				n = snprintf(&newpacket[iCopy],
301127094Sdes				    sizeof(newpacket) - iCopy,
302127094Sdes				    "%u", htons(alias_port));
303127094Sdes				if (n < 0) {
304127094Sdes					DBprintf(("DCC packet construct failure.\n"));
305127094Sdes					goto lBAD_CTCP;
306127094Sdes				}
307127094Sdes				iCopy += n;
308127094Sdes				/*
309127094Sdes				 * Done - truncated cases will be taken
310127094Sdes				 * care of by lBAD_CTCP
311127094Sdes				 */
312127094Sdes				DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port));
313127094Sdes			}
314127094Sdes		}
315127094Sdes		/*
316127094Sdes		 * An uninteresting CTCP - state entered right after '\001'
317127094Sdes		 * has been pushed.  Also used to copy the rest of a DCC,
318127094Sdes		 * after IP address and port has been handled
319127094Sdes		 */
320127094SdeslBAD_CTCP:
321127094Sdes		for (; i < dlen && iCopy < sizeof(newpacket); i++, iCopy++) {
322127094Sdes			newpacket[iCopy] = sptr[i];	/* Copy CTCP unchanged */
323127094Sdes			if (sptr[i] == '\001') {
324127094Sdes				goto lNORMAL_TEXT;
325127094Sdes			}
326127094Sdes		}
327127094Sdes		goto lPACKET_DONE;
328127094Sdes		/* Normal text */
329127094SdeslNORMAL_TEXT:
330127094Sdes		for (; i < dlen && iCopy < sizeof(newpacket); i++, iCopy++) {
331127094Sdes			newpacket[iCopy] = sptr[i];	/* Copy CTCP unchanged */
332127094Sdes			if (sptr[i] == '\001') {
333127094Sdes				goto lCTCP_START;
334127094Sdes			}
335127094Sdes		}
336127094Sdes		/* Handle the end of a packet */
337127094SdeslPACKET_DONE:
338127094Sdes		iCopy = iCopy > maxsize - copyat ? maxsize - copyat : iCopy;
339127094Sdes		memcpy(sptr + copyat, newpacket, iCopy);
34026026Sbrian
34126026Sbrian/* Save information regarding modified seq and ack numbers */
342127094Sdes		{
343127094Sdes			int delta;
34426026Sbrian
345127094Sdes			SetAckModified(link);
346127094Sdes			delta = GetDeltaSeqOut(pip, link);
347127094Sdes			AddSeq(pip, link, delta + copyat + iCopy - dlen);
348127094Sdes		}
34926026Sbrian
350127094Sdes		/* Revise IP header */
351127094Sdes		{
352127094Sdes			u_short new_len;
35399207Sbrian
354127094Sdes			new_len = htons(hlen + iCopy + copyat);
355127094Sdes			DifferentialChecksum(&pip->ip_sum,
356127094Sdes			    &new_len,
357127094Sdes			    &pip->ip_len,
358127094Sdes			    1);
359127094Sdes			pip->ip_len = new_len;
360127094Sdes		}
36126026Sbrian
362127094Sdes		/* Compute TCP checksum for revised packet */
363127094Sdes		tc->th_sum = 0;
364127094Sdes		tc->th_sum = TcpChecksum(pip);
365127094Sdes		return;
366127094Sdes	}
36726026Sbrian}
36826026Sbrian
36926026Sbrian/* Notes:
37026026Sbrian	[Note 1]
37126026Sbrian	The initial search will most often fail; it could be replaced with a 32-bit specific search.
37226026Sbrian	Such a search would be done for 32-bit unsigned value V:
37326026Sbrian	V ^= 0x01010101;				  (Search is for null bytes)
37426026Sbrian	if( ((V-0x01010101)^V) & 0x80808080 ) {
37526026Sbrian     (found a null bytes which was a 01 byte)
37626026Sbrian	}
37726026Sbrian   To assert that the processor is 32-bits, do
37826026Sbrian   extern int ircdccar[32];        (32 bits)
37926026Sbrian   extern int ircdccar[CHAR_BIT*sizeof(unsigned int)];
38026026Sbrian   which will generate a type-error on all but 32-bit machines.
38126026Sbrian
38226026Sbrian	[Note 2] This routine really ought to be replaced with one that
38326026Sbrian	creates a transparent proxy on the aliasing host, to allow arbitary
38426026Sbrian	changes in the TCP stream.  This should not be too difficult given
38526026Sbrian	this base;  I (ee) will try to do this some time later.
38626026Sbrian	*/
387