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