Skip to content

Instantly share code, notes, and snippets.

@kost
Forked from feuvan/datapipe6.c
Created January 22, 2012 06:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kost/1655888 to your computer and use it in GitHub Desktop.
Save kost/1655888 to your computer and use it in GitHub Desktop.
Datapipe6 - ipv6-aware fork of datapipe.c
/*
* Datapipe6 - ipv6-aware fork of datapipe.c
* Sample usage:
* datapipe6 localipv4addr 23 remoteipv6addr 23
*
*
* Written by feuvan <feuvan@feuvan.net>
* Original ipv4 version by Jeff Lawson <jlawson@bovine.net>
* See statement below
*
* ==== Original statement of datapipe.c ====
*
* Datapipe - Create a listen socket to pipe connections to another
* machine/port. 'localport' accepts connections on the machine running
* datapipe, which will connect to 'remoteport' on 'remotehost'.
* It will fork itself into the background on non-Windows machines.
*
* This implementation of the traditional "datapipe" does not depend on
* forking to handle multiple simultaneous clients, and instead is able
* to do all processing from within a single process, making it ideal
* for low-memory environments. The elimination of the fork also
* allows it to be used in environments without fork, such as Win32.
*
* This implementation also differs from most others in that it allows
* the specific IP address of the interface to listen on to be specified.
* This is useful for machines that have multiple IP addresses. The
* specified listening address will also be used for making the outgoing
* connections on.
*
* Note that select() is not used to perform writability testing on the
* outgoing sockets, so conceivably other connections might have delayed
* responses if any of the connected clients or the connection to the
* target machine is slow enough to allow its outgoing buffer to fill
* to capacity.
*
* Compile with:
* cc -O -o datapipe datapipe.c
* On Solaris/SunOS, compile with:
* gcc -Wall datapipe.c -lsocket -lnsl -o datapipe
* On Windows compile with:
* bcc32 /w datapipe.c (Borland C++)
* cl /W3 datapipe.c wsock32.lib (Microsoft Visual C++)
*
* Run as:
* datapipe localhost localport remoteport remotehost
*
*
* written by Jeff Lawson <jlawson@bovine.net>
* inspired by code originally by Todd Vierling, 1995.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#if defined(__WIN32__) || defined(WIN32) || defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#define bzero(p, l) memset(p, 0, l)
#define bcopy(s, t, l) memmove(t, s, l)
// link with Ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")
#else
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <strings.h>
#define recv(x,y,z,a) read(x,y,z)
#define send(x,y,z,a) write(x,y,z)
#define closesocket(s) close(s)
typedef int SOCKET;
#endif
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif
struct client_t
{
int inuse;
SOCKET csock, osock;
time_t activity;
};
#define MAXCLIENTS 100
#define IDLETIMEOUT 300
#define BUF_SIZE 500
const char ident[] = "$Id$";
int main(int argc, char *argv[])
{
struct addrinfo hints;
struct addrinfo *result, *rp, *laddr, *oaddr;
int ret;
struct sockaddr_storage peer_addr;
socklen_t peer_addr_len;
size_t nread;
SOCKET lsock;
SOCKET tmpsock;
char buf[BUF_SIZE];
int i;
struct client_t clients[MAXCLIENTS];
/* check number of command line arguments */
if (argc != 5) {
fprintf(stderr, "Usage: %s localhost localport remotehost remoteport\n", argv[0]);
exit(EXIT_FAILURE);
}
laddr = (struct addrinfo *)malloc(sizeof(struct addrinfo));
oaddr = (struct addrinfo *)malloc(sizeof(struct addrinfo));
#if defined(__WIN32__) || defined(WIN32) || defined(_WIN32)
/* Winsock needs additional startup activities */
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2), &wsaData);
#endif
/* reset all of the client structures */
for (i = 0; i < MAXCLIENTS; i++)
clients[i].inuse = 0;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM; /* Stream socket */
hints.ai_flags = 0;
hints.ai_protocol = 0;
ret = getaddrinfo(argv[1], argv[2], &hints, &result);
if (ret != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
exit(EXIT_FAILURE);
}
/* getaddrinfo() returns a list of address structures.
Try each address until we successfully bind(2).
If socket(2) (or bind(2)) fails, we (close the socket
and) try the next address. */
for (rp = result; rp != NULL; rp = rp->ai_next) {
lsock = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (lsock == -1)
continue;
if (bind(lsock, rp->ai_addr, rp->ai_addrlen) == 0)
break; /* Success */
closesocket(lsock);
}
if (rp == NULL) { /* No address succeeded */
fprintf(stderr, "Could not bind\n");
exit(EXIT_FAILURE);
}
memcpy(laddr, rp, sizeof(struct addrinfo));
laddr->ai_next = NULL;
freeaddrinfo(result); /* No longer needed */
if (listen(lsock, 10)) {
perror("listen");
return 20;
}
/* Comment out as we won't bind local port
if (laddr->ai_family == AF_INET) {
((struct sockaddr_in *) laddr->ai_addr)->sin_port = htons(0);
} else {
((struct sockaddr_in6 *) laddr->ai_addr)->sin6_port = htons(0);
}
*/
/* determine the outgoing ipv6 address and port */
/* Obtain address(es) matching host/port */
bzero(&hints, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = 0;
hints.ai_protocol = 0;
ret = getaddrinfo(argv[3], argv[4], &hints, &result);
if (ret != 0) {
fprintf(stderr, "getaddrinfo: %s \n", gai_strerror(ret));
exit(EXIT_FAILURE);
}
/* getaddrinfo() returns a list of address structures.
Try each address until we successfully connect(2).
If socket(2) (or connect(2)) fails, we (close the socket
and) try the next address. */
for (rp = result; rp != NULL; rp = rp->ai_next) {
tmpsock = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (tmpsock == -1)
continue;
if ((ret = connect(tmpsock, rp->ai_addr, rp->ai_addrlen)) != -1)
{
closesocket(tmpsock);
break;
}
closesocket(tmpsock);
}
if (rp == NULL) { /* No address succeeded */
fprintf(stderr, "Could not connect\n");
exit(EXIT_FAILURE);
}
memcpy(oaddr, rp, sizeof(struct addrinfo));
oaddr->ai_next = NULL;
freeaddrinfo(result); /* No longer needed */
/* fork off into the background. */
#if !defined(__WIN32__) && !defined(WIN32) && !defined(_WIN32)
if ((i = fork()) == -1) {
perror("fork");
return 20;
}
if (i > 0)
return 0;
setsid();
#endif
/* main polling loop. */
while (1)
{
fd_set fdsr;
int maxsock;
struct timeval tv = {1,0};
time_t now = time(NULL);
/* build the list of sockets to check. */
FD_ZERO(&fdsr);
FD_SET(lsock, &fdsr);
maxsock = (int) lsock;
for (i = 0; i < MAXCLIENTS; i++)
if (clients[i].inuse) {
FD_SET(clients[i].csock, &fdsr);
if ((int) clients[i].csock > maxsock)
maxsock = (int) clients[i].csock;
FD_SET(clients[i].osock, &fdsr);
if ((int) clients[i].osock > maxsock)
maxsock = (int) clients[i].osock;
}
if (select(maxsock + 1, &fdsr, NULL, NULL, &tv) < 0) {
return 30;
}
/* check if there are new connections to accept. */
if (FD_ISSET(lsock, &fdsr))
{
SOCKET csock = accept(lsock, NULL, 0);
for (i = 0; i < MAXCLIENTS; i++)
if (!clients[i].inuse) break;
if (i < MAXCLIENTS)
{
/* connect a socket to the outgoing host/port */
SOCKET osock;
if ((osock = socket(oaddr->ai_family, oaddr->ai_socktype, oaddr->ai_protocol)) == -1) {
perror("socket");
closesocket(csock);
}
/* //comment out as we are proxying cross ipv4/ipv6
* else if (bind(osock, laddr->ai_addr, laddr->ai_addrlen)) {
perror("bind");
closesocket(csock);
closesocket(osock);
}
*/
else if (connect(osock, oaddr->ai_addr, oaddr->ai_addrlen)) {
perror("connect");
closesocket(csock);
closesocket(osock);
}
else {
clients[i].osock = osock;
clients[i].csock = csock;
clients[i].activity = now;
clients[i].inuse = 1;
}
} else {
fprintf(stderr, "too many clients\n");
closesocket(csock);
}
}
/* service any client connections that have waiting data. */
for (i = 0; i < MAXCLIENTS; i++)
{
int nbyt, closeneeded = 0;
if (!clients[i].inuse) {
continue;
} else if (FD_ISSET(clients[i].csock, &fdsr)) {
if ((nbyt = recv(clients[i].csock, buf, sizeof(buf), 0)) <= 0 ||
send(clients[i].osock, buf, nbyt, 0) <= 0) closeneeded = 1;
else clients[i].activity = now;
} else if (FD_ISSET(clients[i].osock, &fdsr)) {
if ((nbyt = recv(clients[i].osock, buf, sizeof(buf), 0)) <= 0 ||
send(clients[i].csock, buf, nbyt, 0) <= 0) closeneeded = 1;
else clients[i].activity = now;
} else if (now - clients[i].activity > IDLETIMEOUT) {
closeneeded = 1;
}
if (closeneeded) {
closesocket(clients[i].csock);
closesocket(clients[i].osock);
clients[i].inuse = 0;
}
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment