root/share/examples/ipfilter/samples/proxy.c

/*
 * 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