/* Copyright (C) 2000-2013 Clemens Fuchslocher <clemens@vakuumverpackt.de> */
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <windows.h>
#include <windowsx.h>
#include <Shlwapi.h>
#undef stdin
#undef stdout
#undef stderr
#define stdin _iob
#define stdout (_iob+1)
#define stderr (_iob+2)
EXTERN_C _CRTIMP FILE _iob[3];
const char *ExeName;
#define MIN(a,b) (a)<(b)?(a):(b)
struct{
char *local_port;
char *remote_host;
char *remote_port;
char *bind_address;
unsigned stay_alive; // 0 = unendlich, 1 = genau 1 Connect usw.
int loglevel;
}options;
struct TUNNEL{
SOCKET server_socket;
SOCKET client_socket;
SOCKET remote_socket;
bool build_server();
bool wait_for_client();
bool build_tunnel();
int fd() const;
bool use_tunnel();
void handle_tunnel();
};
void print_usage(void) {
fprintf(stderr, "Usage: %s localport[=]remotehost[:remoteport] [options]\n"
"Options: -h, -? help\n"
" -v version\n"
" -b[=]IP bindaddress\n"
" -l[=]n logging level\n"
" -n stay alive for n connects\n", ExeName);
}
void print_version(void) {
fprintf(stderr, "\
tcptunnel v0.7 Copyright (C) 2000-2012 Clemens Fuchslocher\n\n\
This program is distributed in the hope that it will be useful,\n\
but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
GNU General Public License for more details.\n\n\
Written by Clemens Fuchslocher <clemens@vakuumverpackt.de>\n");
}
void print_missing(const char* message, ...) {
char buf[256];
va_list va;
va_start(va,message);
_vsnprintf(buf,sizeof buf,message,va);
fprintf(stderr, "%s: %s\n", ExeName, buf);
print_usage();
}
void set_options(int argc, char *argv[]) {
int opt;
for (int index=1; index<argc; index++) {
int chr=0;
switch (argv[index][chr]) {
case '-':
case '/': {
opt = argv[index][++chr];
switch (opt) {
case 'b': {
char*p=argv[index]+(++chr);
if (!*p) p=argv[++index];
else if (*p=='=') p++;
options.bind_address = p;
}break;
case 'l': {
char*p=argv[index]+(++chr);
if (!*p) p=argv[++index];
else if (*p=='=') p++;
options.loglevel=atoi(p);
}continue;
case '?':
case 'h': {
print_usage();
exit(0);
}break;
case 'v': {
print_version();
exit(0);
}break;
default: if (isdigit(opt)) options.stay_alive = atoi(argv[index]+1);
else{
print_missing("Unknown option %c!",opt);
exit(1);
}
}
}break;
default: {
if (!options.local_port) {
PTSTR p = strchr(argv[index]+1,'=');
if (p) *p++ = 0;
options.local_port = options.remote_port = argv[index];
if (!p) p = argv[++index];
if (!p) {
print_missing("missing remotehost.");
exit(1);
}
options.remote_host = p;
p = strchr(p,':');
if (p) {
*p++ = 0;
options.remote_port = p;
}
}else{
print_missing("Option expected, not %s! Only one tunnel!",argv[index]);
exit(1);
}
}
}
}
if (!options.local_port) {
print_missing("missing localport.");
exit(1);
}
}
void set_option(char **option, char *value) {
size_t size = sizeof(char) * (strlen(value) + 1);
*option = (char *) malloc(size);
if (*option == NULL) {
perror("set_option: malloc()");
exit(1);
}
strncpy(*option, value, size);
}
bool TUNNEL::build_server() {
int optval;
struct servent *se = getservbyname(options.local_port,NULL);
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof server_addr);
server_addr.sin_port = se ? se->s_port : htons(atoi(options.local_port));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket < 0) {
perror("build_server: socket()");
return false;
}
optval = 1;
if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, (const char *) &optval, sizeof(optval)) < 0) {
perror("build_server: setsockopt(SO_REUSEADDR)");
return false;
}
if (options.bind_address) {
server_addr.sin_addr.s_addr = inet_addr(options.bind_address);
}
if (bind(server_socket, (struct sockaddr *) &server_addr, sizeof server_addr) < 0) {
perror("build_server: bind()");
return false;
}
if (listen(server_socket, 1) < 0) {
perror("build_server: listen()");
return false;
}
return true;
}
char *get_current_timestamp(void) {
static char date_str[40];
GetDateFormat(LOCALE_USER_DEFAULT,DATE_SHORTDATE,NULL,NULL,date_str,sizeof date_str);
return date_str;
}
bool TUNNEL::wait_for_client() {
struct sockaddr_in client_addr;
int client_addr_size;
client_addr_size = sizeof(struct sockaddr_in);
client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &client_addr_size);
if (client_socket < 0) {
if (errno != EINTR) perror("wait_for_client: accept()");
return false;
}
if (options.loglevel) {
printf("> %s tcptunnel: request from %s\n", get_current_timestamp(), inet_ntoa(client_addr.sin_addr));
}
return true;
}
bool TUNNEL::build_tunnel() {
struct sockaddr_in remote_addr;
struct servent *se = getservbyname(options.remote_port,NULL);
struct hostent *he = gethostbyname(options.remote_host);
if (!he) {
perror("build_tunnel: gethostbyname()");
return false;
}
memset(&remote_addr, 0, sizeof remote_addr);
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = se ? se->s_port : htons(atoi(options.remote_port));
memcpy(&remote_addr.sin_addr.s_addr, he->h_addr, he->h_length);
remote_socket = socket(AF_INET, SOCK_STREAM, 0);
if (remote_socket < 0) {
perror("build_tunnel: socket()");
return false;
}
if (connect(remote_socket, (struct sockaddr *) &remote_addr, sizeof remote_addr) < 0) {
perror("build_tunnel: connect()");
return false;
}
return true;
}
int TUNNEL::fd() const{
SOCKET fd = client_socket;
if (fd < remote_socket) fd = remote_socket;
return (int)fd + 1;
}
// print-out mixed hex/ascii dump
void hdump(const char*data, int len, int withascii) {
while (len) {
int i,l=len;
if (l>16) l=16;
for (i=0; i<l; i++) printf("%02X ",(BYTE)data[i]);
if (withascii) {
for ( ;i<16; i++) printf(" ");
for (i=0; i<l; i++) printf("%c",isprint(data[i]) ? data[i] : '.');
}
printf("\n");
data+=l;
len-=l;
}
}
#pragma warning(disable:4200)
struct HEXWND{
HEXWND *next;
HWND Wnd;
char dir; // enthält '>' oder '<'
bool withascii;
int datalen; // Länge der Daten
char data[]; // vorhergehende Daten + Änderungsbits (dahinter)
void DoPaint(HDC dc, RECT* rc);
};
SIZE CharSize; // Zeichenzellengröße von SYSTEM_FIXED_FONT
DWORD GuiThreadId;
HEXWND *HexList; // EVL = Einfach verkettete Liste
static BYTE MixComponent(BYTE c255, BYTE c0, BYTE v) {
return MulDiv(c255-c0,v,255)+c0;
}
static COLORREF MixColor(COLORREF c255, COLORREF c0, BYTE v) {
COLORREF ret=0;
((BYTE*)&ret)[0]=MixComponent(((BYTE*)&c255)[0],((BYTE*)&c0)[0],v);
((BYTE*)&ret)[1]=MixComponent(((BYTE*)&c255)[1],((BYTE*)&c0)[1],v);
((BYTE*)&ret)[2]=MixComponent(((BYTE*)&c255)[2],((BYTE*)&c0)[2],v);
return ret;
}
void HEXWND::DoPaint(HDC dc, RECT* rc) {
SaveDC(dc);
SelectFont(dc,GetStockFont(SYSTEM_FIXED_FONT));
SetTextAlign(dc,TA_UPDATECP);
COLORREF Normal=GetSysColor(COLOR_WINDOWTEXT);
COLORREF Highlight=Normal ^ RGB(0,0,255);
for (int j=0; j<datalen; j+=16) {
MoveToEx(dc,0,(j>>4)*CharSize.cy,NULL);
char s[8];
SetTextColor(dc,Normal);
TextOut(dc,0,0,s,wsprintf(s,"%03X ",j)); // Adresse
int l=datalen-j; if (l>16) l=16; // Zeilenlänge in Bytes
for (int i=0; i<l; i++) {
SetTextColor(dc,MixColor(Highlight,Normal,data[datalen+i+j]));
TextOut(dc,0,0,s,wsprintf(s,"%02X ",(BYTE)data[i+j]));
}
if (withascii) {
MoveToEx(dc,(4+16*3)*CharSize.cx,(j>>4)*CharSize.cy,NULL);
for (int i=0; i<l; i++) {
SetTextColor(dc,MixColor(Highlight,Normal,data[datalen+i+j]));
s[0]=isprint(data[i+j]) ? data[i+j] : '.';
TextOut(dc,0,0,s,1);
}
}
}
RestoreDC(dc,-1);
}
/*
struct hist{
int datalen; // Länge der Daten
char data[64]; // vorhergehende Daten
};
// Funktion liefert 0 bei Gleichheit
int CmpHist(hist*h, char*data, int len) {
int l=h->datalen;
int cmpresult=1; // ungleich (größer) liefern wenn vorherige Daten leer
// 1. Teilschritt: vergleichen
if (l) {
if (l>len) l=len; // Minimum beider
if (l) cmpresult=memcmp(h->data,data,l);
}
// 2. Teilschritt: kopieren
l=MIN(len,sizeof h->data);
memcpy(h->data,data,l);
h->datalen=l;
return cmpresult;
}
*/
LRESULT CALLBACK ShowHexProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
HEXWND*H=(HEXWND*)GetWindowLongPtr(Wnd,0);
switch (Msg) {
case WM_NCCREATE: {
H=(HEXWND*)((CREATESTRUCT*)lParam)->lpCreateParams;
H->Wnd=Wnd;
SetWindowLongPtr(Wnd,0,(LONG_PTR)H);
}break;
case WM_PAINT: {
PAINTSTRUCT ps;
BeginPaint(Wnd,&ps);
H->DoPaint(ps.hdc,&ps.rcPaint);
EndPaint(Wnd,&ps);
}return 0;
}
return DefWindowProc(Wnd,Msg,wParam,lParam);
}
void ShowHexToWnd(char dir, const char*data, int len, bool withascii) {
HEXWND*H;
for (H=HexList;H;H=H->next) {
if (H->dir==dir && H->datalen==len) {
for (int i=0; i<H->datalen; i++) { // byteweise vergleichen
if (H->data[i]!=data[i]) H->data[H->datalen+i] = -1;
}
memcpy(H->data,data,H->datalen);
InvalidateRect(H->Wnd,NULL,FALSE); // TODO: partiell invalidieren
return;
}
}
H=(HEXWND*)new BYTE[sizeof HEXWND + len*2];
H->dir=dir;
H->withascii=withascii;
H->datalen=len;
memcpy(H->data,data,len);
memset(H->data+len,0xFF,len); // Änderungsbits allesamt setzen
// in EVL einhängen (nicht thread-sicher; macht nichts)
H->next=HexList;
HexList=H;
PostThreadMessage(GuiThreadId,0x1234,0,(LPARAM)H);
}
void CALLBACK TimerProc(HWND,UINT,UINT,DWORD) {
for (HEXWND*H=HexList;H;H=H->next) {
bool repaint=false;
for (int i=0; i<H->datalen; i++) {
if (H->data[H->datalen+i]) {
H->data[H->datalen+i]--;
repaint=true;
}
}
if (repaint) InvalidateRect(H->Wnd,NULL,FALSE); // TODO: partiell invalidieren
}
}
void KillWindows() {
for (HEXWND*H=HexList; H; ) {
DestroyWindow(H->Wnd);
HEXWND*n=H->next;
delete H;
H=n;
}
}
void LogData(char dir, const char*data, int len) {
if (options.loglevel) printf("%c %d %c\n", dir, len, dir);
switch (options.loglevel) {
case 0: break;
case 5: ShowHexToWnd(dir,data,len,true); break;
case 4: ShowHexToWnd(dir,data,len,false); break;
case 3: hdump(data,len,true); break;
case 2: hdump(data,len,false); break;
case 1: fwrite(data,1,len,stdout); break;
}
fflush(stdout);
}
bool TUNNEL::use_tunnel() {
// hist to_server, to_client;
for (;;) {
fd_set io;
char buffer[2048];
FD_ZERO(&io);
FD_SET(client_socket, &io);
FD_SET(remote_socket, &io);
if (select(fd(), &io, NULL, NULL, NULL) < 0 ) {
perror("use_tunnel: select()");
break;
}
if (FD_ISSET(client_socket, &io)) {
int count = recv(client_socket, buffer, sizeof(buffer), 0);
if (count < 0) {
perror("use_tunnel: recv(rc.client_socket)");
closesocket(client_socket);
closesocket(remote_socket);
return false;
}else if (!count) {
closesocket(client_socket);
closesocket(remote_socket);
return true;
}
send(remote_socket, buffer, count, 0);
LogData('>',buffer,count);
}
if (FD_ISSET(remote_socket, &io)) {
int count = recv(remote_socket, buffer, sizeof(buffer), 0);
if (count < 0) {
perror("use_tunnel: recv(rc.remote_socket)");
closesocket(client_socket);
closesocket(remote_socket);
return false;
}else if (!count) {
closesocket(client_socket);
closesocket(remote_socket);
return true;
}
send(client_socket, buffer, count, 0);
LogData('<',buffer,count);
}
}
return false;
}
void TUNNEL::handle_tunnel() {
if (build_tunnel()) use_tunnel();
}
// GUI-Thread
DWORD_PTR CALLBACK HexWndThread(void*) {
WNDCLASS wc;
memset(&wc,0,sizeof(wc));
wc.lpfnWndProc=ShowHexProc;
wc.cbWndExtra=sizeof(void*);
wc.hCursor=LoadCursor(0,IDC_ARROW);
wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
wc.lpszClassName="HexWnd";
RegisterClass(&wc);
HDC dc=GetDC(0);
SelectFont(dc,GetStockFont(SYSTEM_FIXED_FONT));
GetTextExtentPoint32(dc,"M",1,&CharSize);
ReleaseDC(0,dc);
SetTimer(0,10,10,TimerProc);
MSG Msg;
while (GetMessage(&Msg,0,0,0)) {
if (Msg.message==0x1234) { // Fenster entstehen lassen
HEXWND*H=(HEXWND*)Msg.lParam;
SIZE sz={CharSize.cx*(3+16*3),CharSize.cy*((H->datalen+15)>>4)};
if (H->withascii) sz.cx+=CharSize.cx*17;
RECT rc;
SetRect(&rc,0,0,sz.cx,sz.cy);
AdjustWindowRectEx(&rc,WS_CAPTION|WS_SYSMENU,FALSE,WS_EX_TOOLWINDOW);
char title[64];
wsprintf(title,"Hex %c %d",H->dir,H->datalen);
CreateWindowEx(WS_EX_TOOLWINDOW,"HexWnd",title,WS_CAPTION|WS_SYSMENU,
CW_USEDEFAULT,0,
rc.right-rc.left,rc.bottom-rc.top,
0,0,0,H);
ShowWindow(H->Wnd,SW_SHOWNA);
continue;
}
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
int main(int argc, char *argv[]) {
WSADATA info;
if (WSAStartup(MAKEWORD(1,1), &info) != 0) {
perror("main: WSAStartup()");
exit(1);
}
ExeName = PathFindFileName(argv[0]);
*PathFindExtension(ExeName)=0;
set_options(argc, argv);
if (options.loglevel>=4) {
CreateThread(NULL,0,HexWndThread,NULL,0,&GuiThreadId);
}
TUNNEL tu;
if (!tu.build_server()) return 1;
do{
if (tu.wait_for_client()) tu.handle_tunnel();
KillWindows();
}while (!options.stay_alive || --options.stay_alive);
PostThreadMessage(GuiThreadId,WM_QUIT,0,0);
closesocket(tu.server_socket);
return 0;
}
EXTERN_C _CRTIMP void _cdecl __getmainargs(int*,char***,char***,void*,void*);
void mainCRTStartup(void) {
int argc;
PTSTR *argv;
char **envp;
__getmainargs(&argc,&argv,&envp,NULL,&envp);
ExitProcess(main(argc,argv));
}
Detected encoding: ANSI (CP1252) | 4
|
|