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