1 1.1 christos /* $NetBSD: proxy.c,v 1.1.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