xref: /freebsd/sys/netinet/libalias/alias_irc.c (revision 1de7b4b805ddbf2429da511c053686ac4591ed89)
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 	*/
491