/* $NetBSD: proxy.c,v 1.7 2006/04/04 16:17:18 martti Exp $ */ /* * Sample transparent proxy program. * * Sample implementation of a program which intercepts a TCP connectiona and * just echos all data back to the origin. Written to work via inetd as a * "nonwait" program running as root; ie. * tcpmux stream tcp nowait root /usr/local/bin/proxy proxy * with a NAT rue like this: * rdr smc0 0/0 port 80 -> 127.0.0.1/32 port 1 */ #include #include #include #include #if !defined(__SVR4) && !defined(__svr4__) #include #else #include #endif #include #include #include #include #include #include #include #include #if defined(sun) && (defined(__svr4__) || defined(__SVR4)) # include # include #endif #include #include #include #include #include #include #include #include #include #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #include "netinet/ip_nat.h" #include "netinet/ipl.h" main(argc, argv) int argc; char *argv[]; { struct sockaddr_in sin, sloc, sout; ipfobj_t obj; natlookup_t natlook; char buffer[512]; int namelen, fd, n; /* * get IP# and port # of the remote end of the connection (at the * origin). */ namelen = sizeof(sin); if (getpeername(0, (struct sockaddr *)&sin, &namelen) == -1) { perror("getpeername"); exit(-1); } /* * get IP# and port # of the local end of the connection (at the * man-in-the-middle). */ namelen = sizeof(sin); if (getsockname(0, (struct sockaddr *)&sloc, &namelen) == -1) { perror("getsockname"); exit(-1); } bzero((char *)&obj, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_size = sizeof(natlook); obj.ipfo_ptr = &natlook; obj.ipfo_type = IPFOBJ_NATLOOKUP; /* * Build up the NAT natlookup structure. */ bzero((char *)&natlook, sizeof(natlook)); natlook.nl_outip = sin.sin_addr; natlook.nl_inip = sloc.sin_addr; natlook.nl_flags = IPN_TCP; natlook.nl_outport = sin.sin_port; natlook.nl_inport = sloc.sin_port; /* * Open the NAT device and lookup the mapping pair. */ fd = open(IPNAT_NAME, O_RDONLY); if (ioctl(fd, SIOCGNATL, &obj) == -1) { perror("ioctl(SIOCGNATL)"); exit(-1); } #define DO_NAT_OUT #ifdef DO_NAT_OUT if (argc > 1) do_nat_out(0, 1, fd, &natlook, argv[1]); #else /* * Log it */ syslog(LOG_DAEMON|LOG_INFO, "connect to %s,%d", inet_ntoa(natlook.nl_realip), ntohs(natlook.nl_realport)); printf("connect to %s,%d\n", inet_ntoa(natlook.nl_realip), ntohs(natlook.nl_realport)); /* * Just echo data read in from stdin to stdout */ while ((n = read(0, buffer, sizeof(buffer))) > 0) if (write(1, buffer, n) != n) break; close(0); #endif } #ifdef DO_NAT_OUT do_nat_out(in, out, fd, nlp, extif) int fd; natlookup_t *nlp; char *extif; { nat_save_t ns, *nsp = &ns; struct sockaddr_in usin; u_32_t sum1, sum2, sumd; int onoff, ofd, slen; ipfobj_t obj; ipnat_t *ipn; nat_t *nat; bzero((char *)&ns, sizeof(ns)); nat = &ns.ipn_nat; nat->nat_p = IPPROTO_TCP; nat->nat_dir = NAT_OUTBOUND; if ((extif != NULL) && (*extif != '\0')) { strncpy(nat->nat_ifnames[0], extif, sizeof(nat->nat_ifnames[0])); strncpy(nat->nat_ifnames[1], extif, sizeof(nat->nat_ifnames[1])); nat->nat_ifnames[0][sizeof(nat->nat_ifnames[0]) - 1] = '\0'; nat->nat_ifnames[1][sizeof(nat->nat_ifnames[1]) - 1] = '\0'; } ofd = socket(AF_INET, SOCK_DGRAM, 0); bzero((char *)&usin, sizeof(usin)); usin.sin_family = AF_INET; usin.sin_addr = nlp->nl_realip; usin.sin_port = nlp->nl_realport; (void) connect(ofd, (struct sockaddr *)&usin, sizeof(usin)); slen = sizeof(usin); (void) getsockname(ofd, (struct sockaddr *)&usin, &slen); close(ofd); printf("local IP# to use: %s\n", inet_ntoa(usin.sin_addr)); if ((ofd = socket(AF_INET, SOCK_STREAM, 0)) == -1) perror("socket"); usin.sin_port = 0; if (bind(ofd, (struct sockaddr *)&usin, sizeof(usin))) perror("bind"); slen = sizeof(usin); if (getsockname(ofd, (struct sockaddr *)&usin, &slen)) perror("getsockname"); printf("local port# to use: %d\n", ntohs(usin.sin_port)); nat->nat_inip = usin.sin_addr; nat->nat_outip = nlp->nl_outip; nat->nat_oip = nlp->nl_realip; sum1 = LONG_SUM(ntohl(usin.sin_addr.s_addr)) + ntohs(usin.sin_port); sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)) + ntohs(nlp->nl_outport); CALC_SUMD(sum1, sum2, sumd); nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16); nat->nat_sumd[1] = nat->nat_sumd[0]; sum1 = LONG_SUM(ntohl(usin.sin_addr.s_addr)); sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)); CALC_SUMD(sum1, sum2, sumd); nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16); nat->nat_inport = usin.sin_port; nat->nat_outport = nlp->nl_outport; nat->nat_oport = nlp->nl_realport; nat->nat_flags = IPN_TCPUDP; bzero((char *)&obj, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_size = sizeof(*nsp); obj.ipfo_ptr = nsp; obj.ipfo_type = IPFOBJ_NATSAVE; onoff = 1; if (ioctl(fd, SIOCSTLCK, &onoff) == 0) { if (ioctl(fd, SIOCSTPUT, &obj) != 0) perror("SIOCSTPUT"); onoff = 0; if (ioctl(fd, SIOCSTLCK, &onoff) != 0) perror("SIOCSTLCK"); } usin.sin_addr = nlp->nl_realip; usin.sin_port = nlp->nl_realport; printf("remote end for connection: %s,%d\n", inet_ntoa(usin.sin_addr), ntohs(usin.sin_port)); fflush(stdout); if (connect(ofd, (struct sockaddr *)&usin, sizeof(usin))) perror("connect"); relay(in, out, ofd); } relay(in, out, net) int in, out, net; { char netbuf[1024], outbuf[1024]; char *nwptr, *nrptr, *owptr, *orptr; size_t nsz, osz; fd_set rd, wr; int i, n, maxfd; n = 0; maxfd = in; if (out > maxfd) maxfd = out; if (net > maxfd) maxfd = net; nrptr = netbuf; nwptr = netbuf; nsz = sizeof(netbuf); orptr = outbuf; owptr = outbuf; osz = sizeof(outbuf); while (n >= 0) { FD_ZERO(&rd); FD_ZERO(&wr); if (nrptr - netbuf < sizeof(netbuf)) FD_SET(in, &rd); if (orptr - outbuf < sizeof(outbuf)) FD_SET(net, &rd); if (nsz < sizeof(netbuf)) FD_SET(net, &wr); if (osz < sizeof(outbuf)) FD_SET(out, &wr); n = select(maxfd + 1, &rd, &wr, NULL, NULL); if ((n > 0) && FD_ISSET(in, &rd)) { i = read(in, nrptr, sizeof(netbuf) - (nrptr - netbuf)); if (i <= 0) break; nsz -= i; nrptr += i; n--; } if ((n > 0) && FD_ISSET(net, &rd)) { i = read(net, orptr, sizeof(outbuf) - (orptr - outbuf)); if (i <= 0) break; osz -= i; orptr += i; n--; } if ((n > 0) && FD_ISSET(out, &wr)) { i = write(out, owptr, orptr - owptr); if (i <= 0) break; osz += i; if (osz == sizeof(outbuf) || owptr == orptr) { orptr = outbuf; owptr = outbuf; } else owptr += i; n--; } if ((n > 0) && FD_ISSET(net, &wr)) { i = write(net, nwptr, nrptr - nwptr); if (i <= 0) break; nsz += i; if (nsz == sizeof(netbuf) || nwptr == nrptr) { nrptr = netbuf; nwptr = netbuf; } else nwptr += i; } } close(net); close(out); close(in); } #endif