bettercap 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +225 -0
- data/README.md +96 -0
- data/bettercap.gemspec +28 -0
- data/bin/bettercap +184 -0
- data/example_proxy_module.rb +21 -0
- data/lib/bettercap/base/ifirewall.rb +28 -0
- data/lib/bettercap/base/ispoofer.rb +24 -0
- data/lib/bettercap/context.rb +124 -0
- data/lib/bettercap/discovery/arp.rb +37 -0
- data/lib/bettercap/discovery/icmp.rb +37 -0
- data/lib/bettercap/discovery/syn.rb +88 -0
- data/lib/bettercap/discovery/udp.rb +74 -0
- data/lib/bettercap/error.rb +16 -0
- data/lib/bettercap/factories/firewall_factory.rb +32 -0
- data/lib/bettercap/factories/parser_factory.rb +53 -0
- data/lib/bettercap/factories/spoofer_factory.rb +36 -0
- data/lib/bettercap/firewalls/linux.rb +55 -0
- data/lib/bettercap/firewalls/osx.rb +70 -0
- data/lib/bettercap/hw-prefixes +19651 -0
- data/lib/bettercap/logger.rb +53 -0
- data/lib/bettercap/monkey/packetfu/utils.rb +96 -0
- data/lib/bettercap/network.rb +131 -0
- data/lib/bettercap/proxy/module.rb +39 -0
- data/lib/bettercap/proxy/proxy.rb +262 -0
- data/lib/bettercap/proxy/request.rb +77 -0
- data/lib/bettercap/proxy/response.rb +76 -0
- data/lib/bettercap/shell.rb +31 -0
- data/lib/bettercap/sniffer/parsers/base.rb +31 -0
- data/lib/bettercap/sniffer/parsers/ftp.rb +19 -0
- data/lib/bettercap/sniffer/parsers/httpauth.rb +45 -0
- data/lib/bettercap/sniffer/parsers/https.rb +36 -0
- data/lib/bettercap/sniffer/parsers/irc.rb +19 -0
- data/lib/bettercap/sniffer/parsers/mail.rb +19 -0
- data/lib/bettercap/sniffer/parsers/ntlmss.rb +38 -0
- data/lib/bettercap/sniffer/parsers/post.rb +24 -0
- data/lib/bettercap/sniffer/parsers/url.rb +28 -0
- data/lib/bettercap/sniffer/sniffer.rb +39 -0
- data/lib/bettercap/spoofers/arp.rb +130 -0
- data/lib/bettercap/spoofers/none.rb +23 -0
- data/lib/bettercap/target.rb +52 -0
- data/lib/bettercap/version.rb +14 -0
- metadata +129 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
BETTERCAP
|
4
|
+
|
5
|
+
Author : Simone 'evilsocket' Margaritelli
|
6
|
+
Email : evilsocket@gmail.com
|
7
|
+
Blog : http://www.evilsocket.net/
|
8
|
+
|
9
|
+
This project is released under the GPL 3 license.
|
10
|
+
|
11
|
+
=end
|
12
|
+
module Logger
|
13
|
+
class << self
|
14
|
+
attr_accessor :logfile, :debug_enabled
|
15
|
+
|
16
|
+
@@semaphore = Mutex.new
|
17
|
+
|
18
|
+
def error(message)
|
19
|
+
write(formatted_message(message, 'E').red)
|
20
|
+
end
|
21
|
+
|
22
|
+
def info(message)
|
23
|
+
write(formatted_message(message, 'I'))
|
24
|
+
end
|
25
|
+
|
26
|
+
def warn(message)
|
27
|
+
write(formatted_message(message, 'W').yellow)
|
28
|
+
end
|
29
|
+
|
30
|
+
def debug(message)
|
31
|
+
if @debug_enabled
|
32
|
+
write(formatted_message(message, 'D').light_black)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def write(message)
|
37
|
+
# make sure that logging is thread safe
|
38
|
+
@@semaphore.synchronize {
|
39
|
+
puts message
|
40
|
+
if !@logfile.nil?
|
41
|
+
f = File.open( @logfile, 'a+t' )
|
42
|
+
f.puts( message.gsub( /\e\[(\d+)(;\d+)*m/, '') + "\n")
|
43
|
+
f.close
|
44
|
+
end
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def formatted_message(message, message_type)
|
50
|
+
"[#{message_type}] #{message}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
BETTERCAP
|
4
|
+
|
5
|
+
Author : Simone 'evilsocket' Margaritelli
|
6
|
+
Email : evilsocket@gmail.com
|
7
|
+
Blog : http://www.evilsocket.net/
|
8
|
+
|
9
|
+
This project is released under the GPL 3 license.
|
10
|
+
|
11
|
+
=end
|
12
|
+
|
13
|
+
# PacketFu::Utils.ifconfig is broken under OS X, it does
|
14
|
+
# not correctly parse the netmask field due to a wrong
|
15
|
+
# regular expression.
|
16
|
+
#
|
17
|
+
# ORIGINAL: https://github.com/packetfu/packetfu/blob/master/lib/packetfu/utils.rb#L204
|
18
|
+
require 'bettercap/logger'
|
19
|
+
|
20
|
+
module PacketFu
|
21
|
+
class Utils
|
22
|
+
def self.ifconfig(iface='eth0')
|
23
|
+
ret = {}
|
24
|
+
iface = iface.to_s.scan(/[0-9A-Za-z]/).join
|
25
|
+
|
26
|
+
Logger.debug "ifconfig #{iface}"
|
27
|
+
|
28
|
+
ifconfig_data = Shell.ifconfig(iface)
|
29
|
+
|
30
|
+
case RUBY_PLATFORM
|
31
|
+
when /linux/i
|
32
|
+
Logger.debug "Linux ifconfig #{iface}:\n#{ifconfig_data}"
|
33
|
+
|
34
|
+
if ifconfig_data =~ /#{iface}/i
|
35
|
+
ifconfig_data = ifconfig_data.split(/[\s]*\n[\s]*/)
|
36
|
+
else
|
37
|
+
raise ArgumentError, "Cannot ifconfig #{iface}"
|
38
|
+
end
|
39
|
+
real_iface = ifconfig_data.first
|
40
|
+
ret[:iface] = real_iface.split.first.downcase.gsub(':','')
|
41
|
+
if real_iface =~ /[\s]HWaddr[\s]+([0-9a-fA-F:]{17})/i
|
42
|
+
ret[:eth_saddr] = $1.downcase
|
43
|
+
ret[:eth_src] = EthHeader.mac2str(ret[:eth_saddr])
|
44
|
+
end
|
45
|
+
ifconfig_data.each do |s|
|
46
|
+
case s
|
47
|
+
when /inet [a-z]+:[\s]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*[a-z]+:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+))?/i
|
48
|
+
ret[:ip_saddr] = $1
|
49
|
+
ret[:ip_src] = [IPAddr.new($1).to_i].pack("N")
|
50
|
+
ret[:ip4_obj] = IPAddr.new($1)
|
51
|
+
ret[:ip4_obj] = ret[:ip4_obj].mask($3) if $3
|
52
|
+
when /inet[\s]+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*Mask[\s]+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+))?/i
|
53
|
+
ret[:ip_saddr] = $1
|
54
|
+
ret[:ip_src] = [IPAddr.new($1).to_i].pack("N")
|
55
|
+
ret[:ip4_obj] = IPAddr.new($1)
|
56
|
+
ret[:ip4_obj] = ret[:ip4_obj].mask($3) if $3
|
57
|
+
when /inet6 [a-z]+:[\s]*([0-9a-fA-F:\x2f]+)/
|
58
|
+
ret[:ip6_saddr] = $1
|
59
|
+
ret[:ip6_obj] = IPAddr.new($1)
|
60
|
+
end
|
61
|
+
end # linux
|
62
|
+
when /darwin/i
|
63
|
+
Logger.debug "OSX ifconfig #{iface}:\n#{ifconfig_data}"
|
64
|
+
|
65
|
+
if ifconfig_data =~ /#{iface}/i
|
66
|
+
ifconfig_data = ifconfig_data.split(/[\s]*\n[\s]*/)
|
67
|
+
else
|
68
|
+
raise ArgumentError, "Cannot ifconfig #{iface}"
|
69
|
+
end
|
70
|
+
real_iface = ifconfig_data.first
|
71
|
+
ret[:iface] = real_iface.split(':')[0]
|
72
|
+
ifconfig_data.each do |s|
|
73
|
+
case s
|
74
|
+
when /ether[\s]([0-9a-fA-F:]{17})/i
|
75
|
+
ret[:eth_saddr] = $1
|
76
|
+
ret[:eth_src] = EthHeader.mac2str(ret[:eth_saddr])
|
77
|
+
when /inet[\s]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*Mask[\s]+(0x[a-f0-9]+))?/i
|
78
|
+
imask = 0
|
79
|
+
if $3
|
80
|
+
imask = $3.to_i(16).to_s(2).count("1")
|
81
|
+
end
|
82
|
+
|
83
|
+
ret[:ip_saddr] = $1
|
84
|
+
ret[:ip_src] = [IPAddr.new($1).to_i].pack("N")
|
85
|
+
ret[:ip4_obj] = IPAddr.new($1)
|
86
|
+
ret[:ip4_obj] = ret[:ip4_obj].mask(imask) if imask
|
87
|
+
when /inet6[\s]*([0-9a-fA-F:\x2f]+)/
|
88
|
+
ret[:ip6_saddr] = $1
|
89
|
+
ret[:ip6_obj] = IPAddr.new($1)
|
90
|
+
end
|
91
|
+
end # darwin
|
92
|
+
end # RUBY_PLATFORM
|
93
|
+
ret
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
BETTERCAP
|
4
|
+
|
5
|
+
Author : Simone 'evilsocket' Margaritelli
|
6
|
+
Email : evilsocket@gmail.com
|
7
|
+
Blog : http://www.evilsocket.net/
|
8
|
+
|
9
|
+
This project is released under the GPL 3 license.
|
10
|
+
|
11
|
+
=end
|
12
|
+
require 'thread'
|
13
|
+
|
14
|
+
require 'bettercap/error'
|
15
|
+
require 'bettercap/logger'
|
16
|
+
require 'bettercap/shell'
|
17
|
+
require 'bettercap/target'
|
18
|
+
require 'bettercap/factories/firewall_factory'
|
19
|
+
require 'bettercap/discovery/icmp'
|
20
|
+
require 'bettercap/discovery/udp'
|
21
|
+
require 'bettercap/discovery/syn'
|
22
|
+
require 'bettercap/discovery/arp'
|
23
|
+
|
24
|
+
class Network
|
25
|
+
|
26
|
+
def Network.is_ip?(ip)
|
27
|
+
if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ ip
|
28
|
+
return $~.captures.all? {|i| i.to_i < 256}
|
29
|
+
end
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def Network.get_gateway
|
34
|
+
nstat = Shell.execute('netstat -nr')
|
35
|
+
|
36
|
+
Logger.debug "NETSTAT:\n#{nstat}"
|
37
|
+
|
38
|
+
out = nstat.split(/\n/).select {|n| n =~ /UG/ }
|
39
|
+
gw = out.first.split[1]
|
40
|
+
|
41
|
+
raise BetterCap::Error, 'Could not detect gateway address' unless is_ip?(gw)
|
42
|
+
gw
|
43
|
+
end
|
44
|
+
|
45
|
+
def Network.get_local_ips
|
46
|
+
ips = []
|
47
|
+
|
48
|
+
Shell.ifconfig.split("\n").each do |line|
|
49
|
+
if line =~ /inet [adr:]*([\d\.]+)/
|
50
|
+
ips << $1
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
ips
|
55
|
+
end
|
56
|
+
|
57
|
+
def Network.get_alive_targets( ctx, timeout = 5 )
|
58
|
+
if ctx.options[:arpcache] == false
|
59
|
+
icmp = IcmpAgent.new timeout
|
60
|
+
udp = UdpAgent.new ctx.ifconfig, ctx.gateway, ctx.iface[:ip_saddr]
|
61
|
+
syn = SynAgent.new ctx.ifconfig, ctx.gateway, ctx.iface[:ip_saddr]
|
62
|
+
|
63
|
+
syn.wait
|
64
|
+
icmp.wait
|
65
|
+
udp.wait
|
66
|
+
else
|
67
|
+
Logger.debug 'Using current ARP cache.'
|
68
|
+
end
|
69
|
+
|
70
|
+
ArpAgent.parse ctx
|
71
|
+
end
|
72
|
+
|
73
|
+
=begin
|
74
|
+
FIXME:
|
75
|
+
|
76
|
+
Apparently on Mac OSX the gem pcaprub ( or libpcap itself ) has
|
77
|
+
a bug, so we can't use 'PacketFu::Utils::arp' since the funtion
|
78
|
+
it's using:
|
79
|
+
|
80
|
+
if cap.save > 0
|
81
|
+
...
|
82
|
+
end
|
83
|
+
|
84
|
+
won't catch anything, instead we're using cap.stream.each.
|
85
|
+
=end
|
86
|
+
def Network.get_hw_address( iface, ip_address, attempts = 2 )
|
87
|
+
hw_address = nil
|
88
|
+
|
89
|
+
attempts.times do
|
90
|
+
arp_pkt = PacketFu::ARPPacket.new
|
91
|
+
|
92
|
+
arp_pkt.eth_saddr = arp_pkt.arp_saddr_mac = iface[:eth_saddr]
|
93
|
+
arp_pkt.eth_daddr = 'ff:ff:ff:ff:ff:ff'
|
94
|
+
arp_pkt.arp_daddr_mac = '00:00:00:00:00:00'
|
95
|
+
arp_pkt.arp_saddr_ip = iface[:ip_saddr]
|
96
|
+
arp_pkt.arp_daddr_ip = ip_address
|
97
|
+
|
98
|
+
cap_thread = Thread.new do
|
99
|
+
target_mac = nil
|
100
|
+
cap = PacketFu::Capture.new(
|
101
|
+
:iface => iface[:iface],
|
102
|
+
:start => true,
|
103
|
+
:filter => "arp src #{ip_address} and ether dst #{arp_pkt.eth_saddr}"
|
104
|
+
)
|
105
|
+
arp_pkt.to_w(iface[:iface])
|
106
|
+
|
107
|
+
timeout = 0
|
108
|
+
|
109
|
+
while target_mac.nil? && timeout <= 5
|
110
|
+
cap.stream.each do |p|
|
111
|
+
arp_response = PacketFu::Packet.parse(p)
|
112
|
+
target_mac = arp_response.arp_saddr_mac if arp_response.arp_saddr_ip == ip_address
|
113
|
+
|
114
|
+
break unless target_mac.nil?
|
115
|
+
end
|
116
|
+
|
117
|
+
timeout += 0.1
|
118
|
+
|
119
|
+
Logger.debug 'Retrying ...'
|
120
|
+
sleep 0.1
|
121
|
+
end
|
122
|
+
target_mac
|
123
|
+
end
|
124
|
+
hw_address = cap_thread.value
|
125
|
+
|
126
|
+
break unless hw_address.nil?
|
127
|
+
end
|
128
|
+
|
129
|
+
hw_address
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
BETTERCAP
|
4
|
+
|
5
|
+
Author : Simone 'evilsocket' Margaritelli
|
6
|
+
Email : evilsocket@gmail.com
|
7
|
+
Blog : http://www.evilsocket.net/
|
8
|
+
|
9
|
+
This project is released under the GPL 3 license.
|
10
|
+
|
11
|
+
=end
|
12
|
+
require 'bettercap/logger'
|
13
|
+
|
14
|
+
module Proxy
|
15
|
+
|
16
|
+
class Module
|
17
|
+
@@modules = []
|
18
|
+
|
19
|
+
def self.modules
|
20
|
+
@@modules
|
21
|
+
end
|
22
|
+
|
23
|
+
# we're enabled by default, yo!
|
24
|
+
def is_enabled?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.register_modules
|
29
|
+
Object.constants.each do |klass|
|
30
|
+
const = Kernel.const_get(klass)
|
31
|
+
if const.respond_to?(:superclass) and const.superclass == self
|
32
|
+
Logger.debug "Registering module #{const}"
|
33
|
+
@@modules << const.new
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,262 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
BETTERCAP
|
4
|
+
|
5
|
+
Author : Simone 'evilsocket' Margaritelli
|
6
|
+
Email : evilsocket@gmail.com
|
7
|
+
Blog : http://www.evilsocket.net/
|
8
|
+
|
9
|
+
This project is released under the GPL 3 license.
|
10
|
+
|
11
|
+
=end
|
12
|
+
|
13
|
+
require 'socket'
|
14
|
+
require 'uri'
|
15
|
+
|
16
|
+
require 'bettercap/logger'
|
17
|
+
require 'bettercap/network'
|
18
|
+
|
19
|
+
module Proxy
|
20
|
+
|
21
|
+
class Proxy
|
22
|
+
def initialize address, port, &processor
|
23
|
+
@socket = nil
|
24
|
+
@address = address
|
25
|
+
@port = port
|
26
|
+
@main_thread = nil
|
27
|
+
@running = false
|
28
|
+
@processor = processor
|
29
|
+
@local_ips = []
|
30
|
+
|
31
|
+
begin
|
32
|
+
@local_ips = Socket.ip_address_list.collect { |x| x.ip_address }
|
33
|
+
rescue
|
34
|
+
Logget.warn 'Could not get local ips using Socket module, using Network.get_local_ips method.'
|
35
|
+
|
36
|
+
@local_ips = Network.get_local_ips
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def start
|
41
|
+
begin
|
42
|
+
@socket = TCPServer.new( @address, @port )
|
43
|
+
@main_thread = Thread.new &method(:server_thread)
|
44
|
+
rescue Exception => e
|
45
|
+
Logger.error "Error starting proxy: #{e.inspect}"
|
46
|
+
@socket.close unless @socket.nil?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def stop
|
51
|
+
begin
|
52
|
+
Logger.info 'Stopping proxy ...'
|
53
|
+
|
54
|
+
if @socket and @running
|
55
|
+
@running = false
|
56
|
+
@socket.close
|
57
|
+
end
|
58
|
+
rescue
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def server_thread
|
65
|
+
Logger.info "Proxy started on #{@address}:#{@port} ...\n"
|
66
|
+
|
67
|
+
@running = true
|
68
|
+
|
69
|
+
begin
|
70
|
+
while @running do
|
71
|
+
Thread.new @socket.accept, &method(:client_thread)
|
72
|
+
end
|
73
|
+
rescue Exception => e
|
74
|
+
if @running
|
75
|
+
Logger.warn "Error while accepting connection: #{e.inspect}"
|
76
|
+
end
|
77
|
+
ensure
|
78
|
+
@socket.close unless @socket.nil?
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def binary_streaming from, to, opts = {}
|
83
|
+
|
84
|
+
total_size = 0
|
85
|
+
|
86
|
+
# if response|request object is available and a content length as well
|
87
|
+
# use it to speed up data streaming with precise data size
|
88
|
+
if not opts[:response].nil?
|
89
|
+
to.write opts[:response].to_s
|
90
|
+
|
91
|
+
total_size = opts[:response].content_length unless opts[:response].content_length.nil?
|
92
|
+
elsif not opts[:request].nil?
|
93
|
+
|
94
|
+
total_size = opts[:request].content_length unless opts[:request].content_length.nil?
|
95
|
+
end
|
96
|
+
|
97
|
+
buff = ""
|
98
|
+
read = 0
|
99
|
+
|
100
|
+
if total_size
|
101
|
+
chunk_size = 1024
|
102
|
+
else
|
103
|
+
chunk_size = [ 1024, total_size ].min
|
104
|
+
end
|
105
|
+
|
106
|
+
if chunk_size > 0
|
107
|
+
loop do
|
108
|
+
from.read chunk_size, buff
|
109
|
+
|
110
|
+
# nothing more to read?
|
111
|
+
break unless buff.size > 0
|
112
|
+
|
113
|
+
to.write buff
|
114
|
+
|
115
|
+
read += buff.size
|
116
|
+
|
117
|
+
# collect into the proper object
|
118
|
+
if not opts[:request].nil? and opts[:request].is_post?
|
119
|
+
opts[:request] << buff
|
120
|
+
end
|
121
|
+
|
122
|
+
# we've done reading?
|
123
|
+
break unless read != total_size
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def html_streaming request, response, from, to
|
129
|
+
buff = ""
|
130
|
+
loop do
|
131
|
+
from.read 1024, buff
|
132
|
+
|
133
|
+
break unless buff.size > 0
|
134
|
+
|
135
|
+
response << buff
|
136
|
+
end
|
137
|
+
|
138
|
+
@processor.call( request, response )
|
139
|
+
|
140
|
+
# Response::to_s will patch the headers if needed
|
141
|
+
to.write response.to_s
|
142
|
+
end
|
143
|
+
|
144
|
+
def log_stream client, request, response
|
145
|
+
client_s = "[#{client}]"
|
146
|
+
verb_s = request.verb
|
147
|
+
request_s = "http://#{request.host}#{request.url}"
|
148
|
+
response_s = "( #{response.content_type} )"
|
149
|
+
request_s = request_s.slice(0..50) + "..." unless request_s.length <= 50
|
150
|
+
|
151
|
+
verb_s = verb_s.light_blue
|
152
|
+
|
153
|
+
if response.code[0] == '2'
|
154
|
+
response_s += " [#{response.code}]".green
|
155
|
+
elsif response.code[0] == '3'
|
156
|
+
response_s += " [#{response.code}]".light_black
|
157
|
+
elsif response.code[0] == '4'
|
158
|
+
response_s += " [#{response.code}]".yellow
|
159
|
+
elsif response.code[0] == '5'
|
160
|
+
response_s += " [#{response.code}]".red
|
161
|
+
else
|
162
|
+
response_s += " [#{response.code}]"
|
163
|
+
end
|
164
|
+
|
165
|
+
Logger.write "#{client_s} #{verb_s} #{request_s} #{response_s}"
|
166
|
+
end
|
167
|
+
|
168
|
+
def is_self_request? request
|
169
|
+
@local_ips.include? IPSocket.getaddress(request.host)
|
170
|
+
end
|
171
|
+
|
172
|
+
def rickroll_lamer client
|
173
|
+
client.write "HTTP/1.1 302 Found\n"
|
174
|
+
client.write "Location: https://www.youtube.com/watch?v=dQw4w9WgXcQ\n\n"
|
175
|
+
end
|
176
|
+
|
177
|
+
def client_thread client
|
178
|
+
client_port, client_ip = Socket.unpack_sockaddr_in(client.getpeername)
|
179
|
+
Logger.debug "New connection from #{client_ip}:#{client_port}"
|
180
|
+
|
181
|
+
server = nil
|
182
|
+
request = Request.new
|
183
|
+
|
184
|
+
begin
|
185
|
+
# read the first line
|
186
|
+
request << client.readline
|
187
|
+
|
188
|
+
loop do
|
189
|
+
line = client.readline
|
190
|
+
request << line
|
191
|
+
|
192
|
+
if line.chomp == ""
|
193
|
+
break
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
raise "Couldn't extract host from the request." unless request.host
|
198
|
+
|
199
|
+
# someone is having fun with us =)
|
200
|
+
if is_self_request? request
|
201
|
+
|
202
|
+
Logger.warn "#{client_ip} is connecting to us directly."
|
203
|
+
|
204
|
+
rickroll_lamer client
|
205
|
+
|
206
|
+
else
|
207
|
+
|
208
|
+
server = TCPSocket.new( request.host, request.port )
|
209
|
+
|
210
|
+
server.write request.to_s
|
211
|
+
|
212
|
+
# this is probably a POST request, collect incoming data
|
213
|
+
if request.content_length > 0
|
214
|
+
Logger.debug "Getting #{request.content_length} bytes from client"
|
215
|
+
|
216
|
+
binary_streaming client, server, :request => request
|
217
|
+
end
|
218
|
+
|
219
|
+
Logger.debug 'Reading response ...'
|
220
|
+
|
221
|
+
response = Response.new
|
222
|
+
|
223
|
+
# read all response headers
|
224
|
+
loop do
|
225
|
+
line = server.readline
|
226
|
+
|
227
|
+
response << line
|
228
|
+
|
229
|
+
break unless not response.headers_done
|
230
|
+
end
|
231
|
+
|
232
|
+
if response.is_textual?
|
233
|
+
log_stream client_ip, request, response
|
234
|
+
|
235
|
+
Logger.debug 'Detected textual response'
|
236
|
+
|
237
|
+
html_streaming request, response, server, client
|
238
|
+
else
|
239
|
+
Logger.debug "[#{client_ip}] -> #{request.host}#{request.url} [#{response.code}]"
|
240
|
+
|
241
|
+
Logger.debug 'Binary streaming'
|
242
|
+
|
243
|
+
binary_streaming server, client, :response => response
|
244
|
+
end
|
245
|
+
|
246
|
+
Logger.debug "#{client_ip}:#{client_port} served."
|
247
|
+
end
|
248
|
+
|
249
|
+
rescue Exception => e
|
250
|
+
if request.host
|
251
|
+
Logger.debug "Error while serving #{request.host}#{request.url}: #{e.inspect}"
|
252
|
+
Logger.debug e.backtrace
|
253
|
+
end
|
254
|
+
ensure
|
255
|
+
client.close
|
256
|
+
server.close unless server.nil?
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
|
262
|
+
end
|