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/10/sys/netinet/libalias/alias_irc.c 190841 2009-04-08 11:56:49Z piso $");
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 withing 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)
86 char *newpacket;
87
88 /* Local defines */
89 #define DBprintf(a)
90
91 static void
92 AliasHandleIrcOut(struct libalias *, struct ip *, struct alias_link *,
93 int maxpacketsize);
94
95 static int
fingerprint(struct libalias * la,struct alias_data * ah)96 fingerprint(struct libalias *la, struct alias_data *ah)
97 {
98
99 if (ah->dport == NULL || ah->dport == NULL || ah->lnk == NULL ||
100 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
108 static int
protohandler(struct libalias * la,struct ip * pip,struct alias_data * ah)109 protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
110 {
111
112 newpacket = malloc(PKTSIZE);
113 if (newpacket) {
114 AliasHandleIrcOut(la, pip, ah->lnk, ah->maxpktsize);
115 free(newpacket);
116 }
117 return (0);
118 }
119
120 struct proto_handler handlers[] = {
121 {
122 .pri = 90,
123 .dir = OUT,
124 .proto = TCP,
125 .fingerprint = &fingerprint,
126 .protohandler = &protohandler
127 },
128 { EOH }
129 };
130
131 static int
mod_handler(module_t mod,int type,void * data)132 mod_handler(module_t mod, int type, void *data)
133 {
134 int error;
135
136 switch (type) {
137 case MOD_LOAD:
138 error = 0;
139 LibAliasAttachHandlers(handlers);
140 break;
141 case MOD_UNLOAD:
142 error = 0;
143 LibAliasDetachHandlers(handlers);
144 break;
145 default:
146 error = EINVAL;
147 }
148 return (error);
149 }
150
151 #ifdef _KERNEL
152 static
153 #endif
154 moduledata_t alias_mod = {
155 "alias_irc", mod_handler, NULL
156 };
157
158 /* Kernel module definition. */
159 #ifdef _KERNEL
160 DECLARE_MODULE(alias_irc, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
161 MODULE_VERSION(alias_irc, 1);
162 MODULE_DEPEND(alias_irc, libalias, 1, 1, 1);
163 #endif
164
165 static void
AliasHandleIrcOut(struct libalias * la,struct ip * pip,struct alias_link * lnk,int maxsize)166 AliasHandleIrcOut(struct libalias *la,
167 struct ip *pip, /* IP packet to examine */
168 struct alias_link *lnk, /* Which link are we on? */
169 int maxsize /* Maximum size of IP packet including
170 * headers */
171 )
172 {
173 int hlen, tlen, dlen;
174 struct in_addr true_addr;
175 u_short true_port;
176 char *sptr;
177 struct tcphdr *tc;
178 int i; /* Iterator through the source */
179
180 /* Calculate data length of TCP packet */
181 tc = (struct tcphdr *)ip_next(pip);
182 hlen = (pip->ip_hl + tc->th_off) << 2;
183 tlen = ntohs(pip->ip_len);
184 dlen = tlen - hlen;
185
186 /*
187 * Return if data length is too short - assume an entire PRIVMSG in
188 * each packet.
189 */
190 if (dlen < (int)sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a") - 1)
191 return;
192
193 /* Place string pointer at beginning of data */
194 sptr = (char *)pip;
195 sptr += hlen;
196 maxsize -= hlen; /* We're interested in maximum size of
197 * data, not packet */
198
199 /* Search for a CTCP command [Note 1] */
200 for (i = 0; i < dlen; i++) {
201 if (sptr[i] == '\001')
202 goto lFOUND_CTCP;
203 }
204 return; /* No CTCP commands in */
205 /* Handle CTCP commands - the buffer may have to be copied */
206 lFOUND_CTCP:
207 {
208 unsigned int copyat = i;
209 unsigned int iCopy = 0; /* How much data have we written to
210 * copy-back string? */
211 unsigned long org_addr; /* Original IP address */
212 unsigned short org_port; /* Original source port
213 * address */
214
215 lCTCP_START:
216 if (i >= dlen || iCopy >= PKTSIZE)
217 goto lPACKET_DONE;
218 newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start
219 * character */
220 /* Start of a CTCP */
221 if (i + 4 >= dlen) /* Too short for DCC */
222 goto lBAD_CTCP;
223 if (sptr[i + 0] != 'D')
224 goto lBAD_CTCP;
225 if (sptr[i + 1] != 'C')
226 goto lBAD_CTCP;
227 if (sptr[i + 2] != 'C')
228 goto lBAD_CTCP;
229 if (sptr[i + 3] != ' ')
230 goto lBAD_CTCP;
231 /* We have a DCC command - handle it! */
232 i += 4; /* Skip "DCC " */
233 if (iCopy + 4 > PKTSIZE)
234 goto lPACKET_DONE;
235 newpacket[iCopy++] = 'D';
236 newpacket[iCopy++] = 'C';
237 newpacket[iCopy++] = 'C';
238 newpacket[iCopy++] = ' ';
239
240 DBprintf(("Found DCC\n"));
241 /*
242 * Skip any extra spaces (should not occur according to
243 * protocol, but DCC breaks CTCP protocol anyway
244 */
245 while (sptr[i] == ' ') {
246 if (++i >= dlen) {
247 DBprintf(("DCC packet terminated in just spaces\n"));
248 goto lPACKET_DONE;
249 }
250 }
251
252 DBprintf(("Transferring command...\n"));
253 while (sptr[i] != ' ') {
254 newpacket[iCopy++] = sptr[i];
255 if (++i >= dlen || iCopy >= PKTSIZE) {
256 DBprintf(("DCC packet terminated during command\n"));
257 goto lPACKET_DONE;
258 }
259 }
260 /* Copy _one_ space */
261 if (i + 1 < dlen && iCopy < PKTSIZE)
262 newpacket[iCopy++] = sptr[i++];
263
264 DBprintf(("Done command - removing spaces\n"));
265 /*
266 * Skip any extra spaces (should not occur according to
267 * protocol, but DCC breaks CTCP protocol anyway
268 */
269 while (sptr[i] == ' ') {
270 if (++i >= dlen) {
271 DBprintf(("DCC packet terminated in just spaces (post-command)\n"));
272 goto lPACKET_DONE;
273 }
274 }
275
276 DBprintf(("Transferring filename...\n"));
277 while (sptr[i] != ' ') {
278 newpacket[iCopy++] = sptr[i];
279 if (++i >= dlen || iCopy >= PKTSIZE) {
280 DBprintf(("DCC packet terminated during filename\n"));
281 goto lPACKET_DONE;
282 }
283 }
284 /* Copy _one_ space */
285 if (i + 1 < dlen && iCopy < PKTSIZE)
286 newpacket[iCopy++] = sptr[i++];
287
288 DBprintf(("Done filename - removing spaces\n"));
289 /*
290 * Skip any extra spaces (should not occur according to
291 * protocol, but DCC breaks CTCP protocol anyway
292 */
293 while (sptr[i] == ' ') {
294 if (++i >= dlen) {
295 DBprintf(("DCC packet terminated in just spaces (post-filename)\n"));
296 goto lPACKET_DONE;
297 }
298 }
299
300 DBprintf(("Fetching IP address\n"));
301 /* Fetch IP address */
302 org_addr = 0;
303 while (i < dlen && isdigit(sptr[i])) {
304 if (org_addr > ULONG_MAX / 10UL) { /* Terminate on overflow */
305 DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i]));
306 goto lBAD_CTCP;
307 }
308 org_addr *= 10;
309 org_addr += sptr[i++] - '0';
310 }
311 DBprintf(("Skipping space\n"));
312 if (i + 1 >= dlen || sptr[i] != ' ') {
313 DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i + 1, dlen, sptr[i]));
314 goto lBAD_CTCP;
315 }
316 /*
317 * Skip any extra spaces (should not occur according to
318 * protocol, but DCC breaks CTCP protocol anyway, so we
319 * might as well play it safe
320 */
321 while (sptr[i] == ' ') {
322 if (++i >= dlen) {
323 DBprintf(("Packet failure - space overflow.\n"));
324 goto lPACKET_DONE;
325 }
326 }
327 DBprintf(("Fetching port number\n"));
328 /* Fetch source port */
329 org_port = 0;
330 while (i < dlen && isdigit(sptr[i])) {
331 if (org_port > 6554) { /* Terminate on overflow
332 * (65536/10 rounded up */
333 DBprintf(("DCC: port number overflow\n"));
334 goto lBAD_CTCP;
335 }
336 org_port *= 10;
337 org_port += sptr[i++] - '0';
338 }
339 /* Skip illegal addresses (or early termination) */
340 if (i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ')) {
341 DBprintf(("Bad port termination\n"));
342 goto lBAD_CTCP;
343 }
344 DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port));
345
346 /* We've got the address and port - now alias it */
347 {
348 struct alias_link *dcc_lnk;
349 struct in_addr destaddr;
350
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 arbitary
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