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