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