ip_irc_pxy.c revision 1.2.4.2 1 /* $NetBSD: ip_irc_pxy.c,v 1.2.4.2 2012/04/17 00:08:16 yamt Exp $ */
2
3 /*
4 * Copyright (C) 2008 by Darren Reed.
5 *
6 * See the IPFILTER.LICENCE file for details on licencing.
7 *
8 * Id: ip_irc_pxy.c,v 2.56.2.1 2012/01/26 05:29:11 darrenr Exp
9 */
10
11 #include <sys/cdefs.h>
12 __KERNEL_RCSID(1, "$NetBSD: ip_irc_pxy.c,v 1.2.4.2 2012/04/17 00:08:16 yamt Exp $");
13
14 #define IPF_IRC_PROXY
15
16 #define IPF_IRCBUFSZ 96 /* This *MUST* be >= 64! */
17
18
19 void ipf_p_irc_main_load(void);
20 void ipf_p_irc_main_unload(void);
21 int ipf_p_irc_new(void *, fr_info_t *, ap_session_t *, nat_t *);
22 int ipf_p_irc_out(void *, fr_info_t *, ap_session_t *, nat_t *);
23 int ipf_p_irc_send(fr_info_t *, nat_t *);
24 int ipf_p_irc_complete(ircinfo_t *, char *, size_t);
25 u_short ipf_irc_atoi(char **);
26
27 static frentry_t ircnatfr;
28
29 int irc_proxy_init = 0;
30
31
32 /*
33 * Initialize local structures.
34 */
35 void
36 ipf_p_irc_main_load(void)
37 {
38 bzero((char *)&ircnatfr, sizeof(ircnatfr));
39 ircnatfr.fr_ref = 1;
40 ircnatfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE;
41 MUTEX_INIT(&ircnatfr.fr_lock, "IRC proxy rule lock");
42 irc_proxy_init = 1;
43 }
44
45
46 void
47 ipf_p_irc_main_unload(void)
48 {
49 if (irc_proxy_init == 1) {
50 MUTEX_DESTROY(&ircnatfr.fr_lock);
51 irc_proxy_init = 0;
52 }
53 }
54
55
56 const char *ipf_p_irc_dcctypes[] = {
57 "CHAT ", /* CHAT chat ipnumber portnumber */
58 "SEND ", /* SEND filename ipnumber portnumber */
59 "MOVE ",
60 "TSEND ",
61 "SCHAT ",
62 NULL,
63 };
64
65
66 /*
67 * :A PRIVMSG B :^ADCC CHAT chat 0 0^A\r\n
68 * PRIVMSG B ^ADCC CHAT chat 0 0^A\r\n
69 */
70
71
72 int
73 ipf_p_irc_complete(ircinfo_t *ircp, char *buf, size_t len)
74 {
75 register char *s, c;
76 register size_t i;
77 u_32_t l;
78 int j, k;
79
80 ircp->irc_ipnum = 0;
81 ircp->irc_port = 0;
82
83 if (len < 31)
84 return 0;
85 s = buf;
86 c = *s++;
87 i = len - 1;
88
89 if ((c != ':') && (c != 'P'))
90 return 0;
91
92 if (c == ':') {
93 /*
94 * Loosely check that the source is a nickname of some sort
95 */
96 s++;
97 c = *s;
98 ircp->irc_snick = s;
99 if (!ISALPHA(c))
100 return 0;
101 i--;
102 for (c = *s; !ISSPACE(c) && (i > 0); i--)
103 c = *s++;
104 if (i < 31)
105 return 0;
106 if (c != 'P')
107 return 0;
108 } else
109 ircp->irc_snick = NULL;
110
111 /*
112 * Check command string
113 */
114 if (strncmp(s, "PRIVMSG ", 8))
115 return 0;
116 i -= 8;
117 s += 8;
118 c = *s;
119 ircp->irc_dnick = s;
120
121 /*
122 * Loosely check that the destination is a nickname of some sort
123 */
124 if (!ISALPHA(c))
125 return 0;
126 for (; !ISSPACE(c) && (i > 0); i--)
127 c = *s++;
128 if (i < 20)
129 return 0;
130 s++,
131 i--;
132
133 /*
134 * Look for a ^A to start the DCC
135 */
136 c = *s;
137 if (c == ':') {
138 s++;
139 c = *s;
140 }
141
142 if (strncmp(s, "\001DCC ", 4))
143 return 0;
144
145 i -= 4;
146 s += 4;
147
148 /*
149 * Check for a recognised DCC command
150 */
151 for (j = 0, k = 0; ipf_p_irc_dcctypes[j]; j++) {
152 k = MIN(strlen(ipf_p_irc_dcctypes[j]), i);
153 if (!strncmp(ipf_p_irc_dcctypes[j], s, k))
154 break;
155 }
156 if (!ipf_p_irc_dcctypes[j])
157 return 0;
158
159 ircp->irc_type = s;
160 i -= k;
161 s += k;
162
163 if (i < 11)
164 return 0;
165
166 /*
167 * Check for the arg
168 */
169 c = *s;
170 if (ISSPACE(c))
171 return 0;
172 ircp->irc_arg = s;
173 for (; (c != ' ') && (c != '\001') && (i > 0); i--)
174 c = *s++;
175
176 if (c == '\001') /* In reality a ^A can quote another ^A...*/
177 return 0;
178
179 if (i < 5)
180 return 0;
181
182 s++;
183 i--;
184 c = *s;
185 if (!ISDIGIT(c))
186 return 0;
187 ircp->irc_addr = s;
188 /*
189 * Get the IP#
190 */
191 for (l = 0; ISDIGIT(c) && (i > 0); i--) {
192 l *= 10;
193 l += c - '0';
194 c = *s++;
195 }
196
197 if (i < 4)
198 return 0;
199
200 if (c != ' ')
201 return 0;
202
203 ircp->irc_ipnum = l;
204 s++;
205 i--;
206 c = *s;
207 if (!ISDIGIT(c))
208 return 0;
209 /*
210 * Get the port#
211 */
212 for (l = 0; ISDIGIT(c) && (i > 0); i--) {
213 l *= 10;
214 l += c - '0';
215 c = *s++;
216 }
217 if (i < 3)
218 return 0;
219 if (strncmp(s, "\001\r\n", 3))
220 return 0;
221 s += 3;
222 ircp->irc_len = s - buf;
223 ircp->irc_port = l;
224 return 1;
225 }
226
227
228 int
229 ipf_p_irc_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
230 {
231 ircinfo_t *irc;
232
233 KMALLOC(irc, ircinfo_t *);
234 if (irc == NULL)
235 return -1;
236
237 fin = fin; /* LINT */
238 nat = nat; /* LINT */
239
240 aps->aps_data = irc;
241 aps->aps_psiz = sizeof(ircinfo_t);
242
243 bzero((char *)irc, sizeof(*irc));
244 return 0;
245 }
246
247
248 int
249 ipf_p_irc_send(fr_info_t *fin, nat_t *nat)
250 {
251 char ctcpbuf[IPF_IRCBUFSZ], newbuf[IPF_IRCBUFSZ];
252 tcphdr_t *tcp, tcph, *tcp2 = &tcph;
253 int off, inc = 0, i, dlen;
254 ipf_main_softc_t *softc;
255 size_t nlen = 0, olen;
256 struct in_addr swip;
257 u_short a5, sp;
258 ircinfo_t *irc;
259 fr_info_t fi;
260 nat_t *nat2;
261 u_int a1;
262 ip_t *ip;
263 mb_t *m;
264 #ifdef MENTAT
265 mb_t *m1;
266 #endif
267 softc = fin->fin_main_soft;
268
269 m = fin->fin_m;
270 ip = fin->fin_ip;
271 tcp = (tcphdr_t *)fin->fin_dp;
272 bzero(ctcpbuf, sizeof(ctcpbuf));
273 off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff;
274
275 #ifdef __sgi
276 dlen = fin->fin_plen - off;
277 #else
278 dlen = MSGDSIZE(m) - off;
279 #endif
280 if (dlen <= 0)
281 return 0;
282 COPYDATA(m, off, MIN(sizeof(ctcpbuf), dlen), ctcpbuf);
283
284 if (dlen <= 0)
285 return 0;
286 ctcpbuf[sizeof(ctcpbuf) - 1] = '\0';
287 *newbuf = '\0';
288
289 irc = nat->nat_aps->aps_data;
290 if (ipf_p_irc_complete(irc, ctcpbuf, dlen) == 0)
291 return 0;
292
293 /*
294 * check that IP address in the DCC reply is the same as the
295 * sender of the command - prevents use for port scanning.
296 */
297 if (irc->irc_ipnum != ntohl(nat->nat_osrcaddr))
298 return 0;
299
300 a5 = irc->irc_port;
301
302 /*
303 * Calculate new address parts for the DCC command
304 */
305 a1 = ntohl(ip->ip_src.s_addr);
306 olen = irc->irc_len;
307 i = irc->irc_addr - ctcpbuf;
308 i++;
309 (void) strncpy(newbuf, ctcpbuf, i);
310 /* DO NOT change these! */
311 #if defined(SNPRINTF) && defined(KERNEL)
312 SNPRINTF(newbuf, sizeof(newbuf) - i, "%u %u\001\r\n", a1, a5);
313 #else
314 (void) sprintf(newbuf, "%u %u\001\r\n", a1, a5);
315 #endif
316
317 nlen = strlen(newbuf);
318 inc = nlen - olen;
319
320 if ((inc + fin->fin_plen) > 65535)
321 return 0;
322
323 #ifdef MENTAT
324 for (m1 = m; m1->b_cont; m1 = m1->b_cont)
325 ;
326 if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) {
327 mblk_t *nm;
328
329 /* alloc enough to keep same trailer space for lower driver */
330 nm = allocb(nlen, BPRI_MED);
331 PANIC((!nm),("ipf_p_irc_out: allocb failed"));
332
333 nm->b_band = m1->b_band;
334 nm->b_wptr += nlen;
335
336 m1->b_wptr -= olen;
337 PANIC((m1->b_wptr < m1->b_rptr),
338 ("ipf_p_irc_out: cannot handle fragmented data block"));
339
340 linkb(m1, nm);
341 } else {
342 # if SOLARIS && defined(ICK_VALID)
343 if (m1->b_datap->db_struiolim == m1->b_wptr)
344 m1->b_datap->db_struiolim += inc;
345 m1->b_datap->db_struioflag &= ~STRUIO_IP;
346 # endif
347 m1->b_wptr += inc;
348 }
349 #else
350 if (inc < 0)
351 m_adj(m, inc);
352 /* the mbuf chain will be extended if necessary by m_copyback() */
353 #endif
354 COPYBACK(m, off, nlen, newbuf);
355
356 if (inc != 0) {
357 #if defined(MENTAT) || defined(__sgi)
358 register u_32_t sum1, sum2;
359
360 sum1 = fin->fin_plen;
361 sum2 = fin->fin_plen + inc;
362
363 /* Because ~1 == -2, We really need ~1 == -1 */
364 if (sum1 > sum2)
365 sum2--;
366 sum2 -= sum1;
367 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
368
369 ipf_fix_outcksum(fin, &ip->ip_sum, sum2);
370 #endif
371 fin->fin_plen += inc;
372 ip->ip_len = htons(fin->fin_plen);
373 fin->fin_dlen += inc;
374 }
375
376 /*
377 * Add skeleton NAT entry for connection which will come back the
378 * other way.
379 */
380 sp = htons(a5);
381 /*
382 * Don't allow the PORT command to specify a port < 1024 due to
383 * security crap.
384 */
385 if (ntohs(sp) < 1024)
386 return 0;
387
388 /*
389 * The server may not make the connection back from port 20, but
390 * it is the most likely so use it here to check for a conflicting
391 * mapping.
392 */
393 bcopy((void *)fin, (void *)&fi, sizeof(fi));
394 fi.fin_data[0] = sp;
395 fi.fin_data[1] = fin->fin_data[1];
396 nat2 = ipf_nat_outlookup(fin, IPN_TCP, nat->nat_pr[1], nat->nat_nsrcip,
397 ip->ip_dst);
398 if (nat2 == NULL) {
399 #ifdef USE_MUTEXES
400 ipf_nat_softc_t *softn = softc->ipf_nat_soft;
401 #endif
402
403 bcopy((void *)fin, (void *)&fi, sizeof(fi));
404 bzero((char *)tcp2, sizeof(*tcp2));
405 tcp2->th_win = htons(8192);
406 tcp2->th_sport = sp;
407 tcp2->th_dport = 0; /* XXX - don't specify remote port */
408 fi.fin_data[0] = ntohs(sp);
409 fi.fin_data[1] = 0;
410 fi.fin_dp = (char *)tcp2;
411 fi.fin_fr = &ircnatfr;
412 fi.fin_dlen = sizeof(*tcp2);
413 fi.fin_plen = fi.fin_hlen + sizeof(*tcp2);
414 swip = ip->ip_src;
415 ip->ip_src = nat->nat_nsrcip;
416 MUTEX_ENTER(&softn->ipf_nat_new);
417 nat2 = ipf_nat_add(&fi, nat->nat_ptr, NULL,
418 NAT_SLAVE|IPN_TCP|SI_W_DPORT, NAT_OUTBOUND);
419 MUTEX_EXIT(&softn->ipf_nat_new);
420 if (nat2 != NULL) {
421 (void) ipf_nat_proto(&fi, nat2, 0);
422 MUTEX_ENTER(&nat2->nat_lock);
423 ipf_nat_update(&fi, nat2);
424 MUTEX_EXIT(&nat2->nat_lock);
425
426 (void) ipf_state_add(softc, &fi, NULL, SI_W_DPORT);
427 }
428 ip->ip_src = swip;
429 }
430 return inc;
431 }
432
433
434 int
435 ipf_p_irc_out(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat)
436 {
437 aps = aps; /* LINT */
438 return ipf_p_irc_send(fin, nat);
439 }
440