sockit 0.0.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 50833e20c884206e0980ecc9f51dac27923f9576
4
+ data.tar.gz: 5655f2fb0c447475ebe9d917ee36cdb4ddb6a4fe
5
+ SHA512:
6
+ metadata.gz: f99c488283970a80aa8f78a4e33833e4de12d5aa771163fcc538a7618783e064e40b973aad0e1256e2c72836beb812bca327d808fa74815c06c9c521b7a9785a
7
+ data.tar.gz: 2a2c634c3dcc3ae1f82206229ca69aaafc93f7fb616de09855b696ca0b2575f82c2e7bbc7bb89cd65e893919ee47b29ad7c6c73c5bd053ad5883e873793ef330
data/.rspec CHANGED
@@ -1 +1 @@
1
- -c -b -fd
1
+ -c -b -fd --require spec_helper
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ sockit
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.3.0
data/.travis.yml CHANGED
@@ -1,14 +1,16 @@
1
1
  language: ruby
2
-
3
2
  rvm:
4
- - ree-1.8.7
5
- - 1.8.7
6
- - 1.9.2
7
- - 1.9.3
8
-
3
+ - 2.0.0
4
+ - 2.2.4
5
+ - 2.3.0
9
6
  branches:
10
7
  only:
11
- - master
12
-
13
- before_script:
14
- - ./bin/setup-travis-ci.sh
8
+ - master
9
+ before_install:
10
+ - "./spec/support/before_install.sh"
11
+ notifications:
12
+ irc:
13
+ channels:
14
+ - "irc.freenode.net#jovelabs"
15
+ use_notice: true
16
+ skip_join: true
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ --markup-provider=redcarpet
2
+ --markup=markdown
data/README.md CHANGED
@@ -1,10 +1,19 @@
1
+ [![Gem Version](https://badge.fury.io/rb/sockit.png)](http://badge.fury.io/rb/sockit)
1
2
  [![Build Status](https://secure.travis-ci.org/zpatten/sockit.png)](http://travis-ci.org/zpatten/sockit)
3
+ [![Coverage Status](https://coveralls.io/repos/zpatten/sockit/badge.png?branch=master)](https://coveralls.io/r/zpatten/sockit)
2
4
  [![Dependency Status](https://gemnasium.com/zpatten/sockit.png)](https://gemnasium.com/zpatten/sockit)
3
5
  [![Code Climate](https://codeclimate.com/github/zpatten/sockit.png)](https://codeclimate.com/github/zpatten/sockit)
4
6
 
5
7
  # SOCKIT
6
8
 
7
- Transparent SOCKS 5 support for TCPSocket
9
+ Transparent SOCKS v4 and SOCKS v5 support for TCPSocket
10
+
11
+ Seamlessly route all TCP for you application through a SOCKS server with nearly zero effort. Once `require`'d and configured all traffic leveraging the `TCPSocket` class will route via your configured SOCKS server.
12
+
13
+ This is especially useful for many cases; here are a couple:
14
+
15
+ - Ensure all outbound traffic from your application appears from a single IP.
16
+ - Ensure development traffic does not give away private IP addresses.
8
17
 
9
18
  ## INSTALLATION
10
19
 
@@ -30,36 +39,36 @@ You can configure on the singleton class or an instance of the class. The SOCKS
30
39
 
31
40
  The defaults are as follows:
32
41
 
33
- TCPSocket.socks do |socks|
34
- socks.version = 5
35
- socks.ignore = ["127.0.0.1"]
36
- socks.debug = false
42
+ Sockit.config do |config|
43
+ config.version = 5
44
+ config.ignore = ["127.0.0.1"]
45
+ config.debug = false
37
46
  end
38
47
 
39
48
  Specify your SOCKS server and port:
40
49
 
41
- TCPSocket.socks do |socks|
42
- socks.host = "127.0.0.1"
43
- socks.port = "1080"
50
+ Sockit.config do |config|
51
+ config.host = "127.0.0.1"
52
+ config.port = "1080"
44
53
  end
45
54
 
46
55
  If you want to use username/password authentication:
47
56
 
48
- TCPSocket.socks do |socks|
49
- socks.username = "username"
50
- socks.password = "password"
57
+ Sockit.config do |config|
58
+ config.username = "username"
59
+ config.password = "password"
51
60
  end
52
61
 
53
62
  Turn on debug output:
54
63
 
55
- TCPSocket.socks do |socks|
56
- socks.debug = true
64
+ Sockit.config do |config|
65
+ config.debug = true
57
66
  end
58
67
 
59
68
  Ignore some more hosts:
60
69
 
61
- TCPSocket.socks do |socks|
62
- socks.ignore << "192.168.0.1"
70
+ Sockit.config do |config|
71
+ config.ignore << "192.168.0.1"
63
72
  end
64
73
 
65
74
 
@@ -85,7 +94,7 @@ Documentation:
85
94
 
86
95
  SOCKIT - Transparent SOCKS 5 support for TCPSocket
87
96
 
88
- * Author: Zachary Patten <zachary@jovelabs.net>
97
+ * Author: Zachary Patten <zachary AT jovelabs DOT com> [![endorse](http://api.coderwall.com/zpatten/endorsecount.png)](http://coderwall.com/zpatten)
89
98
  * Copyright: Copyright (c) Zachary Patten
90
99
  * License: Apache License, Version 2.0
91
100
 
data/bin/sockit CHANGED
@@ -19,8 +19,9 @@
19
19
  #
20
20
  ################################################################################
21
21
 
22
- require "pry"
23
- require "sockit"
22
+ require 'bundler/setup'
23
+ require 'pry'
24
+ require 'sockit'
24
25
 
25
26
  ##
26
27
  #
@@ -0,0 +1,43 @@
1
+ module Sockit
2
+ module Connection
3
+
4
+ def direct_connect(socket, remote_host, remote_port, local_host=nil, local_port=nil)
5
+ log(:yellow, "Directly connecting to #{remote_host}:#{remote_port}")
6
+ socket.__send__(:initialize_tcp, remote_host, remote_port, local_host=nil, local_port=nil)
7
+ log(:green, "Connected to #{remote_host}:#{remote_port}")
8
+ end
9
+
10
+ def connect(socket, host, port)
11
+ log(:yellow, "Connecting to SOCKS v#{config.version} server #{config.host}:#{config.port}")
12
+
13
+ # when doing proxy mode on SS5; we seemingly need to resolve all names first.
14
+ if host !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
15
+ host = Resolv::DNS.new.getaddress(host).to_s
16
+ end
17
+
18
+ data = case config.version.to_i
19
+ when 4 then
20
+ build_v4_connection_request(host, port)
21
+ when 5 then
22
+ build_v5_connection_request(host, port)
23
+ end
24
+ data = data.flatten.join
25
+
26
+ log(:yellow, "Requesting SOCKS v#{config.version} connection to #{host}:#{port}")
27
+ dump(:write, data)
28
+ socket.write(data)
29
+
30
+ log(:yellow, "Waiting for SOCKS v#{config.version} connection reply")
31
+ host, port = case config.version.to_i
32
+ when 4 then
33
+ process_v4_connection_response(socket)
34
+ when 5 then
35
+ process_v5_connection_response(socket)
36
+ end
37
+ log(:green, [host, port].inspect)
38
+
39
+ log(:green, "Connected to #{host}:#{port} via SOCKS v#{config.version} server #{config.host}:#{config.port}")
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,53 @@
1
+ module Sockit
2
+ module Support
3
+
4
+ def is_host_configured?
5
+ (!config.host.nil? && !config.host.empty?)
6
+ end
7
+
8
+ def is_port_configured?
9
+ (!config.port.nil? && !config.port.empty?)
10
+ end
11
+
12
+ def is_version_configured?
13
+ ((config.version == 4) || (config.version == 5))
14
+ end
15
+
16
+ def is_configured?
17
+ (is_host_configured? && is_port_configured? && is_version_configured?)
18
+ end
19
+
20
+ def connect_via_socks?(host)
21
+ (is_configured? && !config.ignore.flatten.include?(host))
22
+ end
23
+
24
+ def is_socks_v5?
25
+ (is_configured? && config.version.to_i == 5)
26
+ end
27
+
28
+ def is_socks_v4?
29
+ (is_configured? && config.version.to_i == 4)
30
+ end
31
+
32
+ def log(color, message)
33
+ return if !config.debug
34
+
35
+ timestamp = Time.now.utc
36
+ puts("%s%s.%06d %s%s" % [COLORS[color], timestamp.strftime("%Y-%m-%d|%H:%M:%S"), timestamp.usec, message, COLORS[:reset]])
37
+ end
38
+
39
+ def dump(action, data)
40
+ return if !config.debug
41
+
42
+ bytes = Array.new
43
+ chars = Array.new
44
+ for x in 0..(data.length - 1) do
45
+ bytes << ("%03d" % data[x].ord)
46
+ chars << ("%03s" % (data[x] =~ /^\w+$/ ? data[x].chr : "..."))
47
+ end
48
+ log(:blue, "#{action.to_s.upcase}: #{bytes.join(" ")}#{COLORS[:reset]}")
49
+ log(:blue, "#{action.to_s.upcase}: #{chars.join(" ")}#{COLORS[:reset]}")
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,62 @@
1
+ module Sockit
2
+ module V4
3
+ module Connection
4
+
5
+ # SOCKS v4 Client Connection Request
6
+ # field 1: SOCKS version number, 1 byte (must be 0x04 for this version)
7
+ # field 2: command code, 1 byte:
8
+ # 0x01 = establish a TCP/IP stream connection
9
+ # field 3: port number in a network byte order, 2 bytes
10
+ # field 4: destination address of
11
+ # 4 bytes for IPv4 address
12
+ # field 5: userid
13
+ # variables bytes for userid, null terminate (0x00)
14
+ def build_v4_connection_request(host, port)
15
+ data = Array.new
16
+ data << [config.version.to_i, 0x01].pack("C*")
17
+
18
+ data << [port.to_i].pack("n")
19
+ if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
20
+ data << [$1.to_i, $2.to_i, $3.to_i, $4.to_i].pack("C*")
21
+ end
22
+ data << config.username
23
+ data << [0x00].pack("C*")
24
+
25
+ data
26
+ end
27
+
28
+ # SOCKS v4 Server Connection Response
29
+ # field 1: version number, 1 byte (must be 0x00)
30
+ # field 2: result code, 1 byte:
31
+ # 90 = request granted
32
+ # 91 = request rejected or failed
33
+ # 92 = request rejected because SOCKS server can not connect to identd on the client
34
+ # 93 = request rejected because the client program and identd report different user-ids
35
+ # field 3: port number in a network byte order, 2 bytes
36
+ # field 4: destination address of
37
+ # 4 bytes for IPv4 address
38
+ def process_v4_connection_response(socket)
39
+ packet = socket.recv(2).unpack("C*")
40
+ dump(:read, packet)
41
+ version = packet[0]
42
+ result_code = packet[1]
43
+
44
+ case result_code
45
+ when 90 then
46
+ log(:green, build_v4_result_code_message(result_code))
47
+ else
48
+ raise SockitError, build_v4_result_code_message(result_code)
49
+ end
50
+
51
+ port = socket.recv(2).unpack("n")
52
+ dump(:read, port)
53
+
54
+ host = socket.recv(4).unpack("C*")
55
+ dump(:read, host)
56
+
57
+ [host.join('.'), port.join]
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,31 @@
1
+ module Sockit
2
+ module V4
3
+ module Support
4
+
5
+ # 90 = request granted
6
+ # 91 = request rejected or failed
7
+ # 92 = request rejected because SOCKS server can not connect to identd on the client
8
+ # 93 = request rejected because the client program and identd report different user-ids
9
+ def build_v4_result_code_message(result_code)
10
+ message = case result_code
11
+ when 90 then
12
+ "Request granted"
13
+ when 91 then
14
+ "Request rejected or failed"
15
+ when 92 then
16
+ "Request rejected because SOCKS server can not connect to identd on the client"
17
+ when 93 then
18
+ "Request rejected because the client program and identd report different user-ids"
19
+ else
20
+ "Unknown"
21
+ end
22
+
23
+ "%s (Code: 0x%02X)" % [message, result_code]
24
+
25
+ rescue
26
+ "Result Code: #{result_code.inspect}"
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,105 @@
1
+ module Sockit
2
+ module V5
3
+ module Authentication
4
+
5
+ def perform_v5_authenticate(socket)
6
+ log(:yellow, "Authenticating with SOCKS server #{config.host}:#{config.port}")
7
+
8
+ # The authentication methods supported are numbered as follows:
9
+ # 0x00: No authentication
10
+ # 0x01: GSSAPI[10]
11
+ # 0x02: Username/Password[11]
12
+ # 0x03-0x7F: methods assigned by IANA[12]
13
+ # 0x80-0xFE: methods reserved for private use
14
+
15
+ # The initial greeting from the client is
16
+ # field 1: SOCKS version number (must be 0x05 for this version)
17
+ # field 2: number of authentication methods supported, 1 byte
18
+ # field 3: authentication methods, variable length, 1 byte per method supported
19
+ if (config.username || config.password)
20
+ data = Array.new
21
+ data << [config.version, 0x02, 0x02, 0x00].pack("C*")
22
+ data = data.flatten.join
23
+
24
+ log(:yellow, "Requesting username/password authentication")
25
+ dump(:write, data)
26
+ socket.write(data)
27
+ else
28
+ data = Array.new
29
+ data << [config.version, 0x01, 0x00].pack("C*")
30
+ data = data.flatten.join
31
+
32
+ log(:yellow, "Requesting no authentication")
33
+ dump(:write, data)
34
+ socket.write(data)
35
+ end
36
+
37
+ # The server's choice is communicated:
38
+ # field 1: SOCKS version, 1 byte (0x05 for this version)
39
+ # field 2: chosen authentication method, 1 byte, or 0xFF if no acceptable methods were offered
40
+ log(:yellow, "Waiting for SOCKS authentication reply")
41
+ auth_reply = socket.recv(2).unpack("C*")
42
+ dump(:read, auth_reply)
43
+ server_socks_version = auth_reply[0]
44
+ server_auth_method = auth_reply[1]
45
+
46
+ if server_socks_version != config.version
47
+ raise SockitError, "SOCKS server does not support version #{config.version}!"
48
+ end
49
+
50
+ if server_auth_method == 0xFF
51
+ raise SockitError, authentication_method(server_auth_method)
52
+ else
53
+ log(:green, authentication_method(server_auth_method))
54
+ end
55
+
56
+ # The subsequent authentication is method-dependent. Username and password authentication (method 0x02) is described in RFC 1929:
57
+ case server_auth_method
58
+ when 0x00 then
59
+ # No authentication
60
+ when 0x01 then
61
+ # GSSAPI
62
+ raise SockitError, "Authentication method GSSAPI not implemented"
63
+ when 0x02 then
64
+ # For username/password authentication the client's authentication request is
65
+ # field 1: version number, 1 byte (must be 0x01)
66
+ # field 2: username length, 1 byte
67
+ # field 3: username
68
+ # field 4: password length, 1 byte
69
+ # field 5: password
70
+ data = Array.new
71
+ data << [0x01].pack("C*")
72
+ data << [config.username.length.to_i].pack("C*")
73
+ data << config.username
74
+ data << [config.password.length.to_i].pack("C*")
75
+ data << config.password
76
+ data = data.flatten.join
77
+
78
+ log(:yellow, "Sending username and password")
79
+ dump(:write, data)
80
+ socket.write(data)
81
+
82
+ # Server response for username/password authentication:
83
+ # field 1: version, 1 byte
84
+ # field 2: status code, 1 byte.
85
+ # 0x00 = success
86
+ # any other value = failure, connection must be closed
87
+ log(:yellow, "Waiting for SOCKS authentication reply")
88
+ auth_reply = socket.recv(2).unpack("C*")
89
+ dump(:read, auth_reply)
90
+ version = auth_reply[0]
91
+ status_code = auth_reply[1]
92
+
93
+ if status_code == 0x00
94
+ log(:green, authentication_status(status_code))
95
+ else
96
+ raise SockitError, authentication_status(status_code)
97
+ end
98
+ end
99
+
100
+ log(:green, "Authenticated to SOCKS server #{config.host}:#{config.port}")
101
+ end
102
+
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,98 @@
1
+ module Sockit
2
+ module V5
3
+ module Connection
4
+
5
+ # SOCKS v5 Client Connection Request
6
+ # field 1: SOCKS version number, 1 byte (must be 0x05 for this version)
7
+ # field 2: command code, 1 byte:
8
+ # 0x01 = establish a TCP/IP stream connection
9
+ # 0x02 = establish a TCP/IP port binding
10
+ # 0x03 = associate a UDP port
11
+ # field 3: reserved, must be 0x00
12
+ # field 4: address type, 1 byte:
13
+ # 0x01 = IPv4 address
14
+ # 0x03 = Domain name
15
+ # 0x04 = IPv6 address
16
+ # field 5: destination address of
17
+ # 4 bytes for IPv4 address
18
+ # 1 byte of name length followed by the name for Domain name
19
+ # 16 bytes for IPv6 address
20
+ # field 6: port number in a network byte order, 2 bytes
21
+ def build_v5_connection_request(host, port)
22
+ data = Array.new
23
+ data << [config.version.to_i, 0x01, 0x00].pack("C*")
24
+
25
+ if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
26
+ data << [0x01].pack("C*")
27
+ data << [$1.to_i, $2.to_i, $3.to_i, $4.to_i].pack("C*")
28
+ elsif host =~ /^[:0-9a-f]+$/
29
+ data << [0x04].pack("C*")
30
+ data << [$1].pack("C*")
31
+ else
32
+ data << [0x03].pack("C*")
33
+ data << [host.length.to_i].pack("C*")
34
+ data << host
35
+ end
36
+ data << [port.to_i].pack("n")
37
+
38
+ data
39
+ end
40
+
41
+ # SOCKS v5 Server Connection Response
42
+ # field 1: SOCKS protocol version, 1 byte (0x05 for this version)
43
+ # field 2: status, 1 byte:
44
+ # 0x00 = request granted
45
+ # 0x01 = general failure
46
+ # 0x02 = connection not allowed by ruleset
47
+ # 0x03 = network unreachable
48
+ # 0x04 = host unreachable
49
+ # 0x05 = connection refused by destination host
50
+ # 0x06 = TTL expired
51
+ # 0x07 = command not supported / protocol error
52
+ # 0x08 = address type not supported
53
+ # field 3: reserved, must be 0x00
54
+ # field 4: address type, 1 byte:
55
+ # 0x01 = IPv4 address
56
+ # 0x03 = Domain name
57
+ # 0x04 = IPv6 address
58
+ # field 5: destination address of
59
+ # 4 bytes for IPv4 address
60
+ # 1 byte of name length followed by the name for Domain name
61
+ # 16 bytes for IPv6 address
62
+ # field 6: network byte order port number, 2 bytes
63
+ def process_v5_connection_response(socket)
64
+ packet = socket.recv(4).unpack("C*")
65
+ dump(:read, packet)
66
+ socks_version = packet[0]
67
+ result_code = packet[1]
68
+ reserved = packet[2]
69
+ address_type = packet[3]
70
+
71
+ if result_code == 0x00
72
+ log(:green, build_v5_result_code_message(result_code))
73
+ else
74
+ raise SockitError, build_v5_result_code_message(result_code)
75
+ end
76
+
77
+ address_length = case address_type
78
+ when 0x01 then
79
+ 4
80
+ when 0x03 then
81
+ data = socket.recv(1).unpack("C*")
82
+ dump(:read, data)
83
+ data[0]
84
+ when 0x04 then
85
+ 16
86
+ end
87
+ host = socket.recv(address_length).unpack("C*")
88
+ dump(:read, host)
89
+
90
+ port = socket.recv(2).unpack("n")
91
+ dump(:read, port)
92
+
93
+ [host.join('.'), port.join]
94
+ end
95
+
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,82 @@
1
+ module Sockit
2
+ module V5
3
+ module Support
4
+
5
+ # 0x00 = request granted
6
+ # 0x01 = general failure
7
+ # 0x02 = connection not allowed by ruleset
8
+ # 0x03 = network unreachable
9
+ # 0x04 = host unreachable
10
+ # 0x05 = connection refused by destination host
11
+ # 0x06 = TTL expired
12
+ # 0x07 = command not supported / protocol error
13
+ # 0x08 = address type not supported
14
+ def build_v5_result_code_message(result_code)
15
+ message = case result_code
16
+ when 0x00 then
17
+ "Request granted"
18
+ when 0x01 then
19
+ "General failure"
20
+ when 0x02 then
21
+ "Connection not allowed by ruleset"
22
+ when 0x03 then
23
+ "Network unreachable"
24
+ when 0x04 then
25
+ "Host unreachable"
26
+ when 0x05 then
27
+ "Connection refused by destination host"
28
+ when 0x06 then
29
+ "TTL expired"
30
+ when 0x07 then
31
+ "Command not supported / Protocol error"
32
+ when 0x08 then
33
+ "Address type not supported"
34
+ else
35
+ "Unknown"
36
+ end
37
+
38
+ "%s (Code: 0x%02X)" % [message, result_code]
39
+
40
+ rescue
41
+ "Result Code: #{result_code.inspect}"
42
+ end
43
+
44
+ # The authentication methods supported are numbered as follows:
45
+ # 0x00: No authentication
46
+ # 0x01: GSSAPI[10]
47
+ # 0x02: Username/Password[11]
48
+ # 0x03-0x7F: methods assigned by IANA[12]
49
+ # 0x80-0xFE: methods reserved for private use
50
+ def authentication_method(auth_method)
51
+ case auth_method
52
+ when 0x00 then
53
+ "No authentication (Code: 0x%02X)" % auth_method
54
+ when 0x01 then
55
+ "GSSAPI authentication (Code: 0x%02X)" % auth_method
56
+ when 0x02 then
57
+ "Username/Password authentication (Code: 0x%02X)" % auth_method
58
+ when 0x03..0x7F then
59
+ "Authentication method assigned by IANA (Code: 0x%02X)" % auth_method
60
+ when 0x80..0xFE then
61
+ "Authentication method reserved for private use (Code: 0x%02X)" % auth_method
62
+ when 0xFF then
63
+ "Unsupported authentication (Code: 0x%02X)" % auth_method
64
+ else
65
+ "Unknown authentication (Code: 0x%02X)" % auth_method
66
+ end
67
+ end
68
+
69
+ # 0x00 = success
70
+ # any other value = failure, connection must be closed
71
+ def authentication_status(auth_status)
72
+ case auth_status
73
+ when 0x00 then
74
+ "Authentication success (Code: 0x%02X)" % auth_status
75
+ else
76
+ "Authentication failure (Code: 0x%02X)" % auth_status
77
+ end
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -19,5 +19,9 @@
19
19
  ################################################################################
20
20
 
21
21
  module Sockit
22
- VERSION = "0.0.4" unless const_defined?(:VERSION)
22
+
23
+ unless const_defined?(:VERSION)
24
+ VERSION = "1.0.0"
25
+ end
26
+
23
27
  end