WinAPI TCP Port Mapper

276
04 августа 2017, 01:22

Нужен исходник прокси сервера, пробрасывающего запросы от клиента (FireFox) к вышестоящему HTTP прокси и обратно.

Смотрел исходники tcptunnel. В принципе, то, что надо, только он однопользовательский, по этому не подходит. Городить поток для каждого клиента тоже не вариант.

Смотрел исходники ArashPartow/proxy. По функционалу подходит полностью, но нужно на WinAPI.

Вот код:

//
// tcpproxy_server.cpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2007 Arash Partow ([url]http://www.partow.net[/url])
// URL: [url]http://www.partow.net/programming/tcpproxy/index.html[/url]
//
// Distributed under the Boost Software License, Version 1.0.
//
//
// Description
// ~~~~~~~~~~~
// The  objective of  the TCP  proxy server  is to  act  as  an
// intermediary  in order  to 'forward'  TCP based  connections
// from external clients onto a singular remote server.
//
// The communication flow in  the direction from the  client to
// the proxy to the server is called the upstream flow, and the
// communication flow in the  direction from the server  to the
// proxy  to  the  client   is  called  the  downstream   flow.
// Furthermore  the   up  and   down  stream   connections  are
// consolidated into a single concept known as a bridge.
//
// In the event  either the downstream  or upstream end  points
// disconnect, the proxy server will proceed to disconnect  the
// other  end  point  and  eventually  destroy  the  associated
// bridge.
//
// The following is a flow and structural diagram depicting the
// various elements  (proxy, server  and client)  and how  they
// connect and interact with each other.
//
//                                    ---> upstream --->           +---------------+
//                                                     +---->------>               |
//                               +-----------+         |           | Remote Server |
//                     +--------->          [x]--->----+  +---<---[x]              |
//                     |         | TCP Proxy |            |        +---------------+
// +-----------+       |  +--<--[x] Server   <-----<------+
// |          [x]--->--+  |      +-----------+
// |  Client   |          |
// |           <-----<----+
// +-----------+
//                <--- downstream <---
//
//

#include <cstdlib>
#include <cstddef>
#include <iostream>
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/thread/mutex.hpp>

namespace tcp_proxy
{
   namespace ip = boost::asio::ip;
   class bridge : public boost::enable_shared_from_this<bridge>
   {
   public:
      typedef ip::tcp::socket socket_type;
      typedef boost::shared_ptr<bridge> ptr_type;
      bridge(boost::asio::io_service& ios)
      : downstream_socket_(ios),
        upstream_socket_  (ios)
      {}
      socket_type& downstream_socket()
      {
         // Client socket
         return downstream_socket_;
      }
      socket_type& upstream_socket()
      {
         // Remote server socket
         return upstream_socket_;
      }
      void start(const std::string& upstream_host, unsigned short upstream_port)
      {
         // Attempt connection to remote server (upstream side)
         upstream_socket_.async_connect(
              ip::tcp::endpoint(
                   boost::asio::ip::address::from_string(upstream_host),
                   upstream_port),
               boost::bind(&bridge::handle_upstream_connect,
                    shared_from_this(),
                    boost::asio::placeholders::error));
      }
      void handle_upstream_connect(const boost::system::error_code& error)
      {
         if (!error)
         {
            // Setup async read from remote server (upstream)
            upstream_socket_.async_read_some(
                 boost::asio::buffer(upstream_data_,max_data_length),
                 boost::bind(&bridge::handle_upstream_read,
                      shared_from_this(),
                      boost::asio::placeholders::error,
                      boost::asio::placeholders::bytes_transferred));
            // Setup async read from client (downstream)
            downstream_socket_.async_read_some(
                 boost::asio::buffer(downstream_data_,max_data_length),
                 boost::bind(&bridge::handle_downstream_read,
                      shared_from_this(),
                      boost::asio::placeholders::error,
                      boost::asio::placeholders::bytes_transferred));
         }
         else
            close();
      }
   private:
      /*
         Section A: Remote Server --> Proxy --> Client
         Process data recieved from remote sever then send to client.
      */
      // Read from remote server complete, now send data to client
      void handle_upstream_read(const boost::system::error_code& error,
                                const size_t& bytes_transferred)
      {
         if (!error)
         {
            async_write(downstream_socket_,
                 boost::asio::buffer(upstream_data_,bytes_transferred),
                 boost::bind(&bridge::handle_downstream_write,
                      shared_from_this(),
                      boost::asio::placeholders::error));
         }
         else
            close();
      }
      // Write to client complete, Async read from remote server
      void handle_downstream_write(const boost::system::error_code& error)
      {
         if (!error)
         {
            upstream_socket_.async_read_some(
                 boost::asio::buffer(upstream_data_,max_data_length),
                 boost::bind(&bridge::handle_upstream_read,
                      shared_from_this(),
                      boost::asio::placeholders::error,
                      boost::asio::placeholders::bytes_transferred));
         }
         else
            close();
      }
      // *** End Of Section A ***

      /*
         Section B: Client --> Proxy --> Remove Server
         Process data recieved from client then write to remove server.
      */
      // Read from client complete, now send data to remote server
      void handle_downstream_read(const boost::system::error_code& error,
                                  const size_t& bytes_transferred)
      {
         if (!error)
         {
            async_write(upstream_socket_,
                  boost::asio::buffer(downstream_data_,bytes_transferred),
                  boost::bind(&bridge::handle_upstream_write,
                        shared_from_this(),
                        boost::asio::placeholders::error));
         }
         else
            close();
      }
      // Write to remote server complete, Async read from client
      void handle_upstream_write(const boost::system::error_code& error)
      {
         if (!error)
         {
            downstream_socket_.async_read_some(
                 boost::asio::buffer(downstream_data_,max_data_length),
                 boost::bind(&bridge::handle_downstream_read,
                      shared_from_this(),
                      boost::asio::placeholders::error,
                      boost::asio::placeholders::bytes_transferred));
         }
         else
            close();
      }
      // *** End Of Section B ***
      void close()
      {
         boost::mutex::scoped_lock lock(mutex_);
         if (downstream_socket_.is_open())
         {
            downstream_socket_.close();
         }
         if (upstream_socket_.is_open())
         {
            upstream_socket_.close();
         }
      }
      socket_type downstream_socket_;
      socket_type upstream_socket_;
      enum { max_data_length = 8192 }; //8KB
      unsigned char downstream_data_[max_data_length];
      unsigned char upstream_data_  [max_data_length];
      boost::mutex mutex_;
   public:
      class acceptor
      {
      public:
         acceptor(boost::asio::io_service& io_service,
                  const std::string& local_host, unsigned short local_port,
                  const std::string& upstream_host, unsigned short upstream_port)
         : io_service_(io_service),
           localhost_address(boost::asio::ip::address_v4::from_string(local_host)),
           acceptor_(io_service_,ip::tcp::endpoint(localhost_address,local_port)),
           upstream_port_(upstream_port),
           upstream_host_(upstream_host)
         {}
         bool accept_connections()
         {
            try
            {
               session_ = boost::shared_ptr<bridge>(new bridge(io_service_));
               acceptor_.async_accept(session_->downstream_socket(),
                    boost::bind(&acceptor::handle_accept,
                         this,
                         boost::asio::placeholders::error));
            }
            catch(std::exception& e)
            {
               std::cerr << "acceptor exception: " << e.what() << std::endl;
               return false;
            }
            return true;
         }
      private:
         void handle_accept(const boost::system::error_code& error)
         {
            if (!error)
            {
               session_->start(upstream_host_,upstream_port_);
               if (!accept_connections())
               {
                  std::cerr << "Failure during call to accept." << std::endl;
               }
            }
            else
            {
               std::cerr << "Error: " << error.message() << std::endl;
            }
         }
         boost::asio::io_service& io_service_;
         ip::address_v4 localhost_address;
         ip::tcp::acceptor acceptor_;
         ptr_type session_;
         unsigned short upstream_port_;
         std::string upstream_host_;
      };
   };
}
int main(int argc, char* argv[])
{
   if (argc != 5)
   {
      std::cerr << "usage: tcpproxy_server <local host ip> <local port> <forward host ip> <forward port>" << std::endl;
      return 1;
   }
   const unsigned short local_port   = static_cast<unsigned short>(::atoi(argv[2]));
   const unsigned short forward_port = static_cast<unsigned short>(::atoi(argv[4]));
   const std::string local_host      = argv[1];
   const std::string forward_host    = argv[3];
   boost::asio::io_service ios;
   try
   {
      tcp_proxy::bridge::acceptor acceptor(ios,
                                           local_host, local_port,
                                           forward_host, forward_port);
      acceptor.accept_connections();
      ios.run();
   }
   catch(std::exception& e)
   {
      std::cerr << "Error: " << e.what() << std::endl;
      return 1;
   }
   return 0;
}
/*
 * [Note] On posix systems the tcp proxy server build command is as follows:
 * c++ -pedantic -ansi -Wall -Werror -O3 -o tcpproxy_server tcpproxy_server.cpp -L/usr/lib -lstdc++ -lpthread -lboost_thread -lboost_system
 */

Может кто сможет подсказать, набросать, как то же самое сделать на WinAPI?

Answer 1

С небольшими правками (массив sockets использовался неверно):

//                            Program  by  KMiNT21
//  (c) KMiNT21 - Ukraine, Zaporizhzhya.   mailto: kmint21@mail.ru  
//      Undeground InformatioN Center [UInC] - http://www.uinc.ru
// Если Вы хотите прочесть мою статью "Пишем прокси сервер" с подробнейшим
// комментированием этого исходника или "Синхронные и асинхронные сокеты
// в Windows", то идете на сервере www.uinc.ru в раздел статьи -
// (www.uinc.ru/articles). Первая - будет там в ближайшие дни (а возможно,
// уже там присутствует), а врорая уже есть. Все комментарии шлите на mail.

#pragma comment( lib, "ws2_32.lib" )
#include <windows.h>
#include <map>
// НАСТРОЙКА  ПАРАМЕТРОВ
#define IN_PORT     5150
#define OUT_IP      "127.0.0.1"
#define OUT_PORT    8079
#define MAX_DATA    100
//#define MAXCONN 1000
#define IDE_MSG 110
#define WM_ASYNC_CLIENTEVENT  WM_USER+1
#define WM_ASYNC_PROXYEVENT WM_USER+10
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void ConnectToProxy(SOCKET);

SOCKET hListenSockTCP = INVALID_SOCKET;
SOCKADDR_IN     myaddrTCP, proxyaddrTCP;
char buf[MAX_DATA];
//SOCKET sockets[MAXCONN];
std::map<SOCKET, SOCKET> sockets;
std::map<SOCKET, SOCKET> rsockets;
HWND hwndMain;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR szCmdLine, int nCmdShow)
{
  WSADATA stWSADataTCPIP;
  if (WSAStartup(0x0101, &stWSADataTCPIP)) MessageBox(0, __TEXT("WSAStartup error !"), __TEXT("NET ERROR!!!"), 0);
  //ZeroMemory(sockets, sizeof(sockets));
  WNDCLASS wc;
  memset(&wc, 0, sizeof(WNDCLASS));
  wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
  wc.lpfnWndProc = (WNDPROC)MainWndProc;
  wc.hInstance = hInst;
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszClassName = __TEXT("CProxy");
  wc.lpszMenuName = NULL;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  if (!RegisterClass(&wc)) return 0;
  hwndMain = CreateWindow(__TEXT("CProxy"), __TEXT("ProxyExample"), WS_MINIMIZEBOX | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_MAXIMIZEBOX | WS_CAPTION | WS_BORDER | WS_SYSMENU | WS_THICKFRAME, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL);
  ShowWindow(hwndMain, SW_SHOW);

  hListenSockTCP = socket(AF_INET, SOCK_STREAM, 0);
  myaddrTCP.sin_family = AF_INET;
  myaddrTCP.sin_addr.s_addr = htonl(INADDR_ANY);
  myaddrTCP.sin_port = htons(IN_PORT);
  if (bind(hListenSockTCP, (LPSOCKADDR)&myaddrTCP, sizeof(struct sockaddr))) { MessageBox(hwndMain, __TEXT("This port in use!"), __TEXT("BIND TCP ERROR!!!"), 0); }
  if (listen(hListenSockTCP, 5)) MessageBox(hwndMain, __TEXT("listen error!"), __TEXT("ERROR!!!"), 0);
  WSAAsyncSelect(hListenSockTCP, hwndMain, WM_ASYNC_CLIENTEVENT, FD_ACCEPT | FD_READ | FD_CLOSE);

  MSG  msg;
  while (GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
  return 0;
}

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  WORD WSAEvent;
  int i;
  DWORD currentsock;
  switch (msg)
  {
  case WM_COMMAND:
    break;
  case WM_DESTROY:
    PostQuitMessage(0);  // 10xu4yourtime
    break;

  case WM_ASYNC_CLIENTEVENT: // Сообщения про события сокетов, подключенных к клиенту...
    currentsock = wParam;
    WSAEvent = WSAGETSELECTEVENT(lParam);
    switch (WSAEvent)
    {
    case FD_CLOSE:
      shutdown(sockets[currentsock], 1);
      closesocket(currentsock);
      return 0;
    case FD_READ:
      // ПЕРЕНАПРАВЛЕНИЕ ДАННЫХ (redirect). Берем от клиента, посылаем на сервер.
      i = recv(currentsock, buf, MAX_DATA, 0);
      send(sockets[currentsock], buf, i, 0); // и отправляем...
      return 0;
    case FD_ACCEPT:
      ConnectToProxy(accept(hListenSockTCP, NULL, NULL));
      return 0;
    }
    break;
  case WM_ASYNC_PROXYEVENT:
    // Найдем соответствующий дескриптор.
    currentsock = rsockets[wParam];
    WSAEvent = WSAGETSELECTEVENT(lParam);
    switch (WSAEvent)
    {
      // Произошло подключение к удаленному хосту
    case FD_CONNECT:
      i = WSAGETSELECTERROR(lParam);
      if (i != 0)
      {
        shutdown(currentsock, 1);
        closesocket(sockets[currentsock]);
        sockets[currentsock] = INVALID_SOCKET;
      }
      return 0;

      // Сервер нас отрубает ...
    case FD_CLOSE:
      shutdown(currentsock, 1);
      closesocket(sockets[currentsock]);
      sockets[currentsock] = INVALID_SOCKET;
      return 0;
      // Перенаправление данных клиенту
    case FD_READ:
      i = recv(sockets[currentsock], buf, MAX_DATA, 0);
      send(currentsock, buf, i, 0);
      return 0;
    }
    break;
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}
// Connect to PROXY Connect to PROXY Connect to PROXY Connect to PROXY Connect to PROXY Connect to PROXY
void ConnectToProxy(SOCKET nofsock)
{
  SOCKADDR_IN rmaddr;
  rmaddr.sin_family = AF_INET;
  rmaddr.sin_addr.s_addr = inet_addr(OUT_IP);
  rmaddr.sin_port = htons(OUT_PORT);
  SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
  sockets[nofsock] = s;
  rsockets[s] = nofsock;
  if (INVALID_SOCKET == s)
    MessageBox(0, __TEXT("INVALID_SOCKET"), __TEXT("ERROR!!!"), 0);
  WSAAsyncSelect(s, hwndMain, WM_ASYNC_PROXYEVENT, FD_CONNECT | FD_READ | FD_CLOSE);
  connect(s, (struct sockaddr *)&rmaddr, sizeof(rmaddr));
  return;  // Connect OK
}

Можно считать, что задача решена. Сделал в проекте 2 конфигурации - для варианта на boost и варианта на winapi. Окно сделал невидимым.

P.S.: Хотел переписать с использованием WsaEventSelect, но WSAWaitForMultipleEvents накладывает ограничение: количество подключений <= WSA_MAXIMUM_WAIT_EVENTS (64). Это ограничение можно обойти, если запускать WSAWaitForMultipleEvents в новом потоке для подключений >= WSA_MAXIMUM_WAIT_EVENTS?

READ ALSO
Как работает wifstream и getline на больших файлах? [требует правки]

Как работает wifstream и getline на больших файлах? [требует правки]

Сделал построчное чтение с файла с помощью wifstream и getlineНа маленьких файлах все хорошо

380
Многопоточность и синхронизация в C++

Многопоточность и синхронизация в C++

Добрый день всем! В ходе работы с многопоточностью в C++ у меня возникли некоторые проблемыЯ качаю некоторые данные(файлы небольшого размера)...

425
Смысловая нагрузка значения &ldquo;минус NaN&rdquo;

Смысловая нагрузка значения “минус NaN”

В другом вопросе обнаружилось, что деление нуля на переменную, содержащую вещественный ноль, в результате даёт значение -nan

235