Mostrando las entradas con la etiqueta bouncer. Mostrar todas las entradas
Mostrando las entradas con la etiqueta bouncer. Mostrar todas las entradas

2013-07-29

Bouncer aggiornado

Después de mucho tiempo volví a programar un toque en ruby. Por necesidades laborales, tuve que usar de nuevo mi bouncer.rb, pero lamentablemente "rdoc/usage" no está en ruby 1.9. Así que lo adapté para que no lo necesite más. Abajo pongo el código:

#!/usr/bin/env ruby

# == Synopsis
#
# redirect.rb: Redirects TCP connections to distant machines. Handles simultaneously many connections.
#
# == Usage
#
# ruby redirect.rb [OPTION]
#
# -h, --help:
#   Show help
#
# --ip ip, -i ip:
#   Accept connections from ip (default 127.0.0.1)
#
# --port port, -p port:
#   Listen on port (default 12345)
#
# --target ip:port, -t ip:port
#   Connect to ip:port (default 127.0.0.1:23456)
#

require 'getoptlong'
#require 'rdoc/usage'
require 'socket'

class Redirect
 def initialize( source_ip, listening_port, target_ip, target_port )
  @source_ip = source_ip
  @target_ip = target_ip
  @target_port = target_port

  @server_socket = TCPServer.new( "", listening_port )
    @server_socket.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
   
  @descriptors = [ @server_socket ]
  @next_step = {}
 end

 def handle_new_connection
  incoming = @server_socket.accept
  if incoming.peeraddr[3] == @source_ip
   begin
    outgoing = TCPSocket.new( @target_ip, @target_port )
   
    @next_step[ incoming ] = outgoing
    @next_step[ outgoing ] = incoming

    @descriptors += [ incoming, outgoing ]
   rescue
    silent_close( incoming )
   end
  else
   silent_close( incoming )
  end
 end

 def silent_close( sock )
  begin
   sock.close
  rescue
   #do nothing intentionally
  end
 end

  def propagate(sock)
    next_sock = @next_step[sock]
    next_sock.write(sock.read_nonblock(1000 * 1000))
  end

 def finish_connection(sock)
  next_sock = @next_step[sock]
  [ sock, next_sock ].each do
   |s|
   silent_close(s)
   @descriptors.delete(s)
   @next_step.delete(s)
  end
 end

 def run
  loop do
   connections = select( @descriptors )
   connections[0].each do
    |sock|
    if sock == @server_socket then
     handle_new_connection
    else
     begin
      sock.eof? ? finish_connection(sock) : propagate(sock)
     rescue
      finish_connection(sock)
     end
    end
   end
  end
 end
end

if $0 == __FILE__ then

 opts = GetoptLong.new(
   [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
   [ '--ip', '-i', GetoptLong::REQUIRED_ARGUMENT ],
   [ '--port', '-p', GetoptLong::REQUIRED_ARGUMENT ],
   [ '--target', '-t', GetoptLong::REQUIRED_ARGUMENT ]
 )

 ip = '127.0.0.1'
 port = '12345'
 target = '127.0.0.1:23456'

 opts.each do
  |opt, arg|
  case opt
  when '--help'
   print """\
Usage:
ruby bouncer.rb [options]

options:
 -i [ip] --ip=[ip]
 -p [port] --port=[port]
 -t [host:port] --target=[host:port]
"""
   exit
  when '--ip'
   ip = arg
  when '--port'
   port = arg
  when '--target'
   target = arg
  end
 end

 port = port.to_i
 target = target.split(":")

 trap("SIGINT") do
   exit
 end

 Redirect.new(ip, port, target[0], target[1].to_i).run
end



Happy hacking,
Aureliano

2009-06-19

Bouncer versión 2

Hice una nueva versión del bouncer que permite bouncear a varios targets desde un mismo host:

#!/usr/bin/env ruby

# == Synopsis
#
# bouncer.rb: Redirects TCP connections to distant machines. Handles simultaneously many connections.
#
# == Usage
#
# ruby redirect.rb [OPTION]
#
# -h, --help:
# Show help
#
# --ip ip, -i ip:
# Accept connections from ip (default 127.0.0.1)
#
# --port port, -p port:
# Listen on port (default 12345)
#
# --target ip:port, -t ip:port
# Connect to ip:port (default 127.0.0.1:23456)
#

require 'getoptlong'
require 'rdoc/usage'
require 'socket'

class Bouncer
def self.for_host( source_ip, start_port, targets )
rules = {}
targets.each_with_index do
|target, index|
rules[ start_port + index ] = target
end
self.new( source_ip, rules )
end
def initialize( source_ip, port_target_map )
@port_target_map = port_target_map
@source_ip = source_ip

@servers = @port_target_map.keys.map do
|port|
ss = TCPServer.new( "", port )
ss.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
ss
end

@descriptors = @servers.clone
@next_step = {}
end

def handle_new_connection( ss )
incoming = ss.accept
if incoming.peeraddr[3] == @source_ip
begin
outgoing = TCPSocket.new( *@port_target_map[incoming.addr[1]] )
@next_step[ incoming ] = outgoing
@next_step[ outgoing ] = incoming

@descriptors += [ incoming, outgoing ]
rescue
silent_close( incoming )
end
else
silent_close( incoming )
end
end

def silent_close( sock )
begin
sock.close
rescue
#do nothing intentionally
end
end

def propagate(sock)
next_sock = @next_step[sock]
next_sock.write(sock.read_nonblock(1000 * 1000))
end

def finish_connection(sock)
next_sock = @next_step[sock]
[ sock, next_sock ].each do
|s|
silent_close(s)
@descriptors.delete(s)
@next_step.delete(s)
end
end

def run
loop do
connections = select( @descriptors )
connections[0].each do
|sock|
if @servers.include? sock then
handle_new_connection( sock )
else
begin
sock.eof? ? finish_connection(sock) : propagate(sock)
rescue
finish_connection(sock)
end
end
end
end
end
end

if $0 == __FILE__ then

opts = GetoptLong.new(
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
[ '--ip', '-i', GetoptLong::REQUIRED_ARGUMENT ],
[ '--port', '-p', GetoptLong::REQUIRED_ARGUMENT ],
[ '--target', '-t', GetoptLong::REQUIRED_ARGUMENT ]
)

ip = '127.0.0.1'
port = '12345'
target = '127.0.0.1:23456'

opts.each do
|opt, arg|
case opt
when '--help'
RDoc::usage
exit
when '--ip'
ip = arg
when '--port'
port = arg
when '--target'
target = arg
end
end

port = port.to_i
target = target.split(":")

trap("SIGINT") do
exit
end

Bouncer.new(ip, port => [target[0], target[1].to_i]).run
end

2009-05-05

Bouncer básico en ruby

Estuve programando un bouncer, usando como base el load balancer que hice el año pasado. Un bouncer es un programa que poner un server TCP en un puerto y cada vez que recibe una conexión abre una conexión a otro lado y las "ata", haciendo que el server al que se conecta le conteste al cliente que se conecta a él.
En mi caso lo programé para tener una salida controlada del entorno de máquinas virtuales que estoy armando para tener más ordenadas las cosas en el trabajo.
Por si les interesa, acá abajo pongo el código completo.


#!/usr/bin/env ruby

# == Synopsis
#
# redirect.rb: Redirects TCP connections to distant machines. Handles simultaneously many connections.
#
# == Usage
#
# ruby redirect.rb [OPTION]
#
# -h, --help:
# Show help
#
# --ip ip, -i ip:
# Accept connections from ip (default 127.0.0.1)
#
# --port port, -p port:
# Listen on port (default 12345)
#
# --target ip:port, -t ip:port
# Connect to ip:port (default 127.0.0.1:23456)
#

require 'getoptlong'
require 'rdoc/usage'
require 'socket'

class Redirect
def initialize( source_ip, listening_port, target_ip, target_port )
@source_ip = source_ip
@target_ip = target_ip
@target_port = target_port

@server_socket = TCPServer.new( "", listening_port )
@server_socket.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )

@descriptors = [ @server_socket ]
@next_step = {}
end

def handle_new_connection
incoming = @server_socket.accept
if incoming.peeraddr[3] == @source_ip
begin
outgoing = TCPSocket.new( @target_ip, @target_port )

@next_step[ incoming ] = outgoing
@next_step[ outgoing ] = incoming

@descriptors += [ incoming, outgoing ]
rescue
silent_close( incoming )
end
else
silent_close( incoming )
end
end

def silent_close( sock )
begin
sock.close
rescue
#do nothing intentionally
end
end

def propagate(sock)
next_sock = @next_step[sock]
next_sock.write(sock.read_nonblock(1000 * 1000))
end

def finish_connection(sock)
next_sock = @next_step[sock]
[ sock, next_sock ].each do
|s|
silent_close(s)
@descriptors.delete(s)
@next_step.delete(s)
end
end

def run
loop do
connections = select( @descriptors )
connections[0].each do
|sock|
if sock == @server_socket then
handle_new_connection
else
begin
sock.eof? ? finish_connection(sock) : propagate(sock)
rescue
finish_connection(sock)
end
end
end
end
end
end

if $0 == __FILE__ then

opts = GetoptLong.new(
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
[ '--ip', '-i', GetoptLong::REQUIRED_ARGUMENT ],
[ '--port', '-p', GetoptLong::REQUIRED_ARGUMENT ],
[ '--target', '-t', GetoptLong::REQUIRED_ARGUMENT ]
)

ip = '127.0.0.1'
port = '12345'
target = '127.0.0.1:23456'

opts.each do
|opt, arg|
case opt
when '--help'
RDoc::usage
exit
when '--ip'
ip = arg
when '--port'
port = arg
when '--target'
target = arg
end
end

port = port.to_i
target = target.split(":")

trap("SIGINT") do
exit
end

Redirect.new(ip, port, target[0], target[1].to_i).run
end