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 53 main(argc, argv) 54 int argc; 55 char *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 134 do_nat_out(in, out, fd, nlp, extif) 135 int fd; 136 natlookup_t *nlp; 137 char *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); 170 printf("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"); 180 printf("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; 220 printf("remote end for connection: %s,%d\n", inet_ntoa(usin.sin_addr), 221 ntohs(usin.sin_port)); 222 fflush(stdout); 223 if (connect(ofd, (struct sockaddr *)&usin, sizeof(usin))) 224 perror("connect"); 225 226 relay(in, out, ofd); 227 } 228 229 230 relay(in, out, net) 231 int 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