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