1/* $NetBSD: ip_irc_pxy.c,v 1.2 2012/02/15 17:55:04 riz 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#define IPF_IRC_PROXY 12 13#define IPF_IRCBUFSZ 96 /* This *MUST* be >= 64! */ 14 15 16int ippr_irc_init(void); 17void ippr_irc_fini(void); 18int ippr_irc_new(fr_info_t *, ap_session_t *, nat_t *); 19int ippr_irc_out(fr_info_t *, ap_session_t *, nat_t *); 20int ippr_irc_send(fr_info_t *, nat_t *); 21int ippr_irc_complete(ircinfo_t *, char *, size_t); 22u_short ipf_irc_atoi(char **); 23 24static frentry_t ircnatfr; 25 26int irc_proxy_init = 0; 27 28 29/* 30 * Initialize local structures. 31 */ 32int 33ippr_irc_init(void) 34{ 35 bzero((char *)&ircnatfr, sizeof(ircnatfr)); 36 ircnatfr.fr_ref = 1; 37 ircnatfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 38 MUTEX_INIT(&ircnatfr.fr_lock, "IRC proxy rule lock"); 39 irc_proxy_init = 1; 40 41 return 0; 42} 43 44 45void 46ippr_irc_fini(void) 47{ 48 if (irc_proxy_init == 1) { 49 MUTEX_DESTROY(&ircnatfr.fr_lock); 50 irc_proxy_init = 0; 51 } 52} 53 54 55const char *ippr_irc_dcctypes[] = { 56 "CHAT ", /* CHAT chat ipnumber portnumber */ 57 "SEND ", /* SEND filename ipnumber portnumber */ 58 "MOVE ", 59 "TSEND ", 60 "SCHAT ", 61 NULL, 62}; 63 64 65/* 66 * :A PRIVMSG B :^ADCC CHAT chat 0 0^A\r\n 67 * PRIVMSG B ^ADCC CHAT chat 0 0^A\r\n 68 */ 69 70 71int 72ippr_irc_complete(ircinfo_t *ircp, char *buf, size_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 228ippr_irc_new(fr_info_t *fin, ap_session_t *aps, nat_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 248ippr_irc_send(fr_info_t *fin, nat_t *nat) 249{ 250 char ctcpbuf[IPF_IRCBUFSZ], newbuf[IPF_IRCBUFSZ]; 251 tcphdr_t *tcp, tcph, *tcp2 = &tcph; 252 int off, inc = 0, i, dlen; 253 size_t nlen = 0, olen; 254 struct in_addr swip; 255 u_short a5, sp; 256 ircinfo_t *irc; 257 fr_info_t fi; 258 nat_t *nat2; 259 u_int a1; 260 ip_t *ip; 261 mb_t *m; 262#ifdef MENTAT 263 mb_t *m1; 264#endif 265 266 m = fin->fin_m; 267 ip = fin->fin_ip; 268 tcp = (tcphdr_t *)fin->fin_dp; 269 bzero(ctcpbuf, sizeof(ctcpbuf)); 270 off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 271 272#ifdef __sgi 273 dlen = fin->fin_plen - off; 274#else 275 dlen = MSGDSIZE(m) - off; 276#endif 277 if (dlen <= 0) 278 return 0; 279 COPYDATA(m, off, MIN(sizeof(ctcpbuf), dlen), ctcpbuf); 280 281 if (dlen <= 0) 282 return 0; 283 ctcpbuf[sizeof(ctcpbuf) - 1] = '\0'; 284 *newbuf = '\0'; 285 286 irc = nat->nat_aps->aps_data; 287 if (ippr_irc_complete(irc, ctcpbuf, dlen) == 0) 288 return 0; 289 290 /* 291 * check that IP address in the PORT/PASV reply is the same as the 292 * sender of the command - prevents using PORT for port scanning. 293 */ 294 if (irc->irc_ipnum != ntohl(nat->nat_inip.s_addr)) 295 return 0; 296 297 a5 = irc->irc_port; 298 299 /* 300 * Calculate new address parts for the DCC command 301 */ 302 a1 = ntohl(ip->ip_src.s_addr); 303 olen = irc->irc_len; 304 i = irc->irc_addr - ctcpbuf; 305 i++; 306 (void) strncpy(newbuf, ctcpbuf, i); 307 /* DO NOT change these! */ 308#if defined(SNPRINTF) && defined(KERNEL) 309 SNPRINTF(newbuf, sizeof(newbuf) - i, "%u %u\001\r\n", a1, a5); 310#else 311 (void) sprintf(newbuf, "%u %u\001\r\n", a1, a5); 312#endif 313 314 nlen = strlen(newbuf); 315 inc = nlen - olen; 316 317 if ((inc + ip->ip_len) > 65535) 318 return 0; 319 320#ifdef MENTAT 321 for (m1 = m; m1->b_cont; m1 = m1->b_cont) 322 ; 323 if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { 324 mblk_t *nm; 325 326 /* alloc enough to keep same trailer space for lower driver */ 327 nm = allocb(nlen, BPRI_MED); 328 PANIC((!nm),("ippr_irc_out: allocb failed")); 329 330 nm->b_band = m1->b_band; 331 nm->b_wptr += nlen; 332 333 m1->b_wptr -= olen; 334 PANIC((m1->b_wptr < m1->b_rptr), 335 ("ippr_irc_out: cannot handle fragmented data block")); 336 337 linkb(m1, nm); 338 } else { 339# if SOLARIS && defined(ICK_VALID) 340 if (m1->b_datap->db_struiolim == m1->b_wptr) 341 m1->b_datap->db_struiolim += inc; 342 m1->b_datap->db_struioflag &= ~STRUIO_IP; 343# endif 344 m1->b_wptr += inc; 345 } 346#else 347 if (inc < 0) 348 m_adj(m, inc); 349 /* the mbuf chain will be extended if necessary by m_copyback() */ 350#endif 351 COPYBACK(m, off, nlen, newbuf); 352 353 if (inc != 0) { 354#if defined(MENTAT) || defined(__sgi) 355 register u_32_t sum1, sum2; 356 357 sum1 = ip->ip_len; 358 sum2 = ip->ip_len + inc; 359 360 /* Because ~1 == -2, We really need ~1 == -1 */ 361 if (sum1 > sum2) 362 sum2--; 363 sum2 -= sum1; 364 sum2 = (sum2 & 0xffff) + (sum2 >> 16); 365 366 fix_outcksum(fin, &ip->ip_sum, sum2); 367#endif 368 ip->ip_len += inc; 369 } 370 371 /* 372 * Add skeleton NAT entry for connection which will come back the 373 * other way. 374 */ 375 sp = htons(a5); 376 /* 377 * Don't allow the PORT command to specify a port < 1024 due to 378 * security crap. 379 */ 380 if (ntohs(sp) < 1024) 381 return 0; 382 383 /* 384 * The server may not make the connection back from port 20, but 385 * it is the most likely so use it here to check for a conflicting 386 * mapping. 387 */ 388 bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); 389 fi.fin_data[0] = sp; 390 fi.fin_data[1] = fin->fin_data[1]; 391 nat2 = nat_outlookup(fin, IPN_TCP, nat->nat_p, nat->nat_inip, 392 ip->ip_dst); 393 if (nat2 == NULL) { 394 bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); 395 bzero((char *)tcp2, sizeof(*tcp2)); 396 tcp2->th_win = htons(8192); 397 tcp2->th_sport = sp; 398 tcp2->th_dport = 0; /* XXX - don't specify remote port */ 399 fi.fin_data[0] = ntohs(sp); 400 fi.fin_data[1] = 0; 401 fi.fin_dp = (char *)tcp2; 402 fi.fin_fr = &ircnatfr; 403 fi.fin_dlen = sizeof(*tcp2); 404 fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); 405 swip = ip->ip_src; 406 ip->ip_src = nat->nat_inip; 407 MUTEX_ENTER(&ipf_nat_new); 408 nat2 = nat_new(&fi, nat->nat_ptr, NULL, 409 NAT_SLAVE|IPN_TCP|SI_W_DPORT, NAT_OUTBOUND); 410 MUTEX_EXIT(&ipf_nat_new); 411 if (nat2 != NULL) { 412 (void) nat_proto(&fi, nat2, 0); 413 MUTEX_ENTER(&nat2->nat_lock); 414 nat_update(&fi, nat2); 415 MUTEX_EXIT(&nat2->nat_lock); 416 417 (void) fr_addstate(&fi, NULL, SI_W_DPORT); 418 } 419 ip->ip_src = swip; 420 } 421 return inc; 422} 423 424 425int 426ippr_irc_out(fr_info_t *fin, ap_session_t *aps, nat_t *nat) 427{ 428 aps = aps; /* LINT */ 429 return ippr_irc_send(fin, nat); 430} 431