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