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