alias_irc.c revision 84195
177701Sbrian/*- 277701Sbrian * Copyright (c) 2001 Charles Mott <cmott@scientech.com> 377701Sbrian * All rights reserved. 477701Sbrian * 577701Sbrian * Redistribution and use in source and binary forms, with or without 677701Sbrian * modification, are permitted provided that the following conditions 777701Sbrian * are met: 877701Sbrian * 1. Redistributions of source code must retain the above copyright 977701Sbrian * notice, this list of conditions and the following disclaimer. 1077701Sbrian * 2. Redistributions in binary form must reproduce the above copyright 1177701Sbrian * notice, this list of conditions and the following disclaimer in the 1277701Sbrian * documentation and/or other materials provided with the distribution. 1377701Sbrian * 1477701Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1577701Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1677701Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1777701Sbrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1877701Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1977701Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2077701Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2177701Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2277701Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2377701Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2477701Sbrian * SUCH DAMAGE. 2577701Sbrian */ 2677701Sbrian 2784195Sdillon#include <sys/cdefs.h> 2884195Sdillon__FBSDID("$FreeBSD: head/sys/netinet/libalias/alias_irc.c 84195 2001-09-30 21:03:33Z dillon $"); 2984195Sdillon 3026026Sbrian/* Alias_irc.c intercepts packages contain IRC CTCP commands, and 3126026Sbrian changes DCC commands to export a port on the aliasing host instead 3226026Sbrian of an aliased host. 3326026Sbrian 3426026Sbrian For this routine to work, the DCC command must fit entirely into a 3526026Sbrian single TCP packet. This will usually happen, but is not 3626026Sbrian guaranteed. 3726026Sbrian 3826026Sbrian The interception is likely to change the length of the packet. 3926026Sbrian The handling of this is copied more-or-less verbatim from 4026026Sbrian ftp_alias.c 4126026Sbrian 4226026Sbrian Initial version: Eivind Eklund <perhaps@yes.no> (ee) 97-01-29 4326026Sbrian 4426026Sbrian Version 2.1: May, 1997 (cjm) 4526026Sbrian Very minor changes to conform with 4626026Sbrian local/global/function naming conventions 4726026Sbrian withing the packet alising module. 4826026Sbrian*/ 4926026Sbrian 5026026Sbrian/* Includes */ 5126026Sbrian#include <ctype.h> 5226026Sbrian#include <stdio.h> 5326026Sbrian#include <string.h> 5426026Sbrian#include <sys/types.h> 5526026Sbrian#include <netinet/in_systm.h> 5626026Sbrian#include <netinet/in.h> 5726026Sbrian#include <netinet/ip.h> 5826026Sbrian#include <netinet/tcp.h> 5926026Sbrian#include <limits.h> 6026026Sbrian 6126026Sbrian#include "alias_local.h" 6226026Sbrian 6326026Sbrian/* Local defines */ 6426026Sbrian#define DBprintf(a) 6526026Sbrian 6626026Sbrian 6726026Sbrianvoid 6826026SbrianAliasHandleIrcOut(struct ip *pip, /* IP packet to examine */ 6926026Sbrian struct alias_link *link, /* Which link are we on? */ 7026026Sbrian int maxsize /* Maximum size of IP packet including headers */ 7126026Sbrian ) 7226026Sbrian{ 7326026Sbrian int hlen, tlen, dlen; 7426026Sbrian struct in_addr true_addr; 7526026Sbrian u_short true_port; 7626026Sbrian char *sptr; 7726026Sbrian struct tcphdr *tc; 7826026Sbrian int i; /* Iterator through the source */ 7926026Sbrian 8026026Sbrian/* Calculate data length of TCP packet */ 8126026Sbrian tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); 8226026Sbrian hlen = (pip->ip_hl + tc->th_off) << 2; 8326026Sbrian tlen = ntohs(pip->ip_len); 8426026Sbrian dlen = tlen - hlen; 8526026Sbrian 8626026Sbrian /* Return if data length is too short - assume an entire PRIVMSG in each packet. */ 8726026Sbrian if (dlen<sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a")-1) 8826026Sbrian return; 8926026Sbrian 9026026Sbrian/* Place string pointer at beginning of data */ 9126026Sbrian sptr = (char *) pip; 9226026Sbrian sptr += hlen; 9326026Sbrian maxsize -= hlen; /* We're interested in maximum size of data, not packet */ 9426026Sbrian 9526026Sbrian /* Search for a CTCP command [Note 1] */ 9626026Sbrian for( i=0; i<dlen; i++ ) { 9726026Sbrian if(sptr[i]=='\001') 9826026Sbrian goto lFOUND_CTCP; 9926026Sbrian } 10026026Sbrian return; /* No CTCP commands in */ 10126026Sbrian /* Handle CTCP commands - the buffer may have to be copied */ 10226026SbrianlFOUND_CTCP: 10326026Sbrian { 10426026Sbrian char newpacket[65536]; /* Estimate of maximum packet size :) */ 10526026Sbrian int copyat = i; /* Same */ 10626026Sbrian int iCopy = 0; /* How much data have we written to copy-back string? */ 10726026Sbrian unsigned long org_addr; /* Original IP address */ 10826026Sbrian unsigned short org_port; /* Original source port address */ 10926026Sbrian lCTCP_START: 11026026Sbrian if( i >= dlen || iCopy >= sizeof(newpacket) ) 11126026Sbrian goto lPACKET_DONE; 11226026Sbrian newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start character */ 11326026Sbrian /* Start of a CTCP */ 11426026Sbrian if( i+4 >= dlen ) /* Too short for DCC */ 11526026Sbrian goto lBAD_CTCP; 11626026Sbrian if( sptr[i+0] != 'D' ) 11726026Sbrian goto lBAD_CTCP; 11826026Sbrian if( sptr[i+1] != 'C' ) 11926026Sbrian goto lBAD_CTCP; 12026026Sbrian if( sptr[i+2] != 'C' ) 12126026Sbrian goto lBAD_CTCP; 12226026Sbrian if( sptr[i+3] != ' ' ) 12326026Sbrian goto lBAD_CTCP; 12426026Sbrian /* We have a DCC command - handle it! */ 12526026Sbrian i+= 4; /* Skip "DCC " */ 12626026Sbrian if( iCopy+4 > sizeof(newpacket) ) 12726026Sbrian goto lPACKET_DONE; 12826026Sbrian newpacket[iCopy++] = 'D'; 12926026Sbrian newpacket[iCopy++] = 'C'; 13026026Sbrian newpacket[iCopy++] = 'C'; 13126026Sbrian newpacket[iCopy++] = ' '; 13226026Sbrian 13326026Sbrian DBprintf(("Found DCC\n")); 13426026Sbrian /* Skip any extra spaces (should not occur according to 13526026Sbrian protocol, but DCC breaks CTCP protocol anyway */ 13626026Sbrian while(sptr[i] == ' ') { 13726026Sbrian if( ++i >= dlen) { 13826026Sbrian DBprintf(("DCC packet terminated in just spaces\n")); 13926026Sbrian goto lPACKET_DONE; 14026026Sbrian } 14126026Sbrian } 14226026Sbrian 14326026Sbrian DBprintf(("Transferring command...\n")); 14426026Sbrian while(sptr[i] != ' ') { 14526026Sbrian newpacket[iCopy++] = sptr[i]; 14626026Sbrian if( ++i >= dlen || iCopy >= sizeof(newpacket) ) { 14726026Sbrian DBprintf(("DCC packet terminated during command\n")); 14826026Sbrian goto lPACKET_DONE; 14926026Sbrian } 15026026Sbrian } 15126026Sbrian /* Copy _one_ space */ 15226026Sbrian if( i+1 < dlen && iCopy < sizeof(newpacket) ) 15326026Sbrian newpacket[iCopy++] = sptr[i++]; 15426026Sbrian 15526026Sbrian DBprintf(("Done command - removing spaces\n")); 15626026Sbrian /* Skip any extra spaces (should not occur according to 15726026Sbrian protocol, but DCC breaks CTCP protocol anyway */ 15826026Sbrian while(sptr[i] == ' ') { 15926026Sbrian if( ++i >= dlen ) { 16026026Sbrian DBprintf(("DCC packet terminated in just spaces (post-command)\n")); 16126026Sbrian goto lPACKET_DONE; 16226026Sbrian } 16326026Sbrian } 16426026Sbrian 16526026Sbrian DBprintf(("Transferring filename...\n")); 16626026Sbrian while(sptr[i] != ' ') { 16726026Sbrian newpacket[iCopy++] = sptr[i]; 16826026Sbrian if( ++i >= dlen || iCopy >= sizeof(newpacket) ) { 16926026Sbrian DBprintf(("DCC packet terminated during filename\n")); 17026026Sbrian goto lPACKET_DONE; 17126026Sbrian } 17226026Sbrian } 17326026Sbrian /* Copy _one_ space */ 17426026Sbrian if( i+1 < dlen && iCopy < sizeof(newpacket) ) 17526026Sbrian newpacket[iCopy++] = sptr[i++]; 17626026Sbrian 17726026Sbrian DBprintf(("Done filename - removing spaces\n")); 17826026Sbrian /* Skip any extra spaces (should not occur according to 17926026Sbrian protocol, but DCC breaks CTCP protocol anyway */ 18026026Sbrian while(sptr[i] == ' ') { 18126026Sbrian if( ++i >= dlen ) { 18226026Sbrian DBprintf(("DCC packet terminated in just spaces (post-filename)\n")); 18326026Sbrian goto lPACKET_DONE; 18426026Sbrian } 18526026Sbrian } 18626026Sbrian 18726026Sbrian DBprintf(("Fetching IP address\n")); 18826026Sbrian /* Fetch IP address */ 18926026Sbrian org_addr = 0; 19026026Sbrian while(i<dlen && isdigit(sptr[i])) { 19126026Sbrian if( org_addr > ULONG_MAX/10UL ) { /* Terminate on overflow */ 19226026Sbrian DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i])); 19326026Sbrian goto lBAD_CTCP; 19426026Sbrian } 19526026Sbrian org_addr *= 10; 19626026Sbrian org_addr += sptr[i++]-'0'; 19726026Sbrian } 19826026Sbrian DBprintf(("Skipping space\n")); 19926026Sbrian if( i+1 >= dlen || sptr[i] != ' ' ) { 20026026Sbrian DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i+1, dlen, sptr[i])); 20126026Sbrian goto lBAD_CTCP; 20226026Sbrian } 20326026Sbrian /* Skip any extra spaces (should not occur according to 20426026Sbrian protocol, but DCC breaks CTCP protocol anyway, so we might 20526026Sbrian as well play it safe */ 20626026Sbrian while(sptr[i] == ' ') { 20726026Sbrian if( ++i >= dlen ) { 20826026Sbrian DBprintf(("Packet failure - space overflow.\n")); 20926026Sbrian goto lPACKET_DONE; 21026026Sbrian } 21126026Sbrian } 21226026Sbrian DBprintf(("Fetching port number\n")); 21326026Sbrian /* Fetch source port */ 21426026Sbrian org_port = 0; 21526026Sbrian while(i<dlen && isdigit(sptr[i])) { 21626026Sbrian if( org_port > 6554 ) { /* Terminate on overflow (65536/10 rounded up*/ 21726026Sbrian DBprintf(("DCC: port number overflow\n")); 21826026Sbrian goto lBAD_CTCP; 21926026Sbrian } 22026026Sbrian org_port *= 10; 22126026Sbrian org_port += sptr[i++]-'0'; 22226026Sbrian } 22326026Sbrian /* Skip illegal addresses (or early termination) */ 22426026Sbrian if( i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ') ) { 22526026Sbrian DBprintf(("Bad port termination\n")); 22626026Sbrian goto lBAD_CTCP; 22726026Sbrian } 22826026Sbrian DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port)); 22926026Sbrian 23026026Sbrian /* We've got the address and port - now alias it */ 23126026Sbrian { 23226026Sbrian struct alias_link *dcc_link; 23326026Sbrian struct in_addr destaddr; 23426026Sbrian 23526026Sbrian 23626026Sbrian true_port = htons(org_port); 23726026Sbrian true_addr.s_addr = htonl(org_addr); 23826026Sbrian destaddr.s_addr = 0; 23926026Sbrian 24082050Sru /* Sanity/Security checking */ 24182050Sru if (!org_addr || !org_port || 24282050Sru pip->ip_src.s_addr != true_addr.s_addr || 24382050Sru org_port < IPPORT_RESERVED) 24482050Sru goto lBAD_CTCP; 24582050Sru 24626026Sbrian /* Steal the FTP_DATA_PORT - it doesn't really matter, and this 24726026Sbrian would probably allow it through at least _some_ 24826026Sbrian firewalls. */ 24967980Sru dcc_link = FindUdpTcpOut(true_addr, destaddr, 25067980Sru true_port, 0, 25167980Sru IPPROTO_TCP, 1); 25226026Sbrian DBprintf(("Got a DCC link\n")); 25326026Sbrian if ( dcc_link ) { 25426026Sbrian struct in_addr alias_address; /* Address from aliasing */ 25526026Sbrian u_short alias_port; /* Port given by aliasing */ 25681962Sbrian int n; 25726026Sbrian 25836711Sbrian#ifndef NO_FW_PUNCH 25932377Seivind /* Generate firewall hole as appropriate */ 26032377Seivind PunchFWHole(dcc_link); 26136711Sbrian#endif 26232377Seivind 26326026Sbrian alias_address = GetAliasAddress(link); 26481962Sbrian n = snprintf(&newpacket[iCopy], 26526026Sbrian sizeof(newpacket)-iCopy, 26635314Sbrian "%lu ", (u_long)htonl(alias_address.s_addr)); 26781962Sbrian if( n < 0 ) { 26881962Sbrian DBprintf(("DCC packet construct failure.\n")); 26981962Sbrian goto lBAD_CTCP; 27081962Sbrian } 27181962Sbrian if( (iCopy += n) >= sizeof(newpacket) ) { /* Truncated/fit exactly - bad news */ 27226026Sbrian DBprintf(("DCC constructed packet overflow.\n")); 27326026Sbrian goto lBAD_CTCP; 27426026Sbrian } 27526026Sbrian alias_port = GetAliasPort(dcc_link); 27681962Sbrian n = snprintf(&newpacket[iCopy], 27726026Sbrian sizeof(newpacket)-iCopy, 27826026Sbrian "%u", htons(alias_port) ); 27981962Sbrian if( n < 0 ) { 28081962Sbrian DBprintf(("DCC packet construct failure.\n")); 28181962Sbrian goto lBAD_CTCP; 28281962Sbrian } 28381962Sbrian iCopy += n; 28426026Sbrian /* Done - truncated cases will be taken care of by lBAD_CTCP */ 28526026Sbrian DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port)); 28626026Sbrian } 28726026Sbrian } 28826026Sbrian /* An uninteresting CTCP - state entered right after '\001' has 28926026Sbrian been pushed. Also used to copy the rest of a DCC, after IP 29026026Sbrian address and port has been handled */ 29126026Sbrian lBAD_CTCP: 29226026Sbrian for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) { 29326026Sbrian newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 29426026Sbrian if(sptr[i] == '\001') { 29526026Sbrian goto lNORMAL_TEXT; 29626026Sbrian } 29726026Sbrian } 29826026Sbrian goto lPACKET_DONE; 29926026Sbrian /* Normal text */ 30026026Sbrian lNORMAL_TEXT: 30126026Sbrian for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) { 30226026Sbrian newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ 30326026Sbrian if(sptr[i] == '\001') { 30426026Sbrian goto lCTCP_START; 30526026Sbrian } 30626026Sbrian } 30726026Sbrian /* Handle the end of a packet */ 30826026Sbrian lPACKET_DONE: 30926026Sbrian iCopy = iCopy > maxsize-copyat ? maxsize-copyat : iCopy; 31026026Sbrian memcpy(sptr+copyat, newpacket, iCopy); 31126026Sbrian 31226026Sbrian/* Save information regarding modified seq and ack numbers */ 31326026Sbrian { 31426026Sbrian int delta; 31526026Sbrian 31626026Sbrian SetAckModified(link); 31726026Sbrian delta = GetDeltaSeqOut(pip, link); 31826026Sbrian AddSeq(pip, link, delta+copyat+iCopy-dlen); 31926026Sbrian } 32026026Sbrian 32126026Sbrian /* Revise IP header */ 32226026Sbrian { 32326026Sbrian u_short new_len; 32426026Sbrian 32526026Sbrian new_len = htons(hlen + iCopy + copyat); 32626026Sbrian DifferentialChecksum(&pip->ip_sum, 32726026Sbrian &new_len, 32826026Sbrian &pip->ip_len, 32926026Sbrian 1); 33026026Sbrian pip->ip_len = new_len; 33126026Sbrian } 33226026Sbrian 33326026Sbrian /* Compute TCP checksum for revised packet */ 33426026Sbrian tc->th_sum = 0; 33526026Sbrian tc->th_sum = TcpChecksum(pip); 33626026Sbrian return; 33726026Sbrian } 33826026Sbrian} 33926026Sbrian 34026026Sbrian/* Notes: 34126026Sbrian [Note 1] 34226026Sbrian The initial search will most often fail; it could be replaced with a 32-bit specific search. 34326026Sbrian Such a search would be done for 32-bit unsigned value V: 34426026Sbrian V ^= 0x01010101; (Search is for null bytes) 34526026Sbrian if( ((V-0x01010101)^V) & 0x80808080 ) { 34626026Sbrian (found a null bytes which was a 01 byte) 34726026Sbrian } 34826026Sbrian To assert that the processor is 32-bits, do 34926026Sbrian extern int ircdccar[32]; (32 bits) 35026026Sbrian extern int ircdccar[CHAR_BIT*sizeof(unsigned int)]; 35126026Sbrian which will generate a type-error on all but 32-bit machines. 35226026Sbrian 35326026Sbrian [Note 2] This routine really ought to be replaced with one that 35426026Sbrian creates a transparent proxy on the aliasing host, to allow arbitary 35526026Sbrian changes in the TCP stream. This should not be too difficult given 35626026Sbrian this base; I (ee) will try to do this some time later. 35726026Sbrian */ 358