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