PageRenderTime 60ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/socksify/lib/socksify.rb

https://code.google.com/p/het-manager/
Ruby | 244 lines | 206 code | 18 blank | 20 comment | 20 complexity | 407d81b3792f8e76d74a79d918463b4c MD5 | raw file
Possible License(s): GPL-3.0
  1. =begin
  2. Copyright (C) 2007 Stephan Maka <stephan@spaceboyz.net>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. =end
  14. require 'socket'
  15. require 'socksify_debug'
  16. class SOCKSError < RuntimeError
  17. def initialize(msg)
  18. Socksify::debug_error("#{self.class}: #{msg}")
  19. super
  20. end
  21. class ServerFailure < SOCKSError
  22. def initialize
  23. super("general SOCKS server failure")
  24. end
  25. end
  26. class NotAllowed < SOCKSError
  27. def initialize
  28. super("connection not allowed by ruleset")
  29. end
  30. end
  31. class NetworkUnreachable < SOCKSError
  32. def initialize
  33. super("Network unreachable")
  34. end
  35. end
  36. class HostUnreachable < SOCKSError
  37. def initialize
  38. super("Host unreachable")
  39. end
  40. end
  41. class ConnectionRefused < SOCKSError
  42. def initialize
  43. super("Connection refused")
  44. end
  45. end
  46. class TTLExpired < SOCKSError
  47. def initialize
  48. super("TTL expired")
  49. end
  50. end
  51. class CommandNotSupported < SOCKSError
  52. def initialize
  53. super("Command not supported")
  54. end
  55. end
  56. class AddressTypeNotSupported < SOCKSError
  57. def initialize
  58. super("Address type not supported")
  59. end
  60. end
  61. def self.for_response_code(code)
  62. case code
  63. when 1
  64. ServerFailure
  65. when 2
  66. NotAllowed
  67. when 3
  68. NetworkUnreachable
  69. when 4
  70. HostUnreachable
  71. when 5
  72. ConnectionRefused
  73. when 6
  74. TTLExpired
  75. when 7
  76. CommandNotSupported
  77. when 8
  78. AddressTypeNotSupported
  79. else
  80. self
  81. end
  82. end
  83. end
  84. class TCPSocket
  85. def self.socks_server
  86. @@socks_server
  87. end
  88. def self.socks_server=(host)
  89. @@socks_server = host
  90. end
  91. def self.socks_port
  92. @@socks_port
  93. end
  94. def self.socks_port=(port)
  95. @@socks_port = port
  96. end
  97. def self.socks_ignores
  98. @@socks_ignores ||= []
  99. end
  100. def self.socks_ignores=(ignores)
  101. @@socks_ignores = ignores
  102. end
  103. alias :initialize_tcp :initialize
  104. # See http://tools.ietf.org/html/rfc1928
  105. def initialize(host=nil, port=0, local_host="0.0.0.0", local_port=0)
  106. socks_server = self.class.socks_server
  107. socks_port = self.class.socks_port
  108. socks_ignores = self.class.socks_ignores
  109. if socks_server and socks_port and not socks_ignores.include?(host)
  110. Socksify::debug_notice "Connecting to SOCKS server #{socks_server}:#{socks_port}"
  111. initialize_tcp socks_server, socks_port
  112. socks_authenticate
  113. if host
  114. socks_connect(host, port)
  115. end
  116. else
  117. Socksify::debug_notice "Connecting directly to #{host}:#{port}"
  118. initialize_tcp host, port, local_host, local_port
  119. Socksify::debug_debug "Connected to #{host}:#{port}"
  120. end
  121. end
  122. # Authentication
  123. def socks_authenticate
  124. Socksify::debug_debug "Sending no authentication"
  125. write "\005\001\000"
  126. Socksify::debug_debug "Waiting for authentication reply"
  127. auth_reply = recv(2).unpack("c2")
  128. if auth_reply[0] != 4 and auth_reply[0] != 5
  129. raise SOCKSError.new("SOCKS version #{auth_reply[0]} not supported")
  130. end
  131. if auth_reply[1] != 0
  132. raise SOCKSError.new("SOCKS authentication method #{auth_reply[1]} neither requested nor supported")
  133. end
  134. end
  135. # Connect
  136. def socks_connect(host, port)
  137. Socksify::debug_debug "Sending destination address"
  138. write "\005\001\000"
  139. if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ # to IPv4 address
  140. write "\001" + [$1.to_i,
  141. $2.to_i,
  142. $3.to_i,
  143. $4.to_i
  144. ].pack('CCCC')
  145. elsif host =~ /^[:0-9a-f]+$/ # to IPv6 address
  146. raise "TCP/IPv6 over SOCKS is not yet supported (inet_pton missing in Ruby & not supported by Tor"
  147. write "\004"
  148. else # to hostname
  149. write "\003" + [host.size].pack('C') + host
  150. end
  151. write [port].pack('n')
  152. socks_receive_reply
  153. Socksify::debug_notice "Connected to #{host}:#{port} over SOCKS"
  154. end
  155. # returns [bind_addr: String, bind_port: Fixnum]
  156. def socks_receive_reply
  157. Socksify::debug_debug "Waiting for SOCKS reply"
  158. connect_reply = recv(4).unpack('c4')
  159. if connect_reply[0] != 5
  160. raise SOCKSError.new("SOCKS version #{connect_reply[0]} is not 5")
  161. end
  162. if connect_reply[1] != 0
  163. raise SOCKSError.for_response_code(connect_reply[1])
  164. end
  165. Socksify::debug_debug "Waiting for bind_addr"
  166. bind_addr_len = case connect_reply[3]
  167. when 1
  168. 4
  169. when 3
  170. recv(1)[0]
  171. when 4
  172. 16
  173. else
  174. raise SOCKSError.for_response_code(connect_reply[3])
  175. end
  176. bind_addr_s = recv(bind_addr_len)
  177. bind_addr = case connect_reply[3]
  178. when 1
  179. "#{bind_addr_s[0]}.#{bind_addr_s[1]}.#{bind_addr_s[2]}.#{bind_addr_s[3]}"
  180. when 3
  181. bind_addr_s
  182. when 4 # Untested!
  183. i = 0
  184. ip6 = ""
  185. bind_addr_s.each_byte do |b|
  186. if i > 0 and i % 2 == 0
  187. ip6 += ":"
  188. end
  189. i += 1
  190. ip6 += b.to_s(16).rjust(2, '0')
  191. end
  192. end
  193. bind_port = recv(bind_addr_len + 2)
  194. [bind_addr, bind_port.unpack('n')]
  195. end
  196. end
  197. module Socksify
  198. def self.resolve(host)
  199. s = TCPSocket.new
  200. begin
  201. Socksify::debug_debug "Sending hostname to resolve: #{host}"
  202. s.write "\005"
  203. if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ # to IPv4 address
  204. s.write "\xF1\000\001" + [$1.to_i,
  205. $2.to_i,
  206. $3.to_i,
  207. $4.to_i
  208. ].pack('CCCC')
  209. elsif host =~ /^[:0-9a-f]+$/ # to IPv6 address
  210. raise "TCP/IPv6 over SOCKS is not yet supported (inet_pton missing in Ruby & not supported by Tor"
  211. s.write "\004"
  212. else # to hostname
  213. s.write "\xF0\000\003" + [host.size].pack('C') + host
  214. end
  215. s.write [0].pack('n') # Port
  216. addr, port = s.socks_receive_reply
  217. Socksify::debug_notice "Resolved #{host} as #{addr} over SOCKS"
  218. addr
  219. ensure
  220. s.close
  221. end
  222. end
  223. end