xref: /freebsd/sys/netinet/libalias/alias_irc.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
1 /*-
2  * Copyright (c) 2001 Charles Mott <cmott@scientech.com>
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  * $FreeBSD$
27  */
28 
29 /* Alias_irc.c intercepts packages contain IRC CTCP commands, and
30 	changes DCC commands to export a port on the aliasing host instead
31 	of an aliased host.
32 
33     For this routine to work, the DCC command must fit entirely into a
34     single TCP packet.  This will usually happen, but is not
35     guaranteed.
36 
37 	 The interception is likely to change the length of the packet.
38 	 The handling of this is copied more-or-less verbatim from
39 	 ftp_alias.c
40 
41     This software is placed into the public domain with no restrictions
42     on its distribution.
43 
44 	 Initial version: Eivind Eklund <perhaps@yes.no> (ee) 97-01-29
45 
46          Version 2.1:  May, 1997 (cjm)
47              Very minor changes to conform with
48              local/global/function naming conventions
49              withing the packet alising module.
50 */
51 
52 /* Includes */
53 #include <ctype.h>
54 #include <stdio.h>
55 #include <string.h>
56 #include <sys/types.h>
57 #include <netinet/in_systm.h>
58 #include <netinet/in.h>
59 #include <netinet/ip.h>
60 #include <netinet/tcp.h>
61 #include <limits.h>
62 
63 #include "alias_local.h"
64 
65 /* Local defines */
66 #define DBprintf(a)
67 
68 
69 void
70 AliasHandleIrcOut(struct ip *pip, /* IP packet to examine */
71 				 struct alias_link *link,		  /* Which link are we on? */
72 				 int maxsize		  /* Maximum size of IP packet including 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 *) ((char *) pip + (pip->ip_hl << 2));
84     hlen = (pip->ip_hl + tc->th_off) << 2;
85     tlen = ntohs(pip->ip_len);
86     dlen = tlen - hlen;
87 
88 	 /* Return if data length is too short - assume an entire PRIVMSG in each packet. */
89     if (dlen<sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a")-1)
90         return;
91 
92 /* Place string pointer at beginning of data */
93     sptr = (char *) pip;
94     sptr += hlen;
95 	 maxsize -= hlen;				  /* We're interested in maximum size of data, not packet */
96 
97 	 /* Search for a CTCP command [Note 1] */
98 	 for(	i=0; i<dlen; i++ ) {
99 		 if(sptr[i]=='\001')
100 			 goto lFOUND_CTCP;
101 	 }
102 	 return;					  /* No CTCP commands in  */
103 	 /* Handle CTCP commands - the buffer may have to be copied */
104 lFOUND_CTCP:
105 	 {
106 		 char newpacket[65536];	  /* Estimate of maximum packet size :) */
107 		 int  copyat = i;			  /* Same */
108 		 int  iCopy = 0;			  /* How much data have we written to copy-back string? */
109 		 unsigned long org_addr;  /* Original IP address */
110 		 unsigned short org_port; /* Original source port address */
111 	 lCTCP_START:
112 		 if( i >= dlen || iCopy >= sizeof(newpacket) )
113 			 goto lPACKET_DONE;
114 		 newpacket[iCopy++] = sptr[i++];	/* Copy the CTCP start character */
115 		 /* Start of a CTCP */
116 		 if( i+4 >= dlen )		  /* Too short for DCC */
117 			 goto lBAD_CTCP;
118 		 if( sptr[i+0] != 'D' )
119 			 goto lBAD_CTCP;
120 		 if( sptr[i+1] != 'C' )
121 			 goto lBAD_CTCP;
122 		 if( sptr[i+2] != 'C' )
123 			 goto lBAD_CTCP;
124 		 if( sptr[i+3] != ' ' )
125 			 goto lBAD_CTCP;
126 		 /* We have a DCC command - handle it! */
127 		 i+= 4;						  /* Skip "DCC " */
128 		 if( iCopy+4 > sizeof(newpacket) )
129 			 goto lPACKET_DONE;
130 		 newpacket[iCopy++] = 'D';
131 		 newpacket[iCopy++] = 'C';
132 		 newpacket[iCopy++] = 'C';
133 		 newpacket[iCopy++] = ' ';
134 
135 		 DBprintf(("Found DCC\n"));
136 		 /* Skip any extra spaces (should not occur according to
137           protocol, but DCC breaks CTCP protocol anyway */
138 		 while(sptr[i] == ' ') {
139 			 if( ++i >= dlen) {
140 				 DBprintf(("DCC packet terminated in just spaces\n"));
141 				 goto lPACKET_DONE;
142 			 }
143 		 }
144 
145 		 DBprintf(("Transferring command...\n"));
146 		 while(sptr[i] != ' ') {
147 			 newpacket[iCopy++] = sptr[i];
148 			 if( ++i >= dlen || iCopy >= sizeof(newpacket) ) {
149 				 DBprintf(("DCC packet terminated during command\n"));
150 				 goto lPACKET_DONE;
151 			 }
152 		 }
153 		 /* Copy _one_ space */
154 		 if( i+1 < dlen && iCopy < sizeof(newpacket) )
155 			 newpacket[iCopy++] = sptr[i++];
156 
157 		 DBprintf(("Done command - removing spaces\n"));
158 		 /* Skip any extra spaces (should not occur according to
159           protocol, but DCC breaks CTCP protocol anyway */
160 		 while(sptr[i] == ' ') {
161 			 if( ++i >= dlen ) {
162 				 DBprintf(("DCC packet terminated in just spaces (post-command)\n"));
163 				 goto lPACKET_DONE;
164 			 }
165 		 }
166 
167 		 DBprintf(("Transferring filename...\n"));
168 		 while(sptr[i] != ' ') {
169 			 newpacket[iCopy++] = sptr[i];
170 			 if( ++i >= dlen || iCopy >= sizeof(newpacket) ) {
171 				 DBprintf(("DCC packet terminated during filename\n"));
172 				 goto lPACKET_DONE;
173 			 }
174 		 }
175 		 /* Copy _one_ space */
176 		 if( i+1 < dlen && iCopy < sizeof(newpacket) )
177 			 newpacket[iCopy++] = sptr[i++];
178 
179 		 DBprintf(("Done filename - removing spaces\n"));
180 		 /* Skip any extra spaces (should not occur according to
181           protocol, but DCC breaks CTCP protocol anyway */
182 		 while(sptr[i] == ' ') {
183 			 if( ++i >= dlen ) {
184 				 DBprintf(("DCC packet terminated in just spaces (post-filename)\n"));
185 				 goto lPACKET_DONE;
186 			 }
187 		 }
188 
189 		 DBprintf(("Fetching IP address\n"));
190 		 /* Fetch IP address */
191 		 org_addr = 0;
192 		 while(i<dlen && isdigit(sptr[i])) {
193 			 if( org_addr > ULONG_MAX/10UL )	{ /* Terminate on overflow */
194 				 DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i]));
195 				 goto lBAD_CTCP;
196 			 }
197 			 org_addr *= 10;
198 			 org_addr += sptr[i++]-'0';
199 		 }
200 		 DBprintf(("Skipping space\n"));
201 		 if( i+1 >= dlen || sptr[i] != ' ' ) {
202 			 DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i+1, dlen, sptr[i]));
203 			 goto lBAD_CTCP;
204 		 }
205 		 /* Skip any extra spaces (should not occur according to
206           protocol, but DCC breaks CTCP protocol anyway, so we might
207           as well play it safe */
208 		 while(sptr[i] == ' ') {
209 			 if( ++i >= dlen ) {
210 				 DBprintf(("Packet failure - space overflow.\n"));
211 				 goto lPACKET_DONE;
212 			 }
213 		 }
214 		 DBprintf(("Fetching port number\n"));
215 		 /* Fetch source port */
216 		 org_port = 0;
217 		 while(i<dlen && isdigit(sptr[i])) {
218 			 if( org_port > 6554 )	{ /* Terminate on overflow (65536/10 rounded up*/
219 				 DBprintf(("DCC: port number overflow\n"));
220 				 goto lBAD_CTCP;
221 			 }
222 			 org_port *= 10;
223 			 org_port += sptr[i++]-'0';
224 		 }
225 		 /* Skip illegal addresses (or early termination) */
226 		 if( i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ') ) {
227 			 DBprintf(("Bad port termination\n"));
228 			 goto lBAD_CTCP;
229 		 }
230 		 DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port));
231 
232 		 /* We've got the address and port - now alias it */
233 		 {
234 			 struct alias_link *dcc_link;
235 			 struct in_addr destaddr;
236 
237 
238 			 true_port = htons(org_port);
239 			 true_addr.s_addr = htonl(org_addr);
240 			 destaddr.s_addr = 0;
241 
242 			 /* Steal the FTP_DATA_PORT - it doesn't really matter, and this
243 				 would probably allow it through at least _some_
244 				 firewalls. */
245 			 dcc_link = FindUdpTcpOut(true_addr, destaddr,
246 						  true_port, 0,
247 						  IPPROTO_TCP, 1);
248 			 DBprintf(("Got a DCC link\n"));
249 			 if ( dcc_link ) {
250 				 struct in_addr alias_address;	/* Address from aliasing */
251 				 u_short alias_port;	/* Port given by aliasing */
252 
253 #ifndef NO_FW_PUNCH
254 				 /* Generate firewall hole as appropriate */
255 				 PunchFWHole(dcc_link);
256 #endif
257 
258 				 alias_address = GetAliasAddress(link);
259 				 iCopy += snprintf(&newpacket[iCopy],
260 										 sizeof(newpacket)-iCopy,
261 										 "%lu ", (u_long)htonl(alias_address.s_addr));
262 				 if( iCopy >= sizeof(newpacket) ) { /* Truncated/fit exactly - bad news */
263 					 DBprintf(("DCC constructed packet overflow.\n"));
264 					 goto lBAD_CTCP;
265 				 }
266 				 alias_port = GetAliasPort(dcc_link);
267 				 iCopy += snprintf(&newpacket[iCopy],
268 										 sizeof(newpacket)-iCopy,
269 										 "%u", htons(alias_port) );
270 				 /* Done - truncated cases will be taken care of by lBAD_CTCP */
271 				 DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port));
272 			 }
273 		 }
274 		 /* An uninteresting CTCP - state entered right after '\001' has
275           been pushed.  Also used to copy the rest of a DCC, after IP
276           address and port has been handled */
277 	 lBAD_CTCP:
278 		 for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) {
279 			 newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */
280 			 if(sptr[i] == '\001') {
281 				 goto lNORMAL_TEXT;
282 			 }
283 		 }
284 		 goto lPACKET_DONE;
285 		 /* Normal text */
286 	 lNORMAL_TEXT:
287 		 for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) {
288 			 newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */
289 			 if(sptr[i] == '\001') {
290 				 goto lCTCP_START;
291 			 }
292 		 }
293 		 /* Handle the end of a packet */
294 	 lPACKET_DONE:
295 		 iCopy = iCopy > maxsize-copyat ? maxsize-copyat : iCopy;
296 		 memcpy(sptr+copyat, newpacket, iCopy);
297 
298 /* Save information regarding modified seq and ack numbers */
299         {
300             int delta;
301 
302             SetAckModified(link);
303             delta = GetDeltaSeqOut(pip, link);
304             AddSeq(pip, link, delta+copyat+iCopy-dlen);
305         }
306 
307 		  /* Revise IP header */
308         {
309 			  u_short new_len;
310 
311 			  new_len = htons(hlen + iCopy + copyat);
312 			  DifferentialChecksum(&pip->ip_sum,
313 										  &new_len,
314 										  &pip->ip_len,
315 										  1);
316 			  pip->ip_len = new_len;
317         }
318 
319 		  /* Compute TCP checksum for revised packet */
320         tc->th_sum = 0;
321         tc->th_sum = TcpChecksum(pip);
322 		  return;
323 	 }
324 }
325 
326 /* Notes:
327 	[Note 1]
328 	The initial search will most often fail; it could be replaced with a 32-bit specific search.
329 	Such a search would be done for 32-bit unsigned value V:
330 	V ^= 0x01010101;				  (Search is for null bytes)
331 	if( ((V-0x01010101)^V) & 0x80808080 ) {
332      (found a null bytes which was a 01 byte)
333 	}
334    To assert that the processor is 32-bits, do
335    extern int ircdccar[32];        (32 bits)
336    extern int ircdccar[CHAR_BIT*sizeof(unsigned int)];
337    which will generate a type-error on all but 32-bit machines.
338 
339 	[Note 2] This routine really ought to be replaced with one that
340 	creates a transparent proxy on the aliasing host, to allow arbitary
341 	changes in the TCP stream.  This should not be too difficult given
342 	this base;  I (ee) will try to do this some time later.
343 	*/
344