1/*
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*-
23 * Copyright (c) 2001 Charles Mott <cmott@scientech.com>
24 * All rights reserved.
25 *
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions
28 * are met:
29 * 1. Redistributions of source code must retain the above copyright
30 *    notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 *    notice, this list of conditions and the following disclaimer in the
33 *    documentation and/or other materials provided with the distribution.
34 *
35 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
36 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
39 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
41 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45 * SUCH DAMAGE.
46 *
47 * Based upon:
48 * $FreeBSD: src/lib/libalias/alias_irc.c,v 1.5.2.4 2001/08/21 16:42:42 ru Exp $
49 */
50
51/* Alias_irc.c intercepts packages contain IRC CTCP commands, and
52	changes DCC commands to export a port on the aliasing host instead
53	of an aliased host.
54
55    For this routine to work, the DCC command must fit entirely into a
56    single TCP packet.  This will usually happen, but is not
57    guaranteed.
58
59	 The interception is likely to change the length of the packet.
60	 The handling of this is copied more-or-less verbatim from
61	 ftp_alias.c
62
63	 Initial version: Eivind Eklund <perhaps@yes.no> (ee) 97-01-29
64
65         Version 2.1:  May, 1997 (cjm)
66             Very minor changes to conform with
67             local/global/function naming conventions
68             withing the packet alising module.
69*/
70
71/* Includes */
72#include <ctype.h>
73#include <stdio.h>
74#include <string.h>
75#include <sys/types.h>
76#include <netinet/in_systm.h>
77#include <netinet/in.h>
78#include <netinet/ip.h>
79#include <netinet/tcp.h>
80#include <limits.h>
81
82#include "alias_local.h"
83
84/* Local defines */
85#define DBprintf(a)
86
87
88void
89AliasHandleIrcOut(struct ip *pip, /* IP packet to examine */
90				 struct alias_link *link,		  /* Which link are we on? */
91				 int maxsize		  /* Maximum size of IP packet including headers */
92				 )
93{
94    int hlen, tlen, dlen;
95    struct in_addr true_addr;
96    u_short true_port;
97    char *sptr;
98    struct tcphdr *tc;
99	 int i;							  /* Iterator through the source */
100
101/* Calculate data length of TCP packet */
102    tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
103    hlen = (pip->ip_hl + tc->th_off) << 2;
104    tlen = ntohs(pip->ip_len);
105    dlen = tlen - hlen;
106
107	 /* Return if data length is too short - assume an entire PRIVMSG in each packet. */
108    if (dlen<sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a")-1)
109        return;
110
111/* Place string pointer at beginning of data */
112    sptr = (char *) pip;
113    sptr += hlen;
114	 maxsize -= hlen;				  /* We're interested in maximum size of data, not packet */
115
116	 /* Search for a CTCP command [Note 1] */
117	 for(	i=0; i<dlen; i++ ) {
118		 if(sptr[i]=='\001')
119			 goto lFOUND_CTCP;
120	 }
121	 return;					  /* No CTCP commands in  */
122	 /* Handle CTCP commands - the buffer may have to be copied */
123lFOUND_CTCP:
124	 {
125		 char newpacket[65536];	  /* Estimate of maximum packet size :) */
126		 int  copyat = i;			  /* Same */
127		 int  iCopy = 0;			  /* How much data have we written to copy-back string? */
128		 in_addr_t org_addr;  /* Original IP address */
129		 unsigned short org_port; /* Original source port address */
130	 lCTCP_START:
131		 if( i >= dlen || iCopy >= sizeof(newpacket) )
132			 goto lPACKET_DONE;
133		 newpacket[iCopy++] = sptr[i++];	/* Copy the CTCP start character */
134		 /* Start of a CTCP */
135		 if( i+4 >= dlen )		  /* Too short for DCC */
136			 goto lBAD_CTCP;
137		 if( sptr[i+0] != 'D' )
138			 goto lBAD_CTCP;
139		 if( sptr[i+1] != 'C' )
140			 goto lBAD_CTCP;
141		 if( sptr[i+2] != 'C' )
142			 goto lBAD_CTCP;
143		 if( sptr[i+3] != ' ' )
144			 goto lBAD_CTCP;
145		 /* We have a DCC command - handle it! */
146		 i+= 4;						  /* Skip "DCC " */
147		 if( iCopy+4 > sizeof(newpacket) )
148			 goto lPACKET_DONE;
149		 newpacket[iCopy++] = 'D';
150		 newpacket[iCopy++] = 'C';
151		 newpacket[iCopy++] = 'C';
152		 newpacket[iCopy++] = ' ';
153
154		 DBprintf(("Found DCC\n"));
155		 /* Skip any extra spaces (should not occur according to
156          protocol, but DCC breaks CTCP protocol anyway */
157		 while(sptr[i] == ' ') {
158			 if( ++i >= dlen) {
159				 DBprintf(("DCC packet terminated in just spaces\n"));
160				 goto lPACKET_DONE;
161			 }
162		 }
163
164		 DBprintf(("Transferring command...\n"));
165		 while(sptr[i] != ' ') {
166			 newpacket[iCopy++] = sptr[i];
167			 if( ++i >= dlen || iCopy >= sizeof(newpacket) ) {
168				 DBprintf(("DCC packet terminated during command\n"));
169				 goto lPACKET_DONE;
170			 }
171		 }
172		 /* Copy _one_ space */
173		 if( i+1 < dlen && iCopy < sizeof(newpacket) )
174			 newpacket[iCopy++] = sptr[i++];
175
176		 DBprintf(("Done command - removing spaces\n"));
177		 /* Skip any extra spaces (should not occur according to
178          protocol, but DCC breaks CTCP protocol anyway */
179		 while(sptr[i] == ' ') {
180			 if( ++i >= dlen ) {
181				 DBprintf(("DCC packet terminated in just spaces (post-command)\n"));
182				 goto lPACKET_DONE;
183			 }
184		 }
185
186		 DBprintf(("Transferring filename...\n"));
187		 while(sptr[i] != ' ') {
188			 newpacket[iCopy++] = sptr[i];
189			 if( ++i >= dlen || iCopy >= sizeof(newpacket) ) {
190				 DBprintf(("DCC packet terminated during filename\n"));
191				 goto lPACKET_DONE;
192			 }
193		 }
194		 /* Copy _one_ space */
195		 if( i+1 < dlen && iCopy < sizeof(newpacket) )
196			 newpacket[iCopy++] = sptr[i++];
197
198		 DBprintf(("Done filename - removing spaces\n"));
199		 /* Skip any extra spaces (should not occur according to
200          protocol, but DCC breaks CTCP protocol anyway */
201		 while(sptr[i] == ' ') {
202			 if( ++i >= dlen ) {
203				 DBprintf(("DCC packet terminated in just spaces (post-filename)\n"));
204				 goto lPACKET_DONE;
205			 }
206		 }
207
208		 DBprintf(("Fetching IP address\n"));
209		 /* Fetch IP address */
210		 org_addr = 0;
211		 while(i<dlen && isdigit(sptr[i])) {
212			 if( org_addr > 0xFFFFFFFF/10UL )	{ /* Terminate on overflow */
213				 DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i]));
214				 goto lBAD_CTCP;
215			 }
216			 org_addr *= 10;
217			 org_addr += sptr[i++]-'0';
218		 }
219		 DBprintf(("Skipping space\n"));
220		 if( i+1 >= dlen || sptr[i] != ' ' ) {
221			 DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i+1, dlen, sptr[i]));
222			 goto lBAD_CTCP;
223		 }
224		 /* Skip any extra spaces (should not occur according to
225          protocol, but DCC breaks CTCP protocol anyway, so we might
226          as well play it safe */
227		 while(sptr[i] == ' ') {
228			 if( ++i >= dlen ) {
229				 DBprintf(("Packet failure - space overflow.\n"));
230				 goto lPACKET_DONE;
231			 }
232		 }
233		 DBprintf(("Fetching port number\n"));
234		 /* Fetch source port */
235		 org_port = 0;
236		 while(i<dlen && isdigit(sptr[i])) {
237			 if( org_port > 6554 )	{ /* Terminate on overflow (65536/10 rounded up*/
238				 DBprintf(("DCC: port number overflow\n"));
239				 goto lBAD_CTCP;
240			 }
241			 org_port *= 10;
242			 org_port += sptr[i++]-'0';
243		 }
244		 /* Skip illegal addresses (or early termination) */
245		 if( i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ') ) {
246			 DBprintf(("Bad port termination\n"));
247			 goto lBAD_CTCP;
248		 }
249		 DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port));
250
251		 /* We've got the address and port - now alias it */
252		 {
253			 struct alias_link *dcc_link;
254			 struct in_addr destaddr;
255
256
257			 true_port = htons(org_port);
258			 true_addr.s_addr = htonl(org_addr);
259			 destaddr.s_addr = 0;
260
261			 /* Sanity/Security checking */
262			 if (!org_addr || !org_port ||
263			     pip->ip_src.s_addr != true_addr.s_addr ||
264			     org_port < IPPORT_RESERVED)
265				 goto lBAD_CTCP;
266
267			 /* Steal the FTP_DATA_PORT - it doesn't really matter, and this
268				 would probably allow it through at least _some_
269				 firewalls. */
270			 dcc_link = FindUdpTcpOut(true_addr, destaddr,
271						  true_port, 0,
272						  IPPROTO_TCP, 1);
273			 DBprintf(("Got a DCC link\n"));
274			 if ( dcc_link ) {
275				 struct in_addr alias_address;	/* Address from aliasing */
276				 u_short alias_port;	/* Port given by aliasing */
277
278#ifndef NO_FW_PUNCH
279				 /* Generate firewall hole as appropriate */
280				 PunchFWHole(dcc_link);
281#endif
282
283				 alias_address = GetAliasAddress(link);
284				 iCopy += snprintf(&newpacket[iCopy],
285										 sizeof(newpacket)-iCopy,
286										 "%u ", (in_addr_t)htonl(alias_address.s_addr));
287				 if( iCopy >= sizeof(newpacket) ) { /* Truncated/fit exactly - bad news */
288					 DBprintf(("DCC constructed packet overflow.\n"));
289					 goto lBAD_CTCP;
290				 }
291				 alias_port = GetAliasPort(dcc_link);
292				 iCopy += snprintf(&newpacket[iCopy],
293										 sizeof(newpacket)-iCopy,
294										 "%u", htons(alias_port) );
295				 /* Done - truncated cases will be taken care of by lBAD_CTCP */
296				 DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port));
297			 }
298		 }
299		 /* An uninteresting CTCP - state entered right after '\001' has
300          been pushed.  Also used to copy the rest of a DCC, after IP
301          address and port has been handled */
302	 lBAD_CTCP:
303		 for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) {
304			 newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */
305			 if(sptr[i] == '\001') {
306				 goto lNORMAL_TEXT;
307			 }
308		 }
309		 goto lPACKET_DONE;
310		 /* Normal text */
311	 lNORMAL_TEXT:
312		 for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) {
313			 newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */
314			 if(sptr[i] == '\001') {
315				 goto lCTCP_START;
316			 }
317		 }
318		 /* Handle the end of a packet */
319	 lPACKET_DONE:
320		 iCopy = iCopy > maxsize-copyat ? maxsize-copyat : iCopy;
321		 memcpy(sptr+copyat, newpacket, iCopy);
322
323/* Save information regarding modified seq and ack numbers */
324        {
325            int delta;
326
327            SetAckModified(link);
328            delta = GetDeltaSeqOut(pip, link);
329            AddSeq(pip, link, delta+copyat+iCopy-dlen);
330        }
331
332		  /* Revise IP header */
333        {
334			  u_short new_len;
335
336			  new_len = htons(hlen + iCopy + copyat);
337			  DifferentialChecksum(&pip->ip_sum,
338										  &new_len,
339										  &pip->ip_len,
340										  1);
341			  pip->ip_len = new_len;
342        }
343
344		  /* Compute TCP checksum for revised packet */
345        tc->th_sum = 0;
346        tc->th_sum = TcpChecksum(pip);
347		  return;
348	 }
349}
350
351/* Notes:
352	[Note 1]
353	The initial search will most often fail; it could be replaced with a 32-bit specific search.
354	Such a search would be done for 32-bit unsigned value V:
355	V ^= 0x01010101;				  (Search is for null bytes)
356	if( ((V-0x01010101)^V) & 0x80808080 ) {
357     (found a null bytes which was a 01 byte)
358	}
359   To assert that the processor is 32-bits, do
360   extern int ircdccar[32];        (32 bits)
361   extern int ircdccar[CHAR_BIT*sizeof(unsigned int)];
362   which will generate a type-error on all but 32-bit machines.
363
364	[Note 2] This routine really ought to be replaced with one that
365	creates a transparent proxy on the aliasing host, to allow arbitary
366	changes in the TCP stream.  This should not be too difficult given
367	this base;  I (ee) will try to do this some time later.
368	*/
369