/* Copyright (C) 1996-1997 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * udpred - a inetd launched udp port redirector * * Version: @(#)udpred.c 0.01 01/15/97 * * Author: Chris Faherty * * syntax: * * udpred toip toport * * sample inetd.conf entry: * * 7000 dgram udp wait root /usr/sbin/tcpd /usr/sbin/udpred 192.168.100.16 7000 * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */ #include #include #include #include #include #include #include #ifdef _WIN32 #include #else #include #include #include #include #include #include #include #endif int host_port; // port we are listening on typedef struct peer { time_t last; struct sockaddr_in sin; struct sockaddr_in dest; int s; // connected socket to remote struct peer *next; } peer_t; peer_t *peers; /* ==================== NET_Init ==================== */ void NET_Init (void) { #ifdef _WIN32 static WSADATA winsockdata; // WORD wVersionRequested; int r; // wVersionRequested = MAKEWORD(1, 1); r = WSAStartup (MAKEWORD(2, 1), &winsockdata); if (r) { fprintf(stderr, "Winsock initialization failed."); exit(1); } printf("Winsock Initialized\n"); #endif } int connectsock(char *host, char *service, char *protocol) { struct hostent *phe; struct servent *pse; struct protoent *ppe; struct sockaddr_in sin; int s, type; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; /* Map service name to port number */ if(pse = getservbyname(service, protocol)) sin.sin_port = pse->s_port; else if((sin.sin_port = htons((u_short)atoi(service))) == 0) { fprintf(stderr, "udpred: can't get \"%s\" service entry\n", service); exit(2); } /* Map host name to IP address, allowing for dotted decimal */ if(phe = gethostbyname(host)) memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length); else if((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) { fprintf(stderr, "udpred: can't get \"%s\" host entry\n", host); exit(2); } /* Map protocol name to protocol number */ if((ppe = getprotobyname(protocol)) == 0) { fprintf(stderr, "udpred: can't get \"%s\" protocol entry\n", protocol); exit(2); } /* Use protocol to choose a socket type */ if(strcmp(protocol, "udp") == 0) type = SOCK_DGRAM; else type = SOCK_STREAM; /* Allocate a socket */ s = socket(PF_INET, type, ppe->p_proto); if(s < 0) { fprintf(stderr, "udpred: can't create socket: %s\n", sys_errlist[errno]); exit(2); } /* Connect the socket */ if(connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { fprintf(stderr, "udpred: can't connect to %s.%s: %s\n", host, service, sys_errlist[errno]); exit(2); } return s; } int main(int argc, char *argv[]) { fd_set rfds; struct timeval tv; int retval; int i1; char buffer[4095]; struct sockaddr_in fsin; int alen; peer_t *p; int s; struct sockaddr_in address; if (argc < 3) { printf("Usage: %s \n", argv[0]); return 1; } NET_Init(); if ((s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { perror("socket"); return 1; } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons((unsigned short)atoi(argv[1])); if (bind (s, (void *)&address, sizeof(address)) == -1) perror("bind"); while(1) { FD_ZERO(&rfds); FD_SET(s, &rfds); i1 = s; for (p = peers; p; p = p->next) { FD_SET(p->s, &rfds); if (p->s >= i1) i1 = p->s + 1; } /* Wait up to two minutes. */ tv.tv_sec = 2; tv.tv_usec = 0; retval = select(i1, &rfds, (fd_set *)0, (fd_set *)0, &tv); if(retval > 0) { if(FD_ISSET(s, &rfds)) { alen = sizeof(fsin); i1 = recvfrom(s, buffer, 4096, 0, (struct sockaddr *) &fsin, &alen); if(i1 > 0) { for (p = peers; p; p = p->next) if (memcmp(&p->sin.sin_addr, &fsin.sin_addr, sizeof(p->sin.sin_addr)) == 0 && memcmp(&p->sin.sin_port, &fsin.sin_port, sizeof(p->sin.sin_port)) == 0) { send(p->s, buffer, i1, 0); time(&p->last); break; } if (p == NULL) { // new peer printf("peer %s:%d added", inet_ntoa(fsin.sin_addr), (int)ntohs(fsin.sin_port)); p = malloc(sizeof *p); p->sin = fsin; p->s = connectsock(argv[2], argv[3], "udp"); p->next = peers; peers = p; send(p->s, buffer, i1, 0); time(&p->last); } } } for (p = peers; p; p = p->next) if(FD_ISSET(p->s, &rfds)) { i1 = recv(p->s, buffer, 4096, 0); if(i1 > 0) { time(&p->last); sendto(s, buffer, i1, 0, (struct sockaddr *) &p->sin, sizeof(p->sin)); } } } else { peer_t *pp; pp = NULL; p = peers; while (p) { if (time(NULL) - p->last > 300) { if (!pp && !p->next) { printf("peer %s:%d removed (timeout)", inet_ntoa(p->sin.sin_addr), (int)ntohs(p->sin.sin_port)); free(p); p = pp = NULL; continue; } pp->next = p->next; printf ("peer %s:%d removed (timeout)", inet_ntoa(p->sin.sin_addr), (int)ntohs(p->sin.sin_port)); free(p); p = pp->next; } else { pp = p; p = p->next; } } } } }