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