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