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