1145519Sdarrenr/*	$FreeBSD$	*/
2145510Sdarrenr
331183Speter/*
431183Speter * Sample transparent proxy program.
531183Speter *
631183Speter * Sample implementation of a program which intercepts a TCP connectiona and
731183Speter * just echos all data back to the origin.  Written to work via inetd as a
831183Speter * "nonwait" program running as root; ie.
931183Speter * tcpmux          stream  tcp     nowait root /usr/local/bin/proxy proxy
1031183Speter * with a NAT rue like this:
1131183Speter * rdr smc0 0/0 port 80 -> 127.0.0.1/32 port 1
1231183Speter */
1331183Speter#include <stdio.h>
1431183Speter#include <string.h>
1531183Speter#include <fcntl.h>
1631183Speter#include <syslog.h>
1731183Speter#if !defined(__SVR4) && !defined(__svr4__)
1831183Speter#include <strings.h>
1931183Speter#else
2031183Speter#include <sys/byteorder.h>
2131183Speter#endif
2231183Speter#include <sys/types.h>
2331183Speter#include <sys/time.h>
2431183Speter#include <sys/param.h>
2531183Speter#include <stdlib.h>
2631183Speter#include <unistd.h>
2731183Speter#include <stddef.h>
2831183Speter#include <sys/socket.h>
2931183Speter#include <sys/ioctl.h>
3031183Speter#if defined(sun) && (defined(__svr4__) || defined(__SVR4))
3131183Speter# include <sys/ioccom.h>
3231183Speter# include <sys/sysmacros.h>
3331183Speter#endif
3431183Speter#include <netinet/in.h>
3531183Speter#include <netinet/in_systm.h>
3631183Speter#include <netinet/ip.h>
3731183Speter#include <netinet/tcp.h>
3831183Speter#include <net/if.h>
3931183Speter#include <netdb.h>
4031183Speter#include <arpa/nameser.h>
4131183Speter#include <arpa/inet.h>
4231183Speter#include <resolv.h>
4331183Speter#include <ctype.h>
4431183Speter#include "netinet/ip_compat.h"
4531183Speter#include "netinet/ip_fil.h"
4692686Sdarrenr#include "netinet/ip_nat.h"
4792686Sdarrenr#include "netinet/ip_state.h"
4831183Speter#include "netinet/ip_proxy.h"
4931183Speter#include "netinet/ip_nat.h"
50145510Sdarrenr#include "netinet/ipl.h"
5131183Speter
5231183Speter
5331183Spetermain(argc, argv)
54255332Scy	int argc;
55255332Scy	char *argv[];
5631183Speter{
5731183Speter	struct	sockaddr_in	sin, sloc, sout;
58145510Sdarrenr	ipfobj_t	obj;
5931183Speter	natlookup_t	natlook;
6031183Speter	char	buffer[512];
6131183Speter	int	namelen, fd, n;
6231183Speter
6331183Speter	/*
6431183Speter	 * get IP# and port # of the remote end of the connection (at the
6531183Speter	 * origin).
6631183Speter	 */
6731183Speter	namelen = sizeof(sin);
6831183Speter	if (getpeername(0, (struct sockaddr *)&sin, &namelen) == -1) {
6931183Speter		perror("getpeername");
7031183Speter		exit(-1);
7131183Speter	}
7231183Speter
7331183Speter	/*
7431183Speter	 * get IP# and port # of the local end of the connection (at the
7531183Speter	 * man-in-the-middle).
7631183Speter	 */
7731183Speter	namelen = sizeof(sin);
7831183Speter	if (getsockname(0, (struct sockaddr *)&sloc, &namelen) == -1) {
7931183Speter		perror("getsockname");
8031183Speter		exit(-1);
8131183Speter	}
8231183Speter
83145510Sdarrenr	bzero((char *)&obj, sizeof(obj));
84145510Sdarrenr	obj.ipfo_rev = IPFILTER_VERSION;
85145510Sdarrenr	obj.ipfo_size = sizeof(natlook);
86145510Sdarrenr	obj.ipfo_ptr = &natlook;
87145510Sdarrenr	obj.ipfo_type = IPFOBJ_NATLOOKUP;
88145510Sdarrenr
8931183Speter	/*
9031183Speter	 * Build up the NAT natlookup structure.
9131183Speter	 */
9231183Speter	bzero((char *)&natlook, sizeof(natlook));
9331183Speter	natlook.nl_outip = sin.sin_addr;
9431183Speter	natlook.nl_inip = sloc.sin_addr;
95145510Sdarrenr	natlook.nl_flags = IPN_TCP;
96153881Sguido	natlook.nl_outport = sin.sin_port;
97153881Sguido	natlook.nl_inport = sloc.sin_port;
9831183Speter
9931183Speter	/*
10031183Speter	 * Open the NAT device and lookup the mapping pair.
10131183Speter	 */
102145510Sdarrenr	fd = open(IPNAT_NAME, O_RDONLY);
103145510Sdarrenr	if (ioctl(fd, SIOCGNATL, &obj) == -1) {
10492686Sdarrenr		perror("ioctl(SIOCGNATL)");
10531183Speter		exit(-1);
10631183Speter	}
10792686Sdarrenr
10892686Sdarrenr#define	DO_NAT_OUT
10992686Sdarrenr#ifdef	DO_NAT_OUT
11092686Sdarrenr	if (argc > 1)
11192686Sdarrenr		do_nat_out(0, 1, fd, &natlook, argv[1]);
11292686Sdarrenr#else
11392686Sdarrenr
11431183Speter	/*
11531183Speter	 * Log it
11631183Speter	 */
11731183Speter	syslog(LOG_DAEMON|LOG_INFO, "connect to %s,%d",
11834739Speter		inet_ntoa(natlook.nl_realip), ntohs(natlook.nl_realport));
11931183Speter	printf("connect to %s,%d\n",
12031183Speter		inet_ntoa(natlook.nl_realip), ntohs(natlook.nl_realport));
12131183Speter
12231183Speter	/*
12331183Speter	 * Just echo data read in from stdin to stdout
12431183Speter	 */
12531183Speter	while ((n = read(0, buffer, sizeof(buffer))) > 0)
12631183Speter		if (write(1, buffer, n) != n)
12731183Speter			break;
12831183Speter	close(0);
12992686Sdarrenr#endif
13031183Speter}
13192686Sdarrenr
13292686Sdarrenr
13392686Sdarrenr#ifdef	DO_NAT_OUT
13492686Sdarrenrdo_nat_out(in, out, fd, nlp, extif)
135255332Scy	int fd;
136255332Scy	natlookup_t *nlp;
137255332Scy	char *extif;
13892686Sdarrenr{
13992686Sdarrenr	nat_save_t ns, *nsp = &ns;
14092686Sdarrenr	struct sockaddr_in usin;
14192686Sdarrenr	u_32_t sum1, sum2, sumd;
14292686Sdarrenr	int onoff, ofd, slen;
143145510Sdarrenr	ipfobj_t obj;
14492686Sdarrenr	ipnat_t *ipn;
14592686Sdarrenr	nat_t *nat;
14692686Sdarrenr
14792686Sdarrenr	bzero((char *)&ns, sizeof(ns));
14892686Sdarrenr
14992686Sdarrenr	nat = &ns.ipn_nat;
15092686Sdarrenr	nat->nat_p = IPPROTO_TCP;
15192686Sdarrenr	nat->nat_dir = NAT_OUTBOUND;
15292686Sdarrenr	if ((extif != NULL) && (*extif != '\0')) {
153145510Sdarrenr		strncpy(nat->nat_ifnames[0], extif,
154145510Sdarrenr			sizeof(nat->nat_ifnames[0]));
155145510Sdarrenr		strncpy(nat->nat_ifnames[1], extif,
156145510Sdarrenr			sizeof(nat->nat_ifnames[1]));
157145510Sdarrenr		nat->nat_ifnames[0][sizeof(nat->nat_ifnames[0]) - 1] = '\0';
158145510Sdarrenr		nat->nat_ifnames[1][sizeof(nat->nat_ifnames[1]) - 1] = '\0';
15992686Sdarrenr	}
16092686Sdarrenr
16192686Sdarrenr	ofd = socket(AF_INET, SOCK_DGRAM, 0);
16292686Sdarrenr	bzero((char *)&usin, sizeof(usin));
16392686Sdarrenr	usin.sin_family = AF_INET;
16492686Sdarrenr	usin.sin_addr = nlp->nl_realip;
16592686Sdarrenr	usin.sin_port = nlp->nl_realport;
16692686Sdarrenr	(void) connect(ofd, (struct sockaddr *)&usin, sizeof(usin));
16792686Sdarrenr	slen = sizeof(usin);
16892686Sdarrenr	(void) getsockname(ofd, (struct sockaddr *)&usin, &slen);
16992686Sdarrenr	close(ofd);
17092686Sdarrenrprintf("local IP# to use: %s\n", inet_ntoa(usin.sin_addr));
17192686Sdarrenr
17292686Sdarrenr	if ((ofd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
17392686Sdarrenr		perror("socket");
17492686Sdarrenr	usin.sin_port = 0;
17592686Sdarrenr	if (bind(ofd, (struct sockaddr *)&usin, sizeof(usin)))
17692686Sdarrenr		perror("bind");
17792686Sdarrenr	slen = sizeof(usin);
17892686Sdarrenr	if (getsockname(ofd, (struct sockaddr *)&usin, &slen))
17992686Sdarrenr		perror("getsockname");
18092686Sdarrenrprintf("local port# to use: %d\n", ntohs(usin.sin_port));
18192686Sdarrenr
18292686Sdarrenr	nat->nat_inip = usin.sin_addr;
18392686Sdarrenr	nat->nat_outip = nlp->nl_outip;
18492686Sdarrenr	nat->nat_oip = nlp->nl_realip;
18592686Sdarrenr
18692686Sdarrenr	sum1 = LONG_SUM(ntohl(usin.sin_addr.s_addr)) + ntohs(usin.sin_port);
18792686Sdarrenr	sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)) + ntohs(nlp->nl_outport);
18892686Sdarrenr	CALC_SUMD(sum1, sum2, sumd);
18992686Sdarrenr	nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
19092686Sdarrenr	nat->nat_sumd[1] = nat->nat_sumd[0];
19192686Sdarrenr
19292686Sdarrenr	sum1 = LONG_SUM(ntohl(usin.sin_addr.s_addr));
19392686Sdarrenr	sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr));
19492686Sdarrenr	CALC_SUMD(sum1, sum2, sumd);
19592686Sdarrenr	nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
19692686Sdarrenr
19792686Sdarrenr	nat->nat_inport = usin.sin_port;
19892686Sdarrenr	nat->nat_outport = nlp->nl_outport;
19992686Sdarrenr	nat->nat_oport = nlp->nl_realport;
20092686Sdarrenr
20192686Sdarrenr	nat->nat_flags = IPN_TCPUDP;
20292686Sdarrenr
203145510Sdarrenr	bzero((char *)&obj, sizeof(obj));
204145510Sdarrenr	obj.ipfo_rev = IPFILTER_VERSION;
205145510Sdarrenr	obj.ipfo_size = sizeof(*nsp);
206145510Sdarrenr	obj.ipfo_ptr = nsp;
207145510Sdarrenr	obj.ipfo_type = IPFOBJ_NATSAVE;
208145510Sdarrenr
20992686Sdarrenr	onoff = 1;
21092686Sdarrenr	if (ioctl(fd, SIOCSTLCK, &onoff) == 0) {
211145510Sdarrenr		if (ioctl(fd, SIOCSTPUT, &obj) != 0)
21292686Sdarrenr			perror("SIOCSTPUT");
21392686Sdarrenr		onoff = 0;
21492686Sdarrenr		if (ioctl(fd, SIOCSTLCK, &onoff) != 0)
21592686Sdarrenr			perror("SIOCSTLCK");
21692686Sdarrenr	}
21792686Sdarrenr
21892686Sdarrenr	usin.sin_addr = nlp->nl_realip;
21992686Sdarrenr	usin.sin_port = nlp->nl_realport;
22092686Sdarrenrprintf("remote end for connection: %s,%d\n", inet_ntoa(usin.sin_addr),
22192686Sdarrenrntohs(usin.sin_port));
22292686Sdarrenrfflush(stdout);
22392686Sdarrenr	if (connect(ofd, (struct sockaddr *)&usin, sizeof(usin)))
22492686Sdarrenr		perror("connect");
22592686Sdarrenr
22692686Sdarrenr	relay(in, out, ofd);
22792686Sdarrenr}
22892686Sdarrenr
22992686Sdarrenr
23092686Sdarrenrrelay(in, out, net)
231255332Scy	int in, out, net;
23292686Sdarrenr{
23392686Sdarrenr	char netbuf[1024], outbuf[1024];
23492686Sdarrenr	char *nwptr, *nrptr, *owptr, *orptr;
23592686Sdarrenr	size_t nsz, osz;
23692686Sdarrenr	fd_set rd, wr;
23792686Sdarrenr	int i, n, maxfd;
23892686Sdarrenr
23992686Sdarrenr	n = 0;
24092686Sdarrenr	maxfd = in;
24192686Sdarrenr	if (out > maxfd)
24292686Sdarrenr		maxfd = out;
24392686Sdarrenr	if (net > maxfd)
24492686Sdarrenr		maxfd = net;
24592686Sdarrenr
24692686Sdarrenr	nrptr = netbuf;
24792686Sdarrenr	nwptr = netbuf;
24892686Sdarrenr	nsz = sizeof(netbuf);
24992686Sdarrenr	orptr = outbuf;
25092686Sdarrenr	owptr = outbuf;
25192686Sdarrenr	osz = sizeof(outbuf);
25292686Sdarrenr
25392686Sdarrenr	while (n >= 0) {
25492686Sdarrenr		FD_ZERO(&rd);
25592686Sdarrenr		FD_ZERO(&wr);
25692686Sdarrenr
25792686Sdarrenr		if (nrptr - netbuf < sizeof(netbuf))
25892686Sdarrenr			FD_SET(in, &rd);
25992686Sdarrenr		if (orptr - outbuf < sizeof(outbuf))
26092686Sdarrenr			FD_SET(net, &rd);
26192686Sdarrenr
26292686Sdarrenr		if (nsz < sizeof(netbuf))
26392686Sdarrenr			FD_SET(net, &wr);
26492686Sdarrenr		if (osz < sizeof(outbuf))
26592686Sdarrenr			FD_SET(out, &wr);
26692686Sdarrenr
26792686Sdarrenr		n = select(maxfd + 1, &rd, &wr, NULL, NULL);
26892686Sdarrenr
26992686Sdarrenr		if ((n > 0) && FD_ISSET(in, &rd)) {
27092686Sdarrenr			i = read(in, nrptr, sizeof(netbuf) - (nrptr - netbuf));
27192686Sdarrenr			if (i <= 0)
27292686Sdarrenr				break;
27392686Sdarrenr			nsz -= i;
27492686Sdarrenr			nrptr += i;
27592686Sdarrenr			n--;
27692686Sdarrenr		}
27792686Sdarrenr
27892686Sdarrenr		if ((n > 0) && FD_ISSET(net, &rd)) {
27992686Sdarrenr			i = read(net, orptr, sizeof(outbuf) - (orptr - outbuf));
28092686Sdarrenr			if (i <= 0)
28192686Sdarrenr				break;
28292686Sdarrenr			osz -= i;
28392686Sdarrenr			orptr += i;
28492686Sdarrenr			n--;
28592686Sdarrenr		}
28692686Sdarrenr
28792686Sdarrenr		if ((n > 0) && FD_ISSET(out, &wr)) {
28892686Sdarrenr			i = write(out, owptr, orptr - owptr);
28992686Sdarrenr			if (i <= 0)
29092686Sdarrenr				break;
29192686Sdarrenr			osz += i;
29292686Sdarrenr			if (osz == sizeof(outbuf) || owptr == orptr) {
29392686Sdarrenr				orptr = outbuf;
29492686Sdarrenr				owptr = outbuf;
29592686Sdarrenr			} else
29692686Sdarrenr				owptr += i;
29792686Sdarrenr			n--;
29892686Sdarrenr		}
29992686Sdarrenr
30092686Sdarrenr		if ((n > 0) && FD_ISSET(net, &wr)) {
30192686Sdarrenr			i = write(net, nwptr, nrptr - nwptr);
30292686Sdarrenr			if (i <= 0)
30392686Sdarrenr				break;
30492686Sdarrenr			nsz += i;
30592686Sdarrenr			if (nsz == sizeof(netbuf) || nwptr == nrptr) {
30692686Sdarrenr				nrptr = netbuf;
30792686Sdarrenr				nwptr = netbuf;
30892686Sdarrenr			} else
30992686Sdarrenr				nwptr += i;
31092686Sdarrenr		}
31192686Sdarrenr	}
31292686Sdarrenr
31392686Sdarrenr	close(net);
31492686Sdarrenr	close(out);
31592686Sdarrenr	close(in);
31692686Sdarrenr}
31792686Sdarrenr#endif
318