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