class ShadowsocksRuby::Protocols::HttpSimpleProtocol

Http Simple Obfuscation Protocol

Specification:

Constants

USER_AGENTS

Attributes

next_protocol[RW]

Public Class Methods

new(params = {}) click to toggle source

@param [Hash] configuration parameters @option params [String] :host shadowsocks server address, required by remoteserver protocol @option params [String] :port shadowsocks server port, required by remoteserver protocol @option params [Boolean] :compatible compatibility with origin mode, default true @option params [String] :obfs_param obfs param, optional

# File lib/shadowsocks_ruby/protocols/obfs/http_simple.rb, line 32
def initialize params = {}
  @params = {:compatible => true }.merge(params)
  @buffer = ''
end

Public Instance Methods

async_recv_until(str) click to toggle source
# File lib/shadowsocks_ruby/protocols/obfs/http_simple.rb, line 166
def async_recv_until str
  @next_protocol.async_recv_until str
end
get_data_from_http_header(buf) click to toggle source

helpers

# File lib/shadowsocks_ruby/protocols/obfs/http_simple.rb, line 158
def get_data_from_http_header buf
  [buf.split("\r\n")[0][/(%..)+/].gsub(/%/,'')].pack("H*")
end
get_host_from_http_header(buf) click to toggle source
# File lib/shadowsocks_ruby/protocols/obfs/http_simple.rb, line 162
def get_host_from_http_header buf
  buf[/^Host: (.+):/,1]
end
tcp_receive_from_localbackend(n)
tcp_receive_from_localbackend_first_packet(n) click to toggle source
# File lib/shadowsocks_ruby/protocols/obfs/http_simple.rb, line 99
def tcp_receive_from_localbackend_first_packet n
  data = async_recv 10
  if (data =~ /^GET|^POST/) == nil
    if @params[:compatible]
      @buffer << data << async_recv(n - data.length)
    else
      raise PharseError, "not a valid http_simple first packet in strict mode"
    end
  else
    buf = ""
    buf << data << async_recv_until("\r\n\r\n")
    host = get_host_from_http_header(buf)
    if host != nil && @params[:obfs_param] != nil
      hosts = @params[:obfs_param]
      if hosts.include?('#')
        hosts, body = hosts.split('#')
        body = body.gsub(/\n/,"\r\n")
        body = body.gsub(/\\n/,"\r\n")
      end
      hosts = hosts.split(",") 
      if !hosts.include?(host)
        raise PharseError, "request host not in :obfs_param"
      end
    end
    ret_buf = get_data_from_http_header(buf)
    raise PharseError, "not a valid request" if ret_buf.length < 4
    @buffer << ret_buf
  end

  class << self
    alias tcp_receive_from_localbackend tcp_receive_from_localbackend_other_packet
  end
  tcp_receive_from_localbackend_other_packet_helper n
end
tcp_receive_from_localbackend_other_packet(n) click to toggle source
# File lib/shadowsocks_ruby/protocols/obfs/http_simple.rb, line 136
def tcp_receive_from_localbackend_other_packet n
  async_recv(n)
end
tcp_receive_from_remoteserver(n)
tcp_receive_from_remoteserver_first_packet(n) click to toggle source
# File lib/shadowsocks_ruby/protocols/obfs/http_simple.rb, line 37
def tcp_receive_from_remoteserver_first_packet n
  class << self
    alias tcp_receive_from_remoteserver tcp_receive_from_remoteserver_other_packet
  end

  async_recv_until("\r\n\r\n")
  async_recv n
end
tcp_receive_from_remoteserver_other_packet(n) click to toggle source
# File lib/shadowsocks_ruby/protocols/obfs/http_simple.rb, line 48
def tcp_receive_from_remoteserver_other_packet n
  async_recv n
end
tcp_send_to_localbackend(data)
tcp_send_to_localbackend_first_packet(data) click to toggle source
# File lib/shadowsocks_ruby/protocols/obfs/http_simple.rb, line 140
def tcp_send_to_localbackend_first_packet data
  class << self
    alias tcp_send_to_localbackend tcp_send_to_localbackend_other_packet
  end

  header = "HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Encoding: gzip\r\nContent-Type: text/html\r\nDate: "
  header << Time.now.strftime('%a, %d %b %Y %H:%M:%S GMT')
  header << "\r\nServer: nginx\r\nVary: Accept-Encoding\r\n\r\n"
  send_data header << data
end
Also aliased as: tcp_send_to_localbackend
tcp_send_to_localbackend_other_packet(data) click to toggle source
# File lib/shadowsocks_ruby/protocols/obfs/http_simple.rb, line 153
def tcp_send_to_localbackend_other_packet data
  send_data data
end
tcp_send_to_remoteserver(data)
tcp_send_to_remoteserver_first_packet(data) click to toggle source
# File lib/shadowsocks_ruby/protocols/obfs/http_simple.rb, line 52
def tcp_send_to_remoteserver_first_packet data
  if data.length > 30 + 64
    headlen = 30 + Random.rand(64 + 1)
  else
    headlen = data.length
  end
  headdata = data.slice!(0, headlen)
  port = ''
  raise ProtocolError, "No :port params" if @params[:port] == nil
  if @params[:port] != 80
    port = ':' << @params[:port].to_s 
  end

  body = nil
  hosts = @params[:obfs_param] || @params[:host]
  raise ProtocolError, "No :host or :obfs_param parameters" if hosts == nil
  if hosts.include?('#')
    hosts, body = hosts.split('#')
    body = body.gsub(/\n/,"\r\n")
    body = body.gsub(/\\n/,"\r\n")
  end
  hosts = hosts.split(",")
  host = hosts[Random.rand(hosts.length)]

  http_head = "GET /" << headdata.unpack("H*")[0].gsub(/../,'%\0') << " HTTP/1.1\r\n"
  http_head << "Host: " << host << port << "\r\n"

  if body != nil
    http_head << body << "\r\n\r\n"
  else
    http_head << "User-Agent: " + USER_AGENTS[Random.rand(USER_AGENTS.length)] << "\r\n"
    http_head << "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\nDNT: 1\r\nConnection: keep-alive\r\n\r\n"
  end
  send_data(http_head << data)
  class << self
    alias tcp_send_to_remoteserver tcp_send_to_remoteserver_other_packet
  end

end
Also aliased as: tcp_send_to_remoteserver
tcp_send_to_remoteserver_other_packet(data) click to toggle source
# File lib/shadowsocks_ruby/protocols/obfs/http_simple.rb, line 94
def tcp_send_to_remoteserver_other_packet data
  send_data data
end