1145519Sdarrenr/*	$FreeBSD$	*/
2145510Sdarrenr
3145510Sdarrenr/*
4145510Sdarrenr * Sample program to be used as a transparent proxy.
5145510Sdarrenr *
6145510Sdarrenr * Must be executed with permission enough to do an ioctl on /dev/ipl
7145510Sdarrenr * or equivalent.  This is just a sample and is only alpha quality.
8145510Sdarrenr * - Darren Reed (8 April 1996)
9145510Sdarrenr */
10145510Sdarrenr#include <unistd.h>
11145510Sdarrenr#include <stdio.h>
12145510Sdarrenr#include <fcntl.h>
13145510Sdarrenr#include <errno.h>
14145510Sdarrenr#include <sys/types.h>
15145510Sdarrenr#include <sys/time.h>
16145510Sdarrenr#include <sys/syslog.h>
17145510Sdarrenr#include <sys/socket.h>
18145510Sdarrenr#include <sys/ioctl.h>
19145510Sdarrenr#include <netinet/in.h>
20145510Sdarrenr#include <net/if.h>
21153881Sguido#include "netinet/ip_compat.h"
22153881Sguido#include "netinet/ip_fil.h"
23153881Sguido#include "netinet/ip_nat.h"
24145554Sdarrenr#include "netinet/ipl.h"
25145510Sdarrenr
26145510Sdarrenr#define	RELAY_BUFSZ	8192
27145510Sdarrenr
28145510Sdarrenrchar	ibuff[RELAY_BUFSZ];
29145510Sdarrenrchar	obuff[RELAY_BUFSZ];
30145510Sdarrenr
31145510Sdarrenrint relay(ifd, ofd, rfd)
32255332Scy	int ifd, ofd, rfd;
33145510Sdarrenr{
34145510Sdarrenr	fd_set	rfds, wfds;
35145510Sdarrenr	char	*irh, *irt, *rrh, *rrt;
36145510Sdarrenr	char	*iwh, *iwt, *rwh, *rwt;
37145510Sdarrenr	int	nfd, n, rw;
38145510Sdarrenr
39145510Sdarrenr	irh = irt = ibuff;
40145510Sdarrenr	iwh = iwt = obuff;
41145510Sdarrenr	nfd = ifd;
42145510Sdarrenr	if (nfd < ofd)
43145510Sdarrenr		nfd = ofd;
44145510Sdarrenr	if (nfd < rfd)
45145510Sdarrenr		nfd = rfd;
46145510Sdarrenr
47145510Sdarrenr	while (1) {
48145510Sdarrenr		FD_ZERO(&rfds);
49145510Sdarrenr		FD_ZERO(&wfds);
50145510Sdarrenr		if (irh > irt)
51145510Sdarrenr			FD_SET(rfd, &wfds);
52145510Sdarrenr		if (irh < (ibuff + RELAY_BUFSZ))
53145510Sdarrenr			FD_SET(ifd, &rfds);
54145510Sdarrenr		if (iwh > iwt)
55145510Sdarrenr			FD_SET(ofd, &wfds);
56145510Sdarrenr		if (iwh < (obuff + RELAY_BUFSZ))
57145510Sdarrenr			FD_SET(rfd, &rfds);
58145510Sdarrenr
59145510Sdarrenr		switch ((n = select(nfd + 1, &rfds, &wfds, NULL, NULL)))
60145510Sdarrenr		{
61145510Sdarrenr		case -1 :
62145510Sdarrenr		case 0 :
63145510Sdarrenr			return -1;
64145510Sdarrenr		default :
65145510Sdarrenr			if (FD_ISSET(ifd, &rfds)) {
66145510Sdarrenr				rw = read(ifd, irh, ibuff + RELAY_BUFSZ - irh);
67145510Sdarrenr				if (rw == -1)
68145510Sdarrenr					return -1;
69145510Sdarrenr				if (rw == 0)
70145510Sdarrenr					return 0;
71145510Sdarrenr				irh += rw;
72145510Sdarrenr				n--;
73145510Sdarrenr			}
74145510Sdarrenr			if (n && FD_ISSET(ofd, &wfds)) {
75145510Sdarrenr				rw = write(ofd, iwt, iwh  - iwt);
76145510Sdarrenr				if (rw == -1)
77145510Sdarrenr					return -1;
78145510Sdarrenr				iwt += rw;
79145510Sdarrenr				n--;
80145510Sdarrenr			}
81145510Sdarrenr			if (n && FD_ISSET(rfd, &rfds)) {
82145510Sdarrenr				rw = read(rfd, iwh, obuff + RELAY_BUFSZ - iwh);
83145510Sdarrenr				if (rw == -1)
84145510Sdarrenr					return -1;
85145510Sdarrenr				if (rw == 0)
86145510Sdarrenr					return 0;
87145510Sdarrenr				iwh += rw;
88145510Sdarrenr				n--;
89145510Sdarrenr			}
90145510Sdarrenr			if (n && FD_ISSET(rfd, &wfds)) {
91145510Sdarrenr				rw = write(rfd, irt, irh  - irt);
92145510Sdarrenr				if (rw == -1)
93145510Sdarrenr					return -1;
94145510Sdarrenr				irt += rw;
95145510Sdarrenr				n--;
96145510Sdarrenr			}
97145510Sdarrenr			if (irh == irt)
98145510Sdarrenr				irh = irt = ibuff;
99145510Sdarrenr			if (iwh == iwt)
100145510Sdarrenr				iwh = iwt = obuff;
101145510Sdarrenr		}
102145510Sdarrenr	}
103145510Sdarrenr}
104145510Sdarrenr
105145510Sdarrenrmain(argc, argv)
106255332Scy	int argc;
107255332Scy	char *argv[];
108145510Sdarrenr{
109145510Sdarrenr	struct	sockaddr_in	sin;
110145510Sdarrenr	ipfobj_t	obj;
111145510Sdarrenr	natlookup_t	nl;
112145510Sdarrenr	natlookup_t	*nlp = &nl;
113145510Sdarrenr	int	fd, sl = sizeof(sl), se;
114145510Sdarrenr
115145510Sdarrenr	openlog(argv[0], LOG_PID|LOG_NDELAY, LOG_DAEMON);
116145510Sdarrenr	if ((fd = open(IPNAT_NAME, O_RDONLY)) == -1) {
117145510Sdarrenr		se = errno;
118145510Sdarrenr		perror("open");
119145510Sdarrenr		errno = se;
120145510Sdarrenr		syslog(LOG_ERR, "open: %m\n");
121145510Sdarrenr		exit(-1);
122145510Sdarrenr	}
123145510Sdarrenr
124145510Sdarrenr	bzero(&obj, sizeof(obj));
125145510Sdarrenr	obj.ipfo_rev = IPFILTER_VERSION;
126145510Sdarrenr	obj.ipfo_size = sizeof(nl);
127145510Sdarrenr	obj.ipfo_ptr = &nl;
128145510Sdarrenr	obj.ipfo_type = IPFOBJ_NATLOOKUP;
129145510Sdarrenr
130145510Sdarrenr	bzero(&nl, sizeof(nl));
131145510Sdarrenr	nl.nl_flags = IPN_TCP;
132145510Sdarrenr
133145510Sdarrenr	bzero(&sin, sizeof(sin));
134145510Sdarrenr	sin.sin_family = AF_INET;
135145510Sdarrenr	sl = sizeof(sin);
136145510Sdarrenr	if (getsockname(0, (struct sockaddr *)&sin, &sl) == -1) {
137145510Sdarrenr		se = errno;
138145510Sdarrenr		perror("getsockname");
139145510Sdarrenr		errno = se;
140145510Sdarrenr		syslog(LOG_ERR, "getsockname: %m\n");
141145510Sdarrenr		exit(-1);
142145510Sdarrenr	} else {
143145510Sdarrenr		nl.nl_inip.s_addr = sin.sin_addr.s_addr;
144145510Sdarrenr		nl.nl_inport = sin.sin_port;
145145510Sdarrenr	}
146145510Sdarrenr
147145510Sdarrenr	bzero(&sin, sizeof(sin));
148145510Sdarrenr	sin.sin_family = AF_INET;
149145510Sdarrenr	sl = sizeof(sin);
150145510Sdarrenr	if (getpeername(0, (struct sockaddr *)&sin, &sl) == -1) {
151145510Sdarrenr		se = errno;
152145510Sdarrenr		perror("getpeername");
153145510Sdarrenr		errno = se;
154145510Sdarrenr		syslog(LOG_ERR, "getpeername: %m\n");
155145510Sdarrenr		exit(-1);
156145510Sdarrenr	} else {
157145510Sdarrenr		nl.nl_outip.s_addr = sin.sin_addr.s_addr;
158145510Sdarrenr		nl.nl_outport = sin.sin_port;
159145510Sdarrenr	}
160145510Sdarrenr
161145510Sdarrenr	if (ioctl(fd, SIOCGNATL, &obj) == -1) {
162145510Sdarrenr		se = errno;
163145510Sdarrenr		perror("ioctl");
164145510Sdarrenr		errno = se;
165145510Sdarrenr		syslog(LOG_ERR, "ioctl: %m\n");
166145510Sdarrenr		exit(-1);
167145510Sdarrenr	}
168145510Sdarrenr
169145510Sdarrenr	sin.sin_port = nl.nl_realport;
170145510Sdarrenr	sin.sin_addr = nl.nl_realip;
171145510Sdarrenr	sl = sizeof(sin);
172145510Sdarrenr
173145510Sdarrenr	fd = socket(AF_INET, SOCK_STREAM, 0);
174145510Sdarrenr	if (connect(fd, (struct sockaddr *)&sin, sl) == -1) {
175145510Sdarrenr		se = errno;
176145510Sdarrenr		perror("connect");
177145510Sdarrenr		errno = se;
178145510Sdarrenr		syslog(LOG_ERR, "connect: %m\n");
179145510Sdarrenr		exit(-1);
180145510Sdarrenr	}
181145510Sdarrenr
182145510Sdarrenr	(void) ioctl(fd, F_SETFL, ioctl(fd, F_GETFL, 0)|O_NONBLOCK);
183145510Sdarrenr	(void) ioctl(0, F_SETFL, ioctl(fd, F_GETFL, 0)|O_NONBLOCK);
184145510Sdarrenr	(void) ioctl(1, F_SETFL, ioctl(fd, F_GETFL, 0)|O_NONBLOCK);
185145510Sdarrenr
186145510Sdarrenr	syslog(LOG_NOTICE, "connected to %s,%d\n", inet_ntoa(sin.sin_addr),
187145510Sdarrenr		ntohs(sin.sin_port));
188145510Sdarrenr	if (relay(0, 1, fd) == -1) {
189145510Sdarrenr		se = errno;
190145510Sdarrenr		perror("relay");
191145510Sdarrenr		errno = se;
192145510Sdarrenr		syslog(LOG_ERR, "relay: %m\n");
193145510Sdarrenr		exit(-1);
194145510Sdarrenr	}
195145510Sdarrenr	exit(0);
196145510Sdarrenr}
197