Home | History | Annotate | Line # | Download | only in samples
      1 /*	$NetBSD: relay.c,v 1.1.1.1 2012/03/23 21:20:15 christos Exp $	*/
      2 
      3 /*
      4  * Sample program to be used as a transparent proxy.
      5  *
      6  * Must be executed with permission enough to do an ioctl on /dev/ipl
      7  * or equivalent.  This is just a sample and is only alpha quality.
      8  * - Darren Reed (8 April 1996)
      9  */
     10 #include <unistd.h>
     11 #include <stdio.h>
     12 #include <fcntl.h>
     13 #include <errno.h>
     14 #include <sys/types.h>
     15 #include <sys/time.h>
     16 #include <sys/syslog.h>
     17 #include <sys/socket.h>
     18 #include <sys/ioctl.h>
     19 #include <netinet/in.h>
     20 #include <net/if.h>
     21 #include "netinet/ip_compat.h"
     22 #include "netinet/ip_fil.h"
     23 #include "netinet/ip_nat.h"
     24 #include "netinet/ipl.h"
     25 
     26 #define	RELAY_BUFSZ	8192
     27 
     28 char	ibuff[RELAY_BUFSZ];
     29 char	obuff[RELAY_BUFSZ];
     30 
     31 int relay(ifd, ofd, rfd)
     32 	int ifd, ofd, rfd;
     33 {
     34 	fd_set	rfds, wfds;
     35 	char	*irh, *irt, *rrh, *rrt;
     36 	char	*iwh, *iwt, *rwh, *rwt;
     37 	int	nfd, n, rw;
     38 
     39 	irh = irt = ibuff;
     40 	iwh = iwt = obuff;
     41 	nfd = ifd;
     42 	if (nfd < ofd)
     43 		nfd = ofd;
     44 	if (nfd < rfd)
     45 		nfd = rfd;
     46 
     47 	while (1) {
     48 		FD_ZERO(&rfds);
     49 		FD_ZERO(&wfds);
     50 		if (irh > irt)
     51 			FD_SET(rfd, &wfds);
     52 		if (irh < (ibuff + RELAY_BUFSZ))
     53 			FD_SET(ifd, &rfds);
     54 		if (iwh > iwt)
     55 			FD_SET(ofd, &wfds);
     56 		if (iwh < (obuff + RELAY_BUFSZ))
     57 			FD_SET(rfd, &rfds);
     58 
     59 		switch ((n = select(nfd + 1, &rfds, &wfds, NULL, NULL)))
     60 		{
     61 		case -1 :
     62 		case 0 :
     63 			return -1;
     64 		default :
     65 			if (FD_ISSET(ifd, &rfds)) {
     66 				rw = read(ifd, irh, ibuff + RELAY_BUFSZ - irh);
     67 				if (rw == -1)
     68 					return -1;
     69 				if (rw == 0)
     70 					return 0;
     71 				irh += rw;
     72 				n--;
     73 			}
     74 			if (n && FD_ISSET(ofd, &wfds)) {
     75 				rw = write(ofd, iwt, iwh  - iwt);
     76 				if (rw == -1)
     77 					return -1;
     78 				iwt += rw;
     79 				n--;
     80 			}
     81 			if (n && FD_ISSET(rfd, &rfds)) {
     82 				rw = read(rfd, iwh, obuff + RELAY_BUFSZ - iwh);
     83 				if (rw == -1)
     84 					return -1;
     85 				if (rw == 0)
     86 					return 0;
     87 				iwh += rw;
     88 				n--;
     89 			}
     90 			if (n && FD_ISSET(rfd, &wfds)) {
     91 				rw = write(rfd, irt, irh  - irt);
     92 				if (rw == -1)
     93 					return -1;
     94 				irt += rw;
     95 				n--;
     96 			}
     97 			if (irh == irt)
     98 				irh = irt = ibuff;
     99 			if (iwh == iwt)
    100 				iwh = iwt = obuff;
    101 		}
    102 	}
    103 }
    104 
    105 main(argc, argv)
    106 	int argc;
    107 	char *argv[];
    108 {
    109 	struct	sockaddr_in	sin;
    110 	ipfobj_t	obj;
    111 	natlookup_t	nl;
    112 	natlookup_t	*nlp = &nl;
    113 	int	fd, sl = sizeof(sl), se;
    114 
    115 	openlog(argv[0], LOG_PID|LOG_NDELAY, LOG_DAEMON);
    116 	if ((fd = open(IPNAT_NAME, O_RDONLY)) == -1) {
    117 		se = errno;
    118 		perror("open");
    119 		errno = se;
    120 		syslog(LOG_ERR, "open: %m\n");
    121 		exit(-1);
    122 	}
    123 
    124 	bzero(&obj, sizeof(obj));
    125 	obj.ipfo_rev = IPFILTER_VERSION;
    126 	obj.ipfo_size = sizeof(nl);
    127 	obj.ipfo_ptr = &nl;
    128 	obj.ipfo_type = IPFOBJ_NATLOOKUP;
    129 
    130 	bzero(&nl, sizeof(nl));
    131 	nl.nl_flags = IPN_TCP;
    132 
    133 	bzero(&sin, sizeof(sin));
    134 	sin.sin_family = AF_INET;
    135 	sl = sizeof(sin);
    136 	if (getsockname(0, (struct sockaddr *)&sin, &sl) == -1) {
    137 		se = errno;
    138 		perror("getsockname");
    139 		errno = se;
    140 		syslog(LOG_ERR, "getsockname: %m\n");
    141 		exit(-1);
    142 	} else {
    143 		nl.nl_inip.s_addr = sin.sin_addr.s_addr;
    144 		nl.nl_inport = sin.sin_port;
    145 	}
    146 
    147 	bzero(&sin, sizeof(sin));
    148 	sin.sin_family = AF_INET;
    149 	sl = sizeof(sin);
    150 	if (getpeername(0, (struct sockaddr *)&sin, &sl) == -1) {
    151 		se = errno;
    152 		perror("getpeername");
    153 		errno = se;
    154 		syslog(LOG_ERR, "getpeername: %m\n");
    155 		exit(-1);
    156 	} else {
    157 		nl.nl_outip.s_addr = sin.sin_addr.s_addr;
    158 		nl.nl_outport = sin.sin_port;
    159 	}
    160 
    161 	if (ioctl(fd, SIOCGNATL, &obj) == -1) {
    162 		se = errno;
    163 		perror("ioctl");
    164 		errno = se;
    165 		syslog(LOG_ERR, "ioctl: %m\n");
    166 		exit(-1);
    167 	}
    168 
    169 	sin.sin_port = nl.nl_realport;
    170 	sin.sin_addr = nl.nl_realip;
    171 	sl = sizeof(sin);
    172 
    173 	fd = socket(AF_INET, SOCK_STREAM, 0);
    174 	if (connect(fd, (struct sockaddr *)&sin, sl) == -1) {
    175 		se = errno;
    176 		perror("connect");
    177 		errno = se;
    178 		syslog(LOG_ERR, "connect: %m\n");
    179 		exit(-1);
    180 	}
    181 
    182 	(void) ioctl(fd, F_SETFL, ioctl(fd, F_GETFL, 0)|O_NONBLOCK);
    183 	(void) ioctl(0, F_SETFL, ioctl(fd, F_GETFL, 0)|O_NONBLOCK);
    184 	(void) ioctl(1, F_SETFL, ioctl(fd, F_GETFL, 0)|O_NONBLOCK);
    185 
    186 	syslog(LOG_NOTICE, "connected to %s,%d\n", inet_ntoa(sin.sin_addr),
    187 		ntohs(sin.sin_port));
    188 	if (relay(0, 1, fd) == -1) {
    189 		se = errno;
    190 		perror("relay");
    191 		errno = se;
    192 		syslog(LOG_ERR, "relay: %m\n");
    193 		exit(-1);
    194 	}
    195 	exit(0);
    196 }
    197