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
main(argc,argv)52 main(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
do_nat_out(in,out,fd,nlp,extif)133 do_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);
169 printf("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");
179 printf("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;
219 printf("remote end for connection: %s,%d\n", inet_ntoa(usin.sin_addr),
220 ntohs(usin.sin_port));
221 fflush(stdout);
222 if (connect(ofd, (struct sockaddr *)&usin, sizeof(usin)))
223 perror("connect");
224
225 relay(in, out, ofd);
226 }
227
228
relay(in,out,net)229 relay(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