Skip to content
Browse files

extract proxy logic

  • Loading branch information...
1 parent 943df10 commit 2b35339a0089272907f08cb0d212e344bce5107e @igrigorik committed Nov 5, 2011
Showing with 112 additions and 5 deletions.
  1. +1 −0 .gitignore
  2. +36 −0 bin/hydra
  3. +2 −1 hydra.gemspec
  4. +1 −4 lib/hydra.rb
  5. +72 −0 lib/hydra/proxy.rb
  6. BIN misc/hydra.png
View
1 .gitignore
@@ -2,3 +2,4 @@
.bundle
Gemfile.lock
pkg/*
+*.graffle
View
36 bin/hydra 100644 → 100755
@@ -0,0 +1,36 @@
+#!/usr/bin/env ruby
+
+lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
+$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
+
+require 'hydra'
+require 'optparse'
+
+ARGV << '--help' if ARGV.empty?
+
+options = {}
+OptionParser.new do |opts|
+ opts.banner = "Usage: hydra [options]"
+
+ opts.on("-l", "--listen PORT", Integer, "Port to listen on") do |v|
+ options[:listen] = v
+ end
+
+ opts.on("-h", "--hosts host1,host2,...", Array, "List of proxy hosts") do |v|
+ options[:hosts] = v
+ end
+
+ opts.on("-k", "--key path/to/key", String, "SSH key for the proxy hosts") do |v|
+ options[:key] = v
+ end
+
+ opts.on("-u", "--user username", String, "SSH username for the proxy hosts") do |v|
+ options[:user] = v
+ end
+
+ opts.on("-v", "--verbose", "Run in debug mode") do |v|
+ options[:verbose] = v
+ end
+end.parse!
+
+Hydra::Proxy.new(options).start!
View
3 hydra.gemspec
@@ -8,11 +8,12 @@ Gem::Specification.new do |s|
s.authors = ["Ilya Grigorik"]
s.email = ["ilya@igvita.com"]
s.homepage = "https://github.com/igrigorik/hydra"
- s.summary = "SOCKS5 Hydra proxy - a multiheaded beast"
+ s.summary = "Load-balanced (multi-headed) SOCKS5 proxy"
s.description = s.summary
s.rubyforge_project = "hydra"
+ s.add_dependency "eventmachine", ">= 1.0.0.beta.4"
s.add_dependency "em-proxy"
s.add_development_dependency "rspec"
View
5 lib/hydra.rb
@@ -1,5 +1,2 @@
require "hydra/version"
-
-module Hydra
- # Your code goes here...
-end
+require "hydra/proxy"
View
72 lib/hydra/proxy.rb
@@ -0,0 +1,72 @@
+require 'em-proxy'
+
+module Hydra
+
+ class Logger
+ [:info, :error].each do |m|
+ define_method m do |msg|
+ msg = msg.join(", ") if msg.is_a? Array
+ puts ["Hydra", m, msg].join(" :: ")
+ end
+ end
+ end
+
+ class Proxy
+ def initialize(options)
+ @listen = options.delete(:listen) || 8080
+ @user = options.delete(:user) || 'root'
+ @key = options.delete(:key)
+ @log = Logger.new
+
+ @hosts = options.delete(:hosts)
+ @hosts = @hosts.inject({}) {|h,k| h[@hosts.index(k)] = k; h}
+
+ @log.info "Establishing #{@hosts.size} SOCKS5 tunnels"
+ @live = {}
+ @pids = []
+ end
+
+ def start!
+ EM.epoll
+ EM.run do
+ @hosts.each do |index, host|
+ tunnel = <<-CMD
+ ssh #{@user}@#{host} -D #{7000 + index} -NT
+ -o StrictHostKeyChecking=no
+ -o ServerAliveInterval=300
+ -o ConnectTimeout=5
+ -o ExitOnForwardFailure=yes
+ -i #{@key}
+ CMD
+
+ @pids << EM.system(tunnel) do |cmd, out|
+ @log.error ["Connection closed", out, @hosts[index]]
+ @live.delete(index)
+
+ if @live.empty?
+ @log.info "No live tunnels left, exiting"
+ exit
+ end
+ end
+
+ @live[index] = host
+ end
+
+ at_exit do
+ pids = @pids.join(' ')
+ unless system("kill #{pids}")
+ @log.error "Could not kill all tunnels: #{pids}"
+ end
+ end
+
+ @log.info "Starting proxy on port #{@listen}"
+ ::Proxy.start(:host => "0.0.0.0", :port => @listen) do |conn|
+ tunnel = @live.keys.shuffle.first
+
+ @log.info ["Routing request to", tunnel, @hosts[tunnel]]
+ conn.server :srv, :host => "127.0.0.1", :port => 7000 + tunnel, :relay_client => true, :relay_server => true
+ end
+ end
+ end
+ end
+end
View
BIN misc/hydra.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 2b35339

Please sign in to comment.
Something went wrong with that request. Please try again.