blob: f55c675324648312ba6b6c05c529bd9445693ae1 [file] [log] [blame] [raw]
/*
* Sample program to be used as a transparent proxy.
*
* Must be executed with permission enough to do an ioctl on /dev/ipl
* or equivalent. This is just a sample and is only alpha quality.
* - Darren Reed (8 April 1996)
*/
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.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_proxy.h"
#include "netinet/ip_nat.h"
#define RELAY_BUFSZ 8192
char ibuff[RELAY_BUFSZ];
char obuff[RELAY_BUFSZ];
int relay(ifd, ofd, rfd)
int ifd, ofd, rfd;
{
fd_set rfds, wfds;
char *irh, *irt, *rrh, *rrt;
char *iwh, *iwt, *rwh, *rwt;
int nfd, n, rw;
irh = irt = ibuff;
iwh = iwt = obuff;
nfd = ifd;
if (nfd < ofd)
nfd = ofd;
if (nfd < rfd)
nfd = rfd;
while (1) {
FD_ZERO(&rfds);
FD_ZERO(&wfds);
if (irh > irt)
FD_SET(rfd, &wfds);
if (irh < (ibuff + RELAY_BUFSZ))
FD_SET(ifd, &rfds);
if (iwh > iwt)
FD_SET(ofd, &wfds);
if (iwh < (obuff + RELAY_BUFSZ))
FD_SET(rfd, &rfds);
switch ((n = select(nfd + 1, &rfds, &wfds, NULL, NULL)))
{
case -1 :
case 0 :
return -1;
default :
if (FD_ISSET(ifd, &rfds)) {
rw = read(ifd, irh, ibuff + RELAY_BUFSZ - irh);
if (rw == -1)
return -1;
if (rw == 0)
return 0;
irh += rw;
n--;
}
if (n && FD_ISSET(ofd, &wfds)) {
rw = write(ofd, iwt, iwh - iwt);
if (rw == -1)
return -1;
iwt += rw;
n--;
}
if (n && FD_ISSET(rfd, &rfds)) {
rw = read(rfd, iwh, obuff + RELAY_BUFSZ - iwh);
if (rw == -1)
return -1;
if (rw == 0)
return 0;
iwh += rw;
n--;
}
if (n && FD_ISSET(rfd, &wfds)) {
rw = write(rfd, irt, irh - irt);
if (rw == -1)
return -1;
irt += rw;
n--;
}
if (irh == irt)
irh = irt = ibuff;
if (iwh == iwt)
iwh = iwt = obuff;
}
}
}
main(argc, argv)
int argc;
char *argv[];
{
struct sockaddr_in sin;
natlookup_t nl, *nlp = &nl;
nattrans_t nattn, *nattnp = &nattn;
int nfd, fd, sl = sizeof(sl), se;
openlog(argv[0], LOG_PID|LOG_NDELAY, LOG_DAEMON);
if ((nfd = open(IPL_NAT, O_RDWR)) == -1) {
se = errno;
perror("open");
errno = se;
syslog(LOG_ERR, "open: %m\n");
exit(-1);
}
bzero(&nl, sizeof(nl));
bzero(&nattn, sizeof(nattn));
nl.nl_flags = IPN_TCP;
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sl = sizeof(sin);
if (getsockname(0, (struct sockaddr *)&sin, &sl) == -1) {
se = errno;
perror("getsockname");
errno = se;
syslog(LOG_ERR, "getsockname: %m\n");
exit(-1);
} else {
nl.nl_inip.s_addr = sin.sin_addr.s_addr;
nl.nl_inport = sin.sin_port;
}
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sl = sizeof(sin);
if (getpeername(0, (struct sockaddr *)&sin, &sl) == -1) {
se = errno;
perror("getpeername");
errno = se;
syslog(LOG_ERR, "getpeername: %m\n");
exit(-1);
} else {
nl.nl_outip.s_addr = sin.sin_addr.s_addr;
nl.nl_outport = sin.sin_port;
}
if (ioctl(nfd, SIOCGNATL, &nlp) == -1) {
se = errno;
perror("ioctl");
errno = se;
syslog(LOG_ERR, "ioctl: %m\n");
exit(-1);
}
/*
* This is to work out which outgoing IP address will be used.
*/
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1) {
se = errno;
perror("socket");
errno = se;
syslog(LOG_ERR, "socket: %m\n");
exit(-1);
}
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr = nl.nl_realip;
sin.sin_port = nl.nl_realport;
if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
se = errno;
perror("connect");
errno = se;
syslog(LOG_ERR, "connect: %m\n");
exit(-1);
}
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sl = sizeof(sin);
if (getsockname(fd, (struct sockaddr *)&sin, &sl) == -1) {
se = errno;
perror("getsockname");
errno = se;
syslog(LOG_ERR, "getsockname: %m\n");
exit(-1);
} else {
nattn.tn_dip = nl.nl_realip;
nattn.tn_lip = sin.sin_addr;
nattn.tn_sip = nl.nl_outip;
nattn.tn_dport = nl.nl_realport;
nattn.tn_sport = nl.nl_outport;
}
close(fd);
fd = -1;
/*
* Now create the real outgoing socket.
*/
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) {
se = errno;
perror("socket");
errno = se;
syslog(LOG_ERR, "socket: %m\n");
exit(-1);
}
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr = nattn.tn_lip;
sin.sin_port = nl.nl_outport;
if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) == 0) {
nattn.tn_lport = sin.sin_port;
syslog(LOG_INFO, "from %s,%hu",
inet_ntoa(nl.nl_outip), nl.nl_outport);
syslog(LOG_INFO, "to %s,%hu",
inet_ntoa(nl.nl_realip), nl.nl_realport);
syslog(LOG_INFO, "via %s,%hu",
inet_ntoa(nattn.tn_lip), nattn.tn_lport);
if (ioctl(nfd, SIOCTRANS, &nattnp) == -1) {
se = errno;
perror("ioctl(SIOCTRANS)");
errno = se;
syslog(LOG_ERR, "ioctl(SIOCTRANS):%d %m\n", errno);
exit(-1);
}
} else {
se = errno;
perror("bind");
errno = se;
syslog(LOG_ERR, "bind: %m\n");
exit(-1);
}
close(nfd);
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = nl.nl_realport;
sin.sin_addr = nl.nl_realip;
sl = sizeof(sin);
syslog(LOG_NOTICE, "connecting to %s,%d\n", inet_ntoa(sin.sin_addr),
ntohs(sin.sin_port));
sleep(3);
if (connect(fd, (struct sockaddr *)&sin, sl) == -1) {
se = errno;
perror("connect");
errno = se;
syslog(LOG_ERR, "connect: %m\n");
exit(-1);
}
(void) ioctl(fd, F_SETFL, ioctl(fd, F_GETFL, 0)|O_NONBLOCK);
(void) ioctl(0, F_SETFL, ioctl(fd, F_GETFL, 0)|O_NONBLOCK);
(void) ioctl(1, F_SETFL, ioctl(fd, F_GETFL, 0)|O_NONBLOCK);
syslog(LOG_NOTICE, "connected to %s,%d\n", inet_ntoa(sin.sin_addr),
ntohs(sin.sin_port));
if (relay(0, 1, fd) == -1) {
se = errno;
perror("relay");
errno = se;
syslog(LOG_ERR, "relay: %m\n");
exit(-1);
}
exit(0);
}