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