Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added Socks5 protocol

  • Loading branch information...
commit 627fff9293cbe218ba2d89afd025c2db0efd9f93 1 parent c6c66c2
@mironov authored
Showing with 102 additions and 0 deletions.
  1. +1 −0  lib/em/protocols.rb
  2. +101 −0 lib/em/protocols/socks5.rb
View
1  lib/em/protocols.rb
@@ -32,5 +32,6 @@ module Protocols
autoload :Postgres3, 'em/protocols/postgres3'
autoload :ObjectProtocol, 'em/protocols/object_protocol'
autoload :Socks4, 'em/protocols/socks4'
+ autoload :Socks5, 'em/protocols/socks5'
end
end
View
101 lib/em/protocols/socks5.rb
@@ -0,0 +1,101 @@
+module EventMachine
+ module Protocols
+ # Basic SOCKS v5 client implementation
+ #
+ # Use as you would any regular connection:
+ #
+ # class MyConn < EM::P::Socks5
+ # def post_init
+ # send_data("sup")
+ # end
+ #
+ # def receive_data(data)
+ # send_data("you said: #{data}")
+ # end
+ # end
+ #
+ # EM.connect socks_host, socks_port, MyConn, host, port
+ #
+ class Socks5 < Connection
+ def initialize(host, port)
+ @host = host
+ @port = port
+ @socks_error_code = nil
+ @buffer = ''
+ @socks_state = :method_negotiation
+ @socks_methods = [0] # TODO: other authentication methods
+ setup_methods
+ end
+
+ def setup_methods
+ class << self
+ def post_init; socks_post_init; end
+ def receive_data(*a); socks_receive_data(*a); end
+ end
+ end
+
+ def restore_methods
+ class << self
+ remove_method :post_init
+ remove_method :receive_data
+ end
+ end
+
+ def socks_post_init
+ packet = [5, @socks_methods.size].pack('CC') + @socks_methods.pack('C*')
+ send_data(packet)
+ end
+
+ def socks_receive_data(data)
+ @buffer << data
+
+ if @socks_state == :method_negotiation
+ return if @buffer.size < 2
+
+ header_resp = @buffer.slice! 0, 2
+ _, method_code = header_resp.unpack("cc")
+
+ if @socks_methods.include?(method_code)
+ @socks_state = :connecting
+ packet = [5, 1, 0].pack("C*")
+
+ if @host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ # IPv4
+ packet << [1, $1.to_i, $2.to_i, $3.to_i, $4.to_i].pack("C*")
+ elsif @host =~ /^[\da-f\:]+$/i # IPv6
+ close_connection
+ raise "IPv6 address support is not implemented"
+ else # Domain
+ packet << [3, @host.length, @host].pack("CCA*")
+ end
+ packet << [@port].pack("n")
+
+ send_data packet
+ else
+ @socks_state = :invalid
+ @socks_error_code = method_code
+ close_connection
+ return
+ end
+ elsif @socks_state == :connecting
+ return if @buffer.size < 10
+
+ header_resp = @buffer.slice! 0, 10
+ _, response_code, _, address_type, _, _ = header_resp.unpack('CCCCNn')
+
+ if response_code == 0
+ @socks_state = :connected
+ restore_methods
+
+ post_init
+ receive_data(@buffer) unless @buffer.empty?
+ else
+ @socks_state = :invalid
+ @socks_error_code = response_code
+ close_connection
+ return
+ end
+ end
+ end
+ end
+ end
+end

0 comments on commit 627fff9

Please sign in to comment.
Something went wrong with that request. Please try again.