Logo Search packages:      
Sourcecode: papaya version File versions  Download package

SocksFive.cpp

#ifdef WIN32
#include <winsock.h>
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif

#ifdef LINUX
#include <stdint.h>
#endif

#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

#include <gtk/gtk.h>

#include "papaya/system.h"

#include "Message.h"
#include "SocksFive.h"
#include "Prefs.h"

/**
 * Default constructor, initialises anything not initialised in Socket()
 */

SocksFive::SocksFive(Connection * c) : Socket(c) {
  state = SOCKS_INITIAL;
  dest_host = NULL;
  dest_port = 0;
}

/**
 * Destructor.
 */

SocksFive::~SocksFive() {
  if (dest_host)
    free(dest_host);
}

/**
 * Creates a connection to a host via the Socks5 proxy.
 */

int SocksFive::create(char * host, int port) {

  dest_port = port;
  dest_host = strdup(host);

  if (!conn->queryPreferences()->getPreference("SocksHost")) {
    new Message("Error", _("Please edit this connection's preferences and specify the socks proxy's name."), true);
    return -1;
  }

  int res = Socket::create(conn->queryPreferences()->getPreference("SocksHost"), conn->queryPreferences()->getPreferenceInteger("SocksPort"));
  if (res == -1) {
    new Message("Error", _("Papaya was unable to contact the configured socks proxy."), true);
    return -1;
  }
  
  return res;
}

/**
 * Checks if we're connected, sending socks initialisation and waiting for
 * response before telling the client that we're connected.
 */

int SocksFive::connected() {

  int res;

  if (Socket::connected()) {
    switch (state) {
    case SOCKS_INITIAL: // Send the request to the server.
      struct socks5_request req;

      req.version = 5;
      req.nmethods = MAX_METHODS;
      req.methods[0] = SOCKS5_NO_AUTH;
      req.methods[1] = SOCKS5_USERNAME_PASSWORD_AUTH;

      write((char *)&req, sizeof(req));
      state = SOCKS_NEGOTIATION_INPROGRESS;
      break;

    case SOCKS_NEGOTIATION_INPROGRESS: // Waiting for a methods response.
      struct socks5_method_response resp;
      char buf[2];

      res = read(2, buf);

      if (res == -1)
      return -1;

      // Not yet connected.
      if (res == 0)
      return 0;

      // Unable to read all 8 bytes - error.
      if (res < 2)
      return -1;

      memcpy(&resp, buf, 2);

      // Set the appropriate state for sub-negotiation
      switch (resp.method) {
      case SOCKS5_NO_AUTH:
      state = SOCKS_AUTH_COMPLETE;
      break;

      case SOCKS5_GSSAPI_AUTH:
      new Message("Error", _("Socks 5 GSSAPI authentication not supported."), true);
      return -1;
      break;

      case SOCKS5_USERNAME_PASSWORD_AUTH:
      state = SOCKS_AUTHENTICATE_USERNAME_PASSWORD;
      break;

      default:
      new Message("Error", _("No acceptable socks 5 authentication method found."), true);
      return -1;
      }

      break;

    case SOCKS_AUTHENTICATE_USERNAME_PASSWORD:
      char outbuf[1024];
      char * user;
      char * pass;

      // Must send entire authentication in a single packet.

      user = conn->queryPreferences()->getPreference("SocksUser");
      pass = conn->queryPreferences()->getPreference("SocksPass");

      outbuf[0] = 1; // Version of the subnegotiation
      outbuf[1] = strlen(user);
      memcpy(outbuf+2, user, strlen(user));
      outbuf[2 + strlen(user)] = strlen(pass);
      memcpy(outbuf+2+strlen(user)+1, pass, strlen(pass));

      write(outbuf, 2 + strlen(user) + 1 + strlen(pass));
      state = SOCKS_AUTHENTICATE_USERNAME_PASSWORD_RESPONSE;
      break;      

    case SOCKS_AUTHENTICATE_USERNAME_PASSWORD_RESPONSE:
      struct socks5_username_response resp3;

      res = read(2, buf);

      if (res == -1)
      return -1;

      // Not yet connected.
      if (res == 0)
      return 0;

      // Unable to read all 2 bytes - error.
      if (res < 2)
      return -1;

      memcpy(&resp3, buf, 2);
      if (resp3.status != 0) {
      new Message("Error", _("Papaya tried to login to the Socks 5 proxy using the username and password you supplied, but the proxy rejected them."), true);
      return -1; // Failure
      }

      state = SOCKS_AUTH_COMPLETE;
      break;

    case SOCKS_AUTH_COMPLETE: // No authentication required at this point.
      struct socks5_fqdn_request req2;
      uint8_t dest_length;

      req2.version = 0x05;
      req2.command = 0x01; // Connect.
      req2.pad = 0x00;
      req2.atyp = 0x03; // We want the server to resolve.
      //      req.daddr = strdup(dest_host);
      req2.dport = htons(dest_port);

      write((char *)&req2, 4);

      dest_length = (uint8_t)strlen(dest_host);
      write((char *)&dest_length, 1);
      write(dest_host, strlen(dest_host));
      write((char *)&(req2.dport), 2);

      state = SOCKS_CONNECTION_REQUESTED;
      break;

    case SOCKS_CONNECTION_REQUESTED:
      struct socks5_fqdn_response resp2;
      char scratch[128];

      read(sizeof(struct socks5_fqdn_response), (char *)&resp2);
      switch (resp2.atyp) {
      case SOCKS5_V4ADDR:
      read(4, scratch);
      break;

      case SOCKS5_V6ADDR:
      read(32, scratch);
      break;

      case SOCKS5_DOMAINNAME:
      printf(_("Can't handle domain name type server response."));
      printf(CRLF);
      abort();
      }

      // Read the port number in.
      read(2, scratch);
      
      state = SOCKS_CONNECTED;
      return 1;

    case SOCKS_CONNECTED:
      return 1;
      
    }

  }
  return 0;
}

Generated by  Doxygen 1.6.0   Back to index