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