{"id":2616,"date":"2018-09-02T16:57:14","date_gmt":"2018-09-02T08:57:14","guid":{"rendered":"https:\/\/nodelay.xyz\/?p=2616"},"modified":"2018-09-02T16:57:14","modified_gmt":"2018-09-02T08:57:14","slug":"arduino-as-socks5-proxy-server","status":"publish","type":"post","link":"https:\/\/dayandcarrot.space\/?p=2616","title":{"rendered":"Arduino as Socks5 Proxy Server"},"content":{"rendered":"<p>Still <span style=\"color: #ff0000;\"><strong>WIP<\/strong> <\/span>because the buffer manipulation is so f**king problematic.<br \/>\nHardware: Arduino Uno + W5100 Ethernet Module<br \/>\nTested working through curl.<\/p>\n<pre class=\"lang:c++ decode:true \">#include &lt;SPI.h&gt;\n#include &lt;Ethernet.h&gt;\nconst uint8_t MAX_CONN = 4;\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/Socks5 Server\n\/\/ Ref: https:\/\/github.com\/mfontanini\/Programs-Scripts\/blob\/master\/socks5\/socks5.cpp\n\/\/\nconst uint16_t SERVER_PORT = 23;\nconst int BUFFER_SIZE = 512;\nbyte socks_buffer[BUFFER_SIZE];\n#define METHOD_NOAUTH       0\n#define METHOD_AUTH         2\n#define METHOD_NOTAVAILABLE 0xff\n\/* Responses *\/\n#define RESP_SUCCEDED       0\n#define RESP_GEN_ERROR      1\n\/* Address type constants *\/\n#define ATYP_IPV4   1\n#define ATYP_DNAME  3\n#define ATYP_IPV6   4\n\/* Command constants *\/\n#define CMD_CONNECT         1\n#define CMD_BIND            2\n#define CMD_UDP_ASSOCIATIVE 3\n#define htons(x) ( ((x)&lt;&lt; 8 &amp; 0xFF00) | \\\n                   ((x)&gt;&gt; 8 &amp; 0x00FF) )\n#define ntohs(x) htons(x)\n#define htonl(x) ( ((x)&lt;&lt;24 &amp; 0xFF000000UL) | \\\n                   ((x)&lt;&lt; 8 &amp; 0x00FF0000UL) | \\\n                   ((x)&gt;&gt; 8 &amp; 0x0000FF00UL) | \\\n                   ((x)&gt;&gt;24 &amp; 0x000000FFUL) )\n#define ntohl(x) htonl(x)\nstruct MethodIdentificationPacket\n{\n  uint8_t version_;\n  uint8_t nmethods_;\n} __attribute__((packed));\nstruct MethodSelectionPacket\n{\n  uint8_t version_;\n  uint8_t method_;\n  MethodSelectionPacket() : version_(5) {\n  }\n} __attribute__((packed));\nstruct SOCKS5RequestHeader\n{\n  uint8_t version_;\n  uint8_t cmd_;\n  uint8_t rsv_;\n  uint8_t atyp_;\n} __attribute__((packed));\nstruct SOCK5IP4RequestBody\n{\n  uint32_t ip_dst_;\n  uint16_t port_;\n} __attribute__((packed));\nstruct SOCK5DNameRequestBody\n{\n  uint8_t length_;\n} __attribute__((packed));\nstruct SOCKS5Response\n{\n  uint8_t version_;\n  uint8_t cmd_;\n  uint8_t rsv_;\n  uint8_t atyp_;\n  uint32_t ip_src_;\n  uint16_t port_src_;\n  SOCKS5Response(bool succ = true) :\n    version_(5),\n    cmd_(succ ? RESP_SUCCEDED : RESP_GEN_ERROR),\n    rsv_(0),\n    atyp_(ATYP_IPV4)\n  { }\n} __attribute__((packed));\nbool handle_handshake(EthernetClient* client)\n{\n  MethodIdentificationPacket packet;\n  int count = client-&gt;read((byte*)&amp;packet, sizeof(packet));\n  if (count != sizeof(packet) || packet.version_ != 5)\n  {\n    Serial.print(\"wrong handshake version!\");\n    return false;\n  }\n  Serial.print((int)packet.version_);\n  Serial.print(\",\");\n  Serial.println((int)packet.nmethods_);\n  count = client-&gt;read(socks_buffer, packet.nmethods_);\n  if (count != packet.nmethods_)\n  {\n    Serial.print(\"wrong methods length:\");\n    Serial.println(count);\n    return false;\n  }\n  MethodSelectionPacket response;\n  for (int i = 0; i &lt; packet.nmethods_; ++i){\n    if (socks_buffer[i] == METHOD_NOAUTH) {\n      response.method_ = METHOD_NOAUTH;\n      break;\n    } else if (socks_buffer[i] == METHOD_AUTH) {\n      response.method_ = METHOD_AUTH;\n      break;\n    } else {\n      response.method_ = METHOD_NOTAVAILABLE;\n    }\n  }\n  if (response.method_ != METHOD_NOAUTH)\n  {\n    Serial.print(\"method not supported:\");\n    Serial.println((int)response.method_);\n    return false;\n  }\n  count = client-&gt;write((byte*)&amp;response, sizeof(response));\n  if (count != sizeof(response))\n  {\n    Serial.println(\"response send failure\");\n  }\n  return true;\n}\nEthernetClient* connect_to_host(uint32_t ip, uint16_t port) {\n  Serial.print(\"connect to \");\n  Serial.print(ip);\n  Serial.print(\"@\");\n  Serial.println(port);\n  EthernetClient* host_conn = new EthernetClient();\n  int result = host_conn-&gt;connect(ip, port);\n  if (host_conn-&gt;connected())\n  {\n    Serial.println(\"connected\");\n    return host_conn;\n  }\n  else\n  {\n    Serial.print(\"failed to connect:\");\n    Serial.println(result);\n    delete host_conn;\n  }\n  return NULL;\n}\nvoid do_proxy(EthernetClient* remote, EthernetClient* client) {\n  \/\/TODO: set a counter to break and let others connect\n  while (remote-&gt;connected() &amp;&amp; client-&gt;connected()) {\n    \/\/ see if we have any requst from client\n    int recv = client-&gt;read(socks_buffer, sizeof(socks_buffer));\n    if (recv &gt; 0) {\n      remote-&gt;write(socks_buffer, recv);\n      Serial.print(\"recv from remote: \");\n      Serial.println(recv);\n    }\n    \/\/ see if we have any response from remote\n    recv = remote-&gt;read(socks_buffer, sizeof(socks_buffer));\n    if (recv &gt; 0) {\n      client-&gt;write(socks_buffer, recv);\n      Serial.print(\"recv from client: \");\n      Serial.println(recv);\n    }\n  }\n}\nbool handle_request(EthernetClient* client) {\n  SOCKS5RequestHeader header;\n  int count = -1;\n  while (count &lt; 0)\n  {\n    count = client-&gt;read((byte*)&amp;header, sizeof(header));\n  }\n  if (header.version_ != 5 || header.cmd_ != CMD_CONNECT || header.rsv_ != 0)\n  {\n    Serial.println(\"request header mismatch\");\n    return false;\n  }\n  EthernetClient* client_sock = NULL;\n  switch(header.atyp_) {\n    case ATYP_IPV4:\n    {\n      SOCK5IP4RequestBody req;\n      count = client-&gt;read((byte*)&amp;req, sizeof(req));\n      if (count != sizeof(req))\n      {\n        Serial.println(\"SOCK5IP4RequestBody recv size mismatch\");\n        return false;\n      }\n      client_sock = connect_to_host(req.ip_dst_, ntohs(req.port_));\n      break;\n    }\n    case ATYP_DNAME:\n      break;\n    default:\n      return false;\n  }\n  if (client_sock == NULL)\n    return false;\n  SOCKS5Response response;\n  response.ip_src_ = 0;\n  response.port_src_ = SERVER_PORT;\n  client-&gt;write((byte*)&amp;response, sizeof(response));\n  do_proxy(client_sock, client);\n  if (client_sock != NULL)\n  {\n    client_sock-&gt;stop();\n    delete client_sock;\n    Serial.println(\"stopped host connection\");\n  }\n}\n\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\n\/\/ Enter a MAC address and IP address for your controller below.\n\/\/ The IP address will be dependent on your local network.\n\/\/ gateway and subnet are optional:\nbyte mac[] = {\n  0xDE, 0xAD, 0xBE, 0xEF, 0xAA, 0xED\n};\nIPAddress ip(192, 168, 31, 207);\nIPAddress myDns(192, 168, 31, 1);\nIPAddress gateway(192, 168, 31, 1);\nIPAddress subnet(255, 255, 255, 0);\n\/\/ telnet defaults to port 23\nEthernetServer server(SERVER_PORT);\nEthernetClient clients[MAX_CONN];\nbool clients_handshaken[MAX_CONN];\nvoid setup() {\n  \/\/ You can use Ethernet.init(pin) to configure the CS pin\n  Ethernet.init(10);  \/\/ Most Arduino shields\n  \/\/Ethernet.init(5);   \/\/ MKR ETH shield\n  \/\/Ethernet.init(0);   \/\/ Teensy 2.0\n  \/\/Ethernet.init(20);  \/\/ Teensy++ 2.0\n  \/\/Ethernet.init(15);  \/\/ ESP8266 with Adafruit Featherwing Ethernet\n  \/\/Ethernet.init(33);  \/\/ ESP32 with Adafruit Featherwing Ethernet\n  \/\/ initialize the Ethernet device\n  Ethernet.begin(mac, ip, myDns, gateway, subnet);\n  \/\/ Open serial communications and wait for port to open:\n  Serial.begin(9600);\n  while (!Serial) {\n    ; \/\/ wait for serial port to connect. Needed for native USB port only\n  }\n  \/\/ Check for Ethernet hardware present\n  if (Ethernet.hardwareStatus() == EthernetNoHardware) {\n    Serial.println(\"Ethernet shield was not found.  Sorry, can't run without hardware. :(\");\n    while (true) {\n      delay(1); \/\/ do nothing, no point running without Ethernet hardware\n    }\n  }\n  if (Ethernet.linkStatus() == LinkOFF) {\n    Serial.println(\"Ethernet cable is not connected.\");\n  }\n  \/\/ start listening for clients\n  server.begin();\n  Serial.print(\"Chat server address:\");\n  Serial.println(Ethernet.localIP());\n}\nvoid loop() {\n  \/\/ check for any new client connecting, and say hello (before any incoming data)\n  EthernetClient newClient = server.accept();\n  if (newClient) {\n    for (byte i = 0; i &lt; MAX_CONN; i++) {\n      if (!clients[i]) {\n        Serial.print(\"We have a new client #\");\n        Serial.println(i);\n        \/\/ Once we \"accept\", the client is no longer tracked by EthernetServer\n        \/\/ so we must store it into our list of clients\n        clients[i] = newClient;\n        clients_handshaken[i] = false;\n        break;\n      }\n    }\n  }\n  \/\/ check for incoming data from all clients\n  for (byte i = 0; i &lt; MAX_CONN; i++) {\n    if (clients[i] &amp;&amp; clients[i].available() &gt; 0) {\n      if (clients_handshaken[i] == true) {\n        Serial.println(\"&gt;&gt;&gt;\");\n        handle_request(&amp;clients[i]);\n      }else if (handle_handshake(&amp;clients[i])) {\n        Serial.println(\"===\");\n        clients_handshaken[i] = true;\n        handle_request(&amp;clients[i]);\n      }\n    }\n  }\n  \/\/ stop any clients which disconnect\n  for (byte i = 0; i &lt; MAX_CONN; i++) {\n    if (clients[i] &amp;&amp; !clients[i].connected()) {\n      Serial.print(\"disconnect client #\");\n      Serial.println(i);\n      clients[i].stop();\n      clients_handshaken[i] = false;\n    }\n  }\n}<\/pre>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Still WIP because the buffer manipulation is so f**king problematic. Hardware: Arduino Uno + W5100 Ethernet Module Tested working through curl. #include &lt;SPI.h&gt; #include &lt;Ethernet.h&gt; const uint8_t MAX_CONN = 4; \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ \/\/Socks5 Server \/\/ Ref: https:\/\/github.com\/mfontanini\/Programs-Scripts\/blob\/master\/socks5\/socks5.cpp \/\/ const uint16_t SERVER_PORT = 23; const int BUFFER_SIZE = 512; byte socks_buffer[BUFFER_SIZE]; #define METHOD_NOAUTH 0 #define METHOD_AUTH 2 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[22],"class_list":["post-2616","post","type-post","status-publish","format-standard","hentry","category-technical","tag-arduino"],"_links":{"self":[{"href":"https:\/\/dayandcarrot.space\/index.php?rest_route=\/wp\/v2\/posts\/2616","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dayandcarrot.space\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dayandcarrot.space\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dayandcarrot.space\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dayandcarrot.space\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2616"}],"version-history":[{"count":0,"href":"https:\/\/dayandcarrot.space\/index.php?rest_route=\/wp\/v2\/posts\/2616\/revisions"}],"wp:attachment":[{"href":"https:\/\/dayandcarrot.space\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2616"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dayandcarrot.space\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2616"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dayandcarrot.space\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2616"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}