]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libalias/alias_irc.c
Add cross-references for the new kldsym(2) man page.
[FreeBSD/FreeBSD.git] / lib / libalias / alias_irc.c
1 /*-
2  * Copyright (c) 2001 Charles Mott <cmott@scientech.com>
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  * $FreeBSD$
27  */
28
29 /* Alias_irc.c intercepts packages contain IRC CTCP commands, and
30         changes DCC commands to export a port on the aliasing host instead
31         of an aliased host.
32
33     For this routine to work, the DCC command must fit entirely into a
34     single TCP packet.  This will usually happen, but is not
35     guaranteed.
36
37          The interception is likely to change the length of the packet.
38          The handling of this is copied more-or-less verbatim from
39          ftp_alias.c
40
41     This software is placed into the public domain with no restrictions
42     on its distribution.
43
44          Initial version: Eivind Eklund <perhaps@yes.no> (ee) 97-01-29
45
46          Version 2.1:  May, 1997 (cjm)
47              Very minor changes to conform with
48              local/global/function naming conventions
49              withing the packet alising module.
50 */
51
52 /* Includes */
53 #include <ctype.h>
54 #include <stdio.h> 
55 #include <string.h>
56 #include <sys/types.h>
57 #include <netinet/in_systm.h>
58 #include <netinet/in.h>
59 #include <netinet/ip.h>
60 #include <netinet/tcp.h>
61 #include <limits.h>
62
63 #include "alias_local.h"
64
65 /* Local defines */
66 #define DBprintf(a)
67
68
69 void
70 AliasHandleIrcOut(struct ip *pip, /* IP packet to examine */
71                                  struct alias_link *link,                 /* Which link are we on? */
72                                  int maxsize              /* Maximum size of IP packet including headers */
73                                  )
74 {       
75     int hlen, tlen, dlen;
76     struct in_addr true_addr;
77     u_short true_port;
78     char *sptr;
79     struct tcphdr *tc;
80          int i;                                                   /* Iterator through the source */
81         
82 /* Calculate data length of TCP packet */
83     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
84     hlen = (pip->ip_hl + tc->th_off) << 2;
85     tlen = ntohs(pip->ip_len);
86     dlen = tlen - hlen;
87
88          /* Return if data length is too short - assume an entire PRIVMSG in each packet. */
89     if (dlen<sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a")-1)
90         return;
91
92 /* Place string pointer at beginning of data */
93     sptr = (char *) pip;  
94     sptr += hlen;
95          maxsize -= hlen;                                 /* We're interested in maximum size of data, not packet */
96
97          /* Search for a CTCP command [Note 1] */
98          for(   i=0; i<dlen; i++ ) {
99                  if(sptr[i]=='\001')
100                          goto lFOUND_CTCP;
101          }
102          return;                                          /* No CTCP commands in  */
103          /* Handle CTCP commands - the buffer may have to be copied */
104 lFOUND_CTCP:
105          {
106                  char newpacket[65536];   /* Estimate of maximum packet size :) */
107                  int  copyat = i;                         /* Same */
108                  int  iCopy = 0;                          /* How much data have we written to copy-back string? */
109                  unsigned long org_addr;  /* Original IP address */
110                  unsigned short org_port; /* Original source port address */
111          lCTCP_START:
112                  if( i >= dlen || iCopy >= sizeof(newpacket) )
113                          goto lPACKET_DONE;
114                  newpacket[iCopy++] = sptr[i++];        /* Copy the CTCP start character */
115                  /* Start of a CTCP */
116                  if( i+4 >= dlen )                /* Too short for DCC */
117                          goto lBAD_CTCP;
118                  if( sptr[i+0] != 'D' )
119                          goto lBAD_CTCP;
120                  if( sptr[i+1] != 'C' )
121                          goto lBAD_CTCP;
122                  if( sptr[i+2] != 'C' )
123                          goto lBAD_CTCP;
124                  if( sptr[i+3] != ' ' )
125                          goto lBAD_CTCP;
126                  /* We have a DCC command - handle it! */
127                  i+= 4;                                           /* Skip "DCC " */
128                  if( iCopy+4 > sizeof(newpacket) )
129                          goto lPACKET_DONE;
130                  newpacket[iCopy++] = 'D';
131                  newpacket[iCopy++] = 'C';
132                  newpacket[iCopy++] = 'C';
133                  newpacket[iCopy++] = ' ';
134
135                  DBprintf(("Found DCC\n"));
136                  /* Skip any extra spaces (should not occur according to
137           protocol, but DCC breaks CTCP protocol anyway */
138                  while(sptr[i] == ' ') {
139                          if( ++i >= dlen) {
140                                  DBprintf(("DCC packet terminated in just spaces\n"));
141                                  goto lPACKET_DONE;
142                          }
143                  }
144
145                  DBprintf(("Transferring command...\n"));
146                  while(sptr[i] != ' ') {
147                          newpacket[iCopy++] = sptr[i];
148                          if( ++i >= dlen || iCopy >= sizeof(newpacket) ) {
149                                  DBprintf(("DCC packet terminated during command\n"));
150                                  goto lPACKET_DONE;
151                          }
152                  }
153                  /* Copy _one_ space */
154                  if( i+1 < dlen && iCopy < sizeof(newpacket) )
155                          newpacket[iCopy++] = sptr[i++];
156
157                  DBprintf(("Done command - removing spaces\n"));
158                  /* Skip any extra spaces (should not occur according to
159           protocol, but DCC breaks CTCP protocol anyway */
160                  while(sptr[i] == ' ') {
161                          if( ++i >= dlen ) {
162                                  DBprintf(("DCC packet terminated in just spaces (post-command)\n"));
163                                  goto lPACKET_DONE;
164                          }
165                  }
166
167                  DBprintf(("Transferring filename...\n"));
168                  while(sptr[i] != ' ') {
169                          newpacket[iCopy++] = sptr[i];
170                          if( ++i >= dlen || iCopy >= sizeof(newpacket) ) {
171                                  DBprintf(("DCC packet terminated during filename\n"));
172                                  goto lPACKET_DONE;
173                          }
174                  }
175                  /* Copy _one_ space */
176                  if( i+1 < dlen && iCopy < sizeof(newpacket) )
177                          newpacket[iCopy++] = sptr[i++];
178
179                  DBprintf(("Done filename - removing spaces\n"));
180                  /* Skip any extra spaces (should not occur according to
181           protocol, but DCC breaks CTCP protocol anyway */
182                  while(sptr[i] == ' ') {
183                          if( ++i >= dlen ) {
184                                  DBprintf(("DCC packet terminated in just spaces (post-filename)\n"));
185                                  goto lPACKET_DONE;
186                          }
187                  }
188
189                  DBprintf(("Fetching IP address\n"));
190                  /* Fetch IP address */
191                  org_addr = 0;
192                  while(i<dlen && isdigit(sptr[i])) {
193                          if( org_addr > ULONG_MAX/10UL )        { /* Terminate on overflow */
194                                  DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i]));
195                                  goto lBAD_CTCP;
196                          }
197                          org_addr *= 10;
198                          org_addr += sptr[i++]-'0';
199                  }
200                  DBprintf(("Skipping space\n"));
201                  if( i+1 >= dlen || sptr[i] != ' ' ) {
202                          DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i+1, dlen, sptr[i]));
203                          goto lBAD_CTCP;
204                  }
205                  /* Skip any extra spaces (should not occur according to
206           protocol, but DCC breaks CTCP protocol anyway, so we might
207           as well play it safe */
208                  while(sptr[i] == ' ') {
209                          if( ++i >= dlen ) {
210                                  DBprintf(("Packet failure - space overflow.\n"));
211                                  goto lPACKET_DONE;
212                          }
213                  }
214                  DBprintf(("Fetching port number\n"));
215                  /* Fetch source port */
216                  org_port = 0;
217                  while(i<dlen && isdigit(sptr[i])) {
218                          if( org_port > 6554 )  { /* Terminate on overflow (65536/10 rounded up*/
219                                  DBprintf(("DCC: port number overflow\n"));
220                                  goto lBAD_CTCP;
221                          }
222                          org_port *= 10;
223                          org_port += sptr[i++]-'0';
224                  }
225                  /* Skip illegal addresses (or early termination) */
226                  if( i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ') ) {
227                          DBprintf(("Bad port termination\n"));
228                          goto lBAD_CTCP;
229                  }
230                  DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port));
231
232                  /* We've got the address and port - now alias it */
233                  {
234                          struct alias_link *dcc_link;
235                          struct in_addr destaddr;
236                          
237
238                          true_port = htons(org_port);
239                          true_addr.s_addr = htonl(org_addr);
240                          destaddr.s_addr = 0;
241
242                          /* Steal the FTP_DATA_PORT - it doesn't really matter, and this
243                                  would probably allow it through at least _some_
244                                  firewalls. */
245                          dcc_link = FindUdpTcpOut(true_addr, destaddr,
246                                                   true_port, 0,
247                                                   IPPROTO_TCP, 1);
248                          DBprintf(("Got a DCC link\n"));
249                          if ( dcc_link ) {
250                                  struct in_addr alias_address;  /* Address from aliasing */
251                                  u_short alias_port;    /* Port given by aliasing */
252
253 #ifndef NO_FW_PUNCH
254                                  /* Generate firewall hole as appropriate */
255                                  PunchFWHole(dcc_link);
256 #endif
257
258                                  alias_address = GetAliasAddress(link);
259                                  iCopy += snprintf(&newpacket[iCopy],
260                                                                                  sizeof(newpacket)-iCopy, 
261                                                                                  "%lu ", (u_long)htonl(alias_address.s_addr));
262                                  if( iCopy >= sizeof(newpacket) ) { /* Truncated/fit exactly - bad news */
263                                          DBprintf(("DCC constructed packet overflow.\n"));
264                                          goto lBAD_CTCP;
265                                  }
266                                  alias_port = GetAliasPort(dcc_link);
267                                  iCopy += snprintf(&newpacket[iCopy],
268                                                                                  sizeof(newpacket)-iCopy, 
269                                                                                  "%u", htons(alias_port) );
270                                  /* Done - truncated cases will be taken care of by lBAD_CTCP */
271                                  DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port));
272                          }
273                  }
274                  /* An uninteresting CTCP - state entered right after '\001' has
275           been pushed.  Also used to copy the rest of a DCC, after IP
276           address and port has been handled */
277          lBAD_CTCP:
278                  for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) {
279                          newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */
280                          if(sptr[i] == '\001') {
281                                  goto lNORMAL_TEXT;
282                          }
283                  }
284                  goto lPACKET_DONE;
285                  /* Normal text */
286          lNORMAL_TEXT:
287                  for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) {
288                          newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */
289                          if(sptr[i] == '\001') {
290                                  goto lCTCP_START;
291                          }
292                  }
293                  /* Handle the end of a packet */
294          lPACKET_DONE:
295                  iCopy = iCopy > maxsize-copyat ? maxsize-copyat : iCopy;
296                  memcpy(sptr+copyat, newpacket, iCopy);
297
298 /* Save information regarding modified seq and ack numbers */
299         {
300             int delta;
301
302             SetAckModified(link);
303             delta = GetDeltaSeqOut(pip, link);
304             AddSeq(pip, link, delta+copyat+iCopy-dlen);
305         }
306
307                   /* Revise IP header */
308         {
309                           u_short new_len;
310                           
311                           new_len = htons(hlen + iCopy + copyat);
312                           DifferentialChecksum(&pip->ip_sum,
313                                                                                   &new_len,
314                                                                                   &pip->ip_len,
315                                                                                   1);
316                           pip->ip_len = new_len;
317         }
318
319                   /* Compute TCP checksum for revised packet */
320         tc->th_sum = 0;
321         tc->th_sum = TcpChecksum(pip);
322                   return;
323          }
324 }
325
326 /* Notes:
327         [Note 1]
328         The initial search will most often fail; it could be replaced with a 32-bit specific search.
329         Such a search would be done for 32-bit unsigned value V:
330         V ^= 0x01010101;                                  (Search is for null bytes)
331         if( ((V-0x01010101)^V) & 0x80808080 ) {
332      (found a null bytes which was a 01 byte)
333         }
334    To assert that the processor is 32-bits, do
335    extern int ircdccar[32];        (32 bits)
336    extern int ircdccar[CHAR_BIT*sizeof(unsigned int)];
337    which will generate a type-error on all but 32-bit machines.
338
339         [Note 2] This routine really ought to be replaced with one that
340         creates a transparent proxy on the aliasing host, to allow arbitary
341         changes in the TCP stream.  This should not be too difficult given
342         this base;  I (ee) will try to do this some time later.
343         */