FreeBSD kernel IPv4 code
alias_irc.c
Go to the documentation of this file.
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
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)
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
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;
139 break;
140 case MOD_UNLOAD:
141 error = 0;
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
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);
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*/
u_short GetAliasPort(struct alias_link *lnk)
Definition: alias_db.c:1529
void PunchFWHole(struct alias_link *lnk)
Definition: alias_db.c:2318
struct in_addr GetAliasAddress(struct alias_link *lnk)
Definition: alias_db.c:1500
void SetAckModified(struct alias_link *lnk)
Definition: alias_db.c:1545
void AddSeq(struct alias_link *lnk, int delta, u_int ip_hl, u_short ip_len, u_long th_seq, u_int th_off)
Definition: alias_db.c:1672
int GetDeltaSeqOut(u_long seq, struct alias_link *lnk)
Definition: alias_db.c:1630
struct alias_link * FindUdpTcpOut(struct libalias *la, struct in_addr src_addr, struct in_addr dst_addr, u_short src_port, u_short dst_port, u_char proto, int create)
Definition: alias_db.c:1174
static int mod_handler(module_t mod, int type, void *data)
Definition: alias_irc.c:131
#define IRC_CONTROL_PORT_NUMBER_2
Definition: alias_irc.c:85
char * newpacket
Definition: alias_irc.c:88
static int fingerprint(struct libalias *la, struct alias_data *ah)
Definition: alias_irc.c:98
static moduledata_t alias_mod
Definition: alias_irc.c:153
#define DBprintf(a)
Definition: alias_irc.c:91
#define IRC_CONTROL_PORT_NUMBER_1
Definition: alias_irc.c:84
static void AliasHandleIrcOut(struct libalias *, struct ip *, struct alias_link *, int maxpacketsize)
Definition: alias_irc.c:165
static int protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
Definition: alias_irc.c:109
__FBSDID("$FreeBSD$")
struct proto_handler handlers[]
Definition: alias_irc.c:119
#define PKTSIZE
Definition: alias_irc.c:87
MODULE_DEPEND(alias_irc, libalias, 1, 1, 1)
MODULE_VERSION(alias_irc, 1)
DECLARE_MODULE(alias_irc, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND)
void DifferentialChecksum(u_short *_cksum, void *_new, void *_old, int _n)
Definition: alias_util.c:154
int LibAliasAttachHandlers(struct proto_handler *p)
Definition: alias_mod.c:82
int LibAliasDetachHandlers(struct proto_handler *p)
Definition: alias_mod.c:98
#define TCP
Definition: alias_mod.h:58
#define EOH
Definition: alias_mod.h:92
#define OUT
Definition: alias_mod.h:53
#define IPPROTO_TCP
Definition: in.h:45
uint16_t * dport
Definition: alias_mod.h:71
uint16_t maxpktsize
Definition: alias_mod.h:72
struct alias_link * lnk
Definition: alias_mod.h:67
Definition: in.h:83
in_addr_t s_addr
Definition: in.h:84
Definition: ip.h:51
u_char ip_hl
Definition: ip.h:53
u_short ip_sum
Definition: ip.h:70
u_short ip_len
Definition: ip.h:61