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