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