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