187 lines
3.8 KiB
C
187 lines
3.8 KiB
C
/* $NetBSD: relay.c,v 1.3 2001/06/07 14:15:39 mrg Exp $ */
|
|
|
|
/*
|
|
* 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 <unistd.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/ioctl.h>
|
|
#include <netinet/in.h>
|
|
#include <net/if.h>
|
|
#include <sys/socket.h>
|
|
#include "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;
|
|
natlookup_t *nlp = &nl;
|
|
int fd, sl = sizeof(sl), se;
|
|
|
|
openlog(argv[0], LOG_PID|LOG_NDELAY, LOG_DAEMON);
|
|
if ((fd = open("/dev/ipnat", O_RDONLY)) == -1) {
|
|
se = errno;
|
|
perror("open");
|
|
errno = se;
|
|
syslog(LOG_ERR, "open: %m\n");
|
|
exit(-1);
|
|
}
|
|
|
|
bzero(&nl, sizeof(nl);
|
|
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(fd, SIOCGNATL, &nlp) == -1) {
|
|
se = errno;
|
|
perror("ioctl");
|
|
errno = se;
|
|
syslog(LOG_ERR, "ioctl: %m\n");
|
|
exit(-1);
|
|
}
|
|
|
|
sin.sin_port = nl.nl_realport;
|
|
sin.sin_addr = nl.nl_realip;
|
|
sl = sizeof(sin);
|
|
|
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
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);
|
|
}
|