ip_irc_pxy.c revision 145516
1/* $FreeBSD: vendor-sys/ipfilter/dist/sys/contrib/ipfilter/netinet/ip_irc_pxy.c 145516 2005-04-25 18:15:41Z darrenr $ */ 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.4 2005/02/04 10:22:55 darrenr Exp 9 */ 10 11#define IPF_IRC_PROXY 12 13#define IPF_IRCBUFSZ 96 /* This *MUST* be >= 64! */ 14 15 16int ippr_irc_init __P((void)); 17void ippr_irc_fini __P((void)); 18int ippr_irc_new __P((fr_info_t *, ap_session_t *, nat_t *)); 19int ippr_irc_out __P((fr_info_t *, ap_session_t *, nat_t *)); 20int ippr_irc_send __P((fr_info_t *, nat_t *)); 21int ippr_irc_complete __P((ircinfo_t *, char *, size_t)); 22u_short ipf_irc_atoi __P((char **)); 23 24static frentry_t ircnatfr; 25 26int irc_proxy_init = 0; 27 28 29/* 30 * Initialize local structures. 31 */ 32int ippr_irc_init() 33{ 34 bzero((char *)&ircnatfr, sizeof(ircnatfr)); 35 ircnatfr.fr_ref = 1; 36 ircnatfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 37 MUTEX_INIT(&ircnatfr.fr_lock, "IRC proxy rule lock"); 38 irc_proxy_init = 1; 39 40 return 0; 41} 42 43 44void ippr_irc_fini() 45{ 46 if (irc_proxy_init == 1) { 47 MUTEX_DESTROY(&ircnatfr.fr_lock); 48 irc_proxy_init = 0; 49 } 50} 51 52 53char *ippr_irc_dcctypes[] = { 54 "CHAT ", /* CHAT chat ipnumber portnumber */ 55 "SEND ", /* SEND filename ipnumber portnumber */ 56 "MOVE ", 57 "TSEND ", 58 "SCHAT ", 59 NULL, 60}; 61 62 63/* 64 * :A PRIVMSG B :^ADCC CHAT chat 0 0^A\r\n 65 * PRIVMSG B ^ADCC CHAT chat 0 0^A\r\n 66 */ 67 68 69int ippr_irc_complete(ircp, buf, len) 70ircinfo_t *ircp; 71char *buf; 72size_t len; 73{ 74 register char *s, c; 75 register size_t i; 76 u_32_t l; 77 int j, k; 78 79 ircp->irc_ipnum = 0; 80 ircp->irc_port = 0; 81 82 if (len < 31) 83 return 0; 84 s = buf; 85 c = *s++; 86 i = len - 1; 87 88 if ((c != ':') && (c != 'P')) 89 return 0; 90 91 if (c == ':') { 92 /* 93 * Loosely check that the source is a nickname of some sort 94 */ 95 s++; 96 c = *s; 97 ircp->irc_snick = s; 98 if (!ISALPHA(c)) 99 return 0; 100 i--; 101 for (c = *s; !ISSPACE(c) && (i > 0); i--) 102 c = *s++; 103 if (i < 31) 104 return 0; 105 if (c != 'P') 106 return 0; 107 } else 108 ircp->irc_snick = NULL; 109 110 /* 111 * Check command string 112 */ 113 if (strncmp(s, "PRIVMSG ", 8)) 114 return 0; 115 i -= 8; 116 s += 8; 117 c = *s; 118 ircp->irc_dnick = s; 119 120 /* 121 * Loosely check that the destination is a nickname of some sort 122 */ 123 if (!ISALPHA(c)) 124 return 0; 125 for (; !ISSPACE(c) && (i > 0); i--) 126 c = *s++; 127 if (i < 20) 128 return 0; 129 s++, 130 i--; 131 132 /* 133 * Look for a ^A to start the DCC 134 */ 135 c = *s; 136 if (c == ':') { 137 s++; 138 c = *s; 139 } 140 141 if (strncmp(s, "\001DCC ", 4)) 142 return 0; 143 144 i -= 4; 145 s += 4; 146 147 /* 148 * Check for a recognised DCC command 149 */ 150 for (j = 0, k = 0; ippr_irc_dcctypes[j]; j++) { 151 k = MIN(strlen(ippr_irc_dcctypes[j]), i); 152 if (!strncmp(ippr_irc_dcctypes[j], s, k)) 153 break; 154 } 155 if (!ippr_irc_dcctypes[j]) 156 return 0; 157 158 ircp->irc_type = s; 159 i -= k; 160 s += k; 161 162 if (i < 11) 163 return 0; 164 165 /* 166 * Check for the arg 167 */ 168 c = *s; 169 if (ISSPACE(c)) 170 return 0; 171 ircp->irc_arg = s; 172 for (; (c != ' ') && (c != '\001') && (i > 0); i--) 173 c = *s++; 174 175 if (c == '\001') /* In reality a ^A can quote another ^A...*/ 176 return 0; 177 178 if (i < 5) 179 return 0; 180 181 s++; 182 i--; 183 c = *s; 184 if (!ISDIGIT(c)) 185 return 0; 186 ircp->irc_addr = s; 187 /* 188 * Get the IP# 189 */ 190 for (l = 0; ISDIGIT(c) && (i > 0); i--) { 191 l *= 10; 192 l += c - '0'; 193 c = *s++; 194 } 195 196 if (i < 4) 197 return 0; 198 199 if (c != ' ') 200 return 0; 201 202 ircp->irc_ipnum = l; 203 s++; 204 i--; 205 c = *s; 206 if (!ISDIGIT(c)) 207 return 0; 208 /* 209 * Get the port# 210 */ 211 for (l = 0; ISDIGIT(c) && (i > 0); i--) { 212 l *= 10; 213 l += c - '0'; 214 c = *s++; 215 } 216 if (i < 3) 217 return 0; 218 if (strncmp(s, "\001\r\n", 3)) 219 return 0; 220 s += 3; 221 ircp->irc_len = s - buf; 222 ircp->irc_port = l; 223 return 1; 224} 225 226 227int ippr_irc_new(fin, aps, nat) 228fr_info_t *fin; 229ap_session_t *aps; 230nat_t *nat; 231{ 232 ircinfo_t *irc; 233 234 KMALLOC(irc, ircinfo_t *); 235 if (irc == NULL) 236 return -1; 237 238 fin = fin; /* LINT */ 239 nat = nat; /* LINT */ 240 241 aps->aps_data = irc; 242 aps->aps_psiz = sizeof(ircinfo_t); 243 244 bzero((char *)irc, sizeof(*irc)); 245 return 0; 246} 247 248 249int ippr_irc_send(fin, nat) 250fr_info_t *fin; 251nat_t *nat; 252{ 253 char ctcpbuf[IPF_IRCBUFSZ], newbuf[IPF_IRCBUFSZ]; 254 tcphdr_t *tcp, tcph, *tcp2 = &tcph; 255 int off, inc = 0, i, dlen; 256 size_t nlen = 0, olen; 257 struct in_addr swip; 258 u_short a5, sp; 259 ircinfo_t *irc; 260 fr_info_t fi; 261 nat_t *nat2; 262 u_int a1; 263 ip_t *ip; 264 mb_t *m; 265#ifdef MENTAT 266 mb_t *m1; 267#endif 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 (ippr_irc_complete(irc, ctcpbuf, dlen) == 0) 291 return 0; 292 293 /* 294 * check that IP address in the PORT/PASV reply is the same as the 295 * sender of the command - prevents using PORT for port scanning. 296 */ 297 if (irc->irc_ipnum != ntohl(nat->nat_inip.s_addr)) 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 + ip->ip_len) > 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),("ippr_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 ("ippr_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 = ip->ip_len; 361 sum2 = ip->ip_len + 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 fix_outcksum(fin, &ip->ip_sum, sum2); 370#endif 371 ip->ip_len += inc; 372 } 373 374 /* 375 * Add skeleton NAT entry for connection which will come back the 376 * other way. 377 */ 378 sp = htons(a5); 379 /* 380 * Don't allow the PORT command to specify a port < 1024 due to 381 * security crap. 382 */ 383 if (ntohs(sp) < 1024) 384 return 0; 385 386 /* 387 * The server may not make the connection back from port 20, but 388 * it is the most likely so use it here to check for a conflicting 389 * mapping. 390 */ 391 bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); 392 fi.fin_data[0] = sp; 393 fi.fin_data[1] = fin->fin_data[1]; 394 nat2 = nat_outlookup(fin, IPN_TCP, nat->nat_p, nat->nat_inip, 395 ip->ip_dst); 396 if (nat2 == NULL) { 397 bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); 398 bzero((char *)tcp2, sizeof(*tcp2)); 399 tcp2->th_win = htons(8192); 400 tcp2->th_sport = sp; 401 tcp2->th_dport = 0; /* XXX - don't specify remote port */ 402 fi.fin_state = NULL; 403 fi.fin_nat = NULL; 404 fi.fin_data[0] = ntohs(sp); 405 fi.fin_data[1] = 0; 406 fi.fin_dp = (char *)tcp2; 407 fi.fin_fr = &ircnatfr; 408 fi.fin_dlen = sizeof(*tcp2); 409 fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); 410 swip = ip->ip_src; 411 ip->ip_src = nat->nat_inip; 412 nat2 = nat_new(&fi, nat->nat_ptr, NULL, 413 NAT_SLAVE|IPN_TCP|SI_W_DPORT, NAT_OUTBOUND); 414 if (nat2 != NULL) { 415 (void) nat_proto(&fi, nat2, 0); 416 nat_update(&fi, nat2, nat2->nat_ptr); 417 418 (void) fr_addstate(&fi, NULL, SI_W_DPORT); 419 if (fi.fin_state != NULL) 420 fr_statederef(&fi, (ipstate_t **)&fi.fin_state); 421 } 422 ip->ip_src = swip; 423 } 424 return inc; 425} 426 427 428int ippr_irc_out(fin, aps, nat) 429fr_info_t *fin; 430ap_session_t *aps; 431nat_t *nat; 432{ 433 aps = aps; /* LINT */ 434 return ippr_irc_send(fin, nat); 435} 436