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

868 views Asked by At

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
1

There are 1 answers

5
Eric Duminil On BEST ANSWER

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