bettercap 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|