ip_irc_pxy.c revision 145516
1145516Sdarrenr/* $FreeBSD: vendor-sys/ipfilter/dist/sys/contrib/ipfilter/netinet/ip_irc_pxy.c 145516 2005-04-25 18:15:41Z darrenr $ */ 2145516Sdarrenr 3145516Sdarrenr/* 4145516Sdarrenr * Copyright (C) 2000-2003 Darren Reed 5145516Sdarrenr * 6145516Sdarrenr * See the IPFILTER.LICENCE file for details on licencing. 7145516Sdarrenr * 8145516Sdarrenr * Id: ip_irc_pxy.c,v 2.39.2.4 2005/02/04 10:22:55 darrenr Exp 9145516Sdarrenr */ 10145516Sdarrenr 11145516Sdarrenr#define IPF_IRC_PROXY 12145516Sdarrenr 13145516Sdarrenr#define IPF_IRCBUFSZ 96 /* This *MUST* be >= 64! */ 14145516Sdarrenr 15145516Sdarrenr 16145516Sdarrenrint ippr_irc_init __P((void)); 17145516Sdarrenrvoid ippr_irc_fini __P((void)); 18145516Sdarrenrint ippr_irc_new __P((fr_info_t *, ap_session_t *, nat_t *)); 19145516Sdarrenrint ippr_irc_out __P((fr_info_t *, ap_session_t *, nat_t *)); 20145516Sdarrenrint ippr_irc_send __P((fr_info_t *, nat_t *)); 21145516Sdarrenrint ippr_irc_complete __P((ircinfo_t *, char *, size_t)); 22145516Sdarrenru_short ipf_irc_atoi __P((char **)); 23145516Sdarrenr 24145516Sdarrenrstatic frentry_t ircnatfr; 25145516Sdarrenr 26145516Sdarrenrint irc_proxy_init = 0; 27145516Sdarrenr 28145516Sdarrenr 29145516Sdarrenr/* 30145516Sdarrenr * Initialize local structures. 31145516Sdarrenr */ 32145516Sdarrenrint ippr_irc_init() 33145516Sdarrenr{ 34145516Sdarrenr bzero((char *)&ircnatfr, sizeof(ircnatfr)); 35145516Sdarrenr ircnatfr.fr_ref = 1; 36145516Sdarrenr ircnatfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 37145516Sdarrenr MUTEX_INIT(&ircnatfr.fr_lock, "IRC proxy rule lock"); 38145516Sdarrenr irc_proxy_init = 1; 39145516Sdarrenr 40145516Sdarrenr return 0; 41145516Sdarrenr} 42145516Sdarrenr 43145516Sdarrenr 44145516Sdarrenrvoid ippr_irc_fini() 45145516Sdarrenr{ 46145516Sdarrenr if (irc_proxy_init == 1) { 47145516Sdarrenr MUTEX_DESTROY(&ircnatfr.fr_lock); 48145516Sdarrenr irc_proxy_init = 0; 49145516Sdarrenr } 50145516Sdarrenr} 51145516Sdarrenr 52145516Sdarrenr 53145516Sdarrenrchar *ippr_irc_dcctypes[] = { 54145516Sdarrenr "CHAT ", /* CHAT chat ipnumber portnumber */ 55145516Sdarrenr "SEND ", /* SEND filename ipnumber portnumber */ 56145516Sdarrenr "MOVE ", 57145516Sdarrenr "TSEND ", 58145516Sdarrenr "SCHAT ", 59145516Sdarrenr NULL, 60145516Sdarrenr}; 61145516Sdarrenr 62145516Sdarrenr 63145516Sdarrenr/* 64145516Sdarrenr * :A PRIVMSG B :^ADCC CHAT chat 0 0^A\r\n 65145516Sdarrenr * PRIVMSG B ^ADCC CHAT chat 0 0^A\r\n 66145516Sdarrenr */ 67145516Sdarrenr 68145516Sdarrenr 69145516Sdarrenrint ippr_irc_complete(ircp, buf, len) 70145516Sdarrenrircinfo_t *ircp; 71145516Sdarrenrchar *buf; 72145516Sdarrenrsize_t len; 73145516Sdarrenr{ 74145516Sdarrenr register char *s, c; 75145516Sdarrenr register size_t i; 76145516Sdarrenr u_32_t l; 77145516Sdarrenr int j, k; 78145516Sdarrenr 79145516Sdarrenr ircp->irc_ipnum = 0; 80145516Sdarrenr ircp->irc_port = 0; 81145516Sdarrenr 82145516Sdarrenr if (len < 31) 83145516Sdarrenr return 0; 84145516Sdarrenr s = buf; 85145516Sdarrenr c = *s++; 86145516Sdarrenr i = len - 1; 87145516Sdarrenr 88145516Sdarrenr if ((c != ':') && (c != 'P')) 89145516Sdarrenr return 0; 90145516Sdarrenr 91145516Sdarrenr if (c == ':') { 92145516Sdarrenr /* 93145516Sdarrenr * Loosely check that the source is a nickname of some sort 94145516Sdarrenr */ 95145516Sdarrenr s++; 96145516Sdarrenr c = *s; 97145516Sdarrenr ircp->irc_snick = s; 98145516Sdarrenr if (!ISALPHA(c)) 99145516Sdarrenr return 0; 100145516Sdarrenr i--; 101145516Sdarrenr for (c = *s; !ISSPACE(c) && (i > 0); i--) 102145516Sdarrenr c = *s++; 103145516Sdarrenr if (i < 31) 104145516Sdarrenr return 0; 105145516Sdarrenr if (c != 'P') 106145516Sdarrenr return 0; 107145516Sdarrenr } else 108145516Sdarrenr ircp->irc_snick = NULL; 109145516Sdarrenr 110145516Sdarrenr /* 111145516Sdarrenr * Check command string 112145516Sdarrenr */ 113145516Sdarrenr if (strncmp(s, "PRIVMSG ", 8)) 114145516Sdarrenr return 0; 115145516Sdarrenr i -= 8; 116145516Sdarrenr s += 8; 117145516Sdarrenr c = *s; 118145516Sdarrenr ircp->irc_dnick = s; 119145516Sdarrenr 120145516Sdarrenr /* 121145516Sdarrenr * Loosely check that the destination is a nickname of some sort 122145516Sdarrenr */ 123145516Sdarrenr if (!ISALPHA(c)) 124145516Sdarrenr return 0; 125145516Sdarrenr for (; !ISSPACE(c) && (i > 0); i--) 126145516Sdarrenr c = *s++; 127145516Sdarrenr if (i < 20) 128145516Sdarrenr return 0; 129145516Sdarrenr s++, 130145516Sdarrenr i--; 131145516Sdarrenr 132145516Sdarrenr /* 133145516Sdarrenr * Look for a ^A to start the DCC 134145516Sdarrenr */ 135145516Sdarrenr c = *s; 136145516Sdarrenr if (c == ':') { 137145516Sdarrenr s++; 138145516Sdarrenr c = *s; 139145516Sdarrenr } 140145516Sdarrenr 141145516Sdarrenr if (strncmp(s, "\001DCC ", 4)) 142145516Sdarrenr return 0; 143145516Sdarrenr 144145516Sdarrenr i -= 4; 145145516Sdarrenr s += 4; 146145516Sdarrenr 147145516Sdarrenr /* 148145516Sdarrenr * Check for a recognised DCC command 149145516Sdarrenr */ 150145516Sdarrenr for (j = 0, k = 0; ippr_irc_dcctypes[j]; j++) { 151145516Sdarrenr k = MIN(strlen(ippr_irc_dcctypes[j]), i); 152145516Sdarrenr if (!strncmp(ippr_irc_dcctypes[j], s, k)) 153145516Sdarrenr break; 154145516Sdarrenr } 155145516Sdarrenr if (!ippr_irc_dcctypes[j]) 156145516Sdarrenr return 0; 157145516Sdarrenr 158145516Sdarrenr ircp->irc_type = s; 159145516Sdarrenr i -= k; 160145516Sdarrenr s += k; 161145516Sdarrenr 162145516Sdarrenr if (i < 11) 163145516Sdarrenr return 0; 164145516Sdarrenr 165145516Sdarrenr /* 166145516Sdarrenr * Check for the arg 167145516Sdarrenr */ 168145516Sdarrenr c = *s; 169145516Sdarrenr if (ISSPACE(c)) 170145516Sdarrenr return 0; 171145516Sdarrenr ircp->irc_arg = s; 172145516Sdarrenr for (; (c != ' ') && (c != '\001') && (i > 0); i--) 173145516Sdarrenr c = *s++; 174145516Sdarrenr 175145516Sdarrenr if (c == '\001') /* In reality a ^A can quote another ^A...*/ 176145516Sdarrenr return 0; 177145516Sdarrenr 178145516Sdarrenr if (i < 5) 179145516Sdarrenr return 0; 180145516Sdarrenr 181145516Sdarrenr s++; 182145516Sdarrenr i--; 183145516Sdarrenr c = *s; 184145516Sdarrenr if (!ISDIGIT(c)) 185145516Sdarrenr return 0; 186145516Sdarrenr ircp->irc_addr = s; 187145516Sdarrenr /* 188145516Sdarrenr * Get the IP# 189145516Sdarrenr */ 190145516Sdarrenr for (l = 0; ISDIGIT(c) && (i > 0); i--) { 191145516Sdarrenr l *= 10; 192145516Sdarrenr l += c - '0'; 193145516Sdarrenr c = *s++; 194145516Sdarrenr } 195145516Sdarrenr 196145516Sdarrenr if (i < 4) 197145516Sdarrenr return 0; 198145516Sdarrenr 199145516Sdarrenr if (c != ' ') 200145516Sdarrenr return 0; 201145516Sdarrenr 202145516Sdarrenr ircp->irc_ipnum = l; 203145516Sdarrenr s++; 204145516Sdarrenr i--; 205145516Sdarrenr c = *s; 206145516Sdarrenr if (!ISDIGIT(c)) 207145516Sdarrenr return 0; 208145516Sdarrenr /* 209145516Sdarrenr * Get the port# 210145516Sdarrenr */ 211145516Sdarrenr for (l = 0; ISDIGIT(c) && (i > 0); i--) { 212145516Sdarrenr l *= 10; 213145516Sdarrenr l += c - '0'; 214145516Sdarrenr c = *s++; 215145516Sdarrenr } 216145516Sdarrenr if (i < 3) 217145516Sdarrenr return 0; 218145516Sdarrenr if (strncmp(s, "\001\r\n", 3)) 219145516Sdarrenr return 0; 220145516Sdarrenr s += 3; 221145516Sdarrenr ircp->irc_len = s - buf; 222145516Sdarrenr ircp->irc_port = l; 223145516Sdarrenr return 1; 224145516Sdarrenr} 225145516Sdarrenr 226145516Sdarrenr 227145516Sdarrenrint ippr_irc_new(fin, aps, nat) 228145516Sdarrenrfr_info_t *fin; 229145516Sdarrenrap_session_t *aps; 230145516Sdarrenrnat_t *nat; 231145516Sdarrenr{ 232145516Sdarrenr ircinfo_t *irc; 233145516Sdarrenr 234145516Sdarrenr KMALLOC(irc, ircinfo_t *); 235145516Sdarrenr if (irc == NULL) 236145516Sdarrenr return -1; 237145516Sdarrenr 238145516Sdarrenr fin = fin; /* LINT */ 239145516Sdarrenr nat = nat; /* LINT */ 240145516Sdarrenr 241145516Sdarrenr aps->aps_data = irc; 242145516Sdarrenr aps->aps_psiz = sizeof(ircinfo_t); 243145516Sdarrenr 244145516Sdarrenr bzero((char *)irc, sizeof(*irc)); 245145516Sdarrenr return 0; 246145516Sdarrenr} 247145516Sdarrenr 248145516Sdarrenr 249145516Sdarrenrint ippr_irc_send(fin, nat) 250145516Sdarrenrfr_info_t *fin; 251145516Sdarrenrnat_t *nat; 252145516Sdarrenr{ 253145516Sdarrenr char ctcpbuf[IPF_IRCBUFSZ], newbuf[IPF_IRCBUFSZ]; 254145516Sdarrenr tcphdr_t *tcp, tcph, *tcp2 = &tcph; 255145516Sdarrenr int off, inc = 0, i, dlen; 256145516Sdarrenr size_t nlen = 0, olen; 257145516Sdarrenr struct in_addr swip; 258145516Sdarrenr u_short a5, sp; 259145516Sdarrenr ircinfo_t *irc; 260145516Sdarrenr fr_info_t fi; 261145516Sdarrenr nat_t *nat2; 262145516Sdarrenr u_int a1; 263145516Sdarrenr ip_t *ip; 264145516Sdarrenr mb_t *m; 265145516Sdarrenr#ifdef MENTAT 266145516Sdarrenr mb_t *m1; 267145516Sdarrenr#endif 268145516Sdarrenr 269145516Sdarrenr m = fin->fin_m; 270145516Sdarrenr ip = fin->fin_ip; 271145516Sdarrenr tcp = (tcphdr_t *)fin->fin_dp; 272145516Sdarrenr bzero(ctcpbuf, sizeof(ctcpbuf)); 273145516Sdarrenr off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 274145516Sdarrenr 275145516Sdarrenr#ifdef __sgi 276145516Sdarrenr dlen = fin->fin_plen - off; 277145516Sdarrenr#else 278145516Sdarrenr dlen = MSGDSIZE(m) - off; 279145516Sdarrenr#endif 280145516Sdarrenr if (dlen <= 0) 281145516Sdarrenr return 0; 282145516Sdarrenr COPYDATA(m, off, MIN(sizeof(ctcpbuf), dlen), ctcpbuf); 283145516Sdarrenr 284145516Sdarrenr if (dlen <= 0) 285145516Sdarrenr return 0; 286145516Sdarrenr ctcpbuf[sizeof(ctcpbuf) - 1] = '\0'; 287145516Sdarrenr *newbuf = '\0'; 288145516Sdarrenr 289145516Sdarrenr irc = nat->nat_aps->aps_data; 290145516Sdarrenr if (ippr_irc_complete(irc, ctcpbuf, dlen) == 0) 291145516Sdarrenr return 0; 292145516Sdarrenr 293145516Sdarrenr /* 294145516Sdarrenr * check that IP address in the PORT/PASV reply is the same as the 295145516Sdarrenr * sender of the command - prevents using PORT for port scanning. 296145516Sdarrenr */ 297145516Sdarrenr if (irc->irc_ipnum != ntohl(nat->nat_inip.s_addr)) 298145516Sdarrenr return 0; 299145516Sdarrenr 300145516Sdarrenr a5 = irc->irc_port; 301145516Sdarrenr 302145516Sdarrenr /* 303145516Sdarrenr * Calculate new address parts for the DCC command 304145516Sdarrenr */ 305145516Sdarrenr a1 = ntohl(ip->ip_src.s_addr); 306145516Sdarrenr olen = irc->irc_len; 307145516Sdarrenr i = irc->irc_addr - ctcpbuf; 308145516Sdarrenr i++; 309145516Sdarrenr (void) strncpy(newbuf, ctcpbuf, i); 310145516Sdarrenr /* DO NOT change these! */ 311145516Sdarrenr#if defined(SNPRINTF) && defined(KERNEL) 312145516Sdarrenr SNPRINTF(newbuf, sizeof(newbuf) - i, "%u %u\001\r\n", a1, a5); 313145516Sdarrenr#else 314145516Sdarrenr (void) sprintf(newbuf, "%u %u\001\r\n", a1, a5); 315145516Sdarrenr#endif 316145516Sdarrenr 317145516Sdarrenr nlen = strlen(newbuf); 318145516Sdarrenr inc = nlen - olen; 319145516Sdarrenr 320145516Sdarrenr if ((inc + ip->ip_len) > 65535) 321145516Sdarrenr return 0; 322145516Sdarrenr 323145516Sdarrenr#ifdef MENTAT 324145516Sdarrenr for (m1 = m; m1->b_cont; m1 = m1->b_cont) 325145516Sdarrenr ; 326145516Sdarrenr if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { 327145516Sdarrenr mblk_t *nm; 328145516Sdarrenr 329145516Sdarrenr /* alloc enough to keep same trailer space for lower driver */ 330145516Sdarrenr nm = allocb(nlen, BPRI_MED); 331145516Sdarrenr PANIC((!nm),("ippr_irc_out: allocb failed")); 332145516Sdarrenr 333145516Sdarrenr nm->b_band = m1->b_band; 334145516Sdarrenr nm->b_wptr += nlen; 335145516Sdarrenr 336145516Sdarrenr m1->b_wptr -= olen; 337145516Sdarrenr PANIC((m1->b_wptr < m1->b_rptr), 338145516Sdarrenr ("ippr_irc_out: cannot handle fragmented data block")); 339145516Sdarrenr 340145516Sdarrenr linkb(m1, nm); 341145516Sdarrenr } else { 342145516Sdarrenr# if SOLARIS && defined(ICK_VALID) 343145516Sdarrenr if (m1->b_datap->db_struiolim == m1->b_wptr) 344145516Sdarrenr m1->b_datap->db_struiolim += inc; 345145516Sdarrenr m1->b_datap->db_struioflag &= ~STRUIO_IP; 346145516Sdarrenr# endif 347145516Sdarrenr m1->b_wptr += inc; 348145516Sdarrenr } 349145516Sdarrenr#else 350145516Sdarrenr if (inc < 0) 351145516Sdarrenr m_adj(m, inc); 352145516Sdarrenr /* the mbuf chain will be extended if necessary by m_copyback() */ 353145516Sdarrenr#endif 354145516Sdarrenr COPYBACK(m, off, nlen, newbuf); 355145516Sdarrenr 356145516Sdarrenr if (inc != 0) { 357145516Sdarrenr#if defined(MENTAT) || defined(__sgi) 358145516Sdarrenr register u_32_t sum1, sum2; 359145516Sdarrenr 360145516Sdarrenr sum1 = ip->ip_len; 361145516Sdarrenr sum2 = ip->ip_len + inc; 362145516Sdarrenr 363145516Sdarrenr /* Because ~1 == -2, We really need ~1 == -1 */ 364145516Sdarrenr if (sum1 > sum2) 365145516Sdarrenr sum2--; 366145516Sdarrenr sum2 -= sum1; 367145516Sdarrenr sum2 = (sum2 & 0xffff) + (sum2 >> 16); 368145516Sdarrenr 369145516Sdarrenr fix_outcksum(fin, &ip->ip_sum, sum2); 370145516Sdarrenr#endif 371145516Sdarrenr ip->ip_len += inc; 372145516Sdarrenr } 373145516Sdarrenr 374145516Sdarrenr /* 375145516Sdarrenr * Add skeleton NAT entry for connection which will come back the 376145516Sdarrenr * other way. 377145516Sdarrenr */ 378145516Sdarrenr sp = htons(a5); 379145516Sdarrenr /* 380145516Sdarrenr * Don't allow the PORT command to specify a port < 1024 due to 381145516Sdarrenr * security crap. 382145516Sdarrenr */ 383145516Sdarrenr if (ntohs(sp) < 1024) 384145516Sdarrenr return 0; 385145516Sdarrenr 386145516Sdarrenr /* 387145516Sdarrenr * The server may not make the connection back from port 20, but 388145516Sdarrenr * it is the most likely so use it here to check for a conflicting 389145516Sdarrenr * mapping. 390145516Sdarrenr */ 391145516Sdarrenr bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); 392145516Sdarrenr fi.fin_data[0] = sp; 393145516Sdarrenr fi.fin_data[1] = fin->fin_data[1]; 394145516Sdarrenr nat2 = nat_outlookup(fin, IPN_TCP, nat->nat_p, nat->nat_inip, 395145516Sdarrenr ip->ip_dst); 396145516Sdarrenr if (nat2 == NULL) { 397145516Sdarrenr bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); 398145516Sdarrenr bzero((char *)tcp2, sizeof(*tcp2)); 399145516Sdarrenr tcp2->th_win = htons(8192); 400145516Sdarrenr tcp2->th_sport = sp; 401145516Sdarrenr tcp2->th_dport = 0; /* XXX - don't specify remote port */ 402145516Sdarrenr fi.fin_state = NULL; 403145516Sdarrenr fi.fin_nat = NULL; 404145516Sdarrenr fi.fin_data[0] = ntohs(sp); 405145516Sdarrenr fi.fin_data[1] = 0; 406145516Sdarrenr fi.fin_dp = (char *)tcp2; 407145516Sdarrenr fi.fin_fr = &ircnatfr; 408145516Sdarrenr fi.fin_dlen = sizeof(*tcp2); 409145516Sdarrenr fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); 410145516Sdarrenr swip = ip->ip_src; 411145516Sdarrenr ip->ip_src = nat->nat_inip; 412145516Sdarrenr nat2 = nat_new(&fi, nat->nat_ptr, NULL, 413145516Sdarrenr NAT_SLAVE|IPN_TCP|SI_W_DPORT, NAT_OUTBOUND); 414145516Sdarrenr if (nat2 != NULL) { 415145516Sdarrenr (void) nat_proto(&fi, nat2, 0); 416145516Sdarrenr nat_update(&fi, nat2, nat2->nat_ptr); 417145516Sdarrenr 418145516Sdarrenr (void) fr_addstate(&fi, NULL, SI_W_DPORT); 419145516Sdarrenr if (fi.fin_state != NULL) 420145516Sdarrenr fr_statederef(&fi, (ipstate_t **)&fi.fin_state); 421145516Sdarrenr } 422145516Sdarrenr ip->ip_src = swip; 423145516Sdarrenr } 424145516Sdarrenr return inc; 425145516Sdarrenr} 426145516Sdarrenr 427145516Sdarrenr 428145516Sdarrenrint ippr_irc_out(fin, aps, nat) 429145516Sdarrenrfr_info_t *fin; 430145516Sdarrenrap_session_t *aps; 431145516Sdarrenrnat_t *nat; 432145516Sdarrenr{ 433145516Sdarrenr aps = aps; /* LINT */ 434145516Sdarrenr return ippr_irc_send(fin, nat); 435145516Sdarrenr} 436