1 /*	$FreeBSD$	*/
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