Skip to content

Instantly share code, notes, and snippets.

@feuvan
Created January 16, 2011 12:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save feuvan/781726 to your computer and use it in GitHub Desktop.
Save feuvan/781726 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;
}
@feuvan
Copy link
Author

feuvan commented Jan 16, 2011

If you don't like the inconsistent code style, please help refine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment