1
2/*
3 * Sample program to be used as a transparent proxy.
4 *
5 * Must be executed with permission enough to do an ioctl on /dev/ipl
6 * or equivalent.  This is just a sample and is only alpha quality.
7 * - Darren Reed (8 April 1996)
8 */
9#include <unistd.h>
10#include <stdio.h>
11#include <fcntl.h>
12#include <errno.h>
13#include <sys/types.h>
14#include <sys/time.h>
15#include <sys/syslog.h>
16#include <sys/socket.h>
17#include <sys/ioctl.h>
18#include <netinet/in.h>
19#include <net/if.h>
20#include "netinet/ip_compat.h"
21#include "netinet/ip_fil.h"
22#include "netinet/ip_nat.h"
23#include "netinet/ipl.h"
24
25#define	RELAY_BUFSZ	8192
26
27char	ibuff[RELAY_BUFSZ];
28char	obuff[RELAY_BUFSZ];
29
30int relay(ifd, ofd, rfd)
31	int ifd, ofd, rfd;
32{
33	fd_set	rfds, wfds;
34	char	*irh, *irt, *rrh, *rrt;
35	char	*iwh, *iwt, *rwh, *rwt;
36	int	nfd, n, rw;
37
38	irh = irt = ibuff;
39	iwh = iwt = obuff;
40	nfd = ifd;
41	if (nfd < ofd)
42		nfd = ofd;
43	if (nfd < rfd)
44		nfd = rfd;
45
46	while (1) {
47		FD_ZERO(&rfds);
48		FD_ZERO(&wfds);
49		if (irh > irt)
50			FD_SET(rfd, &wfds);
51		if (irh < (ibuff + RELAY_BUFSZ))
52			FD_SET(ifd, &rfds);
53		if (iwh > iwt)
54			FD_SET(ofd, &wfds);
55		if (iwh < (obuff + RELAY_BUFSZ))
56			FD_SET(rfd, &rfds);
57
58		switch ((n = select(nfd + 1, &rfds, &wfds, NULL, NULL)))
59		{
60		case -1 :
61		case 0 :
62			return(-1);
63		default :
64			if (FD_ISSET(ifd, &rfds)) {
65				rw = read(ifd, irh, ibuff + RELAY_BUFSZ - irh);
66				if (rw == -1)
67					return(-1);
68				if (rw == 0)
69					return(0);
70				irh += rw;
71				n--;
72			}
73			if (n && FD_ISSET(ofd, &wfds)) {
74				rw = write(ofd, iwt, iwh  - iwt);
75				if (rw == -1)
76					return(-1);
77				iwt += rw;
78				n--;
79			}
80			if (n && FD_ISSET(rfd, &rfds)) {
81				rw = read(rfd, iwh, obuff + RELAY_BUFSZ - iwh);
82				if (rw == -1)
83					return(-1);
84				if (rw == 0)
85					return(0);
86				iwh += rw;
87				n--;
88			}
89			if (n && FD_ISSET(rfd, &wfds)) {
90				rw = write(rfd, irt, irh  - irt);
91				if (rw == -1)
92					return(-1);
93				irt += rw;
94				n--;
95			}
96			if (irh == irt)
97				irh = irt = ibuff;
98			if (iwh == iwt)
99				iwh = iwt = obuff;
100		}
101	}
102}
103
104main(argc, argv)
105	int argc;
106	char *argv[];
107{
108	struct	sockaddr_in	sin;
109	ipfobj_t	obj;
110	natlookup_t	nl;
111	natlookup_t	*nlp = &nl;
112	int	fd, sl = sizeof(sl), se;
113
114	openlog(argv[0], LOG_PID|LOG_NDELAY, LOG_DAEMON);
115	if ((fd = open(IPNAT_NAME, O_RDONLY)) == -1) {
116		se = errno;
117		perror("open");
118		errno = se;
119		syslog(LOG_ERR, "open: %m\n");
120		exit(-1);
121	}
122
123	bzero(&obj, sizeof(obj));
124	obj.ipfo_rev = IPFILTER_VERSION;
125	obj.ipfo_size = sizeof(nl);
126	obj.ipfo_ptr = &nl;
127	obj.ipfo_type = IPFOBJ_NATLOOKUP;
128
129	bzero(&nl, sizeof(nl));
130	nl.nl_flags = IPN_TCP;
131
132	bzero(&sin, sizeof(sin));
133	sin.sin_family = AF_INET;
134	sl = sizeof(sin);
135	if (getsockname(0, (struct sockaddr *)&sin, &sl) == -1) {
136		se = errno;
137		perror("getsockname");
138		errno = se;
139		syslog(LOG_ERR, "getsockname: %m\n");
140		exit(-1);
141	} else {
142		nl.nl_inip.s_addr = sin.sin_addr.s_addr;
143		nl.nl_inport = sin.sin_port;
144	}
145
146	bzero(&sin, sizeof(sin));
147	sin.sin_family = AF_INET;
148	sl = sizeof(sin);
149	if (getpeername(0, (struct sockaddr *)&sin, &sl) == -1) {
150		se = errno;
151		perror("getpeername");
152		errno = se;
153		syslog(LOG_ERR, "getpeername: %m\n");
154		exit(-1);
155	} else {
156		nl.nl_outip.s_addr = sin.sin_addr.s_addr;
157		nl.nl_outport = sin.sin_port;
158	}
159
160	if (ioctl(fd, SIOCGNATL, &obj) == -1) {
161		se = errno;
162		perror("ioctl");
163		errno = se;
164		syslog(LOG_ERR, "ioctl: %m\n");
165		exit(-1);
166	}
167
168	sin.sin_port = nl.nl_realport;
169	sin.sin_addr = nl.nl_realip;
170	sl = sizeof(sin);
171
172	fd = socket(AF_INET, SOCK_STREAM, 0);
173	if (connect(fd, (struct sockaddr *)&sin, sl) == -1) {
174		se = errno;
175		perror("connect");
176		errno = se;
177		syslog(LOG_ERR, "connect: %m\n");
178		exit(-1);
179	}
180
181	(void) ioctl(fd, F_SETFL, ioctl(fd, F_GETFL, 0)|O_NONBLOCK);
182	(void) ioctl(0, F_SETFL, ioctl(fd, F_GETFL, 0)|O_NONBLOCK);
183	(void) ioctl(1, F_SETFL, ioctl(fd, F_GETFL, 0)|O_NONBLOCK);
184
185	syslog(LOG_NOTICE, "connected to %s,%d\n", inet_ntoa(sin.sin_addr),
186		ntohs(sin.sin_port));
187	if (relay(0, 1, fd) == -1) {
188		se = errno;
189		perror("relay");
190		errno = se;
191		syslog(LOG_ERR, "relay: %m\n");
192		exit(-1);
193	}
194	exit(0);
195}
196