]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netinet/libalias/alias_irc.c
Remove some excessive brackets.
[FreeBSD/FreeBSD.git] / sys / netinet / libalias / alias_irc.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2001 Charles Mott <cm@linktel.net>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 /* Alias_irc.c intercepts packages contain IRC CTCP commands, and
33         changes DCC commands to export a port on the aliasing host instead
34         of an aliased host.
35
36     For this routine to work, the DCC command must fit entirely into a
37     single TCP packet.  This will usually happen, but is not
38     guaranteed.
39
40          The interception is likely to change the length of the packet.
41          The handling of this is copied more-or-less verbatim from
42          ftp_alias.c
43
44          Initial version: Eivind Eklund <perhaps@yes.no> (ee) 97-01-29
45
46          Version 2.1:  May, 1997 (cjm)
47              Very minor changes to conform with
48              local/global/function naming conventions
49              within the packet alising module.
50 */
51
52 /* Includes */
53 #ifdef _KERNEL
54 #include <sys/param.h>
55 #include <sys/ctype.h>
56 #include <sys/limits.h>
57 #include <sys/systm.h>
58 #include <sys/kernel.h>
59 #include <sys/module.h>
60 #else
61 #include <ctype.h>
62 #include <errno.h>
63 #include <sys/types.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <limits.h>
68 #endif
69
70 #include <netinet/in_systm.h>
71 #include <netinet/in.h>
72 #include <netinet/ip.h>
73 #include <netinet/tcp.h>
74
75 #ifdef _KERNEL
76 #include <netinet/libalias/alias.h>
77 #include <netinet/libalias/alias_local.h>
78 #include <netinet/libalias/alias_mod.h>
79 #else
80 #include "alias_local.h"
81 #include "alias_mod.h"
82 #endif
83
84 #define IRC_CONTROL_PORT_NUMBER_1 6667
85 #define IRC_CONTROL_PORT_NUMBER_2 6668
86
87 #define PKTSIZE (IP_MAXPACKET + 1)
88 char *newpacket;
89
90 /* Local defines */
91 #define DBprintf(a)
92
93 static void
94 AliasHandleIrcOut(struct libalias *, struct ip *, struct alias_link *,  
95                   int maxpacketsize);
96
97 static int
98 fingerprint(struct libalias *la, struct alias_data *ah)
99 {
100
101         if (ah->dport == NULL || ah->lnk == NULL || ah->maxpktsize == 0)
102                 return (-1);
103         if (ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_1
104             || ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_2)
105                 return (0);
106         return (-1);
107 }
108
109 static int
110 protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
111 {
112
113         newpacket = malloc(PKTSIZE);
114         if (newpacket) {
115                 AliasHandleIrcOut(la, pip, ah->lnk, ah->maxpktsize);
116                 free(newpacket);
117         }
118         return (0);
119 }
120
121 struct proto_handler handlers[] = {
122         {
123           .pri = 90,
124           .dir = OUT,
125           .proto = TCP,
126           .fingerprint = &fingerprint,
127           .protohandler = &protohandler
128         },
129         { EOH }
130 };
131
132 static int
133 mod_handler(module_t mod, int type, void *data)
134 {
135         int error;
136
137         switch (type) {
138         case MOD_LOAD:
139                 error = 0;
140                 LibAliasAttachHandlers(handlers);
141                 break;
142         case MOD_UNLOAD:
143                 error = 0;
144                 LibAliasDetachHandlers(handlers);
145                 break;
146         default:
147                 error = EINVAL;
148         }
149         return (error);
150 }
151
152 #ifdef _KERNEL
153 static
154 #endif
155 moduledata_t alias_mod = {
156        "alias_irc", mod_handler, NULL
157 };
158
159 /* Kernel module definition. */
160 #ifdef  _KERNEL
161 DECLARE_MODULE(alias_irc, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
162 MODULE_VERSION(alias_irc, 1);
163 MODULE_DEPEND(alias_irc, libalias, 1, 1, 1);
164 #endif
165
166 static void
167 AliasHandleIrcOut(struct libalias *la,
168     struct ip *pip,             /* IP packet to examine */
169     struct alias_link *lnk,     /* Which link are we on? */
170     int maxsize                 /* Maximum size of IP packet including
171                                  * headers */
172 )
173 {
174         int hlen, tlen, dlen;
175         struct in_addr true_addr;
176         u_short true_port;
177         char *sptr;
178         struct tcphdr *tc;
179         int i;                  /* Iterator through the source */
180
181 /* Calculate data length of TCP packet */
182         tc = (struct tcphdr *)ip_next(pip);
183         hlen = (pip->ip_hl + tc->th_off) << 2;
184         tlen = ntohs(pip->ip_len);
185         dlen = tlen - hlen;
186
187         /*
188          * Return if data length is too short - assume an entire PRIVMSG in
189          * each packet.
190          */
191         if (dlen < (int)sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a") - 1)
192                 return;
193
194 /* Place string pointer at beginning of data */
195         sptr = (char *)pip;
196         sptr += hlen;
197         maxsize -= hlen;        /* We're interested in maximum size of
198                                  * data, not packet */
199
200         /* Search for a CTCP command [Note 1] */
201         for (i = 0; i < dlen; i++) {
202                 if (sptr[i] == '\001')
203                         goto lFOUND_CTCP;
204         }
205         return;                 /* No CTCP commands in  */
206         /* Handle CTCP commands - the buffer may have to be copied */
207 lFOUND_CTCP:
208         {
209                 unsigned int copyat = i;
210                 unsigned int iCopy = 0; /* How much data have we written to
211                                          * copy-back string? */
212                 unsigned long org_addr; /* Original IP address */
213                 unsigned short org_port;        /* Original source port
214                                                  * address */
215
216 lCTCP_START:
217                 if (i >= dlen || iCopy >= PKTSIZE)
218                         goto lPACKET_DONE;
219                 newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start
220                                                  * character */
221                 /* Start of a CTCP */
222                 if (i + 4 >= dlen)      /* Too short for DCC */
223                         goto lBAD_CTCP;
224                 if (sptr[i + 0] != 'D')
225                         goto lBAD_CTCP;
226                 if (sptr[i + 1] != 'C')
227                         goto lBAD_CTCP;
228                 if (sptr[i + 2] != 'C')
229                         goto lBAD_CTCP;
230                 if (sptr[i + 3] != ' ')
231                         goto lBAD_CTCP;
232                 /* We have a DCC command - handle it! */
233                 i += 4;         /* Skip "DCC " */
234                 if (iCopy + 4 > PKTSIZE)
235                         goto lPACKET_DONE;
236                 newpacket[iCopy++] = 'D';
237                 newpacket[iCopy++] = 'C';
238                 newpacket[iCopy++] = 'C';
239                 newpacket[iCopy++] = ' ';
240
241                 DBprintf(("Found DCC\n"));
242                 /*
243                  * Skip any extra spaces (should not occur according to
244                  * protocol, but DCC breaks CTCP protocol anyway
245                  */
246                 while (sptr[i] == ' ') {
247                         if (++i >= dlen) {
248                                 DBprintf(("DCC packet terminated in just spaces\n"));
249                                 goto lPACKET_DONE;
250                         }
251                 }
252
253                 DBprintf(("Transferring command...\n"));
254                 while (sptr[i] != ' ') {
255                         newpacket[iCopy++] = sptr[i];
256                         if (++i >= dlen || iCopy >= PKTSIZE) {
257                                 DBprintf(("DCC packet terminated during command\n"));
258                                 goto lPACKET_DONE;
259                         }
260                 }
261                 /* Copy _one_ space */
262                 if (i + 1 < dlen && iCopy < PKTSIZE)
263                         newpacket[iCopy++] = sptr[i++];
264
265                 DBprintf(("Done command - removing spaces\n"));
266                 /*
267                  * Skip any extra spaces (should not occur according to
268                  * protocol, but DCC breaks CTCP protocol anyway
269                  */
270                 while (sptr[i] == ' ') {
271                         if (++i >= dlen) {
272                                 DBprintf(("DCC packet terminated in just spaces (post-command)\n"));
273                                 goto lPACKET_DONE;
274                         }
275                 }
276
277                 DBprintf(("Transferring filename...\n"));
278                 while (sptr[i] != ' ') {
279                         newpacket[iCopy++] = sptr[i];
280                         if (++i >= dlen || iCopy >= PKTSIZE) {
281                                 DBprintf(("DCC packet terminated during filename\n"));
282                                 goto lPACKET_DONE;
283                         }
284                 }
285                 /* Copy _one_ space */
286                 if (i + 1 < dlen && iCopy < PKTSIZE)
287                         newpacket[iCopy++] = sptr[i++];
288
289                 DBprintf(("Done filename - removing spaces\n"));
290                 /*
291                  * Skip any extra spaces (should not occur according to
292                  * protocol, but DCC breaks CTCP protocol anyway
293                  */
294                 while (sptr[i] == ' ') {
295                         if (++i >= dlen) {
296                                 DBprintf(("DCC packet terminated in just spaces (post-filename)\n"));
297                                 goto lPACKET_DONE;
298                         }
299                 }
300
301                 DBprintf(("Fetching IP address\n"));
302                 /* Fetch IP address */
303                 org_addr = 0;
304                 while (i < dlen && isdigit(sptr[i])) {
305                         if (org_addr > ULONG_MAX / 10UL) {      /* Terminate on overflow */
306                                 DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i]));
307                                 goto lBAD_CTCP;
308                         }
309                         org_addr *= 10;
310                         org_addr += sptr[i++] - '0';
311                 }
312                 DBprintf(("Skipping space\n"));
313                 if (i + 1 >= dlen || sptr[i] != ' ') {
314                         DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i + 1, dlen, sptr[i]));
315                         goto lBAD_CTCP;
316                 }
317                 /*
318                  * Skip any extra spaces (should not occur according to
319                  * protocol, but DCC breaks CTCP protocol anyway, so we
320                  * might as well play it safe
321                  */
322                 while (sptr[i] == ' ') {
323                         if (++i >= dlen) {
324                                 DBprintf(("Packet failure - space overflow.\n"));
325                                 goto lPACKET_DONE;
326                         }
327                 }
328                 DBprintf(("Fetching port number\n"));
329                 /* Fetch source port */
330                 org_port = 0;
331                 while (i < dlen && isdigit(sptr[i])) {
332                         if (org_port > 6554) {  /* Terminate on overflow
333                                                  * (65536/10 rounded up */
334                                 DBprintf(("DCC: port number overflow\n"));
335                                 goto lBAD_CTCP;
336                         }
337                         org_port *= 10;
338                         org_port += sptr[i++] - '0';
339                 }
340                 /* Skip illegal addresses (or early termination) */
341                 if (i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ')) {
342                         DBprintf(("Bad port termination\n"));
343                         goto lBAD_CTCP;
344                 }
345                 DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port));
346
347                 /* We've got the address and port - now alias it */
348                 {
349                         struct alias_link *dcc_lnk;
350                         struct in_addr destaddr;
351
352
353                         true_port = htons(org_port);
354                         true_addr.s_addr = htonl(org_addr);
355                         destaddr.s_addr = 0;
356
357                         /* Sanity/Security checking */
358                         if (!org_addr || !org_port ||
359                             pip->ip_src.s_addr != true_addr.s_addr ||
360                             org_port < IPPORT_RESERVED)
361                                 goto lBAD_CTCP;
362
363                         /*
364                          * Steal the FTP_DATA_PORT - it doesn't really
365                          * matter, and this would probably allow it through
366                          * at least _some_ firewalls.
367                          */
368                         dcc_lnk = FindUdpTcpOut(la, true_addr, destaddr,
369                             true_port, 0,
370                             IPPROTO_TCP, 1);
371                         DBprintf(("Got a DCC link\n"));
372                         if (dcc_lnk) {
373                                 struct in_addr alias_address;   /* Address from aliasing */
374                                 u_short alias_port;     /* Port given by
375                                                          * aliasing */
376                                 int n;
377
378 #ifndef NO_FW_PUNCH
379                                 /* Generate firewall hole as appropriate */
380                                 PunchFWHole(dcc_lnk);
381 #endif
382
383                                 alias_address = GetAliasAddress(lnk);
384                                 n = snprintf(&newpacket[iCopy],
385                                     PKTSIZE - iCopy,
386                                     "%lu ", (u_long) htonl(alias_address.s_addr));
387                                 if (n < 0) {
388                                         DBprintf(("DCC packet construct failure.\n"));
389                                         goto lBAD_CTCP;
390                                 }
391                                 if ((iCopy += n) >= PKTSIZE) {  /* Truncated/fit exactly
392                                                                                  * - bad news */
393                                         DBprintf(("DCC constructed packet overflow.\n"));
394                                         goto lBAD_CTCP;
395                                 }
396                                 alias_port = GetAliasPort(dcc_lnk);
397                                 n = snprintf(&newpacket[iCopy],
398                                     PKTSIZE - iCopy,
399                                     "%u", htons(alias_port));
400                                 if (n < 0) {
401                                         DBprintf(("DCC packet construct failure.\n"));
402                                         goto lBAD_CTCP;
403                                 }
404                                 iCopy += n;
405                                 /*
406                                  * Done - truncated cases will be taken
407                                  * care of by lBAD_CTCP
408                                  */
409                                 DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port));
410                         }
411                 }
412                 /*
413                  * An uninteresting CTCP - state entered right after '\001'
414                  * has been pushed.  Also used to copy the rest of a DCC,
415                  * after IP address and port has been handled
416                  */
417 lBAD_CTCP:
418                 for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) {
419                         newpacket[iCopy] = sptr[i];     /* Copy CTCP unchanged */
420                         if (sptr[i] == '\001') {
421                                 goto lNORMAL_TEXT;
422                         }
423                 }
424                 goto lPACKET_DONE;
425                 /* Normal text */
426 lNORMAL_TEXT:
427                 for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) {
428                         newpacket[iCopy] = sptr[i];     /* Copy CTCP unchanged */
429                         if (sptr[i] == '\001') {
430                                 goto lCTCP_START;
431                         }
432                 }
433                 /* Handle the end of a packet */
434 lPACKET_DONE:
435                 iCopy = iCopy > maxsize - copyat ? maxsize - copyat : iCopy;
436                 memcpy(sptr + copyat, newpacket, iCopy);
437
438 /* Save information regarding modified seq and ack numbers */
439                 {
440                         int delta;
441
442                         SetAckModified(lnk);
443                         tc = (struct tcphdr *)ip_next(pip);                             
444                         delta = GetDeltaSeqOut(tc->th_seq, lnk);
445                         AddSeq(lnk, delta + copyat + iCopy - dlen, pip->ip_hl,
446                             pip->ip_len, tc->th_seq, tc->th_off);
447                 }
448
449                 /* Revise IP header */
450                 {
451                         u_short new_len;
452
453                         new_len = htons(hlen + iCopy + copyat);
454                         DifferentialChecksum(&pip->ip_sum,
455                             &new_len,
456                             &pip->ip_len,
457                             1);
458                         pip->ip_len = new_len;
459                 }
460
461                 /* Compute TCP checksum for revised packet */
462                 tc->th_sum = 0;
463 #ifdef _KERNEL
464                 tc->th_x2 = 1;
465 #else
466                 tc->th_sum = TcpChecksum(pip);
467 #endif
468                 return;
469         }
470 }
471
472 /* Notes:
473         [Note 1]
474         The initial search will most often fail; it could be replaced with a 32-bit specific search.
475         Such a search would be done for 32-bit unsigned value V:
476         V ^= 0x01010101;                                  (Search is for null bytes)
477         if( ((V-0x01010101)^V) & 0x80808080 ) {
478      (found a null bytes which was a 01 byte)
479         }
480    To assert that the processor is 32-bits, do
481    extern int ircdccar[32];        (32 bits)
482    extern int ircdccar[CHAR_BIT*sizeof(unsigned int)];
483    which will generate a type-error on all but 32-bit machines.
484
485         [Note 2] This routine really ought to be replaced with one that
486         creates a transparent proxy on the aliasing host, to allow arbitrary
487         changes in the TCP stream.  This should not be too difficult given
488         this base;  I (ee) will try to do this some time later.
489         */