#!/usr/bin/env ruby # -*- encoding: binary -*- lib = File.expand_path('../../lib', __FILE__) $LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib) require 'English' require 'netsoul/config' require 'netsoul/errors' require 'netsoul/logging' require 'netsoul/message' require 'socket' module Netsoul class Client include Logging SOCKET_READ_TIMEOUT = 12 * 60 SOCKET_WRITE_TIMEOUT = 10 attr_reader :started def initialize(*args) opts = args.last.is_a?(Hash) ? args.last : {} @config = Config.new(opts) @started = false end def auth_ag sock_send(Message.auth_ag) fail Netsoul::IdentificationError, 'Identification failed.'.freeze unless sock_get.split(' ')[1] == '002'.freeze end private :auth_ag def auth_method if @config.auth_method == :krb5 sock_send(Message.kerberos_auth(@config)) else sock_send(Message.standard_auth(@config)) end fail Netsoul::AuthenticationError, 'Authentication failed.'.freeze unless sock_get.split(' ')[1] == '002' end private :auth_method def auth_status sock_send(Message.attach) sock_send(Message.user_state(@config.state, Time.now.to_i)) end private :auth_status def connect @sock = TCPSocket.new(@config.server_host, @config.server_port) fail Netsoul::SocketError, 'Could not open a socket. Connection is unavailable.'.freeze unless @sock _cmd, _socket_num, md5_hash, client_ip, client_port, _server_timestamp = sock_get.split @config.build_user_connection_info md5_hash: md5_hash, client_ip: client_ip, client_port: client_port auth_ag auth_method auth_status @started = true end def disconnect sock_send(Message.ns_exit) ensure sock_close end def sock_send(str) _, sock = IO.select(nil, [@sock], nil, SOCKET_WRITE_TIMEOUT) fail Netsoul::SocketError, 'Timeout or fail on write socket' if sock.nil? || sock.empty? sock.first.puts str log :info, "[send] #{str.chomp}" end def sock_get sock, = IO.select([@sock], nil, nil, SOCKET_READ_TIMEOUT) fail Netsoul::SocketError, 'Timeout or fail on read socket' if sock.nil? || sock.empty? res = sock.first.gets log :info, "[get ] #{res.chomp}" if res res || '' end def sock_close @started = false @sock.close rescue nil end end end $stderr.sync = true require 'optparse' require 'yaml' options = {} OptionParser.new do |opts| opts.banner = 'Usage: netsoul-ruby [options]'.freeze opts.separator ''.freeze opts.separator 'Netsoul-Ruby options:'.freeze opts.on('-c'.freeze, '--config FILE'.freeze, 'Configuration file in YAML'.freeze) do |file| options[:user_opts] = YAML.load_file(file) if File.file?(file) end opts.on('-h', '--help', 'Display this screen') do puts opts exit end end.parse! unless ENV.to_a.count { |k, _v| %w(NETSOUL_LOGIN NETSOUL_SOCKS_PASSWORD).include?(k) } == 2 || ENV.to_a.count { |k, _v| %w(NETSOUL_LOGIN NETSOUL_UNIX_PASSWORD NETSOUL_AUTH_METHOD).include?(k) } == 3 puts '[ERROR] You have to specify a configuration file or environment variables' exit end retry_count = 10 retry_wait_time = 10.0 RETRY_WAIT_FACTOR = 1.50 # Each time retry is called in Exception, current 'retry_wait_time' is increased with this factor begin c = Netsoul::Client.new options[:user_opts] c.connect if c.started retry_count = 10 retry_wait_time = 1.0 loop do res = c.sock_get c.sock_send res if res.to_s.match(/^ping.*/) sleep 1 end end rescue Interrupt c.disconnect if c.started puts '!!! [SIGINT] !!!' exit 42 rescue => e puts "[ERROR]: #{e}#{$INPUT_RECORD_SEPARATOR}[RETRY_COUNT]: #{retry_count}" c.disconnect c = nil if retry_count > 0 retry_count -= 1 retry_wait_time *= RETRY_WAIT_FACTOR sleep(retry_wait_time) && retry end exit 42 end