312 lines
7.0 KiB
C
312 lines
7.0 KiB
C
/* $NetBSD: proxy.c,v 1.4 2004/07/23 05:39:04 martti Exp $ */
|
|
|
|
/*
|
|
* Sample transparent proxy program.
|
|
*
|
|
* Sample implementation of a program which intercepts a TCP connectiona and
|
|
* just echos all data back to the origin. Written to work via inetd as a
|
|
* "nonwait" program running as root; ie.
|
|
* tcpmux stream tcp nowait root /usr/local/bin/proxy proxy
|
|
* with a NAT rue like this:
|
|
* rdr smc0 0/0 port 80 -> 127.0.0.1/32 port 1
|
|
*/
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <syslog.h>
|
|
#if !defined(__SVR4) && !defined(__svr4__)
|
|
#include <strings.h>
|
|
#else
|
|
#include <sys/byteorder.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/param.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <stddef.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#if defined(sun) && (defined(__svr4__) || defined(__SVR4))
|
|
# include <sys/ioccom.h>
|
|
# include <sys/sysmacros.h>
|
|
#endif
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/tcp.h>
|
|
#include <net/if.h>
|
|
#include <netdb.h>
|
|
#include <arpa/nameser.h>
|
|
#include <arpa/inet.h>
|
|
#include <resolv.h>
|
|
#include <ctype.h>
|
|
#include "netinet/ip_compat.h"
|
|
#include "netinet/ip_fil.h"
|
|
#include "netinet/ip_nat.h"
|
|
#include "netinet/ip_state.h"
|
|
#include "netinet/ip_proxy.h"
|
|
#include "netinet/ip_nat.h"
|
|
#include "netinet/ipl.h"
|
|
|
|
|
|
main(argc, argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
struct sockaddr_in sin, sloc, sout;
|
|
ipfobj_t obj;
|
|
natlookup_t natlook;
|
|
natlookup_t *natlookp = &natlook;
|
|
char buffer[512];
|
|
int namelen, fd, n;
|
|
|
|
/*
|
|
* get IP# and port # of the remote end of the connection (at the
|
|
* origin).
|
|
*/
|
|
namelen = sizeof(sin);
|
|
if (getpeername(0, (struct sockaddr *)&sin, &namelen) == -1) {
|
|
perror("getpeername");
|
|
exit(-1);
|
|
}
|
|
|
|
/*
|
|
* get IP# and port # of the local end of the connection (at the
|
|
* man-in-the-middle).
|
|
*/
|
|
namelen = sizeof(sin);
|
|
if (getsockname(0, (struct sockaddr *)&sloc, &namelen) == -1) {
|
|
perror("getsockname");
|
|
exit(-1);
|
|
}
|
|
|
|
bzero((char *)&obj, sizeof(obj));
|
|
obj.ipfo_rev = IPFILTER_VERSION;
|
|
obj.ipfo_size = sizeof(natlook);
|
|
obj.ipfo_ptr = &natlook;
|
|
obj.ipfo_type = IPFOBJ_NATLOOKUP;
|
|
|
|
/*
|
|
* Build up the NAT natlookup structure.
|
|
*/
|
|
bzero((char *)&natlook, sizeof(natlook));
|
|
natlook.nl_outip = sin.sin_addr;
|
|
natlook.nl_inip = sloc.sin_addr;
|
|
natlook.nl_flags = IPN_TCP;
|
|
natlook.nl_outport = ntohs(sin.sin_port);
|
|
natlook.nl_inport = ntohs(sloc.sin_port);
|
|
|
|
/*
|
|
* Open the NAT device and lookup the mapping pair.
|
|
*/
|
|
fd = open(IPNAT_NAME, O_RDONLY);
|
|
if (ioctl(fd, SIOCGNATL, &natlookp) == -1) {
|
|
perror("ioctl(SIOCGNATL)");
|
|
exit(-1);
|
|
}
|
|
|
|
#define DO_NAT_OUT
|
|
#ifdef DO_NAT_OUT
|
|
if (argc > 1)
|
|
do_nat_out(0, 1, fd, &natlook, argv[1]);
|
|
#else
|
|
|
|
/*
|
|
* Log it
|
|
*/
|
|
syslog(LOG_DAEMON|LOG_INFO, "connect to %s,%d",
|
|
inet_ntoa(natlook.nl_realip), ntohs(natlook.nl_realport));
|
|
printf("connect to %s,%d\n",
|
|
inet_ntoa(natlook.nl_realip), ntohs(natlook.nl_realport));
|
|
|
|
/*
|
|
* Just echo data read in from stdin to stdout
|
|
*/
|
|
while ((n = read(0, buffer, sizeof(buffer))) > 0)
|
|
if (write(1, buffer, n) != n)
|
|
break;
|
|
close(0);
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef DO_NAT_OUT
|
|
do_nat_out(in, out, fd, nlp, extif)
|
|
int fd;
|
|
natlookup_t *nlp;
|
|
char *extif;
|
|
{
|
|
nat_save_t ns, *nsp = &ns;
|
|
struct sockaddr_in usin;
|
|
u_32_t sum1, sum2, sumd;
|
|
int onoff, ofd, slen;
|
|
ipnat_t *ipn;
|
|
nat_t *nat;
|
|
|
|
bzero((char *)&ns, sizeof(ns));
|
|
|
|
nat = &ns.ipn_nat;
|
|
nat->nat_p = IPPROTO_TCP;
|
|
nat->nat_dir = NAT_OUTBOUND;
|
|
if ((extif != NULL) && (*extif != '\0')) {
|
|
strncpy(nat->nat_ifnames[0], extif,
|
|
sizeof(nat->nat_ifnames[0]));
|
|
strncpy(nat->nat_ifnames[1], extif,
|
|
sizeof(nat->nat_ifnames[1]));
|
|
nat->nat_ifnames[0][sizeof(nat->nat_ifnames[0]) - 1] = '\0';
|
|
nat->nat_ifnames[1][sizeof(nat->nat_ifnames[1]) - 1] = '\0';
|
|
}
|
|
|
|
ofd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
bzero((char *)&usin, sizeof(usin));
|
|
usin.sin_family = AF_INET;
|
|
usin.sin_addr = nlp->nl_realip;
|
|
usin.sin_port = nlp->nl_realport;
|
|
(void) connect(ofd, (struct sockaddr *)&usin, sizeof(usin));
|
|
slen = sizeof(usin);
|
|
(void) getsockname(ofd, (struct sockaddr *)&usin, &slen);
|
|
close(ofd);
|
|
printf("local IP# to use: %s\n", inet_ntoa(usin.sin_addr));
|
|
|
|
if ((ofd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
|
perror("socket");
|
|
usin.sin_port = 0;
|
|
if (bind(ofd, (struct sockaddr *)&usin, sizeof(usin)))
|
|
perror("bind");
|
|
slen = sizeof(usin);
|
|
if (getsockname(ofd, (struct sockaddr *)&usin, &slen))
|
|
perror("getsockname");
|
|
printf("local port# to use: %d\n", ntohs(usin.sin_port));
|
|
|
|
nat->nat_inip = usin.sin_addr;
|
|
nat->nat_outip = nlp->nl_outip;
|
|
nat->nat_oip = nlp->nl_realip;
|
|
|
|
sum1 = LONG_SUM(ntohl(usin.sin_addr.s_addr)) + ntohs(usin.sin_port);
|
|
sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)) + ntohs(nlp->nl_outport);
|
|
CALC_SUMD(sum1, sum2, sumd);
|
|
nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
|
|
nat->nat_sumd[1] = nat->nat_sumd[0];
|
|
|
|
sum1 = LONG_SUM(ntohl(usin.sin_addr.s_addr));
|
|
sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr));
|
|
CALC_SUMD(sum1, sum2, sumd);
|
|
nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
|
|
|
|
nat->nat_inport = usin.sin_port;
|
|
nat->nat_outport = nlp->nl_outport;
|
|
nat->nat_oport = nlp->nl_realport;
|
|
|
|
nat->nat_flags = IPN_TCPUDP;
|
|
|
|
onoff = 1;
|
|
if (ioctl(fd, SIOCSTLCK, &onoff) == 0) {
|
|
if (ioctl(fd, SIOCSTPUT, &nsp) != 0)
|
|
perror("SIOCSTPUT");
|
|
onoff = 0;
|
|
if (ioctl(fd, SIOCSTLCK, &onoff) != 0)
|
|
perror("SIOCSTLCK");
|
|
}
|
|
|
|
usin.sin_addr = nlp->nl_realip;
|
|
usin.sin_port = nlp->nl_realport;
|
|
printf("remote end for connection: %s,%d\n", inet_ntoa(usin.sin_addr),
|
|
ntohs(usin.sin_port));
|
|
fflush(stdout);
|
|
if (connect(ofd, (struct sockaddr *)&usin, sizeof(usin)))
|
|
perror("connect");
|
|
|
|
relay(in, out, ofd);
|
|
}
|
|
|
|
|
|
relay(in, out, net)
|
|
int in, out, net;
|
|
{
|
|
char netbuf[1024], outbuf[1024];
|
|
char *nwptr, *nrptr, *owptr, *orptr;
|
|
size_t nsz, osz;
|
|
fd_set rd, wr;
|
|
int i, n, maxfd;
|
|
|
|
n = 0;
|
|
maxfd = in;
|
|
if (out > maxfd)
|
|
maxfd = out;
|
|
if (net > maxfd)
|
|
maxfd = net;
|
|
|
|
nrptr = netbuf;
|
|
nwptr = netbuf;
|
|
nsz = sizeof(netbuf);
|
|
orptr = outbuf;
|
|
owptr = outbuf;
|
|
osz = sizeof(outbuf);
|
|
|
|
while (n >= 0) {
|
|
FD_ZERO(&rd);
|
|
FD_ZERO(&wr);
|
|
|
|
if (nrptr - netbuf < sizeof(netbuf))
|
|
FD_SET(in, &rd);
|
|
if (orptr - outbuf < sizeof(outbuf))
|
|
FD_SET(net, &rd);
|
|
|
|
if (nsz < sizeof(netbuf))
|
|
FD_SET(net, &wr);
|
|
if (osz < sizeof(outbuf))
|
|
FD_SET(out, &wr);
|
|
|
|
n = select(maxfd + 1, &rd, &wr, NULL, NULL);
|
|
|
|
if ((n > 0) && FD_ISSET(in, &rd)) {
|
|
i = read(in, nrptr, sizeof(netbuf) - (nrptr - netbuf));
|
|
if (i <= 0)
|
|
break;
|
|
nsz -= i;
|
|
nrptr += i;
|
|
n--;
|
|
}
|
|
|
|
if ((n > 0) && FD_ISSET(net, &rd)) {
|
|
i = read(net, orptr, sizeof(outbuf) - (orptr - outbuf));
|
|
if (i <= 0)
|
|
break;
|
|
osz -= i;
|
|
orptr += i;
|
|
n--;
|
|
}
|
|
|
|
if ((n > 0) && FD_ISSET(out, &wr)) {
|
|
i = write(out, owptr, orptr - owptr);
|
|
if (i <= 0)
|
|
break;
|
|
osz += i;
|
|
if (osz == sizeof(outbuf) || owptr == orptr) {
|
|
orptr = outbuf;
|
|
owptr = outbuf;
|
|
} else
|
|
owptr += i;
|
|
n--;
|
|
}
|
|
|
|
if ((n > 0) && FD_ISSET(net, &wr)) {
|
|
i = write(net, nwptr, nrptr - nwptr);
|
|
if (i <= 0)
|
|
break;
|
|
nsz += i;
|
|
if (nsz == sizeof(netbuf) || nwptr == nrptr) {
|
|
nrptr = netbuf;
|
|
nwptr = netbuf;
|
|
} else
|
|
nwptr += i;
|
|
}
|
|
}
|
|
|
|
close(net);
|
|
close(out);
|
|
close(in);
|
|
}
|
|
#endif
|