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