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