Home | History | Annotate | Line # | Download | only in samples
      1  1.1  christos /*	$NetBSD: proxy.c,v 1.1.1.1 2012/03/23 21:20:15 christos Exp $	*/
      2  1.1  christos 
      3  1.1  christos /*
      4  1.1  christos  * Sample transparent proxy program.
      5  1.1  christos  *
      6  1.1  christos  * Sample implementation of a program which intercepts a TCP connectiona and
      7  1.1  christos  * just echos all data back to the origin.  Written to work via inetd as a
      8  1.1  christos  * "nonwait" program running as root; ie.
      9  1.1  christos  * tcpmux          stream  tcp     nowait root /usr/local/bin/proxy proxy
     10  1.1  christos  * with a NAT rue like this:
     11  1.1  christos  * rdr smc0 0/0 port 80 -> 127.0.0.1/32 port 1
     12  1.1  christos  */
     13  1.1  christos #include <stdio.h>
     14  1.1  christos #include <string.h>
     15  1.1  christos #include <fcntl.h>
     16  1.1  christos #include <syslog.h>
     17  1.1  christos #if !defined(__SVR4) && !defined(__svr4__)
     18  1.1  christos #include <strings.h>
     19  1.1  christos #else
     20  1.1  christos #include <sys/byteorder.h>
     21  1.1  christos #endif
     22  1.1  christos #include <sys/types.h>
     23  1.1  christos #include <sys/time.h>
     24  1.1  christos #include <sys/param.h>
     25  1.1  christos #include <stdlib.h>
     26  1.1  christos #include <unistd.h>
     27  1.1  christos #include <stddef.h>
     28  1.1  christos #include <sys/socket.h>
     29  1.1  christos #include <sys/ioctl.h>
     30  1.1  christos #if defined(sun) && (defined(__svr4__) || defined(__SVR4))
     31  1.1  christos # include <sys/ioccom.h>
     32  1.1  christos # include <sys/sysmacros.h>
     33  1.1  christos #endif
     34  1.1  christos #include <netinet/in.h>
     35  1.1  christos #include <netinet/in_systm.h>
     36  1.1  christos #include <netinet/ip.h>
     37  1.1  christos #include <netinet/tcp.h>
     38  1.1  christos #include <net/if.h>
     39  1.1  christos #include <netdb.h>
     40  1.1  christos #include <arpa/nameser.h>
     41  1.1  christos #include <arpa/inet.h>
     42  1.1  christos #include <resolv.h>
     43  1.1  christos #include <ctype.h>
     44  1.1  christos #include "netinet/ip_compat.h"
     45  1.1  christos #include "netinet/ip_fil.h"
     46  1.1  christos #include "netinet/ip_nat.h"
     47  1.1  christos #include "netinet/ip_state.h"
     48  1.1  christos #include "netinet/ip_proxy.h"
     49  1.1  christos #include "netinet/ip_nat.h"
     50  1.1  christos #include "netinet/ipl.h"
     51  1.1  christos 
     52  1.1  christos 
     53  1.1  christos main(argc, argv)
     54  1.1  christos 	int argc;
     55  1.1  christos 	char *argv[];
     56  1.1  christos {
     57  1.1  christos 	struct	sockaddr_in	sin, sloc, sout;
     58  1.1  christos 	ipfobj_t	obj;
     59  1.1  christos 	natlookup_t	natlook;
     60  1.1  christos 	char	buffer[512];
     61  1.1  christos 	int	namelen, fd, n;
     62  1.1  christos 
     63  1.1  christos 	/*
     64  1.1  christos 	 * get IP# and port # of the remote end of the connection (at the
     65  1.1  christos 	 * origin).
     66  1.1  christos 	 */
     67  1.1  christos 	namelen = sizeof(sin);
     68  1.1  christos 	if (getpeername(0, (struct sockaddr *)&sin, &namelen) == -1) {
     69  1.1  christos 		perror("getpeername");
     70  1.1  christos 		exit(-1);
     71  1.1  christos 	}
     72  1.1  christos 
     73  1.1  christos 	/*
     74  1.1  christos 	 * get IP# and port # of the local end of the connection (at the
     75  1.1  christos 	 * man-in-the-middle).
     76  1.1  christos 	 */
     77  1.1  christos 	namelen = sizeof(sin);
     78  1.1  christos 	if (getsockname(0, (struct sockaddr *)&sloc, &namelen) == -1) {
     79  1.1  christos 		perror("getsockname");
     80  1.1  christos 		exit(-1);
     81  1.1  christos 	}
     82  1.1  christos 
     83  1.1  christos 	bzero((char *)&obj, sizeof(obj));
     84  1.1  christos 	obj.ipfo_rev = IPFILTER_VERSION;
     85  1.1  christos 	obj.ipfo_size = sizeof(natlook);
     86  1.1  christos 	obj.ipfo_ptr = &natlook;
     87  1.1  christos 	obj.ipfo_type = IPFOBJ_NATLOOKUP;
     88  1.1  christos 
     89  1.1  christos 	/*
     90  1.1  christos 	 * Build up the NAT natlookup structure.
     91  1.1  christos 	 */
     92  1.1  christos 	bzero((char *)&natlook, sizeof(natlook));
     93  1.1  christos 	natlook.nl_outip = sin.sin_addr;
     94  1.1  christos 	natlook.nl_inip = sloc.sin_addr;
     95  1.1  christos 	natlook.nl_flags = IPN_TCP;
     96  1.1  christos 	natlook.nl_outport = sin.sin_port;
     97  1.1  christos 	natlook.nl_inport = sloc.sin_port;
     98  1.1  christos 
     99  1.1  christos 	/*
    100  1.1  christos 	 * Open the NAT device and lookup the mapping pair.
    101  1.1  christos 	 */
    102  1.1  christos 	fd = open(IPNAT_NAME, O_RDONLY);
    103  1.1  christos 	if (ioctl(fd, SIOCGNATL, &obj) == -1) {
    104  1.1  christos 		perror("ioctl(SIOCGNATL)");
    105  1.1  christos 		exit(-1);
    106  1.1  christos 	}
    107  1.1  christos 
    108  1.1  christos #define	DO_NAT_OUT
    109  1.1  christos #ifdef	DO_NAT_OUT
    110  1.1  christos 	if (argc > 1)
    111  1.1  christos 		do_nat_out(0, 1, fd, &natlook, argv[1]);
    112  1.1  christos #else
    113  1.1  christos 
    114  1.1  christos 	/*
    115  1.1  christos 	 * Log it
    116  1.1  christos 	 */
    117  1.1  christos 	syslog(LOG_DAEMON|LOG_INFO, "connect to %s,%d",
    118  1.1  christos 		inet_ntoa(natlook.nl_realip), ntohs(natlook.nl_realport));
    119  1.1  christos 	printf("connect to %s,%d\n",
    120  1.1  christos 		inet_ntoa(natlook.nl_realip), ntohs(natlook.nl_realport));
    121  1.1  christos 
    122  1.1  christos 	/*
    123  1.1  christos 	 * Just echo data read in from stdin to stdout
    124  1.1  christos 	 */
    125  1.1  christos 	while ((n = read(0, buffer, sizeof(buffer))) > 0)
    126  1.1  christos 		if (write(1, buffer, n) != n)
    127  1.1  christos 			break;
    128  1.1  christos 	close(0);
    129  1.1  christos #endif
    130  1.1  christos }
    131  1.1  christos 
    132  1.1  christos 
    133  1.1  christos #ifdef	DO_NAT_OUT
    134  1.1  christos do_nat_out(in, out, fd, nlp, extif)
    135  1.1  christos 	int fd;
    136  1.1  christos 	natlookup_t *nlp;
    137  1.1  christos 	char *extif;
    138  1.1  christos {
    139  1.1  christos 	nat_save_t ns, *nsp = &ns;
    140  1.1  christos 	struct sockaddr_in usin;
    141  1.1  christos 	u_32_t sum1, sum2, sumd;
    142  1.1  christos 	int onoff, ofd, slen;
    143  1.1  christos 	ipfobj_t obj;
    144  1.1  christos 	ipnat_t *ipn;
    145  1.1  christos 	nat_t *nat;
    146  1.1  christos 
    147  1.1  christos 	bzero((char *)&ns, sizeof(ns));
    148  1.1  christos 
    149  1.1  christos 	nat = &ns.ipn_nat;
    150  1.1  christos 	nat->nat_p = IPPROTO_TCP;
    151  1.1  christos 	nat->nat_dir = NAT_OUTBOUND;
    152  1.1  christos 	if ((extif != NULL) && (*extif != '\0')) {
    153  1.1  christos 		strncpy(nat->nat_ifnames[0], extif,
    154  1.1  christos 			sizeof(nat->nat_ifnames[0]));
    155  1.1  christos 		strncpy(nat->nat_ifnames[1], extif,
    156  1.1  christos 			sizeof(nat->nat_ifnames[1]));
    157  1.1  christos 		nat->nat_ifnames[0][sizeof(nat->nat_ifnames[0]) - 1] = '\0';
    158  1.1  christos 		nat->nat_ifnames[1][sizeof(nat->nat_ifnames[1]) - 1] = '\0';
    159  1.1  christos 	}
    160  1.1  christos 
    161  1.1  christos 	ofd = socket(AF_INET, SOCK_DGRAM, 0);
    162  1.1  christos 	bzero((char *)&usin, sizeof(usin));
    163  1.1  christos 	usin.sin_family = AF_INET;
    164  1.1  christos 	usin.sin_addr = nlp->nl_realip;
    165  1.1  christos 	usin.sin_port = nlp->nl_realport;
    166  1.1  christos 	(void) connect(ofd, (struct sockaddr *)&usin, sizeof(usin));
    167  1.1  christos 	slen = sizeof(usin);
    168  1.1  christos 	(void) getsockname(ofd, (struct sockaddr *)&usin, &slen);
    169  1.1  christos 	close(ofd);
    170  1.1  christos printf("local IP# to use: %s\n", inet_ntoa(usin.sin_addr));
    171  1.1  christos 
    172  1.1  christos 	if ((ofd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    173  1.1  christos 		perror("socket");
    174  1.1  christos 	usin.sin_port = 0;
    175  1.1  christos 	if (bind(ofd, (struct sockaddr *)&usin, sizeof(usin)))
    176  1.1  christos 		perror("bind");
    177  1.1  christos 	slen = sizeof(usin);
    178  1.1  christos 	if (getsockname(ofd, (struct sockaddr *)&usin, &slen))
    179  1.1  christos 		perror("getsockname");
    180  1.1  christos printf("local port# to use: %d\n", ntohs(usin.sin_port));
    181  1.1  christos 
    182  1.1  christos 	nat->nat_inip = usin.sin_addr;
    183  1.1  christos 	nat->nat_outip = nlp->nl_outip;
    184  1.1  christos 	nat->nat_oip = nlp->nl_realip;
    185  1.1  christos 
    186  1.1  christos 	sum1 = LONG_SUM(ntohl(usin.sin_addr.s_addr)) + ntohs(usin.sin_port);
    187  1.1  christos 	sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)) + ntohs(nlp->nl_outport);
    188  1.1  christos 	CALC_SUMD(sum1, sum2, sumd);
    189  1.1  christos 	nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
    190  1.1  christos 	nat->nat_sumd[1] = nat->nat_sumd[0];
    191  1.1  christos 
    192  1.1  christos 	sum1 = LONG_SUM(ntohl(usin.sin_addr.s_addr));
    193  1.1  christos 	sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr));
    194  1.1  christos 	CALC_SUMD(sum1, sum2, sumd);
    195  1.1  christos 	nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
    196  1.1  christos 
    197  1.1  christos 	nat->nat_inport = usin.sin_port;
    198  1.1  christos 	nat->nat_outport = nlp->nl_outport;
    199  1.1  christos 	nat->nat_oport = nlp->nl_realport;
    200  1.1  christos 
    201  1.1  christos 	nat->nat_flags = IPN_TCPUDP;
    202  1.1  christos 
    203  1.1  christos 	bzero((char *)&obj, sizeof(obj));
    204  1.1  christos 	obj.ipfo_rev = IPFILTER_VERSION;
    205  1.1  christos 	obj.ipfo_size = sizeof(*nsp);
    206  1.1  christos 	obj.ipfo_ptr = nsp;
    207  1.1  christos 	obj.ipfo_type = IPFOBJ_NATSAVE;
    208  1.1  christos 
    209  1.1  christos 	onoff = 1;
    210  1.1  christos 	if (ioctl(fd, SIOCSTLCK, &onoff) == 0) {
    211  1.1  christos 		if (ioctl(fd, SIOCSTPUT, &obj) != 0)
    212  1.1  christos 			perror("SIOCSTPUT");
    213  1.1  christos 		onoff = 0;
    214  1.1  christos 		if (ioctl(fd, SIOCSTLCK, &onoff) != 0)
    215  1.1  christos 			perror("SIOCSTLCK");
    216  1.1  christos 	}
    217  1.1  christos 
    218  1.1  christos 	usin.sin_addr = nlp->nl_realip;
    219  1.1  christos 	usin.sin_port = nlp->nl_realport;
    220  1.1  christos printf("remote end for connection: %s,%d\n", inet_ntoa(usin.sin_addr),
    221  1.1  christos ntohs(usin.sin_port));
    222  1.1  christos fflush(stdout);
    223  1.1  christos 	if (connect(ofd, (struct sockaddr *)&usin, sizeof(usin)))
    224  1.1  christos 		perror("connect");
    225  1.1  christos 
    226  1.1  christos 	relay(in, out, ofd);
    227  1.1  christos }
    228  1.1  christos 
    229  1.1  christos 
    230  1.1  christos relay(in, out, net)
    231  1.1  christos 	int in, out, net;
    232  1.1  christos {
    233  1.1  christos 	char netbuf[1024], outbuf[1024];
    234  1.1  christos 	char *nwptr, *nrptr, *owptr, *orptr;
    235  1.1  christos 	size_t nsz, osz;
    236  1.1  christos 	fd_set rd, wr;
    237  1.1  christos 	int i, n, maxfd;
    238  1.1  christos 
    239  1.1  christos 	n = 0;
    240  1.1  christos 	maxfd = in;
    241  1.1  christos 	if (out > maxfd)
    242  1.1  christos 		maxfd = out;
    243  1.1  christos 	if (net > maxfd)
    244  1.1  christos 		maxfd = net;
    245  1.1  christos 
    246  1.1  christos 	nrptr = netbuf;
    247  1.1  christos 	nwptr = netbuf;
    248  1.1  christos 	nsz = sizeof(netbuf);
    249  1.1  christos 	orptr = outbuf;
    250  1.1  christos 	owptr = outbuf;
    251  1.1  christos 	osz = sizeof(outbuf);
    252  1.1  christos 
    253  1.1  christos 	while (n >= 0) {
    254  1.1  christos 		FD_ZERO(&rd);
    255  1.1  christos 		FD_ZERO(&wr);
    256  1.1  christos 
    257  1.1  christos 		if (nrptr - netbuf < sizeof(netbuf))
    258  1.1  christos 			FD_SET(in, &rd);
    259  1.1  christos 		if (orptr - outbuf < sizeof(outbuf))
    260  1.1  christos 			FD_SET(net, &rd);
    261  1.1  christos 
    262  1.1  christos 		if (nsz < sizeof(netbuf))
    263  1.1  christos 			FD_SET(net, &wr);
    264  1.1  christos 		if (osz < sizeof(outbuf))
    265  1.1  christos 			FD_SET(out, &wr);
    266  1.1  christos 
    267  1.1  christos 		n = select(maxfd + 1, &rd, &wr, NULL, NULL);
    268  1.1  christos 
    269  1.1  christos 		if ((n > 0) && FD_ISSET(in, &rd)) {
    270  1.1  christos 			i = read(in, nrptr, sizeof(netbuf) - (nrptr - netbuf));
    271  1.1  christos 			if (i <= 0)
    272  1.1  christos 				break;
    273  1.1  christos 			nsz -= i;
    274  1.1  christos 			nrptr += i;
    275  1.1  christos 			n--;
    276  1.1  christos 		}
    277  1.1  christos 
    278  1.1  christos 		if ((n > 0) && FD_ISSET(net, &rd)) {
    279  1.1  christos 			i = read(net, orptr, sizeof(outbuf) - (orptr - outbuf));
    280  1.1  christos 			if (i <= 0)
    281  1.1  christos 				break;
    282  1.1  christos 			osz -= i;
    283  1.1  christos 			orptr += i;
    284  1.1  christos 			n--;
    285  1.1  christos 		}
    286  1.1  christos 
    287  1.1  christos 		if ((n > 0) && FD_ISSET(out, &wr)) {
    288  1.1  christos 			i = write(out, owptr, orptr - owptr);
    289  1.1  christos 			if (i <= 0)
    290  1.1  christos 				break;
    291  1.1  christos 			osz += i;
    292  1.1  christos 			if (osz == sizeof(outbuf) || owptr == orptr) {
    293  1.1  christos 				orptr = outbuf;
    294  1.1  christos 				owptr = outbuf;
    295  1.1  christos 			} else
    296  1.1  christos 				owptr += i;
    297  1.1  christos 			n--;
    298  1.1  christos 		}
    299  1.1  christos 
    300  1.1  christos 		if ((n > 0) && FD_ISSET(net, &wr)) {
    301  1.1  christos 			i = write(net, nwptr, nrptr - nwptr);
    302  1.1  christos 			if (i <= 0)
    303  1.1  christos 				break;
    304  1.1  christos 			nsz += i;
    305  1.1  christos 			if (nsz == sizeof(netbuf) || nwptr == nrptr) {
    306  1.1  christos 				nrptr = netbuf;
    307  1.1  christos 				nwptr = netbuf;
    308  1.1  christos 			} else
    309  1.1  christos 				nwptr += i;
    310  1.1  christos 		}
    311  1.1  christos 	}
    312  1.1  christos 
    313  1.1  christos 	close(net);
    314  1.1  christos 	close(out);
    315  1.1  christos 	close(in);
    316  1.1  christos }
    317  1.1  christos #endif
    318