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