RBXLegacy-src/sudppipe_modified/sudppipe.c

742 lines
24 KiB
C

/*
Copyright 2005,2006,2007,2008,2009 Luigi Auriemma
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
http://www.gnu.org/licenses/gpl-2.0.txt
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
#include <ctype.h>
#include "acpdump2.h"
#include "show_dump.h"
#ifdef WIN32
#include <winsock.h>
#include "winerr.h"
#define close closesocket
#define in_addr_t uint32_t
#define LOADDLL hLib = LoadLibrary(fname); \
if(!hLib) winerr();
#define GETFUNC(x,y) x = (void *)GetProcAddress(hLib, y); \
if(!quiet) printf(" %-10s %p\n", y, x);
//if(!x) winerr();
#define CLOSEDLL FreeLibrary(hLib);
#define set_priority SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)
HINSTANCE hLib = NULL;
void winerr(void);
#else
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/time.h>
#include <dlfcn.h> // -ldl
#define LOADDLL hLib = dlopen(fname, RTLD_LAZY); \
if(!hLib) { \
fprintf(stderr, "\nError: %s\n\n", dlerror()); \
exit(1); \
}
#define GETFUNC(x,y) x = (void *)dlsym(hLib, y); \
if(!quiet) printf(" %-10s %08x\n", y, (uint32_t)x);
//error = dlerror();
//if(error || !x) {
//fprintf(stderr, "\nError: %s\n\n", error);
//exit(1);
#define CLOSEDLL dlclose(hLib);
#define set_priority nice(-10)
void *hLib = NULL;
#define SOCKET int
#define SOCKET_ERROR (-1)
#endif
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
#define VER "0.4.1"
#define BUFFSZ 0xffff
#define RECVFROMF(X) \
psz = sizeof(struct sockaddr_in); \
len = recvfrom(X, buff, BUFFSZ, 0, (struct sockaddr *)&peerl, &psz); \
if(len < 0) continue;
#define SENDTOFC(X,Y) \
if(sendtof(sdl, buff, len, &c->peer, X) != len) { \
c = check_sd(&c->peer, 1); /* it's ever c->peer */ \
Y; \
if(!c) break; \
}
#ifndef IP_TOS
#define IP_TOS 3
#endif
// default: __cdecl
static int (* sudp_init)(u8 *) = NULL; // initialization
static int (* sudp_pck)(u8 *, int) = NULL; // modification of the packet
static int (* sudp_vis)(u8 *, int) = NULL; // modification for visualization only
//static SOCKET (*mysocket)(int af, int type, int protocol) = NULL;
//static int (*myconnect)(SOCKET s, const struct sockaddr *name, int namelen) = NULL;
//static SOCKET (*myaccept)(SOCKET s, const struct sockaddr *name, int *namelen) = NULL;
//static int (*mybind)(SOCKET s, const struct sockaddr *name, int namelen) = NULL;
//static int (*myclose)(SOCKET s) = NULL;
//static int (*myrecv)(SOCKET s, char *buf, int len, int flags) = NULL;
static int (*myrecvfrom)(SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen) = NULL;
//static int (*mysend)(SOCKET s, char **retbuf, int len, int flags) = NULL;
static int (*mysendto)(SOCKET s, char **retbuf, int len, int flags, const struct sockaddr *to, int tolen) = NULL;
struct clients_struct {
int sd; // it's needed to use a different source port for each packet
struct sockaddr_in peer;
time_t timez;
struct clients_struct *next;
} *clients = NULL;
struct sockaddr_in *dhost = NULL;
in_addr_t lhost = INADDR_ANY,
Lhost = INADDR_ANY;
int multisock = 0,
samesock = 0,
quiet = 0,
timeout = 60; // NAT uses a timeout of 5 minutes (300 seconds)
int sendtof(int s, char *buf, int len, struct sockaddr_in *to, int do_mysendto);
int bind_udp_socket(struct sockaddr_in *peer, in_addr_t iface, u16 port);
struct clients_struct *check_sd(struct sockaddr_in *peer, int force_remove);
struct sockaddr_in *create_peer_array(u8 *list, u16 default_port);
void show_peer_array(u8 *str, struct sockaddr_in *peer);
void loaddll(u8 *fname, u8 *par);
in_addr_t resolv(char *host);
void std_err(void);
int main(int argc, char *argv[]) {
struct clients_struct *c = NULL,
*tmpc;
struct sockaddr_in peerl,
peer0,
*psrc = NULL,
*pdst = NULL;
struct timeval tout;
FILE *fdcap = NULL;
fd_set readset;
int sdl = 0,
sdi = 0,
sd0 = 0,
selsock = 0,
i,
len = 0,
psz = 0,
hexdump = 0,
t,
everyone = 0,
priority = 0;
u16 port,
lport,
inject = 0;
u8 tmp[16],
*buff = NULL,
*acpfile = NULL,
*dllname = NULL,
*dllpar = NULL;
#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(1,0), &wsadata);
#endif
setbuf(stdout, NULL);
setbuf(stderr, NULL);
if(argc < 4) {
fputs("\n"
"Simple UDP proxy/pipe "VER"\n"
"by Luigi Auriemma\n"
"e-mail: aluigi@autistici.org\n"
"web: aluigi.org\n"
"\n", stderr);
printf("\n"
"Usage: %s [options] <server*> <server_port> <local_port>\n"
"\n"
"Options:\n"
"-x show the hex dump of each packet\n"
"-a FILE create a CAP (tcpdump) file in which storing all the packets\n"
"-b IP bind only the input interface identified with IP\n"
"-B IP as above but works only for the outgoing socket, this means you can\n"
" decide to use a secondary interface for connecting to the host (for\n"
" example using a wireless connection instead of the main one)\n"
"-l LIB load a dll/so file which will be used to process all the incoming\n"
" packets. The library must contain the following __cdecl functions:\n"
" int sudp_init(char *data); // if you need initialization\n"
" int sudp_pck(char *data, int len); // each packet goes here\n"
" int sudp_vis(char *data, int len); // for visualization only\n"
" int myrecvfrom(...cut...); // proxocket plugin\n"
" int mysendto(...cut...); // proxocket plugin\n"
"-L PAR parameter for the initialization of the above function,\n"
" if the plugin library supports parameters use -L \"\" for help/list\n"
"-e forward each packet to anyone (clients and server) except the sender,\n"
" it works just like a chat or a broadcaster\n"
"-i PORT injection option, listen on the port PORT and each packet received\n"
" here is sent to the server from all the connected clients\n"
"-X in case of multiple hosts assigns a new socket to each client, this\n"
" is useful if the hosts are the same for using multiple source ports\n"
"-Y just the opposite of the above one, ANY client has the same port\n"
"-t SECS seconds of inactivity after which closing the client socket (%d)\n"
"-p increase process priority\n"
"-q quiet output\n"
"\n"
"* <server> can be also a sequence of hostnames and IP addresses separated by\n"
" comma to which will be sent and received all the packets at the same time.\n"
" <server> can also contain the port using the syntax IP:PORT, such port will\n"
" override the default one set by <server_port>.\n"
" if <server> is 0 the tool will consider <server_port> as a local port and\n"
" will act just like a double binding mode (experimental!)\n"
"\n", argv[0], timeout);
exit(1);
}
argc -= 3;
for(i = 1; i < argc; i++) {
if(((argv[i][0] != '-') && (argv[i][0] != '/')) || (strlen(argv[i]) != 2)) {
printf("\nError: wrong argument (%s)\n", argv[i]);
exit(1);
}
switch(argv[i][1]) {
case 'x': hexdump = 1; break;
case 'a': acpfile = argv[++i]; break;
case 'b': lhost = resolv(argv[++i]); break;
case 'B': Lhost = resolv(argv[++i]); break;
case 'l': dllname = argv[++i]; break;
case 'L': dllpar = argv[++i]; break;
case 'e': everyone = 1; break;
case 'i': inject = atoi(argv[++i]); break;
case 'p': priority = 1; break;
case 'q': quiet = 1; break;
case 'X': multisock = 1; break;
case 'Y': samesock = 1; break;
case 't': timeout = atoi(argv[++i]); break;
default: {
fprintf(stderr, "\nError: wrong command-line argument (%s)\n\n", argv[i]);
exit(1);
} break;
}
}
port = atoi(argv[argc + 1]);
lport = atoi(argv[argc + 2]);
dhost = create_peer_array(argv[argc], port);
if(lhost == INADDR_NONE) std_err();
if(Lhost == INADDR_NONE) std_err();
sdl = bind_udp_socket(NULL, lhost, lport);
if(inject) sdi = bind_udp_socket(NULL, lhost, inject);
if(samesock) {
if(!quiet) fprintf(stderr, "- same socket/port mode\n");
samesock = bind_udp_socket(NULL, Lhost, 0);
}
if(multisock) {
if(!quiet) fprintf(stderr, "- multi socket/port mode\n");
}
if(!quiet) {
if(dhost[0].sin_addr.s_addr) {
show_peer_array("- remote hosts: ", dhost);
} else {
fprintf(stderr, "- double binding\n");
fprintf(stderr, "- dest_port %hu\n", port);
}
}
if(dllname) loaddll(dllname, dllpar);
if(acpfile) {
if(!quiet) printf("- create ACP file %s\n", acpfile);
fdcap = fopen(acpfile, "rb");
if(fdcap) {
fclose(fdcap);
fprintf(stderr, "- do you want to overwrite (Y) or append (A) the file? (y/a/N)\n ");
fgets(tmp, sizeof(tmp), stdin);
t = tmp[0];
if(t == 'a') {
} else if(t == 'y') {
} else return(0);
} else {
t = 0;
}
fdcap = fopen(acpfile, (t == 'a') ? "ab" : "wb");
if(!fdcap) std_err();
if(t != 'a') create_acp(fdcap);
}
if(priority) set_priority;
if(!dhost[0].sin_addr.s_addr) {
sd0 = bind_udp_socket(&peer0, Lhost, port);
printf("- wait first packet from the server (double-binding mode)\n");
FD_ZERO(&readset); // wait first client's packet, this is NEEDED!
FD_SET(sd0, &readset);
if(select(sd0 + 1, &readset, NULL, NULL, NULL)
< 0) std_err();
}
printf("- ready\n");
FD_ZERO(&readset); // wait first client's packet, this is NEEDED!
FD_SET(sdl, &readset);
if(select(sdl + 1, &readset, NULL, NULL, NULL)
< 0) std_err();
buff = malloc(BUFFSZ);
if(!buff) std_err();
clients = NULL;
for(;;) {
FD_ZERO(&readset);
FD_SET(sdl, &readset);
selsock = sdl;
if(sd0) {
FD_SET(sd0, &readset);
if(sd0 > selsock) selsock = sd0;
}
if(sdi) {
FD_SET(sdi, &readset);
if(sdi > selsock) selsock = sdi;
}
for(c = clients; c; c = c->next) {
FD_SET(c->sd, &readset);
if(c->sd > selsock) selsock = c->sd;
}
tout.tv_sec = timeout; // this is useful if we want to free memory
tout.tv_usec = 0; // ...rarely used but I think it's good here
t = select(selsock + 1, &readset, NULL, NULL, &tout);
if(t < 0) std_err();
if(!t) { // timeout reached, call check_sd for removing the old clients
memset(&peerl, 0, sizeof(struct sockaddr_in));
check_sd(&peerl, 0);
continue;
}
if(sdi && (FD_ISSET(sdi, &readset))) {
RECVFROMF(sdi)
if(!quiet) printf("- packet injection from %s:%hu (%d bytes)\n",
inet_ntoa(peerl.sin_addr), ntohs(peerl.sin_port), len);
psrc = &peerl;
pdst = &dhost[0]; // the first one is enough, it's used only for the CAP file
if(sudp_pck) len = sudp_pck(buff, len); // packets modification
for(c = clients; c; c = c->next) {
if(sd0) sendtof(sd0, buff, len, &peer0, 1);
for(i = 0; dhost[i].sin_addr.s_addr; i++) {
sendtof(c->sd, buff, len, &dhost[i], 1);
}
}
if(everyone) {
for(c = clients; c; c = c->next) {
SENDTOFC(1,)
}
}
} else if(sd0 && FD_ISSET(sd0, &readset)) { // experimental and useless
RECVFROMF(sd0)
psrc = &peerl;
pdst = &peer0; // yes it's wrong but it's not necessary
if(sudp_pck) len = sudp_pck(buff, len); // packets modification
for(c = clients; c; c = c->next) {
SENDTOFC(1,)
}
if(everyone) {
// nothing to do here
}
} else if(FD_ISSET(sdl, &readset)) {
RECVFROMF(sdl)
c = check_sd(&peerl, 0); // check if this is a new or existent client
if(!c) continue;
psrc = &c->peer;
pdst = &dhost[0]; // the first one is enough, it's used only for the CAP file
if(sudp_pck) len = sudp_pck(buff, len); // packets modification
if(sd0) sendtof(sd0, buff, len, &peer0, 1);
if(multisock) {
i = 0;
for(c = clients; c; c = c->next) {
if(!dhost[i].sin_addr.s_addr) break;
if(memcmp(&c->peer, &peerl, sizeof(struct sockaddr_in))) continue;
sendtof(c->sd, buff, len, &dhost[i], 1);
i++;
}
} else {
for(i = 0; dhost[i].sin_addr.s_addr; i++) {
sendtof(c->sd, buff, len, &dhost[i], 1);
}
}
if(everyone) {
tmpc = c;
for(c = clients; c; c = c->next) {
if(c == tmpc) continue;
SENDTOFC(1,)
}
}
} else {
for(c = clients; c; c = c->next) {
if(!FD_ISSET(c->sd, &readset)) continue;
RECVFROMF(c->sd)
psrc = &peerl;
pdst = &c->peer;
if(sudp_pck) len = sudp_pck(buff, len); // packets modification
if(myrecvfrom) len = myrecvfrom(c->sd, buff, len, 0, (struct sockaddr *)psrc, &psz);
SENDTOFC(0, pdst = NULL) // like SENDTOFC but without the handling of mysendto
if(everyone || samesock) {
tmpc = c;
for(c = clients; c; c = c->next) {
if(c == tmpc) continue;
SENDTOFC(1,)
}
}
break;
}
}
if(!psrc || !pdst) continue; // the following is only for visualization
if(fdcap) acp_dump(
fdcap, SOCK_DGRAM, IPPROTO_UDP,
psrc->sin_addr.s_addr, psrc->sin_port, pdst->sin_addr.s_addr, pdst->sin_port,
buff, len,
NULL, NULL, NULL, NULL);
if(sudp_vis) len = sudp_vis(buff, len);
if(hexdump) {
if(!quiet) {
printf("\n%s:%hu -> ", inet_ntoa(psrc->sin_addr), ntohs(psrc->sin_port));
printf("%s:%hu\n", inet_ntoa(pdst->sin_addr), ntohs(pdst->sin_port));
}
show_dump(buff, len, stdout);
}
}
close(sdl);
if(sdi) close(sdi);
if(sd0) close(sd0);
if(fdcap) fclose(fdcap);
if(hLib) CLOSEDLL
free(buff);
return(0);
}
int sendtof(int s, char *buf, int len, struct sockaddr_in *to, int do_mysendto) {
int oldlen = 0,
ret;
char *oldbuf = NULL;
if(!do_mysendto) {
return(sendto(s, buf, len, 0, (struct sockaddr *)to, sizeof(struct sockaddr_in)));
}
if(mysendto) {
oldbuf = buf;
oldlen = len;
ret = mysendto(s, &buf, len, 0, (struct sockaddr *)to, sizeof(struct sockaddr_in));
if(ret >= 0) {
// call real function
} else if(ret == SOCKET_ERROR) {
goto quit_and_free;
} else {
ret = oldlen;
goto quit_and_free;
}
len = ret;
}
ret = sendto(s, buf, len, 0, (struct sockaddr *)to, sizeof(struct sockaddr_in));
quit_and_free:
if(mysendto) {
if(oldbuf != buf) free(buf);
if(ret == len) ret = oldlen;
}
return(ret);
}
struct clients_struct *check_sd(struct sockaddr_in *peer, int force_remove) {
struct clients_struct *c,
*tmp,
*prev,
*ret;
time_t curr;
int multi = 0;
curr = time(NULL);
prev = NULL;
ret = NULL;
for(c = clients; c; ) {
if((c->peer.sin_addr.s_addr == peer->sin_addr.s_addr) && (c->peer.sin_port == peer->sin_port)) {
c->timez = curr;
ret = c;
if(force_remove) {
c->timez = (curr - timeout) - 1;
ret = prev;
}
}
if((curr - c->timez) >= timeout) {
if(!quiet) printf("- remove %s:%hu\n",
inet_ntoa(c->peer.sin_addr), ntohs(c->peer.sin_port));
tmp = c->next;
if(samesock) {
// do NOT close c->sd!
} else {
close(c->sd);
}
free(c);
if(prev) { // second, third and so on
prev->next = tmp;
} else { // the first only
clients = tmp;
}
c = tmp; // already the next
} else {
prev = c;
c = c->next; // get the next
}
}
if(force_remove) return(ret);
if(ret) return(ret);
if((peer->sin_addr.s_addr == INADDR_ANY) || (peer->sin_addr.s_addr == INADDR_NONE) || !peer->sin_port) return(NULL);
multisock_doit:
c = malloc(sizeof(struct clients_struct));
if(!c) return(NULL);
if(prev) {
prev->next = c;
} else {
clients = c;
}
if(samesock) {
c->sd = samesock;
} else {
c->sd = bind_udp_socket(NULL, Lhost, 0);
}
memcpy(&c->peer, peer, sizeof(struct sockaddr_in));
c->timez = curr;
c->next = NULL;
if(!quiet) printf("- add %s:%hu\n",
inet_ntoa(c->peer.sin_addr), ntohs(c->peer.sin_port));
if(multisock) {
prev = c;
multi++;
if(dhost[multi].sin_addr.s_addr) goto multisock_doit;
}
return(c);
}
struct sockaddr_in *create_peer_array(u8 *list, u16 default_port) {
struct sockaddr_in *ret;
int i,
size = 1;
u16 port;
u8 *p1,
*p2;
for(p2 = list; (p1 = strchr(p2, ',')); size++, p2 = p1 + 1);
ret = calloc(size + 1, sizeof(struct sockaddr_in));
if(!ret) std_err();
for(i = 0;;) {
p1 = strchr(list, ',');
if(p1) *p1 = 0;
port = default_port;
p2 = strchr(list, ':');
if(p2) {
*p2 = 0;
port = atoi(p2 + 1);
}
while(*list == ' ') list++;
ret[i].sin_addr.s_addr = resolv(list);
ret[i].sin_port = htons(port);
ret[i].sin_family = AF_INET;
i++;
if(!p1) break;
list = p1 + 1;
}
return(ret);
}
void show_peer_array(u8 *str, struct sockaddr_in *peer) {
int i;
fputs(str, stderr);
for(i = 0; peer[i].sin_addr.s_addr; i++) {
if(i) fprintf(stderr, ", ");
fprintf(stderr, "%s:%hu", inet_ntoa(peer[i].sin_addr), ntohs(peer[i].sin_port));
}
fputc('\n', stderr);
}
int bind_udp_socket(struct sockaddr_in *peer, in_addr_t iface, u16 port) {
struct sockaddr_in peer_tmp;
int sd;
static const int
on = 1,
tos = 0x10;
static const struct
linger ling = {1,1};
if(!peer) peer = &peer_tmp;
peer->sin_addr.s_addr = iface;
peer->sin_port = htons(port);
peer->sin_family = AF_INET;
if((iface != INADDR_ANY) || port) {
if(!quiet) printf("- bind UDP port %hu on interface %s\n",
ntohs(peer->sin_port), inet_ntoa(peer->sin_addr));
}
sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sd < 0) std_err();
setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
if(bind(sd, (struct sockaddr *)peer, sizeof(struct sockaddr_in))
< 0) std_err();
setsockopt(sd, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling));
setsockopt(sd, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof(on));
setsockopt(sd, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(tos));
return(sd);
}
void loaddll(u8 *fname, u8 *par) {
if(!fname) return;
printf("- load library %s\n", fname);
LOADDLL;
GETFUNC(sudp_init, "sudp_init");
GETFUNC(sudp_pck, "sudp_pck");
GETFUNC(sudp_vis, "sudp_vis");
GETFUNC(myrecvfrom, "myrecvfrom");
GETFUNC(mysendto, "mysendto");
if(sudp_init && sudp_init(par)) {
fprintf(stderr, "\nError: plugin initialization failed\n\n");
CLOSEDLL
exit(1);
}
}
in_addr_t resolv(char *host) {
struct hostent *hp;
in_addr_t host_ip;
host_ip = inet_addr(host);
if(host_ip == INADDR_NONE) {
hp = gethostbyname(host);
if(!hp) {
fprintf(stderr, "\nError: unable to resolv hostname (%s)\n", host);
exit(1);
} else host_ip = *(in_addr_t *)hp->h_addr;
}
return(host_ip);
}
#ifndef WIN32
void std_err(void) {
perror("\nError");
exit(1);
}
#else
void winerr(void) {
char *error;
if(!FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&error,
0,
NULL)) {
error = strerror(errno);
}
printf("\nError: %s\n", error);
//LocalFree(error);
exit(1);
}
#endif