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