| /* |
| * 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 <stdio.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <syslog.h> |
| #if !defined(__SVR4) && !defined(__svr4__) |
| #include <strings.h> |
| #else |
| #include <sys/byteorder.h> |
| #endif |
| #include <sys/types.h> |
| #include <sys/time.h> |
| #include <sys/param.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <stddef.h> |
| #include <sys/socket.h> |
| #include <sys/ioctl.h> |
| #if defined(sun) && (defined(__svr4__) || defined(__SVR4)) |
| # include <sys/ioccom.h> |
| # include <sys/sysmacros.h> |
| #endif |
| #include <netinet/in.h> |
| #include <netinet/in_systm.h> |
| #include <netinet/ip.h> |
| #include <netinet/tcp.h> |
| #include <net/if.h> |
| #include <netdb.h> |
| #include <arpa/nameser.h> |
| #include <arpa/inet.h> |
| #include <resolv.h> |
| #include <ctype.h> |
| #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 |