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: stable/11/sys/netinet/libalias/alias_irc.c 336279 2018-07-14 15:32:36Z markj $");
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	     within 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)
86char *newpacket;
87
88/* Local defines */
89#define DBprintf(a)
90
91static void
92AliasHandleIrcOut(struct libalias *, struct ip *, struct alias_link *,
93		  int maxpacketsize);
94
95static int
96fingerprint(struct libalias *la, struct alias_data *ah)
97{
98
99	if (ah->dport == NULL || ah->lnk == NULL || ah->maxpktsize == 0)
100		return (-1);
101	if (ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_1
102	    || ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_2)
103		return (0);
104	return (-1);
105}
106
107static int
108protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
109{
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 */
205lFOUND_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
214lCTCP_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
351			true_port = htons(org_port);
352			true_addr.s_addr = htonl(org_addr);
353			destaddr.s_addr = 0;
354
355			/* Sanity/Security checking */
356			if (!org_addr || !org_port ||
357			    pip->ip_src.s_addr != true_addr.s_addr ||
358			    org_port < IPPORT_RESERVED)
359				goto lBAD_CTCP;
360
361			/*
362			 * Steal the FTP_DATA_PORT - it doesn't really
363			 * matter, and this would probably allow it through
364			 * at least _some_ firewalls.
365			 */
366			dcc_lnk = FindUdpTcpOut(la, true_addr, destaddr,
367			    true_port, 0,
368			    IPPROTO_TCP, 1);
369			DBprintf(("Got a DCC link\n"));
370			if (dcc_lnk) {
371				struct in_addr alias_address;	/* Address from aliasing */
372				u_short alias_port;	/* Port given by
373							 * aliasing */
374				int n;
375
376#ifndef NO_FW_PUNCH
377				/* Generate firewall hole as appropriate */
378				PunchFWHole(dcc_lnk);
379#endif
380
381				alias_address = GetAliasAddress(lnk);
382				n = snprintf(&newpacket[iCopy],
383				    PKTSIZE - iCopy,
384				    "%lu ", (u_long) htonl(alias_address.s_addr));
385				if (n < 0) {
386					DBprintf(("DCC packet construct failure.\n"));
387					goto lBAD_CTCP;
388				}
389				if ((iCopy += n) >= PKTSIZE) {	/* Truncated/fit exactly
390										 * - bad news */
391					DBprintf(("DCC constructed packet overflow.\n"));
392					goto lBAD_CTCP;
393				}
394				alias_port = GetAliasPort(dcc_lnk);
395				n = snprintf(&newpacket[iCopy],
396				    PKTSIZE - iCopy,
397				    "%u", htons(alias_port));
398				if (n < 0) {
399					DBprintf(("DCC packet construct failure.\n"));
400					goto lBAD_CTCP;
401				}
402				iCopy += n;
403				/*
404				 * Done - truncated cases will be taken
405				 * care of by lBAD_CTCP
406				 */
407				DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port));
408			}
409		}
410		/*
411		 * An uninteresting CTCP - state entered right after '\001'
412		 * has been pushed.  Also used to copy the rest of a DCC,
413		 * after IP address and port has been handled
414		 */
415lBAD_CTCP:
416		for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) {
417			newpacket[iCopy] = sptr[i];	/* Copy CTCP unchanged */
418			if (sptr[i] == '\001') {
419				goto lNORMAL_TEXT;
420			}
421		}
422		goto lPACKET_DONE;
423		/* Normal text */
424lNORMAL_TEXT:
425		for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) {
426			newpacket[iCopy] = sptr[i];	/* Copy CTCP unchanged */
427			if (sptr[i] == '\001') {
428				goto lCTCP_START;
429			}
430		}
431		/* Handle the end of a packet */
432lPACKET_DONE:
433		iCopy = iCopy > maxsize - copyat ? maxsize - copyat : iCopy;
434		memcpy(sptr + copyat, newpacket, iCopy);
435
436/* Save information regarding modified seq and ack numbers */
437		{
438			int delta;
439
440			SetAckModified(lnk);
441			tc = (struct tcphdr *)ip_next(pip);
442			delta = GetDeltaSeqOut(tc->th_seq, lnk);
443			AddSeq(lnk, delta + copyat + iCopy - dlen, pip->ip_hl,
444			    pip->ip_len, tc->th_seq, tc->th_off);
445		}
446
447		/* Revise IP header */
448		{
449			u_short new_len;
450
451			new_len = htons(hlen + iCopy + copyat);
452			DifferentialChecksum(&pip->ip_sum,
453			    &new_len,
454			    &pip->ip_len,
455			    1);
456			pip->ip_len = new_len;
457		}
458
459		/* Compute TCP checksum for revised packet */
460		tc->th_sum = 0;
461#ifdef _KERNEL
462		tc->th_x2 = 1;
463#else
464		tc->th_sum = TcpChecksum(pip);
465#endif
466		return;
467	}
468}
469
470/* Notes:
471	[Note 1]
472	The initial search will most often fail; it could be replaced with a 32-bit specific search.
473	Such a search would be done for 32-bit unsigned value V:
474	V ^= 0x01010101;				  (Search is for null bytes)
475	if( ((V-0x01010101)^V) & 0x80808080 ) {
476     (found a null bytes which was a 01 byte)
477	}
478   To assert that the processor is 32-bits, do
479   extern int ircdccar[32];        (32 bits)
480   extern int ircdccar[CHAR_BIT*sizeof(unsigned int)];
481   which will generate a type-error on all but 32-bit machines.
482
483	[Note 2] This routine really ought to be replaced with one that
484	creates a transparent proxy on the aliasing host, to allow arbitrary
485	changes in the TCP stream.  This should not be too difficult given
486	this base;  I (ee) will try to do this some time later.
487	*/
488