1/*	$NetBSD$	*/
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
28char	ibuff[RELAY_BUFSZ];
29char	obuff[RELAY_BUFSZ];
30
31int 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
105main(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