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