sockit 0.0.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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