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