network_cmds-511.tar.gz
[apple/network_cmds.git] / alias / alias_irc.c
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
88 void
89 AliasHandleIrcOut(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 */
123 lFOUND_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 */