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