xref: /freebsd/share/examples/ipfilter/samples/relay.c (revision 2a63c3be158216222d89a073dcbd6a72ee4aab5a)
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 
27 char	ibuff[RELAY_BUFSZ];
28 char	obuff[RELAY_BUFSZ];
29 
relay(ifd,ofd,rfd)30 int 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 
main(argc,argv)104 main(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