]>
Commit | Line | Data |
---|---|---|
b7080c8e A |
1 | /* Alias_irc.c intercepts packages contain IRC CTCP commands, and |
2 | changes DCC commands to export a port on the aliasing host instead | |
3 | of an aliased host. | |
4 | ||
5 | For this routine to work, the DCC command must fit entirely into a | |
6 | single TCP packet. This will usually happen, but is not | |
7 | guaranteed. | |
8 | ||
9 | The interception is likely to change the length of the packet. | |
10 | The handling of this is copied more-or-less verbatim from | |
11 | ftp_alias.c | |
12 | ||
13 | This software is placed into the public domain with no restrictions | |
14 | on its distribution. | |
15 | ||
16 | Initial version: Eivind Eklund <perhaps@yes.no> (ee) 97-01-29 | |
17 | ||
18 | Version 2.1: May, 1997 (cjm) | |
19 | Very minor changes to conform with | |
20 | local/global/function naming conventions | |
21 | withing the packet alising module. | |
22 | */ | |
23 | ||
24 | /* Includes */ | |
25 | #include <ctype.h> | |
26 | #include <stdio.h> | |
27 | #include <string.h> | |
28 | #include <sys/types.h> | |
29 | #include <netinet/in_systm.h> | |
30 | #include <netinet/in.h> | |
31 | #include <netinet/ip.h> | |
32 | #include <netinet/tcp.h> | |
33 | #include <limits.h> | |
34 | ||
35 | #include "alias_local.h" | |
36 | ||
37 | /* Local defines */ | |
38 | #define DBprintf(a) | |
39 | ||
40 | ||
41 | void | |
42 | AliasHandleIrcOut(struct ip *pip, /* IP packet to examine */ | |
43 | struct alias_link *link, /* Which link are we on? */ | |
44 | int maxsize /* Maximum size of IP packet including headers */ | |
45 | ) | |
46 | { | |
47 | int hlen, tlen, dlen; | |
48 | struct in_addr true_addr; | |
49 | u_short true_port; | |
50 | char *sptr; | |
51 | struct tcphdr *tc; | |
52 | int i; /* Iterator through the source */ | |
53 | ||
54 | /* Calculate data length of TCP packet */ | |
55 | tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)); | |
56 | hlen = (pip->ip_hl + tc->th_off) << 2; | |
57 | tlen = ntohs(pip->ip_len); | |
58 | dlen = tlen - hlen; | |
59 | ||
60 | /* Return if data length is too short - assume an entire PRIVMSG in each packet. */ | |
61 | if (dlen<sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a")-1) | |
62 | return; | |
63 | ||
64 | /* Place string pointer at beginning of data */ | |
65 | sptr = (char *) pip; | |
66 | sptr += hlen; | |
67 | maxsize -= hlen; /* We're interested in maximum size of data, not packet */ | |
68 | ||
69 | /* Search for a CTCP command [Note 1] */ | |
70 | for( i=0; i<dlen; i++ ) { | |
71 | if(sptr[i]=='\001') | |
72 | goto lFOUND_CTCP; | |
73 | } | |
74 | return; /* No CTCP commands in */ | |
75 | /* Handle CTCP commands - the buffer may have to be copied */ | |
76 | lFOUND_CTCP: | |
77 | { | |
78 | char newpacket[65536]; /* Estimate of maximum packet size :) */ | |
79 | int copyat = i; /* Same */ | |
80 | int iCopy = 0; /* How much data have we written to copy-back string? */ | |
81 | unsigned long org_addr; /* Original IP address */ | |
82 | unsigned short org_port; /* Original source port address */ | |
83 | lCTCP_START: | |
84 | if( i >= dlen || iCopy >= sizeof(newpacket) ) | |
85 | goto lPACKET_DONE; | |
86 | newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start character */ | |
87 | /* Start of a CTCP */ | |
88 | if( i+4 >= dlen ) /* Too short for DCC */ | |
89 | goto lBAD_CTCP; | |
90 | if( sptr[i+0] != 'D' ) | |
91 | goto lBAD_CTCP; | |
92 | if( sptr[i+1] != 'C' ) | |
93 | goto lBAD_CTCP; | |
94 | if( sptr[i+2] != 'C' ) | |
95 | goto lBAD_CTCP; | |
96 | if( sptr[i+3] != ' ' ) | |
97 | goto lBAD_CTCP; | |
98 | /* We have a DCC command - handle it! */ | |
99 | i+= 4; /* Skip "DCC " */ | |
100 | if( iCopy+4 > sizeof(newpacket) ) | |
101 | goto lPACKET_DONE; | |
102 | newpacket[iCopy++] = 'D'; | |
103 | newpacket[iCopy++] = 'C'; | |
104 | newpacket[iCopy++] = 'C'; | |
105 | newpacket[iCopy++] = ' '; | |
106 | ||
107 | DBprintf(("Found DCC\n")); | |
108 | /* Skip any extra spaces (should not occur according to | |
109 | protocol, but DCC breaks CTCP protocol anyway */ | |
110 | while(sptr[i] == ' ') { | |
111 | if( ++i >= dlen) { | |
112 | DBprintf(("DCC packet terminated in just spaces\n")); | |
113 | goto lPACKET_DONE; | |
114 | } | |
115 | } | |
116 | ||
117 | DBprintf(("Transferring command...\n")); | |
118 | while(sptr[i] != ' ') { | |
119 | newpacket[iCopy++] = sptr[i]; | |
120 | if( ++i >= dlen || iCopy >= sizeof(newpacket) ) { | |
121 | DBprintf(("DCC packet terminated during command\n")); | |
122 | goto lPACKET_DONE; | |
123 | } | |
124 | } | |
125 | /* Copy _one_ space */ | |
126 | if( i+1 < dlen && iCopy < sizeof(newpacket) ) | |
127 | newpacket[iCopy++] = sptr[i++]; | |
128 | ||
129 | DBprintf(("Done command - removing spaces\n")); | |
130 | /* Skip any extra spaces (should not occur according to | |
131 | protocol, but DCC breaks CTCP protocol anyway */ | |
132 | while(sptr[i] == ' ') { | |
133 | if( ++i >= dlen ) { | |
134 | DBprintf(("DCC packet terminated in just spaces (post-command)\n")); | |
135 | goto lPACKET_DONE; | |
136 | } | |
137 | } | |
138 | ||
139 | DBprintf(("Transferring filename...\n")); | |
140 | while(sptr[i] != ' ') { | |
141 | newpacket[iCopy++] = sptr[i]; | |
142 | if( ++i >= dlen || iCopy >= sizeof(newpacket) ) { | |
143 | DBprintf(("DCC packet terminated during filename\n")); | |
144 | goto lPACKET_DONE; | |
145 | } | |
146 | } | |
147 | /* Copy _one_ space */ | |
148 | if( i+1 < dlen && iCopy < sizeof(newpacket) ) | |
149 | newpacket[iCopy++] = sptr[i++]; | |
150 | ||
151 | DBprintf(("Done filename - removing spaces\n")); | |
152 | /* Skip any extra spaces (should not occur according to | |
153 | protocol, but DCC breaks CTCP protocol anyway */ | |
154 | while(sptr[i] == ' ') { | |
155 | if( ++i >= dlen ) { | |
156 | DBprintf(("DCC packet terminated in just spaces (post-filename)\n")); | |
157 | goto lPACKET_DONE; | |
158 | } | |
159 | } | |
160 | ||
161 | DBprintf(("Fetching IP address\n")); | |
162 | /* Fetch IP address */ | |
163 | org_addr = 0; | |
164 | while(i<dlen && isdigit(sptr[i])) { | |
165 | if( org_addr > ULONG_MAX/10UL ) { /* Terminate on overflow */ | |
166 | DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i])); | |
167 | goto lBAD_CTCP; | |
168 | } | |
169 | org_addr *= 10; | |
170 | org_addr += sptr[i++]-'0'; | |
171 | } | |
172 | DBprintf(("Skipping space\n")); | |
173 | if( i+1 >= dlen || sptr[i] != ' ' ) { | |
174 | DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i+1, dlen, sptr[i])); | |
175 | goto lBAD_CTCP; | |
176 | } | |
177 | /* Skip any extra spaces (should not occur according to | |
178 | protocol, but DCC breaks CTCP protocol anyway, so we might | |
179 | as well play it safe */ | |
180 | while(sptr[i] == ' ') { | |
181 | if( ++i >= dlen ) { | |
182 | DBprintf(("Packet failure - space overflow.\n")); | |
183 | goto lPACKET_DONE; | |
184 | } | |
185 | } | |
186 | DBprintf(("Fetching port number\n")); | |
187 | /* Fetch source port */ | |
188 | org_port = 0; | |
189 | while(i<dlen && isdigit(sptr[i])) { | |
190 | if( org_port > 6554 ) { /* Terminate on overflow (65536/10 rounded up*/ | |
191 | DBprintf(("DCC: port number overflow\n")); | |
192 | goto lBAD_CTCP; | |
193 | } | |
194 | org_port *= 10; | |
195 | org_port += sptr[i++]-'0'; | |
196 | } | |
197 | /* Skip illegal addresses (or early termination) */ | |
198 | if( i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ') ) { | |
199 | DBprintf(("Bad port termination\n")); | |
200 | goto lBAD_CTCP; | |
201 | } | |
202 | DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port)); | |
203 | ||
204 | /* We've got the address and port - now alias it */ | |
205 | { | |
206 | struct alias_link *dcc_link; | |
207 | struct in_addr destaddr; | |
208 | ||
209 | ||
210 | true_port = htons(org_port); | |
211 | true_addr.s_addr = htonl(org_addr); | |
212 | destaddr.s_addr = 0; | |
213 | ||
214 | /* Steal the FTP_DATA_PORT - it doesn't really matter, and this | |
215 | would probably allow it through at least _some_ | |
216 | firewalls. */ | |
217 | dcc_link = FindUdpTcpOut (true_addr, | |
218 | destaddr, | |
219 | true_port, | |
220 | 0, IPPROTO_TCP); | |
221 | DBprintf(("Got a DCC link\n")); | |
222 | if ( dcc_link ) { | |
223 | struct in_addr alias_address; /* Address from aliasing */ | |
224 | u_short alias_port; /* Port given by aliasing */ | |
225 | ||
226 | #ifndef NO_FW_PUNCH | |
227 | /* Generate firewall hole as appropriate */ | |
228 | PunchFWHole(dcc_link); | |
229 | #endif | |
230 | ||
231 | alias_address = GetAliasAddress(link); | |
232 | iCopy += snprintf(&newpacket[iCopy], | |
233 | sizeof(newpacket)-iCopy, | |
234 | "%lu ", (u_long)htonl(alias_address.s_addr)); | |
235 | if( iCopy >= sizeof(newpacket) ) { /* Truncated/fit exactly - bad news */ | |
236 | DBprintf(("DCC constructed packet overflow.\n")); | |
237 | goto lBAD_CTCP; | |
238 | } | |
239 | alias_port = GetAliasPort(dcc_link); | |
240 | iCopy += snprintf(&newpacket[iCopy], | |
241 | sizeof(newpacket)-iCopy, | |
242 | "%u", htons(alias_port) ); | |
243 | /* Done - truncated cases will be taken care of by lBAD_CTCP */ | |
244 | DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port)); | |
245 | } | |
246 | } | |
247 | /* An uninteresting CTCP - state entered right after '\001' has | |
248 | been pushed. Also used to copy the rest of a DCC, after IP | |
249 | address and port has been handled */ | |
250 | lBAD_CTCP: | |
251 | for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) { | |
252 | newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ | |
253 | if(sptr[i] == '\001') { | |
254 | goto lNORMAL_TEXT; | |
255 | } | |
256 | } | |
257 | goto lPACKET_DONE; | |
258 | /* Normal text */ | |
259 | lNORMAL_TEXT: | |
260 | for(; i<dlen && iCopy<sizeof(newpacket); i++,iCopy++) { | |
261 | newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */ | |
262 | if(sptr[i] == '\001') { | |
263 | goto lCTCP_START; | |
264 | } | |
265 | } | |
266 | /* Handle the end of a packet */ | |
267 | lPACKET_DONE: | |
268 | iCopy = iCopy > maxsize-copyat ? maxsize-copyat : iCopy; | |
269 | memcpy(sptr+copyat, newpacket, iCopy); | |
270 | ||
271 | /* Save information regarding modified seq and ack numbers */ | |
272 | { | |
273 | int delta; | |
274 | ||
275 | SetAckModified(link); | |
276 | delta = GetDeltaSeqOut(pip, link); | |
277 | AddSeq(pip, link, delta+copyat+iCopy-dlen); | |
278 | } | |
279 | ||
280 | /* Revise IP header */ | |
281 | { | |
282 | u_short new_len; | |
283 | ||
284 | new_len = htons(hlen + iCopy + copyat); | |
285 | DifferentialChecksum(&pip->ip_sum, | |
286 | &new_len, | |
287 | &pip->ip_len, | |
288 | 1); | |
289 | pip->ip_len = new_len; | |
290 | } | |
291 | ||
292 | /* Compute TCP checksum for revised packet */ | |
293 | tc->th_sum = 0; | |
294 | tc->th_sum = TcpChecksum(pip); | |
295 | return; | |
296 | } | |
297 | } | |
298 | ||
299 | /* Notes: | |
300 | [Note 1] | |
301 | The initial search will most often fail; it could be replaced with a 32-bit specific search. | |
302 | Such a search would be done for 32-bit unsigned value V: | |
303 | V ^= 0x01010101; (Search is for null bytes) | |
304 | if( ((V-0x01010101)^V) & 0x80808080 ) { | |
305 | (found a null bytes which was a 01 byte) | |
306 | } | |
307 | To assert that the processor is 32-bits, do | |
308 | extern int ircdccar[32]; (32 bits) | |
309 | extern int ircdccar[CHAR_BIT*sizeof(unsigned int)]; | |
310 | which will generate a type-error on all but 32-bit machines. | |
311 | ||
312 | [Note 2] This routine really ought to be replaced with one that | |
313 | creates a transparent proxy on the aliasing host, to allow arbitary | |
314 | changes in the TCP stream. This should not be too difficult given | |
315 | this base; I (ee) will try to do this some time later. | |
316 | */ |