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