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