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