1145516Sdarrenr/* 2255332Scy * Copyright (C) 2012 by Darren Reed. 3145516Sdarrenr * 4145516Sdarrenr * See the IPFILTER.LICENCE file for details on licencing. 5145516Sdarrenr * 6255332Scy * $Id$ 7145516Sdarrenr */ 8145516Sdarrenr 9145516Sdarrenr#define IPF_IRC_PROXY 10145516Sdarrenr 11145516Sdarrenr#define IPF_IRCBUFSZ 96 /* This *MUST* be >= 64! */ 12145516Sdarrenr 13145516Sdarrenr 14255332Scyvoid ipf_p_irc_main_load __P((void)); 15255332Scyvoid ipf_p_irc_main_unload __P((void)); 16255332Scyint ipf_p_irc_new __P((void *, fr_info_t *, ap_session_t *, nat_t *)); 17255332Scyint ipf_p_irc_out __P((void *, fr_info_t *, ap_session_t *, nat_t *)); 18255332Scyint ipf_p_irc_send __P((fr_info_t *, nat_t *)); 19255332Scyint ipf_p_irc_complete __P((ircinfo_t *, char *, size_t)); 20145516Sdarrenru_short ipf_irc_atoi __P((char **)); 21145516Sdarrenr 22145516Sdarrenrstatic frentry_t ircnatfr; 23145516Sdarrenr 24145516Sdarrenrint irc_proxy_init = 0; 25145516Sdarrenr 26145516Sdarrenr 27145516Sdarrenr/* 28145516Sdarrenr * Initialize local structures. 29145516Sdarrenr */ 30255332Scyvoid 31255332Scyipf_p_irc_main_load() 32145516Sdarrenr{ 33145516Sdarrenr bzero((char *)&ircnatfr, sizeof(ircnatfr)); 34145516Sdarrenr ircnatfr.fr_ref = 1; 35145516Sdarrenr ircnatfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 36145516Sdarrenr MUTEX_INIT(&ircnatfr.fr_lock, "IRC proxy rule lock"); 37145516Sdarrenr irc_proxy_init = 1; 38145516Sdarrenr} 39145516Sdarrenr 40145516Sdarrenr 41255332Scyvoid 42255332Scyipf_p_irc_main_unload() 43145516Sdarrenr{ 44145516Sdarrenr if (irc_proxy_init == 1) { 45145516Sdarrenr MUTEX_DESTROY(&ircnatfr.fr_lock); 46145516Sdarrenr irc_proxy_init = 0; 47145516Sdarrenr } 48145516Sdarrenr} 49145516Sdarrenr 50145516Sdarrenr 51255332Scyconst char *ipf_p_irc_dcctypes[] = { 52145516Sdarrenr "CHAT ", /* CHAT chat ipnumber portnumber */ 53145516Sdarrenr "SEND ", /* SEND filename ipnumber portnumber */ 54145516Sdarrenr "MOVE ", 55145516Sdarrenr "TSEND ", 56145516Sdarrenr "SCHAT ", 57145516Sdarrenr NULL, 58145516Sdarrenr}; 59145516Sdarrenr 60145516Sdarrenr 61145516Sdarrenr/* 62145516Sdarrenr * :A PRIVMSG B :^ADCC CHAT chat 0 0^A\r\n 63145516Sdarrenr * PRIVMSG B ^ADCC CHAT chat 0 0^A\r\n 64145516Sdarrenr */ 65145516Sdarrenr 66145516Sdarrenr 67255332Scyint 68255332Scyipf_p_irc_complete(ircp, buf, len) 69255332Scy ircinfo_t *ircp; 70255332Scy char *buf; 71255332Scy size_t len; 72145516Sdarrenr{ 73145516Sdarrenr register char *s, c; 74145516Sdarrenr register size_t i; 75145516Sdarrenr u_32_t l; 76145516Sdarrenr int j, k; 77145516Sdarrenr 78145516Sdarrenr ircp->irc_ipnum = 0; 79145516Sdarrenr ircp->irc_port = 0; 80145516Sdarrenr 81145516Sdarrenr if (len < 31) 82145516Sdarrenr return 0; 83145516Sdarrenr s = buf; 84145516Sdarrenr c = *s++; 85145516Sdarrenr i = len - 1; 86145516Sdarrenr 87145516Sdarrenr if ((c != ':') && (c != 'P')) 88145516Sdarrenr return 0; 89145516Sdarrenr 90145516Sdarrenr if (c == ':') { 91145516Sdarrenr /* 92145516Sdarrenr * Loosely check that the source is a nickname of some sort 93145516Sdarrenr */ 94145516Sdarrenr s++; 95145516Sdarrenr c = *s; 96145516Sdarrenr ircp->irc_snick = s; 97145516Sdarrenr if (!ISALPHA(c)) 98145516Sdarrenr return 0; 99145516Sdarrenr i--; 100145516Sdarrenr for (c = *s; !ISSPACE(c) && (i > 0); i--) 101145516Sdarrenr c = *s++; 102145516Sdarrenr if (i < 31) 103145516Sdarrenr return 0; 104145516Sdarrenr if (c != 'P') 105145516Sdarrenr return 0; 106145516Sdarrenr } else 107145516Sdarrenr ircp->irc_snick = NULL; 108145516Sdarrenr 109145516Sdarrenr /* 110145516Sdarrenr * Check command string 111145516Sdarrenr */ 112145516Sdarrenr if (strncmp(s, "PRIVMSG ", 8)) 113145516Sdarrenr return 0; 114145516Sdarrenr i -= 8; 115145516Sdarrenr s += 8; 116145516Sdarrenr c = *s; 117145516Sdarrenr ircp->irc_dnick = s; 118145516Sdarrenr 119145516Sdarrenr /* 120145516Sdarrenr * Loosely check that the destination is a nickname of some sort 121145516Sdarrenr */ 122145516Sdarrenr if (!ISALPHA(c)) 123145516Sdarrenr return 0; 124145516Sdarrenr for (; !ISSPACE(c) && (i > 0); i--) 125145516Sdarrenr c = *s++; 126145516Sdarrenr if (i < 20) 127145516Sdarrenr return 0; 128145516Sdarrenr s++, 129145516Sdarrenr i--; 130145516Sdarrenr 131145516Sdarrenr /* 132145516Sdarrenr * Look for a ^A to start the DCC 133145516Sdarrenr */ 134145516Sdarrenr c = *s; 135145516Sdarrenr if (c == ':') { 136145516Sdarrenr s++; 137145516Sdarrenr c = *s; 138145516Sdarrenr } 139145516Sdarrenr 140145516Sdarrenr if (strncmp(s, "\001DCC ", 4)) 141145516Sdarrenr return 0; 142145516Sdarrenr 143145516Sdarrenr i -= 4; 144145516Sdarrenr s += 4; 145145516Sdarrenr 146145516Sdarrenr /* 147145516Sdarrenr * Check for a recognised DCC command 148145516Sdarrenr */ 149255332Scy for (j = 0, k = 0; ipf_p_irc_dcctypes[j]; j++) { 150255332Scy k = MIN(strlen(ipf_p_irc_dcctypes[j]), i); 151255332Scy if (!strncmp(ipf_p_irc_dcctypes[j], s, k)) 152145516Sdarrenr break; 153145516Sdarrenr } 154255332Scy if (!ipf_p_irc_dcctypes[j]) 155145516Sdarrenr return 0; 156145516Sdarrenr 157145516Sdarrenr ircp->irc_type = s; 158145516Sdarrenr i -= k; 159145516Sdarrenr s += k; 160145516Sdarrenr 161145516Sdarrenr if (i < 11) 162145516Sdarrenr return 0; 163145516Sdarrenr 164145516Sdarrenr /* 165145516Sdarrenr * Check for the arg 166145516Sdarrenr */ 167145516Sdarrenr c = *s; 168145516Sdarrenr if (ISSPACE(c)) 169145516Sdarrenr return 0; 170145516Sdarrenr ircp->irc_arg = s; 171145516Sdarrenr for (; (c != ' ') && (c != '\001') && (i > 0); i--) 172145516Sdarrenr c = *s++; 173145516Sdarrenr 174145516Sdarrenr if (c == '\001') /* In reality a ^A can quote another ^A...*/ 175145516Sdarrenr return 0; 176145516Sdarrenr 177145516Sdarrenr if (i < 5) 178145516Sdarrenr return 0; 179145516Sdarrenr 180145516Sdarrenr s++; 181145516Sdarrenr i--; 182145516Sdarrenr c = *s; 183145516Sdarrenr if (!ISDIGIT(c)) 184145516Sdarrenr return 0; 185145516Sdarrenr ircp->irc_addr = s; 186145516Sdarrenr /* 187145516Sdarrenr * Get the IP# 188145516Sdarrenr */ 189145516Sdarrenr for (l = 0; ISDIGIT(c) && (i > 0); i--) { 190145516Sdarrenr l *= 10; 191145516Sdarrenr l += c - '0'; 192145516Sdarrenr c = *s++; 193145516Sdarrenr } 194145516Sdarrenr 195145516Sdarrenr if (i < 4) 196145516Sdarrenr return 0; 197145516Sdarrenr 198145516Sdarrenr if (c != ' ') 199145516Sdarrenr return 0; 200145516Sdarrenr 201145516Sdarrenr ircp->irc_ipnum = l; 202145516Sdarrenr s++; 203145516Sdarrenr i--; 204145516Sdarrenr c = *s; 205145516Sdarrenr if (!ISDIGIT(c)) 206145516Sdarrenr return 0; 207145516Sdarrenr /* 208145516Sdarrenr * Get the port# 209145516Sdarrenr */ 210145516Sdarrenr for (l = 0; ISDIGIT(c) && (i > 0); i--) { 211145516Sdarrenr l *= 10; 212145516Sdarrenr l += c - '0'; 213145516Sdarrenr c = *s++; 214145516Sdarrenr } 215145516Sdarrenr if (i < 3) 216145516Sdarrenr return 0; 217145516Sdarrenr if (strncmp(s, "\001\r\n", 3)) 218145516Sdarrenr return 0; 219145516Sdarrenr s += 3; 220145516Sdarrenr ircp->irc_len = s - buf; 221145516Sdarrenr ircp->irc_port = l; 222145516Sdarrenr return 1; 223145516Sdarrenr} 224145516Sdarrenr 225145516Sdarrenr 226255332Scyint 227255332Scyipf_p_irc_new(arg, fin, aps, nat) 228255332Scy void *arg; 229255332Scy fr_info_t *fin; 230255332Scy ap_session_t *aps; 231255332Scy nat_t *nat; 232145516Sdarrenr{ 233145516Sdarrenr ircinfo_t *irc; 234145516Sdarrenr 235255332Scy if (fin->fin_v != 4) 236255332Scy return -1; 237255332Scy 238145516Sdarrenr KMALLOC(irc, ircinfo_t *); 239145516Sdarrenr if (irc == NULL) 240145516Sdarrenr return -1; 241145516Sdarrenr 242145516Sdarrenr nat = nat; /* LINT */ 243145516Sdarrenr 244145516Sdarrenr aps->aps_data = irc; 245145516Sdarrenr aps->aps_psiz = sizeof(ircinfo_t); 246145516Sdarrenr 247145516Sdarrenr bzero((char *)irc, sizeof(*irc)); 248145516Sdarrenr return 0; 249145516Sdarrenr} 250145516Sdarrenr 251145516Sdarrenr 252255332Scyint 253255332Scyipf_p_irc_send(fin, nat) 254255332Scy fr_info_t *fin; 255255332Scy nat_t *nat; 256145516Sdarrenr{ 257145516Sdarrenr char ctcpbuf[IPF_IRCBUFSZ], newbuf[IPF_IRCBUFSZ]; 258145516Sdarrenr tcphdr_t *tcp, tcph, *tcp2 = &tcph; 259145516Sdarrenr int off, inc = 0, i, dlen; 260255332Scy ipf_main_softc_t *softc; 261145516Sdarrenr size_t nlen = 0, olen; 262145516Sdarrenr struct in_addr swip; 263145516Sdarrenr u_short a5, sp; 264145516Sdarrenr ircinfo_t *irc; 265145516Sdarrenr fr_info_t fi; 266145516Sdarrenr nat_t *nat2; 267145516Sdarrenr u_int a1; 268145516Sdarrenr ip_t *ip; 269145516Sdarrenr mb_t *m; 270145516Sdarrenr#ifdef MENTAT 271145516Sdarrenr mb_t *m1; 272145516Sdarrenr#endif 273255332Scy softc = fin->fin_main_soft; 274145516Sdarrenr 275145516Sdarrenr m = fin->fin_m; 276145516Sdarrenr ip = fin->fin_ip; 277145516Sdarrenr tcp = (tcphdr_t *)fin->fin_dp; 278145516Sdarrenr bzero(ctcpbuf, sizeof(ctcpbuf)); 279145516Sdarrenr off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; 280145516Sdarrenr 281145516Sdarrenr#ifdef __sgi 282145516Sdarrenr dlen = fin->fin_plen - off; 283145516Sdarrenr#else 284145516Sdarrenr dlen = MSGDSIZE(m) - off; 285145516Sdarrenr#endif 286145516Sdarrenr if (dlen <= 0) 287145516Sdarrenr return 0; 288145516Sdarrenr COPYDATA(m, off, MIN(sizeof(ctcpbuf), dlen), ctcpbuf); 289145516Sdarrenr 290145516Sdarrenr if (dlen <= 0) 291145516Sdarrenr return 0; 292145516Sdarrenr ctcpbuf[sizeof(ctcpbuf) - 1] = '\0'; 293145516Sdarrenr *newbuf = '\0'; 294145516Sdarrenr 295145516Sdarrenr irc = nat->nat_aps->aps_data; 296255332Scy if (ipf_p_irc_complete(irc, ctcpbuf, dlen) == 0) 297145516Sdarrenr return 0; 298145516Sdarrenr 299145516Sdarrenr /* 300255332Scy * check that IP address in the DCC reply is the same as the 301255332Scy * sender of the command - prevents use for port scanning. 302145516Sdarrenr */ 303255332Scy if (irc->irc_ipnum != ntohl(nat->nat_osrcaddr)) 304145516Sdarrenr return 0; 305145516Sdarrenr 306145516Sdarrenr a5 = irc->irc_port; 307145516Sdarrenr 308145516Sdarrenr /* 309145516Sdarrenr * Calculate new address parts for the DCC command 310145516Sdarrenr */ 311145516Sdarrenr a1 = ntohl(ip->ip_src.s_addr); 312145516Sdarrenr olen = irc->irc_len; 313145516Sdarrenr i = irc->irc_addr - ctcpbuf; 314145516Sdarrenr i++; 315145516Sdarrenr (void) strncpy(newbuf, ctcpbuf, i); 316145516Sdarrenr /* DO NOT change these! */ 317145516Sdarrenr#if defined(SNPRINTF) && defined(KERNEL) 318145516Sdarrenr SNPRINTF(newbuf, sizeof(newbuf) - i, "%u %u\001\r\n", a1, a5); 319145516Sdarrenr#else 320145516Sdarrenr (void) sprintf(newbuf, "%u %u\001\r\n", a1, a5); 321145516Sdarrenr#endif 322145516Sdarrenr 323145516Sdarrenr nlen = strlen(newbuf); 324145516Sdarrenr inc = nlen - olen; 325145516Sdarrenr 326255332Scy if ((inc + fin->fin_plen) > 65535) 327145516Sdarrenr return 0; 328145516Sdarrenr 329145516Sdarrenr#ifdef MENTAT 330145516Sdarrenr for (m1 = m; m1->b_cont; m1 = m1->b_cont) 331145516Sdarrenr ; 332145516Sdarrenr if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { 333145516Sdarrenr mblk_t *nm; 334145516Sdarrenr 335145516Sdarrenr /* alloc enough to keep same trailer space for lower driver */ 336145516Sdarrenr nm = allocb(nlen, BPRI_MED); 337255332Scy PANIC((!nm),("ipf_p_irc_out: allocb failed")); 338145516Sdarrenr 339145516Sdarrenr nm->b_band = m1->b_band; 340145516Sdarrenr nm->b_wptr += nlen; 341145516Sdarrenr 342145516Sdarrenr m1->b_wptr -= olen; 343145516Sdarrenr PANIC((m1->b_wptr < m1->b_rptr), 344255332Scy ("ipf_p_irc_out: cannot handle fragmented data block")); 345145516Sdarrenr 346145516Sdarrenr linkb(m1, nm); 347145516Sdarrenr } else { 348145516Sdarrenr# if SOLARIS && defined(ICK_VALID) 349145516Sdarrenr if (m1->b_datap->db_struiolim == m1->b_wptr) 350145516Sdarrenr m1->b_datap->db_struiolim += inc; 351145516Sdarrenr m1->b_datap->db_struioflag &= ~STRUIO_IP; 352145516Sdarrenr# endif 353145516Sdarrenr m1->b_wptr += inc; 354145516Sdarrenr } 355145516Sdarrenr#else 356145516Sdarrenr if (inc < 0) 357145516Sdarrenr m_adj(m, inc); 358145516Sdarrenr /* the mbuf chain will be extended if necessary by m_copyback() */ 359145516Sdarrenr#endif 360145516Sdarrenr COPYBACK(m, off, nlen, newbuf); 361255332Scy fin->fin_flx |= FI_DOCKSUM; 362145516Sdarrenr 363145516Sdarrenr if (inc != 0) { 364145516Sdarrenr#if defined(MENTAT) || defined(__sgi) 365145516Sdarrenr register u_32_t sum1, sum2; 366145516Sdarrenr 367255332Scy sum1 = fin->fin_plen; 368255332Scy sum2 = fin->fin_plen + inc; 369145516Sdarrenr 370145516Sdarrenr /* Because ~1 == -2, We really need ~1 == -1 */ 371145516Sdarrenr if (sum1 > sum2) 372145516Sdarrenr sum2--; 373145516Sdarrenr sum2 -= sum1; 374145516Sdarrenr sum2 = (sum2 & 0xffff) + (sum2 >> 16); 375145516Sdarrenr 376255332Scy ipf_fix_outcksum(0, &ip->ip_sum, sum2, 0); 377145516Sdarrenr#endif 378255332Scy fin->fin_plen += inc; 379255332Scy ip->ip_len = htons(fin->fin_plen); 380255332Scy fin->fin_dlen += inc; 381145516Sdarrenr } 382145516Sdarrenr 383145516Sdarrenr /* 384145516Sdarrenr * Add skeleton NAT entry for connection which will come back the 385145516Sdarrenr * other way. 386145516Sdarrenr */ 387145516Sdarrenr sp = htons(a5); 388145516Sdarrenr /* 389145516Sdarrenr * Don't allow the PORT command to specify a port < 1024 due to 390145516Sdarrenr * security crap. 391145516Sdarrenr */ 392145516Sdarrenr if (ntohs(sp) < 1024) 393145516Sdarrenr return 0; 394145516Sdarrenr 395145516Sdarrenr /* 396145516Sdarrenr * The server may not make the connection back from port 20, but 397145516Sdarrenr * it is the most likely so use it here to check for a conflicting 398145516Sdarrenr * mapping. 399145516Sdarrenr */ 400145516Sdarrenr bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); 401145516Sdarrenr fi.fin_data[0] = sp; 402145516Sdarrenr fi.fin_data[1] = fin->fin_data[1]; 403255332Scy nat2 = ipf_nat_outlookup(fin, IPN_TCP, nat->nat_pr[1], nat->nat_nsrcip, 404145516Sdarrenr ip->ip_dst); 405145516Sdarrenr if (nat2 == NULL) { 406255332Scy#ifdef USE_MUTEXES 407255332Scy ipf_nat_softc_t *softn = softc->ipf_nat_soft; 408255332Scy#endif 409255332Scy 410145516Sdarrenr bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); 411145516Sdarrenr bzero((char *)tcp2, sizeof(*tcp2)); 412145516Sdarrenr tcp2->th_win = htons(8192); 413145516Sdarrenr tcp2->th_sport = sp; 414145516Sdarrenr tcp2->th_dport = 0; /* XXX - don't specify remote port */ 415145516Sdarrenr fi.fin_data[0] = ntohs(sp); 416145516Sdarrenr fi.fin_data[1] = 0; 417145516Sdarrenr fi.fin_dp = (char *)tcp2; 418145516Sdarrenr fi.fin_fr = &ircnatfr; 419145516Sdarrenr fi.fin_dlen = sizeof(*tcp2); 420145516Sdarrenr fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); 421145516Sdarrenr swip = ip->ip_src; 422255332Scy ip->ip_src = nat->nat_nsrcip; 423255332Scy MUTEX_ENTER(&softn->ipf_nat_new); 424255332Scy nat2 = ipf_nat_add(&fi, nat->nat_ptr, NULL, 425145516Sdarrenr NAT_SLAVE|IPN_TCP|SI_W_DPORT, NAT_OUTBOUND); 426255332Scy MUTEX_EXIT(&softn->ipf_nat_new); 427145516Sdarrenr if (nat2 != NULL) { 428255332Scy (void) ipf_nat_proto(&fi, nat2, 0); 429255332Scy MUTEX_ENTER(&nat2->nat_lock); 430255332Scy ipf_nat_update(&fi, nat2); 431255332Scy MUTEX_EXIT(&nat2->nat_lock); 432145516Sdarrenr 433255332Scy (void) ipf_state_add(softc, &fi, NULL, SI_W_DPORT); 434145516Sdarrenr } 435145516Sdarrenr ip->ip_src = swip; 436145516Sdarrenr } 437145516Sdarrenr return inc; 438145516Sdarrenr} 439145516Sdarrenr 440145516Sdarrenr 441255332Scyint 442255332Scyipf_p_irc_out(arg, fin, aps, nat) 443255332Scy void *arg; 444255332Scy fr_info_t *fin; 445255332Scy ap_session_t *aps; 446255332Scy nat_t *nat; 447145516Sdarrenr{ 448145516Sdarrenr aps = aps; /* LINT */ 449255332Scy return ipf_p_irc_send(fin, nat); 450145516Sdarrenr} 451