/* * Copyright (C) 2000-2003 Darren Reed * * See the IPFILTER.LICENCE file for details on licencing. * * $Id: ip_irc_pxy.c,v 2.39.2.4 2005/02/04 10:22:55 darrenr Exp $ * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #define IPF_IRC_PROXY #define IPF_IRCBUFSZ 96 /* This *MUST* be >= 64! */ typedef struct ifs_ircpxy { frentry_t ircnatfr; int irc_proxy_init; } ifs_ircpxy_t; int ippr_irc_init __P((void **, ipf_stack_t *)); void ippr_irc_fini __P((void **, ipf_stack_t *)); int ippr_irc_new __P((fr_info_t *, ap_session_t *, nat_t *, void *)); int ippr_irc_out __P((fr_info_t *, ap_session_t *, nat_t *, void *)); int ippr_irc_send __P((fr_info_t *, nat_t *, ifs_ircpxy_t *)); int ippr_irc_complete __P((ircinfo_t *, char *, size_t)); u_short ipf_irc_atoi __P((char **)); /* * Initialize local structures. */ /*ARGSUSED*/ int ippr_irc_init(private, ifs) void **private; ipf_stack_t *ifs; { ifs_ircpxy_t *ifsirc; KMALLOC(ifsirc, ifs_ircpxy_t *); if (ifsirc == NULL) return -1; bzero((char *)&ifsirc->ircnatfr, sizeof(ifsirc->ircnatfr)); ifsirc->ircnatfr.fr_ref = 1; ifsirc->ircnatfr.fr_flags = FR_INQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; MUTEX_INIT(&ifsirc->ircnatfr.fr_lock, "IRC proxy rule lock"); ifsirc->irc_proxy_init = 1; *private = (void *)ifsirc; return 0; } /*ARGSUSED*/ void ippr_irc_fini(private, ifs) void **private; ipf_stack_t *ifs; { ifs_ircpxy_t *ifsirc = *((ifs_ircpxy_t **)private); if (ifsirc->irc_proxy_init == 1) { MUTEX_DESTROY(&ifsirc->ircnatfr.fr_lock); ifsirc->irc_proxy_init = 0; } KFREE(ifsirc); *private = NULL; } static char *ippr_irc_dcctypes[] = { "CHAT ", /* CHAT chat ipnumber portnumber */ "SEND ", /* SEND filename ipnumber portnumber */ "MOVE ", "TSEND ", "SCHAT ", NULL, }; /* * :A PRIVMSG B :^ADCC CHAT chat 0 0^A\r\n * PRIVMSG B ^ADCC CHAT chat 0 0^A\r\n */ int ippr_irc_complete(ircp, buf, len) ircinfo_t *ircp; char *buf; size_t len; { register char *s, c; register size_t i; u_32_t l; int j, k; ircp->irc_ipnum = 0; ircp->irc_port = 0; if (len < 31) return 0; s = buf; c = *s++; i = len - 1; if ((c != ':') && (c != 'P')) return 0; if (c == ':') { /* * Loosely check that the source is a nickname of some sort */ s++; c = *s; ircp->irc_snick = s; if (!ISALPHA(c)) return 0; i--; for (c = *s; !ISSPACE(c) && (i > 0); i--) c = *s++; if (i < 31) return 0; if (c != 'P') return 0; } else ircp->irc_snick = NULL; /* * Check command string */ if (strncmp(s, "PRIVMSG ", 8)) return 0; i -= 8; s += 8; c = *s; ircp->irc_dnick = s; /* * Loosely check that the destination is a nickname of some sort */ if (!ISALPHA(c)) return 0; for (; !ISSPACE(c) && (i > 0); i--) c = *s++; if (i < 20) return 0; s++, i--; /* * Look for a ^A to start the DCC */ c = *s; if (c == ':') { s++; c = *s; } if (strncmp(s, "\001DCC ", 4)) return 0; i -= 4; s += 4; /* * Check for a recognised DCC command */ for (j = 0, k = 0; ippr_irc_dcctypes[j]; j++) { k = MIN(strlen(ippr_irc_dcctypes[j]), i); if (!strncmp(ippr_irc_dcctypes[j], s, k)) break; } if (!ippr_irc_dcctypes[j]) return 0; ircp->irc_type = s; i -= k; s += k; if (i < 11) return 0; /* * Check for the arg */ c = *s; if (ISSPACE(c)) return 0; ircp->irc_arg = s; for (; (c != ' ') && (c != '\001') && (i > 0); i--) c = *s++; if (c == '\001') /* In reality a ^A can quote another ^A...*/ return 0; if (i < 5) return 0; s++; i--; c = *s; if (!ISDIGIT(c)) return 0; ircp->irc_addr = s; /* * Get the IP# */ for (l = 0; ISDIGIT(c) && (i > 0); i--) { l *= 10; l += c - '0'; c = *s++; } if (i < 4) return 0; if (c != ' ') return 0; ircp->irc_ipnum = l; s++; i--; c = *s; if (!ISDIGIT(c)) return 0; /* * Get the port# */ for (l = 0; ISDIGIT(c) && (i > 0); i--) { l *= 10; l += c - '0'; c = *s++; } if (i < 3) return 0; if (strncmp(s, "\001\r\n", 3)) return 0; s += 3; ircp->irc_len = s - buf; ircp->irc_port = l; return 1; } /*ARGSUSED*/ int ippr_irc_new(fin, aps, nat, private) fr_info_t *fin; ap_session_t *aps; nat_t *nat; void *private; { ircinfo_t *irc; KMALLOC(irc, ircinfo_t *); if (irc == NULL) return -1; fin = fin; /* LINT */ nat = nat; /* LINT */ aps->aps_data = irc; aps->aps_psiz = sizeof(ircinfo_t); bzero((char *)irc, sizeof(*irc)); return 0; } int ippr_irc_send(fin, nat, ifsirc) fr_info_t *fin; nat_t *nat; ifs_ircpxy_t *ifsirc; { char ctcpbuf[IPF_IRCBUFSZ], newbuf[IPF_IRCBUFSZ]; tcphdr_t *tcp, tcph, *tcp2 = &tcph; int off, inc = 0, i, dlen; size_t nlen = 0, olen; struct in_addr swip; u_short a5, sp; ircinfo_t *irc; fr_info_t fi; nat_t *nat2; u_int a1; ip_t *ip; mb_t *m; #ifdef MENTAT mb_t *m1; #endif m = fin->fin_m; ip = fin->fin_ip; tcp = (tcphdr_t *)fin->fin_dp; bzero(ctcpbuf, sizeof(ctcpbuf)); off = (char *)tcp - (char *)ip + (TCP_OFF(tcp) << 2) + fin->fin_ipoff; #ifdef __sgi dlen = fin->fin_plen - off; #else dlen = MSGDSIZE(m) - off; #endif if (dlen <= 0) return 0; COPYDATA(m, off, MIN(sizeof(ctcpbuf), dlen), ctcpbuf); if (dlen <= 0) return 0; ctcpbuf[sizeof(ctcpbuf) - 1] = '\0'; *newbuf = '\0'; irc = nat->nat_aps->aps_data; if (ippr_irc_complete(irc, ctcpbuf, dlen) == 0) return 0; /* * check that IP address in the PORT/PASV reply is the same as the * sender of the command - prevents using PORT for port scanning. */ if (irc->irc_ipnum != ntohl(nat->nat_inip.s_addr)) return 0; a5 = irc->irc_port; /* * Calculate new address parts for the DCC command */ a1 = ntohl(ip->ip_src.s_addr); olen = irc->irc_len; i = irc->irc_addr - ctcpbuf; i++; (void) strncpy(newbuf, ctcpbuf, i); /* DO NOT change these! */ #if defined(SNPRINTF) && defined(KERNEL) (void) SNPRINTF(newbuf, sizeof(newbuf) - i, "%u %u\001\r\n", a1, a5); #else (void) sprintf(newbuf, "%u %u\001\r\n", a1, a5); #endif nlen = strlen(newbuf); inc = nlen - olen; if ((inc + ip->ip_len) > 65535) return 0; #ifdef MENTAT for (m1 = m; m1->b_cont; m1 = m1->b_cont) ; if ((inc > 0) && (m1->b_datap->db_lim - m1->b_wptr < inc)) { mblk_t *nm; /* alloc enough to keep same trailer space for lower driver */ nm = allocb(nlen, BPRI_MED); PANIC((!nm),("ippr_irc_out: allocb failed")); nm->b_band = m1->b_band; nm->b_wptr += nlen; m1->b_wptr -= olen; PANIC((m1->b_wptr < m1->b_rptr), ("ippr_irc_out: cannot handle fragmented data block")); linkb(m1, nm); } else { # if SOLARIS && defined(ICK_VALID) if (m1->b_datap->db_struiolim == m1->b_wptr) m1->b_datap->db_struiolim += inc; m1->b_datap->db_struioflag &= ~STRUIO_IP; # endif m1->b_wptr += inc; } #else if (inc < 0) m_adj(m, inc); /* the mbuf chain will be extended if necessary by m_copyback() */ #endif COPYBACK(m, off, nlen, newbuf); if (inc != 0) { #if defined(MENTAT) || defined(__sgi) register u_32_t sum1, sum2; sum1 = ip->ip_len; sum2 = ip->ip_len + inc; /* Because ~1 == -2, We really need ~1 == -1 */ if (sum1 > sum2) sum2--; sum2 -= sum1; sum2 = (sum2 & 0xffff) + (sum2 >> 16); fix_outcksum(&ip->ip_sum, sum2); #endif ip->ip_len += inc; } /* * Add skeleton NAT entry for connection which will come back the * other way. */ sp = htons(a5); /* * Don't allow the PORT command to specify a port < 1024 due to * security crap. */ if (ntohs(sp) < 1024) return 0; /* * The server may not make the connection back from port 20, but * it is the most likely so use it here to check for a conflicting * mapping. */ bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); fi.fin_data[0] = sp; fi.fin_data[1] = fin->fin_data[1]; nat2 = nat_outlookup(fin, IPN_TCP, nat->nat_p, nat->nat_inip, ip->ip_dst); if (nat2 == NULL) { bcopy((caddr_t)fin, (caddr_t)&fi, sizeof(fi)); bzero((char *)tcp2, sizeof(*tcp2)); tcp2->th_win = htons(8192); tcp2->th_sport = sp; tcp2->th_dport = 0; /* XXX - don't specify remote port */ fi.fin_data[0] = ntohs(sp); fi.fin_data[1] = 0; fi.fin_dp = (char *)tcp2; fi.fin_fr = &ifsirc->ircnatfr; fi.fin_dlen = sizeof(*tcp2); fi.fin_plen = fi.fin_hlen + sizeof(*tcp2); swip = ip->ip_src; ip->ip_src = nat->nat_inip; nat2 = nat_new(&fi, nat->nat_ptr, NULL, NAT_SLAVE|IPN_TCP|SI_W_DPORT, NAT_OUTBOUND); if (nat2 != NULL) { (void) nat_proto(&fi, nat2, 0); nat_update(&fi, nat2, nat2->nat_ptr); (void) fr_addstate(&fi, NULL, SI_W_DPORT); } ip->ip_src = swip; } return inc; } int ippr_irc_out(fin, aps, nat, private) fr_info_t *fin; ap_session_t *aps; nat_t *nat; void *private; { aps = aps; /* LINT */ return ippr_irc_send(fin, nat, (ifs_ircpxy_t *)private); }