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