Class: ShadowsocksRuby::Protocols::HttpSimpleProtocol

Inherits:
Object
  • Object
show all
Includes:
BufferHelper, DummyHelper
Defined in:
lib/shadowsocks_ruby/protocols/obfs/http_simple.rb

Overview

Constant Summary collapse

USER_AGENTS =
["Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0",
"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/44.0",
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/27.0.1453.93 Chrome/27.0.1453.93 Safari/537.36",
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0",
"Mozilla/5.0 (compatible; WOW64; MSIE 10.0; Windows NT 6.2)",
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C)",
"Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko",
"Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/BuildID) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36",
"Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
"Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3"]

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from BufferHelper

#tcp_receive_from_localbackend_in_buffer, #tcp_receive_from_localbackend_other_packet_helper, #tcp_receive_from_remoteserver_in_buffer, #tcp_receive_from_remoteserver_other_packet_helper

Methods included from DummyHelper

#async_recv, #raise_me, #send_data

Constructor Details

#initialize(params = {}) ⇒ HttpSimpleProtocol

Returns a new instance of HttpSimpleProtocol.

Parameters:

  • params (Hash) (defaults to: {})

    Configuration parameters

Options Hash (params):

  • :host (String)

    shadowsocks server address, required by remoteserver protocol

  • :port (String)

    shadowsocks server port, required by remoteserver protocol

  • :compatible (Boolean)

    compatibility with origin mode, default true

  • :obfs_param (String)

    obfs param, optional



32
33
34
35
# File 'lib/shadowsocks_ruby/protocols/obfs/http_simple.rb', line 32

def initialize params = {}
  @params = {:compatible => true }.merge(params)
  @buffer = ''
end

Instance Attribute Details

#next_protocolObject

Returns the value of attribute next_protocol.



12
13
14
# File 'lib/shadowsocks_ruby/protocols/obfs/http_simple.rb', line 12

def next_protocol
  @next_protocol
end

Instance Method Details

#async_recv_until(str) ⇒ Object



166
167
168
# 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) ⇒ Object

helpers



158
159
160
# 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) ⇒ Object



162
163
164
# 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_first_packet(n) ⇒ Object Also known as: tcp_receive_from_localbackend



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# 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) ⇒ Object



136
137
138
# 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_first_packet(n) ⇒ Object Also known as: tcp_receive_from_remoteserver



37
38
39
40
41
42
43
44
# 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) ⇒ Object



48
49
50
# 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_first_packet(data) ⇒ Object Also known as: tcp_send_to_localbackend



140
141
142
143
144
145
146
147
148
149
# 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

#tcp_send_to_localbackend_other_packet(data) ⇒ Object



153
154
155
# 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_first_packet(data) ⇒ Object Also known as: tcp_send_to_remoteserver

Raises:



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 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

#tcp_send_to_remoteserver_other_packet(data) ⇒ Object



94
95
96
# File 'lib/shadowsocks_ruby/protocols/obfs/http_simple.rb', line 94

def tcp_send_to_remoteserver_other_packet data
  send_data data
end