Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

SOCKS5 #135

Closed
wants to merge 4 commits into from

3 participants

@mironov

I've implemented basic socks v5 client.
The code is similar to the socks4 class and partially taken from the em-http-request gem.
Currently only "No authentication" method is supported.

@igrigorik

I've extracted socks5 into a common component: https://github.com/igrigorik/em-socksify

In theory, no need to pull this into core anymore.

@sodabrew
Collaborator

Closing - thanks for your ongoing maintenance of em-socksify @igrigorik!

@sodabrew sodabrew closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 25, 2010
  1. @mironov

    Added Socks5 protocol

    mironov authored
Commits on Nov 26, 2010
  1. @mironov

    IPv6 address support

    mironov authored
  2. @mironov

    Simplified IPv6 check

    mironov authored
Commits on Nov 27, 2010
  1. @mironov
This page is out of date. Refresh to see the latest.
Showing with 123 additions and 0 deletions.
  1. +1 −0  lib/em/protocols.rb
  2. +122 −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
122 lib/em/protocols/socks5.rb
@@ -0,0 +1,122 @@
+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.include?(":") # IPv6
+ l, r = if @host =~ /^(.*)::(.*)$/
+ [$1,$2].map {|i| i.split ":"}
+ else
+ [@host.split(":"),[]]
+ end
+ dec_groups = (l + Array.new(8-l.size-r.size, '0') + r).map {|i| i.hex}
+ packet << ([4] + dec_groups).pack("Cn8")
+ 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 < 4
+
+ header_resp = @buffer.slice! 0, 4
+ _, response_code, _, address_type = header_resp.unpack("C*")
+
+ if response_code == 0
+ case address_type
+ when 1
+ @buffer.slice! 0, 4
+ when 3
+ len = @buffer.slice! 0, 1
+ @buffer.slice! 0, len.unpack("C").first
+ when 4
+ @buffer.slice! 0, 16
+ else
+ @socks_state = :invalid
+ @socks_error_code = address_type
+ close_connection
+ return
+ end
+ @buffer.slice! 0, 2
+
+ @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
Something went wrong with that request. Please try again.