#!/usr/bin/ruby # this is a TCP proxy which dumps the transmitted data in hex on stdout # (c) Yoann Guillot 2009, license: wtfpl v2 require 'socket' require 'enumerator' class String def hexdump(fmt=nil, ctx={}) fmt ||= ['c', 'd', 'a'] ctx[:pos] ||= 0 ctx[:lastline] ||= [] ctx[:lastdup] unpack('C*').each_slice(16) { |s| if s != ctx[:lastline] ctx[:lastdup] = false print '%04x ' % ctx[:pos] print s.map { |b| '%02x' % b }.join(' ').ljust(3*16-1) + ' ' if fmt.include? 'c' print s.pack('C*').unpack('L*').map { |bb| '%08x' % bb }.join(' ').ljust(9*4-1) + ' ' if fmt.include? 'd' print s.map { |c| (32..126).include?(c) ? c : ?. }.pack('C*') if fmt.include? 'a' puts elsif not ctx[:lastdup] ctx[:lastdup] = true puts '*' end ctx[:lastline] = s ctx[:pos] += s.length } puts '%04x' % ctx[:pos] if not ctx[:noend] end end def bouncepkt(clt, srv, timeout=nil) s2c = '' c2s = '' loop do break if not IO.select([clt, srv], nil, nil, timeout) while s2c.length < 1024*16 and IO.select([srv], nil, nil, 0) str = (srv.read(1) rescue nil) break if not str or str.empty? s2c << str end while c2s.length < 1024*16 and IO.select([clt], nil, nil, 0) str = (clt.read(1) rescue nil) break if not str or str.empty? c2s << str end if s2c.length > 0 and IO.select(nil, [clt], nil, 0) puts 's -> c' s2c.hexdump(['c', 'a']) clt.write s2c s2c.replace '' end if c2s.length > 0 and IO.select(nil, [srv], nil, 0) puts 'c -> s' c2s.hexdump(['c', 'a']) srv.write c2s c2s.replace '' end end end if $0 == __FILE__ if ARGV.length < 4 abort "usage: bnc []" end lhost = ARGV.shift lport = Integer(ARGV.shift) rhost = ARGV.shift rport = Integer(ARGV.shift) timeout = Integer(ARGV.shift) if not ARGV.empty? s = TCPServer.new(lhost, lport) loop do puts "waiting..." a = s.accept puts "incoming connection" c = TCPSocket.new(rhost, rport) begin bouncepkt(a, c, timeout) rescue SystemCallError end puts "connection closed" a.close c.close end end