em-socksify 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
data/.rspec ADDED
File without changes
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # EM-Socksify: Transparent SOCKS support for any EventMachine protocol
2
+
3
+ Dealing with SOCKS proxies is pain. EM-Socksify provides a simple shim to setup & negotiate a SOCKS5 connection for any EventMachine protocol. To add SOCKS support, all you have to do is include the module and provide your destination address.
4
+
5
+ ## Example: Routing HTTP request via SOCKS5 proxy
6
+
7
+ class Handler < EM::Connection
8
+ include EM::Socksify
9
+
10
+ def connection_completed
11
+ socksify('google.ca', 80) do
12
+ send_data "GET / HTTP/1.1\r\nConnection:close\r\nHost: google.ca\r\n\r\n"
13
+ end
14
+ end
15
+
16
+ def receive_data(data)
17
+ p data
18
+ end
19
+ end
20
+
21
+ EM.run do
22
+ EventMachine.connect SOCKS_HOST, SOCKS_PORT, Handler
23
+ end
24
+
25
+ What's happening here? First, we open a raw TCP connection to the SOCKS proxy (after all, all data will flow through it). Then, we provide a Handler connection class, which includes "EM::Socksify". Once the TCP connection is established, EventMachine calls the **connection_completed** method in our handler. Here, we call socksify with the actual destination host & port (address that we actually want to get to), and the module does the rest.
26
+
27
+ After you call socksify, the module temporarily intercepts your receive_data callbacks, negotiates the SOCKS connection (version, authentication, etc), and then once all is done, returns the control back to your code. Simple as that.
28
+
29
+ For SOCKS proxies which require authentication, use:
30
+
31
+ socksify(destination_host, destination_port, username, password)
32
+
33
+
34
+ ## Wishlist
35
+
36
+ - IPV6 support
37
+ - SOCKS4 support
38
+
39
+ ## Resources
40
+
41
+ - [SOCKS on Wikipedia](http://en.wikipedia.org/wiki/SOCKS)
42
+ - [Socksify-Ruby](https://github.com/astro/socksify-ruby) for regular Ruby TCPSocket
43
+
44
+ # License
45
+
46
+ (The MIT License)
47
+ Copyright © 2011 Ilya Grigorik
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "em-socksify/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "em-socksify"
7
+ s.version = EventMachine::Socksify::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ilya Grigorik"]
10
+ s.email = ["ilya@igvita.com"]
11
+ s.homepage = "http://github.com/igrigorik/em-socksify"
12
+ s.summary = "EventMachine SOCKSify shim: adds SOCKS support to any protocol"
13
+ s.description = s.summary
14
+
15
+ s.rubyforge_project = "em-socksify"
16
+
17
+ s.add_dependency "eventmachine"
18
+ s.add_development_dependency "rspec"
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+ end
@@ -0,0 +1,122 @@
1
+ module EventMachine
2
+ module Socksify
3
+
4
+ def socksify(host, port, username = nil, password = nil, &blk)
5
+ @host = host
6
+ @port = port
7
+ @username = username
8
+ @password = password
9
+ @callback = blk
10
+
11
+ class << self
12
+ def receive_data(data); proxy_receive_data(data); end
13
+ end
14
+
15
+ send_socks_handshake
16
+ end
17
+
18
+ def proxy_receive_data(data)
19
+ @data ||= ''
20
+ @data << data
21
+ parse_socks_response
22
+ end
23
+
24
+ def send_socks_handshake
25
+ # Method Negotiation as described on
26
+ # http://www.faqs.org/rfcs/rfc1928.html Section 3
27
+ @socks_state = :method_negotiation
28
+
29
+ methods = socks_methods
30
+ send_data [5, methods.size].pack('CC') + methods.pack('C*')
31
+ end
32
+
33
+ def send_socks_connect_request
34
+ begin
35
+ # TO-DO: Implement address types for IPv6 and Domain
36
+ ip_address = Socket.gethostbyname(@host).last
37
+ send_data [5, 1, 0, 1, ip_address, @port].flatten.pack('CCCCA4n')
38
+
39
+ rescue
40
+ fail("could not resolve host")
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ # parses socks 5 server responses as specified
47
+ # on http://www.faqs.org/rfcs/rfc1928.html
48
+ def parse_socks_response
49
+ if @socks_state == :method_negotiation
50
+ return if not @data.size >= 2
51
+
52
+ _, method = @data.slice!(0,2).unpack('CC')
53
+
54
+ if socks_methods.include?(method)
55
+ if method == 0
56
+ @socks_state = :connecting
57
+ send_socks_connect_request
58
+
59
+ elsif method == 2
60
+ @socks_state = :authenticating
61
+ send_data [5, @username.length, @username, @password.length, @password].pack('CCA*CA*')
62
+ end
63
+
64
+ else
65
+ fail("proxy did not accept method")
66
+ end
67
+
68
+ elsif @socks_state == :authenticating
69
+ return if not @data.size >= 2
70
+
71
+ _, status_code = @data.slice!(0, 2).unpack('CC')
72
+
73
+ if status_code == 0 # success
74
+ @socks_state = :connecting
75
+ send_socks_connect_request
76
+
77
+ else # error
78
+ fail "access denied by proxy"
79
+ end
80
+
81
+ elsif @socks_state == :connecting
82
+ return if not @data.size >= 10
83
+
84
+ _, response_code, _, address_type, _, _ = @data.slice(0, 10).unpack('CCCCNn')
85
+
86
+ if response_code == 0 # success
87
+ @socks_state = :connected
88
+
89
+ class << self
90
+ remove_method :receive_data
91
+ end
92
+
93
+ @callback.call
94
+
95
+ else # error
96
+ error_messages = {
97
+ 1 => "general socks server failure",
98
+ 2 => "connection not allowed by ruleset",
99
+ 3 => "network unreachable",
100
+ 4 => "host unreachable",
101
+ 5 => "connection refused",
102
+ 6 => "TTL expired",
103
+ 7 => "command not supported",
104
+ 8 => "address type not supported"
105
+ }
106
+
107
+ error_message = error_messages[response_code] || "unknown error (code: #{response_code})"
108
+ fail "socks5 connect error: #{error_message}"
109
+ end
110
+ end
111
+ end
112
+
113
+ def socks_methods
114
+ methods = []
115
+ methods << 2 if !@username.nil? # 2 => Username/Password Authentication
116
+ methods << 0 # 0 => No Authentication Required
117
+
118
+ methods
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,5 @@
1
+ module EventMachine
2
+ module Socksify
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ require 'eventmachine'
2
+
3
+ require 'em-socksify/socksify'
data/spec/helper.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'em-socksify'
@@ -0,0 +1,33 @@
1
+ require 'helper'
2
+
3
+ describe EventMachine do
4
+
5
+ it "should negotiate a socks connection" do
6
+
7
+ class Handler < EM::Connection
8
+ include EM::Socksify
9
+
10
+ def connection_completed
11
+ socksify('google.ca', 80) do
12
+ send_data "GET / HTTP/1.1\r\nConnection:close\r\nHost: google.ca\r\n\r\n"
13
+ end
14
+ end
15
+
16
+ def receive_data(data)
17
+ @received ||= ''
18
+ @received << data
19
+ end
20
+
21
+ def unbind
22
+ @received.size.should > 0
23
+ @received[0,4].should == 'HTTP'
24
+ EM.stop
25
+ end
26
+ end
27
+
28
+ EM.run do
29
+ EventMachine.connect '127.0.0.1', 8080, Handler
30
+ end
31
+ end
32
+
33
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-socksify
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Ilya Grigorik
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-01-23 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: eventmachine
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: rspec
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 0
41
+ version: "0"
42
+ type: :development
43
+ version_requirements: *id002
44
+ description: "EventMachine SOCKSify shim: adds SOCKS support to any protocol"
45
+ email:
46
+ - ilya@igvita.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files: []
52
+
53
+ files:
54
+ - .gitignore
55
+ - .rspec
56
+ - Gemfile
57
+ - README.md
58
+ - Rakefile
59
+ - em-socksify.gemspec
60
+ - lib/em-socksify.rb
61
+ - lib/em-socksify/socksify.rb
62
+ - lib/em-socksify/version.rb
63
+ - spec/helper.rb
64
+ - spec/socksify_spec.rb
65
+ has_rdoc: true
66
+ homepage: http://github.com/igrigorik/em-socksify
67
+ licenses: []
68
+
69
+ post_install_message:
70
+ rdoc_options: []
71
+
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ requirements: []
89
+
90
+ rubyforge_project: em-socksify
91
+ rubygems_version: 1.3.6
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: "EventMachine SOCKSify shim: adds SOCKS support to any protocol"
95
+ test_files:
96
+ - spec/helper.rb
97
+ - spec/socksify_spec.rb