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