Source file: /~heha/hs/finger.zip/SRC/TcpTunnel.cpp

/* 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
Wrong umlauts? - Assume file is ANSI (CP1252) encoded