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