class Snxvpn::CLI
Attributes
config[R]
http[R]
Public Class Methods
new(profile, config_path: File.join(ENV['HOME'], '.snxvpn'))
click to toggle source
# File lib/snxvpn/cli.rb, line 16 def initialize(profile, config_path: File.join(ENV['HOME'], '.snxvpn')) @config = Config.new(config_path, profile) @http = Net::HTTP.new(@config[:host], 443) @http.use_ssl = true @cookies = ["selected_realm=#{@config[:realm]}"] end
Public Instance Methods
run(retries = 10)
click to toggle source
# File lib/snxvpn/cli.rb, line 23 def run(retries = 10) # find RSA.js resp = get(config[:entry_path]) rsa_path = resp.body.match(/<script .*?src *\= *["'](.*RSA.js)["']/).to_a[1] raise RetryableError, "Unable to detect a RSA.js script reference on login page" unless rsa_path # store paths login_path = resp.uri rsa_path = File.expand_path(rsa_path, File.dirname(login_path)) # fetch RSA.js and parse RSA rsa = RSA.parse(get(rsa_path).body) raise RetryableError "Unable to detect modulus/exponent in RSA.js script" unless rsa # post to login resp = post(login_path, password: rsa.hex_encrypt(config[:password]), userName: config[:username], selectedRealm: config[:realm], loginType: config[:login_type], HeightData: '', vpid_prefix: '', ) raise RetryableError, "Expected redirect to multi-challenge, but got #{resp.uri}" unless resp.uri.include?('MultiChallenge') # request OTP until successful inputs = resp.body.scan(/<input.*?type="hidden".*?name="(username|params|HeightData)"(?:.*?value="(.+?)")?/) payload = Hash[inputs] while resp.uri.include?('MultiChallenge') print " + Enter one-time password: " otp = gets.strip payload['password'] = rsa.hex_encrypt(otp) resp = post(resp.uri, payload) end # request extender info ext_info = ExtInfo.new get("/SNX/extender").body raise RetryableError, "Unable to retrieve extender information" if ext_info.empty? output, status = Open3.capture2(config[:snx_path], '-Z') raise RetryableError, "Unable to start snx: #{output}" unless status.success? Socket.tcp('127.0.0.1', 7776) do |sock| sock.write(ext_info.payload) sock.recv(4096) # read answer puts ' = Connected! Please leave this running to keep VPN open.' sock.recv(4096) # block until snx process dies end puts ' ! Connection closed. Exiting...' rescue RetryableError raise if retries < 1 puts ' ! #{e.message}. Retrying...' sleep 1 run(retries - 1) end
Private Instance Methods
get(path, retries = 10)
click to toggle source
# File lib/snxvpn/cli.rb, line 83 def get(path, retries = 10) raise ArgumentError, 'too many HTTP redirects' if retries.zero? resp = @http.get(path, headers) handle_response(path, resp) case resp when Net::HTTPSuccess then resp when Net::HTTPRedirection then get(resp['location'], retries - 1) when Net::HTTPNotFound then raise "unexpected response from GET #{path} - #{resp.code} #{resp.message}" unless path.include?(config[:entry_path]) resp else raise "unexpected response from GET #{path} - #{resp.code} #{resp.message}" end end
handle_response(path, resp)
click to toggle source
# File lib/snxvpn/cli.rb, line 120 def handle_response(path, resp) resp.uri = path @cookies += Array(resp.get_fields('set-cookie')).map do |str| str.split('; ')[0] end @cookies.uniq! end
headers()
click to toggle source
# File lib/snxvpn/cli.rb, line 116 def headers {'Cookie' => @cookies.join('; ')} unless @cookies.empty? end
post(path, payload)
click to toggle source
# File lib/snxvpn/cli.rb, line 102 def post(path, payload) resp = @http.post(path, URI.encode_www_form(payload), headers) handle_response(path, resp) case resp when Net::HTTPSuccess then resp when Net::HTTPRedirection then get(resp['location']) else raise "unexpected response from POST #{path} - #{resp.code} #{resp.message}" end end