/////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2002 Ultr@VNC Team Members. All Rights Reserved.
//
// 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.
//
// Program is based on the
// http://www.imasy.or.jp/~gotoh/ssh/connect.c
// Written By Shun-ichi GOTO <gotoh@taiyo.co.jp>
//
// If the source code for the program is not available from the place from
// which you received this file, check
// http://ultravnc.sourceforge.net/
//
// Linux port (C) 2005 Jari Korhonen, jarit1.korhonen@dnainternet.net
/////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <memory.h>
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <stdarg.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <netdb.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define TRUE 1
#define FALSE 0
#define SOCKET_ERROR -1
#define LOCAL_SOCKET 1
#define METHOD_DIRECT 1
#ifndef FD_ALLOC
#define FD_ALLOC(nfds) ((fd_set*)malloc((nfds+7)/8))
#endif
#define rfbProtocolVersionFormat "RFB %03d.%03d\n"
#define rfbProtocolMajorVersion 0
#define rfbProtocolMinorVersion 0
#define sz_rfbProtocolVersionMsg 12
#define MAX_HOST_NAME_LEN 250
#define RFB_PORT_OFFSET 5900
typedef char rfbProtocolVersionMsg[13]; /* allow extra byte for null */
typedef struct _repeaterinfo {
int viewer;
int server;
unsigned long code;
unsigned long timestamp;
int used;
} repeaterinfo;
repeaterinfo Servers[20];
repeaterinfo Viewers[20];
int relay_method;
int notstopped;
u_short viewer_port;
u_short server_port;
char *dest_host = NULL;
u_short dest_port;
int ReadExact(int sock, char *buf, int len);
unsigned long Find_viewer_list(repeaterinfo * Viewerstruct);
void debug(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "UltraVnc> ");
vfprintf(stderr, fmt, args);
va_end(args);
}
void Clean_server_List()
{
int i;
for (i = 0; i < 20; i++) {
Servers[i].code = 0;
Servers[i].used = FALSE;
}
}
void Add_server_list(repeaterinfo * Serverstruct)
{
int i;
for (i = 0; i < 20; i++) {
if (Servers[i].code == Serverstruct->code) {
debug("Add_server_list(): similar server already there (reconnect ?)\n");
return;
}
}
for (i = 0; i < 20; i++) {
if (Servers[i].code == 0) {
debug("Add_server_list(): Server added to list %i\n", Serverstruct->code);
Servers[i].code = Serverstruct->code;
Servers[i].viewer = Serverstruct->viewer;
Servers[i].server = Serverstruct->server;
Servers[i].timestamp = time(NULL); /*1 second accuracy is enough ?*/
Servers[i].used = FALSE;
return;
}
}
}
void Remove_server_list(unsigned long code)
{
int i;
for (i = 0; i < 20; i++) {
if (Servers[i].code == code) {
debug("Remove_server_list(): Server Removed from list %i\n", code);
Servers[i].code = 0;
Servers[i].used = FALSE;
return;
}
}
}
unsigned long Find_server_list(repeaterinfo * Serverstruct)
{
int i;
for (i = 0; i < 20; i++) {
if (Servers[i].code == Serverstruct->code) {
Servers[i].used = TRUE;
return i;
}
}
return 99;
}
void Clean_viewer_List()
{
int i;
for (i = 0; i < 20; i++) {
Viewers[i].code = 0;
Servers[i].used = FALSE;
}
}
void Add_viewer_list(repeaterinfo * Viewerstruct)
{
int i;
for (i = 0; i < 20; i++) {
if (Viewers[i].code == Viewerstruct->code) {
debug("Add_viewer_list(): similar viewer already there (reconnect ?)\n");
return;
}
}
for (i = 0; i < 20; i++) {
if (Viewers[i].code == 0) {
debug("Add_viewer_list(): Viewer added to list %i\n", Viewerstruct->code);
Viewers[i].code = Viewerstruct->code;
Viewers[i].viewer = Viewerstruct->viewer;
Viewers[i].server = Viewerstruct->server;
Viewers[i].timestamp = time(NULL);
Viewers[i].used = FALSE;
return;
}
}
}
void Remove_viewer_list(unsigned long code)
{
int i;
for (i = 0; i < 20; i++) {
if (Viewers[i].code == code) {
debug("Remove_viewer_list(): Viewer removed from list %i\n", code);
Viewers[i].code = 0;
Viewers[i].used = FALSE;
return;
}
}
}
unsigned long Find_viewer_list(repeaterinfo * Viewerstruct)
{
int i;
for (i = 0; i < 20; i++) {
if (Viewers[i].code == Viewerstruct->code) {
Viewers[i].used = TRUE;
debug("Find_viewer_list(): viewer found at %i, code = %i\n", i, Viewers[i].code);
return i;
}
}
return 99;
}
void error(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "ERROR: ");
vfprintf(stderr, fmt, args);
va_end(args);
}
int
local_resolve(const char *host, struct sockaddr_in *addr,
struct sockaddr_in *ns)
{
struct hostent *ent;
if (strspn(host, "0123456789.") == strlen(host)) {
/* given by IPv4 address */
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(host);
} else {
debug("local_resolve(): resolving host by name: %s\n", host);
ent = gethostbyname(host);
if (ent) {
memcpy(&addr->sin_addr, ent->h_addr, ent->h_length);
addr->sin_family = ent->h_addrtype;
debug("local_resolve(): resolved: %s (%s)\n", host, inet_ntoa(addr->sin_addr));
} else {
debug("local_resolve(): failed to resolve locally.\n");
return -1; /* failed */
}
}
return 0; /* good */
}
int open_connection(const char *host, u_short port)
{
int s;
struct sockaddr_in saddr;
if (relay_method == METHOD_DIRECT) {
host = dest_host;
port = dest_port;
}
if (local_resolve(host, &saddr, NULL) < 0) {
error("open_connection(): can't resolve hostname: %s\n", host);
return SOCKET_ERROR;
}
saddr.sin_port = htons(port);
debug("open_connection(): connecting to %s:%u\n", inet_ntoa(saddr.sin_addr), port);
s = socket(AF_INET, SOCK_STREAM, 0);
if (connect(s, (struct sockaddr *) &saddr, sizeof(saddr)) ==
SOCKET_ERROR) {
debug("open_connection(): connect() failed.\n");
return SOCKET_ERROR;
}
return s;
}
void fatal(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "FATAL: ");
vfprintf(stderr, fmt, args);
va_end(args);
}
void report_bytes(char *prefix, char *buf, int len)
{
debug("%s", prefix);
while (0 < len) {
fprintf(stderr, " %02x", *(unsigned char *) buf);
buf++;
len--;
}
fprintf(stderr, "\n");
return;
}
void *do_repeater(void *lpParam)
{
/** vars for viewer input data **/
char viewerbuf[1024]; /* viewer input buffer */
int viewerbuf_len; /* available data in viewerbuf */
int f_viewer; /* read viewer input more? */
/** vars for server input data **/
char serverbuf[1024]; /* server input buffer */
int serverbuf_len; /* available data in serverbuf */
int f_server; /* read server input more? */
/** other variables **/
int nfds, len;
fd_set *ifds, *ofds;
int viewer = 0;
int server = 0;
unsigned long code = 0;
repeaterinfo *inout = (repeaterinfo *) lpParam;
viewer = inout->viewer;
server = inout->server;
code = inout->code;
/* repeater between stdin/out and socket */
nfds = ((viewer < server) ? server : viewer) + 1;
ifds = FD_ALLOC(nfds);
ofds = FD_ALLOC(nfds);
f_viewer = 1; /* yes, read from viewer */
f_server = 1; /* yes, read from server */
viewerbuf_len = 0;
serverbuf_len = 0;
while (f_viewer || f_server) {
FD_ZERO(ifds);
FD_ZERO(ofds);
/** prepare for reading viewer input **/
if (f_viewer && (viewerbuf_len < sizeof(viewerbuf))) {
FD_SET(viewer, ifds);
}
/** prepare for reading server input **/
if (f_server && (serverbuf_len < sizeof(serverbuf))) {
FD_SET(server, ifds);
}
if (select(nfds, ifds, ofds, NULL, NULL) == -1) {
/* some error */
error("do_repeater(): select() failed, errno=%d (%s)\n", errno, strerror(errno));
Remove_server_list(code);
Remove_viewer_list(code);
return NULL;
}
/* server => viewer */
if (FD_ISSET(server, ifds) && (serverbuf_len < sizeof(serverbuf))) {
len =
recv(server, serverbuf + serverbuf_len, sizeof(serverbuf) - serverbuf_len, 0);
if (len == 0) {
debug("do_repeater(): connection closed by server\n");
f_server = 0; /* no more read from socket */
f_viewer = 0;
close(viewer);
close(server);
viewer = 0;
server = 0;
} else if (len == -1) {
if (errno != ECONNRESET) {
/* error */
fatal("do_repeater(): recv() failed, errno=%d (%s)\n", errno, strerror(errno));
} else {
debug("do_repeater(): ECONNRESET detected\n");
}
} else {
serverbuf_len += len;
}
}
/* viewer => server */
if (FD_ISSET(viewer, ifds) && (viewerbuf_len < sizeof(viewerbuf))) {
len = recv(viewer, viewerbuf + viewerbuf_len, sizeof(viewerbuf) - viewerbuf_len, 0);
if (len == 0) {
debug("do_repeater(): connection closed by viewer\n");
close(viewer);
close(server);
viewer = 0;
server = 0;
f_server = 0;
f_viewer = 0;
} else if (len == -1) {
/* error on reading from stdin */
debug("do_repeater(): recv() failed, errno = %d (%s)\n", errno, strerror(errno));
Remove_server_list(code);
Remove_viewer_list(code);
return NULL;
} else {
/* repeat */
viewerbuf_len += len;
}
}
/* flush data in viewerbuffer to server */
if (0 < viewerbuf_len) {
len = send(server, viewerbuf, viewerbuf_len, 0);
if (len == -1) {
debug("do_repeater(): send() failed, errno=%d (%s)\n", errno, strerror(errno));
//return NULL;
} else if (0 < len) {
/* move data on to top of buffer */
viewerbuf_len -= len;
if (0 < viewerbuf_len)
memcpy(viewerbuf, viewerbuf + len, viewerbuf_len);
assert(0 <= viewerbuf_len);
}
}
/* flush data in serverbuffer to viewer */
if (0 < serverbuf_len) {
len = send(viewer, serverbuf, serverbuf_len, 0);
if (len == -1) {
debug("do_repeater(): output (viewer) failed, errno=%d (%s)\n", errno, strerror(errno));
//return NULL;
} else {
serverbuf_len -= len;
if (len < serverbuf_len)
memcpy(serverbuf, serverbuf + len, serverbuf_len);
assert(0 <= serverbuf_len);
}
}
}
Remove_server_list(code);
Remove_viewer_list(code);
return NULL;
}
int ParseDisplay(char *display, char *phost, int hostlen, int *pport)
{
int tmp_port;
char *colonpos;
debug("ParseDisplay() start: display = %s, hostlen = %d\n", display, hostlen);
colonpos = strchr(display, ':');
if (hostlen < (int) strlen(display)) {
return FALSE;
}
if (colonpos == NULL) {
// No colon -- use default port number
tmp_port = RFB_PORT_OFFSET;
strncpy(phost, display, MAX_HOST_NAME_LEN);
} else {
strncpy(phost, display, colonpos - display);
phost[colonpos - display] = '\0';
if (colonpos[1] == ':') {
// Two colons -- interpret as a port number
if (sscanf(colonpos + 2, "%d", &tmp_port) != 1) {
return FALSE;
}
} else {
// One colon -- interpret as a display number or port number
if (sscanf(colonpos + 1, "%d", &tmp_port) != 1) {
return FALSE;
}
// RealVNC method - If port < 100 interpret as display number else as Port number
if (tmp_port < 100)
tmp_port += RFB_PORT_OFFSET;
}
}
*pport = tmp_port;
debug("ParseDisplay() end: phost = %s, pport = %d\n", phost, tmp_port);
return TRUE;
}
int WriteExact(int sock, char *buf, int len)
{
int n;
while (len > 0) {
n = send(sock, buf, len, 0);
if (n > 0) {
buf += n;
len -= n;
} else if (n == 0) {
fprintf(stderr, "WriteExact: write returned 0?\n");
exit(1);
} else {
return n;
}
}
return 1;
}
int ReadExact(int sock, char *buf, int len)
{
int n;
while (len > 0) {
n = recv(sock, buf, len, 0);
if (n > 0) {
buf += n;
len -= n;
} else {
return n;
}
}
return 1;
}
void *server_listen(void *lpParam)
{
int server; /* socket */
int viewer; /* viewer input */
int sock;
int connection;
struct sockaddr_in name;
struct sockaddr client;
int socklen;
char serverid[256];
char serverhost[256];
int serverport;
repeaterinfo teststruct;
pthread_t repeater_thread;
int read_exact_ret;
////////////
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock < 0)
fatal("server_listen(): socket() failed, errno=%d (%s)\n", errno, strerror(errno));
else
debug("server_listen(): socket() initialized\n");
name.sin_family = AF_INET;
name.sin_port = htons(server_port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sock, (struct sockaddr *) &name, sizeof(name)) < 0)
fatal("server_listen(): bind() failed, errno=%d (%s)\n", errno, strerror(errno));
else
debug("server_listen(): bind() succeeded to port %i\n", server_port);
if (listen(sock, 1) < 0)
fatal("server_listen(): listen() failed, errno=%d (%s)\n", errno, strerror(errno));
else
debug("server_listen(): listen() succeeded\n");
socklen = sizeof(client);
while (notstopped) {
debug("server_listen(): Waiting for connection ...\n");
connection = accept(sock, &client, &socklen);
if (connection < 0)
debug("server_listen(): accept() failed, errno=%d (%s)\n", errno, strerror(errno));
else {
debug("server_listen(): accept() succeeded, connection = %d\n", connection);
read_exact_ret = ReadExact(connection, serverid, MAX_HOST_NAME_LEN);
if (read_exact_ret < 0) {
debug("server_listen(): Reading Proxy settings error");
close(connection);
goto end;
}
else {
debug("server_listen(): ReadExact() ok, returned %d\n", read_exact_ret);
}
ParseDisplay(serverid, serverhost, 255, &serverport);
strcpy(dest_host, serverhost);
dest_port = serverport;
viewer = connection;
if (strcmp(dest_host, "ID") == 0) {
teststruct.server = connection;
teststruct.code = serverport;
Add_server_list(&teststruct);
if (Find_viewer_list(&teststruct) != 99) {
//found
//initiate connection
Find_server_list(&teststruct);
teststruct.viewer =
Viewers[Find_viewer_list(&teststruct)].viewer;
teststruct.server = connection;
pthread_create(&repeater_thread, NULL, do_repeater, (void *) &teststruct);
}
}
else {
debug("dest_host name is not ID\n");
}
}
end:;
}
notstopped = FALSE;
debug("server_listen(): relaying done.\n");
close(server);
close(viewer);
return NULL;
}
void *timer(void *lpParam)
{
while (notstopped) {
int i;
unsigned long tick = time(NULL);
for (i = 0; i < 20; i++) {
if (tick - Viewers[i].timestamp > 300
&& Viewers[i].used == FALSE && Viewers[i].code != 0) {
//
close(Viewers[i].viewer);
debug("timer(): Removing viewer %i %i \n", Viewers[i].code, i);
Remove_viewer_list(Viewers[i].code);
}
if (tick - Servers[i].timestamp > 300
&& Servers[i].used == FALSE && Servers[i].code != 0) {
//
close(Servers[i].server);
debug("timer(): Removing server %i\n", Servers[i].code);
Remove_server_list(Servers[i].code);
}
}
sleep(60);
debug("timer(): Searching old connections\n");
}
return NULL;
}
int main(int argc, char **argv)
{
int sock; /* socket */
int viewer; /* viewer input */
char viewerid[256];
char viewerhost[256];
int viewerport;
int connection;
struct sockaddr_in name;
struct sockaddr client;
int socklen;
pthread_t server_listen_thread;
pthread_t timer_thread;
pthread_t repeater_thread;
repeaterinfo teststruct;
rfbProtocolVersionMsg pv;
notstopped = TRUE;
relay_method = METHOD_DIRECT;
Clean_server_List();
Clean_viewer_List();
////////////
viewer_port = 5900;
server_port = 5500;
if (argc == 2)
viewer_port = atoi(argv[1]);
if (argc == 3)
server_port = atoi(argv[2]);
dest_host = malloc(256);
////////////
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock < 0)
fatal("main(): socket() failed, errno=%d (%s)\n", errno, strerror(errno));
else
debug("main(): socket() initialized\n");
name.sin_family = AF_INET;
name.sin_port = htons(viewer_port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sock, (struct sockaddr *) &name, sizeof(name)) < 0)
fatal("main(): bind() failed, errno=%d (%s)\n", errno, strerror(errno));
else
debug("main(): bind() succeeded to port %i\n", viewer_port);
if (listen(sock, 1) < 0)
fatal("main(): listen() failed, errno=%d (%s)\n", errno, strerror(errno));
else
debug("main(): listen() succeeded\n");
socklen = sizeof(client);
pthread_create(&server_listen_thread, NULL, server_listen, (void *) &teststruct);
pthread_create(&timer_thread, NULL, timer, (void *) &teststruct);
while (notstopped) {
debug("main(): Waiting for Viewer connection ...\n");
connection = accept(sock, &client, &socklen);
if (connection < 0)
debug("main(): accept() failed, errno=%d (%s)\n", errno, strerror(errno));
else {
debug("main(): accept() connection \n");
sprintf(pv, rfbProtocolVersionFormat, rfbProtocolMajorVersion,
rfbProtocolMinorVersion);
if (WriteExact(connection, pv, sz_rfbProtocolVersionMsg) < 0) {
debug("main(): Writing protocol version error");
close(connection);
goto end;
}
if (ReadExact(connection, viewerid, MAX_HOST_NAME_LEN) < 0) {
debug("main(): Reading Proxy settings error");
close(connection);
goto end;
}
ParseDisplay(viewerid, viewerhost, 255, &viewerport);
strcpy(dest_host, viewerhost);
dest_port = viewerport;
debug("main(): Viewer %s port %d \n", dest_host, viewerport);
viewer = connection;
if (strcmp(dest_host, "ID") == 0) {
teststruct.viewer = viewer;
teststruct.code = viewerport;
Add_viewer_list(&teststruct);
if (Find_server_list(&teststruct) != 99) {
//found
//initiate connection
Find_viewer_list(&teststruct);
teststruct.viewer = viewer;
teststruct.server = Servers[Find_server_list(&teststruct)].server;
pthread_create(&repeater_thread, NULL, do_repeater,(void *) &teststruct);
}
}
}
end:;
}
notstopped = FALSE;
debug("main(): relaying done.\n");
close(sock);
close(viewer);
return 0;
}
|