Search code examples
ruby-on-railsrubysocksnet-httptcpsocket

How do I set TCPSocket attributes for only a method and not globally?


I'm using Rails 4.2.7 with Ruby 2.3.0. I have this code for getting a web page through a SOCKS Proxy

require "resolv-replace.rb"
require 'open-uri'
require 'uri'
require "socksify"
require 'socksify/http'
...

  def get_content_via_socks(socks_server, socks_port, url, headers)
    TCPSocket::socks_server = socks_server
    TCPSocket::socks_port = socks_port 
    uri = URI(url)
    res1 = Net::HTTP.SOCKSProxy(nil, nil).start(uri.host, uri.port, :use_ssl => (uri.scheme == "https")) do |http|
      http.read_timeout = 500
      puts "launching #{uri} on #{socks_server}:#{socks_port}"
      resp = http.get(uri, initheader = headers)
    end
  end

The only problem with this is even though I have set

    TCPSocket::socks_server = socks_server
    TCPSocket::socks_port = socks_port 

within my local method, it seems to affect the entire application. That is when I call

http         = Net::HTTP.new(uri.host, uri.port)

from another method, it fails unless the above socks_server and socks_port are running. How do I set the socks_server and socks_port for that method only and not affect the rest of the application? Please don't say set the parameters here

res1 = Net::HTTP.SOCKSProxy(nil, nil)

These parameters are meaningless. You can set them to "abc" and "def" and they will behave exactly the same as if you set them to valid values.

Edit: Including my Gemfile

source 'https://rubygems.org'

gem 'rails', '~> 5.0.1'
# Use SCSS for stylesheets
gem 'sass-rails'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier'
# Use CoffeeScript for .coffee assets and views
gem 'coffee-rails'
# See https://github.com/rails/execjs#readme for more supported runtimes
# gem 'therubyracer', platforms: :ruby

# Use jquery as the JavaScript library
gem 'jquery-rails'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
#gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 3.0'

gem 'addressable'
gem 'pg'
gem 'pundit'
gem 'omniauth-oauth2', '~> 1.3.1'
gem 'omniauth-google-oauth2'
gem 'omniauth-facebook'
gem 'omniauth-twitter'
gem 'omniauth-linkedin-oauth2'
gem 'jquery-ui-rails'
#gem 'jquery-cookie-rails'
gem 'will_paginate'
gem 'bootstrap-sass'
gem 'autoprefixer-rails'
gem 'compass-rails'
gem 'pdf-reader'
gem 'jquery-turbolinks'
gem 'activerecord-import'
gem 'w3c_validators'
gem 'whenever', :require => false
gem 'roo-xls'
gem 'socksify'

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platform: :mri
end

group :development do
  # Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
  gem 'web-console'
  gem 'listen', '~> 3.0.5'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

group :production do
  gem 'unicorn'
  gem 'puma'
end

Solution

  • Notes

    Please don't say set the parameters here

    res1 = Net::HTTP.SOCKSProxy(nil, nil)

    Sorry, but I'll have to say that you should set those parameters. :)

    I couldn't get your example to work when setting TCPSocket::socks_server, but it worked just fine when using Net::HTTP.SOCKSProxy(socks_server, socks_port).

    I used :

    $ ruby -v && gem list socksify
    ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
    *** LOCAL GEMS ***
    socksify (1.7.0)
    

    Setup

    On a linux client, I connect to two linux servers with those commands, in 2 separate terminals at the same time :

    ssh -D 1085 linux_server1 -N
    ssh -D 1084 linux_server2 -N
    

    I now have 2 Socks proxies on my client, running on port 1084 and 1085.

    Test

    The methods connect to [http://whatismyip.akamai.com][1] to check if the SOCKS proxy is being used.

    require 'socket'
    require 'socksify'
    require 'socksify/http'
    require 'uri'
    require 'net/http'
    
    def translate_ips(str)
      my_ips = {
        "XXX.XXX.XXX.XXX" => 'local_ip  :(',
        "YYY.YYY.YYY.YYY" => 'socks_ip1 :D',
        "ZZZ.ZZZ.ZZZ.ZZZ" => 'socks_ip2 :D'
      }
      str.gsub(/\d{1,3}\.\d{1,3}\.\d{1,3}.\d{1,3}/,my_ips)
    end
    
    def get_content_via_socks_v1(socks_server, socks_port, url, headers)
      TCPSocket::socks_server = socks_server
      TCPSocket::socks_port = socks_port 
      uri = URI(url)
      res1 = Net::HTTP.SOCKSProxy(nil, nil).start(uri.host, uri.port, :use_ssl => (uri.scheme == "https")) do |http|
        http.read_timeout = 500
        puts "launching #{uri} on #{socks_server}:#{socks_port}"
        resp = http.get(uri, initheader = headers)
      end
    end
    
    def get_content_via_socks_v2(socks_server, socks_port, url, headers)
      uri = URI(url)
      Net::HTTP.SOCKSProxy(socks_server, socks_port).start(uri.host, uri.port, use_ssl: (uri.scheme == 'https')) do |http|
        http.read_timeout = 500
        puts "launching #{uri} on #{socks_server}:#{socks_port}"
        http.get(uri, headers)
      end
    end
    
    puts translate_ips(get_content_via_socks_v1('localhost', 1085, 'http://whatismyip.akamai.com', nil).body)
    puts translate_ips(get_content_via_socks_v1('localhost', 1084, 'http://whatismyip.akamai.com', nil).body)
    puts
    puts translate_ips(get_content_via_socks_v2('localhost', 1085, 'http://whatismyip.akamai.com', nil).body)
    puts translate_ips(get_content_via_socks_v2('localhost', 1084, 'http://whatismyip.akamai.com', nil).body)
    

    It outputs :

    launching http://whatismyip.akamai.com on localhost:1085
    local_ip  :(
    launching http://whatismyip.akamai.com on localhost:1084
    local_ip  :(
    
    launching http://whatismyip.akamai.com on localhost:1085
    socks_ip1 :D
    launching http://whatismyip.akamai.com on localhost:1084
    socks_ip2 :D
    

    The Net::HTTP.SOCKSProxy(socks_server, socks_port) version works just fine!

    If I try to use Net::HTTP.SOCKSProxy("bogus", "bogus"), the method raises an exception:

    in `rescue in block in connect': Failed to open TCP connection to whatismyip.akamai.com:80 (getaddrinfo: Servname not supported for ai_socktype) (SocketError)
    

    Multiple threads

    Setting TCPSocket::socks_server at the beginning of the method wouldn't be thread-safe.

    Using a Net::HTTP.SOCKSProxy with a block should prevent race-conditions :

    threads = []
    10.times do
      sleep 0.1
      threads << Thread.new { puts translate_ips(get_content_via_socks_v2('localhost', 1085, 'http://whatismyip.akamai.com', nil).body)}
      threads << Thread.new { puts translate_ips(get_content_via_socks_v2('localhost', 1084, 'http://whatismyip.akamai.com', nil).body)}
    end
    
    threads.join
    

    It outputs :

    launching http://whatismyip.akamai.com on localhost:1084
    socks_ip2 :D
    launching http://whatismyip.akamai.com on localhost:1085
    socks_ip1 :D
    launching http://whatismyip.akamai.com on localhost:1084
    socks_ip2 :D
    launching http://whatismyip.akamai.com on localhost:1085
    socks_ip1 :D
    launching http://whatismyip.akamai.com on localhost:1084
    socks_ip2 :D
    launching http://whatismyip.akamai.com on localhost:1085
    socks_ip1 :D
    launching http://whatismyip.akamai.com on localhost:1084
    socks_ip2 :D
    launching http://whatismyip.akamai.com on localhost:1085
    socks_ip1 :D
    launching http://whatismyip.akamai.com on localhost:1084
    socks_ip2 :D
    launching http://whatismyip.akamai.com on localhost:1085
    socks_ip1 :D
    launching http://whatismyip.akamai.com on localhost:1084
    socks_ip2 :D
    launching http://whatismyip.akamai.com on localhost:1085
    socks_ip1 :D
    launching http://whatismyip.akamai.com on localhost:1084
    socks_ip2 :D
    launching http://whatismyip.akamai.com on localhost:1085
    socks_ip1 :D
    launching http://whatismyip.akamai.com on localhost:1084
    socks_ip2 :D
    launching http://whatismyip.akamai.com on localhost:1085
    socks_ip1 :D
    launching http://whatismyip.akamai.com on localhost:1084
    socks_ip2 :D
    launching http://whatismyip.akamai.com on localhost:1085
    socks_ip1 :D
    

    Test with your Gemfile

    With

    ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
    

    and your Gemfile on Linux Mint 17, the script above worked just fine when launched with

    rails runner get_content_via_socks.rb
    

    I created an empty Gemset and only added your gems. The resulting Gemfile.lock was :

    GEM
      remote: https://rubygems.org/
      specs:
        Ascii85 (1.0.2)
        actioncable (5.0.1)
          actionpack (= 5.0.1)
          nio4r (~> 1.2)
          websocket-driver (~> 0.6.1)
        actionmailer (5.0.1)
          actionpack (= 5.0.1)
          actionview (= 5.0.1)
          activejob (= 5.0.1)
          mail (~> 2.5, >= 2.5.4)
          rails-dom-testing (~> 2.0)
        actionpack (5.0.1)
          actionview (= 5.0.1)
          activesupport (= 5.0.1)
          rack (~> 2.0)
          rack-test (~> 0.6.3)
          rails-dom-testing (~> 2.0)
          rails-html-sanitizer (~> 1.0, >= 1.0.2)
        actionview (5.0.1)
          activesupport (= 5.0.1)
          builder (~> 3.1)
          erubis (~> 2.7.0)
          rails-dom-testing (~> 2.0)
          rails-html-sanitizer (~> 1.0, >= 1.0.2)
        activejob (5.0.1)
          activesupport (= 5.0.1)
          globalid (>= 0.3.6)
        activemodel (5.0.1)
          activesupport (= 5.0.1)
        activerecord (5.0.1)
          activemodel (= 5.0.1)
          activesupport (= 5.0.1)
          arel (~> 7.0)
        activerecord-import (0.16.2)
          activerecord (>= 3.2)
        activesupport (5.0.1)
          concurrent-ruby (~> 1.0, >= 1.0.2)
          i18n (~> 0.7)
          minitest (~> 5.1)
          tzinfo (~> 1.1)
        addressable (2.5.0)
          public_suffix (~> 2.0, >= 2.0.2)
        afm (0.2.2)
        arel (7.1.4)
        autoprefixer-rails (6.6.1)
          execjs
        bootstrap-sass (3.3.7)
          autoprefixer-rails (>= 5.2.1)
          sass (>= 3.3.4)
        builder (3.2.3)
        byebug (9.0.6)
        chronic (0.10.2)
        chunky_png (1.3.8)
        coffee-rails (4.2.1)
          coffee-script (>= 2.2.0)
          railties (>= 4.0.0, < 5.2.x)
        coffee-script (2.4.1)
          coffee-script-source
          execjs
        coffee-script-source (1.12.2)
        compass (1.0.3)
          chunky_png (~> 1.2)
          compass-core (~> 1.0.2)
          compass-import-once (~> 1.0.5)
          rb-fsevent (>= 0.9.3)
          rb-inotify (>= 0.9)
          sass (>= 3.3.13, < 3.5)
        compass-core (1.0.3)
          multi_json (~> 1.0)
          sass (>= 3.3.0, < 3.5)
        compass-import-once (1.0.5)
          sass (>= 3.2, < 3.5)
        compass-rails (3.0.2)
          compass (~> 1.0.0)
          sass-rails (< 5.1)
          sprockets (< 4.0)
        concurrent-ruby (1.0.4)
        debug_inspector (0.0.2)
        erubis (2.7.0)
        execjs (2.7.0)
        faraday (0.10.1)
          multipart-post (>= 1.2, < 3)
        ffi (1.9.17)
        globalid (0.3.7)
          activesupport (>= 4.1.0)
        hashery (2.1.2)
        hashie (3.4.6)
        i18n (0.7.0)
        jquery-rails (4.2.2)
          rails-dom-testing (>= 1, < 3)
          railties (>= 4.2.0)
          thor (>= 0.14, < 2.0)
        jquery-turbolinks (2.1.0)
          railties (>= 3.1.0)
          turbolinks
        jquery-ui-rails (6.0.1)
          railties (>= 3.2.16)
        json (2.0.3)
        jwt (1.5.6)
        kgio (2.11.0)
        libv8 (3.16.14.17)
        listen (3.0.8)
          rb-fsevent (~> 0.9, >= 0.9.4)
          rb-inotify (~> 0.9, >= 0.9.7)
        loofah (2.0.3)
          nokogiri (>= 1.5.9)
        mail (2.6.4)
          mime-types (>= 1.16, < 4)
        method_source (0.8.2)
        mime-types (3.1)
          mime-types-data (~> 3.2015)
        mime-types-data (3.2016.0521)
        mini_portile2 (2.1.0)
        minitest (5.10.1)
        multi_json (1.12.1)
        multi_xml (0.6.0)
        multipart-post (2.0.0)
        nio4r (1.2.1)
        nokogiri (1.7.0.1)
          mini_portile2 (~> 2.1.0)
        oauth (0.5.1)
        oauth2 (1.3.0)
          faraday (>= 0.8, < 0.11)
          jwt (~> 1.0)
          multi_json (~> 1.3)
          multi_xml (~> 0.5)
          rack (>= 1.2, < 3)
        omniauth (1.3.1)
          hashie (>= 1.2, < 4)
          rack (>= 1.0, < 3)
        omniauth-facebook (4.0.0)
          omniauth-oauth2 (~> 1.2)
        omniauth-google-oauth2 (0.4.1)
          jwt (~> 1.5.2)
          multi_json (~> 1.3)
          omniauth (>= 1.1.1)
          omniauth-oauth2 (>= 1.3.1)
        omniauth-linkedin-oauth2 (0.1.5)
          omniauth (~> 1.0)
          omniauth-oauth2
        omniauth-oauth (1.1.0)
          oauth
          omniauth (~> 1.0)
        omniauth-oauth2 (1.3.1)
          oauth2 (~> 1.0)
          omniauth (~> 1.2)
        omniauth-twitter (1.3.0)
          omniauth-oauth (~> 1.1)
          rack
        pdf-reader (1.4.1)
          Ascii85 (~> 1.0.0)
          afm (~> 0.2.1)
          hashery (~> 2.0)
          ruby-rc4
          ttfunk
        pg (0.19.0)
        public_suffix (2.0.5)
        puma (3.6.2)
        pundit (1.1.0)
          activesupport (>= 3.0.0)
        rack (2.0.1)
        rack-test (0.6.3)
          rack (>= 1.0)
        rails (5.0.1)
          actioncable (= 5.0.1)
          actionmailer (= 5.0.1)
          actionpack (= 5.0.1)
          actionview (= 5.0.1)
          activejob (= 5.0.1)
          activemodel (= 5.0.1)
          activerecord (= 5.0.1)
          activesupport (= 5.0.1)
          bundler (>= 1.3.0, < 2.0)
          railties (= 5.0.1)
          sprockets-rails (>= 2.0.0)
        rails-dom-testing (2.0.2)
          activesupport (>= 4.2.0, < 6.0)
          nokogiri (~> 1.6)
        rails-html-sanitizer (1.0.3)
          loofah (~> 2.0)
        railties (5.0.1)
          actionpack (= 5.0.1)
          activesupport (= 5.0.1)
          method_source
          rake (>= 0.8.7)
          thor (>= 0.18.1, < 2.0)
        raindrops (0.17.0)
        rake (12.0.0)
        rb-fsevent (0.9.8)
        rb-inotify (0.9.7)
          ffi (>= 0.5.0)
        ref (2.0.0)
        roo (2.7.1)
          nokogiri (~> 1)
          rubyzip (~> 1.1, < 2.0.0)
        roo-xls (1.0.0)
          nokogiri
          roo (>= 2.0.0beta1, < 3)
          spreadsheet (> 0.9.0)
        ruby-ole (1.2.12)
        ruby-rc4 (0.1.5)
        rubyzip (1.2.0)
        sass (3.4.23)
        sass-rails (5.0.6)
          railties (>= 4.0.0, < 6)
          sass (~> 3.1)
          sprockets (>= 2.8, < 4.0)
          sprockets-rails (>= 2.0, < 4.0)
          tilt (>= 1.1, < 3)
        socksify (1.7.0)
        spreadsheet (1.1.4)
          ruby-ole (>= 1.0)
        spring (2.0.0)
          activesupport (>= 4.2)
        spring-watcher-listen (2.0.1)
          listen (>= 2.7, < 4.0)
          spring (>= 1.2, < 3.0)
        sprockets (3.7.1)
          concurrent-ruby (~> 1.0)
          rack (> 1, < 3)
        sprockets-rails (3.2.0)
          actionpack (>= 4.0)
          activesupport (>= 4.0)
          sprockets (>= 3.0.0)
        therubyracer (0.12.3)
          libv8 (~> 3.16.14.15)
          ref
        thor (0.19.4)
        thread_safe (0.3.5)
        tilt (2.0.5)
        ttfunk (1.4.0)
        turbolinks (5.0.1)
          turbolinks-source (~> 5)
        turbolinks-source (5.0.0)
        tzinfo (1.2.2)
          thread_safe (~> 0.1)
        uglifier (3.0.4)
          execjs (>= 0.3.0, < 3)
        unicorn (5.2.0)
          kgio (~> 2.6)
          raindrops (~> 0.7)
        w3c_validators (1.3.1)
          json (~> 2.0)
          nokogiri (~> 1.6)
        web-console (3.4.0)
          actionview (>= 5.0)
          activemodel (>= 5.0)
          debug_inspector
          railties (>= 5.0)
        websocket-driver (0.6.4)
          websocket-extensions (>= 0.1.0)
        websocket-extensions (0.1.2)
        whenever (0.9.7)
          chronic (>= 0.6.3)
        will_paginate (3.1.5)
    
    PLATFORMS
      ruby
    
    DEPENDENCIES
      activerecord-import
      addressable
      autoprefixer-rails
      bootstrap-sass
      byebug
      coffee-rails
      compass-rails
      jquery-rails
      jquery-turbolinks
      jquery-ui-rails
      listen (~> 3.0.5)
      omniauth-facebook
      omniauth-google-oauth2
      omniauth-linkedin-oauth2
      omniauth-oauth2 (~> 1.3.1)
      omniauth-twitter
      pdf-reader
      pg
      puma
      pundit
      rails (~> 5.0.1)
      roo-xls
      sass-rails
      socksify
      spring
      spring-watcher-listen (~> 2.0.0)
      therubyracer
      turbolinks
      uglifier
      unicorn
      w3c_validators
      web-console
      whenever
      will_paginate
    
    BUNDLED WITH
       1.13.7
    
    
    
    
    
      [1]: http://whatismyip.akamai.com