-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathserver.rb
206 lines (160 loc) · 5.17 KB
/
server.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
require "logger"
require "socket"
require_relative "./packet.rb"
require_relative "./config.rb"
require_relative "./crypto.rb"
require_relative "./ghost_server.rb"
require "json"
require "eventmachine"
# frozen_string_literal: true
# Actual logic of the server
class Blade3Server < EM::Connection
include EM::P::LineProtocol
# Handle new client connection
def post_init
@logger = Logger.new(STDOUT)
@logger.info "New connection to Blade3Server"
@inbound_crypto = Crypto.new(false)
@outbound_crypto = Crypto.new(true, @inbound_crypto.iv)
@stats = Hash.new
@stats["packet_outbound_total"] = 0
@stats["packet_inbound_total"] = 0
@stats["packet_outbound_total_size"] = 0
@stats["packet_inbound_total_size"] = 0
@stats["connection_inbound_total"] = 0
@stats["connection_outbound_total"] = 0
iv = @inbound_crypto.iv
iv.gsub("\n", "\0")
welcome = Packet.new
welcome.packet_type = PacketType::WELCOME
welcome.body = {
version: Config::VERSION,
ruby_version: RUBY_VERSION,
ruby_copyright: RUBY_COPYRIGHT,
ruby_platform: RUBY_PLATFORM,
ruby_description: RUBY_DESCRIPTION,
iv: Base64::encode64(iv).delete("\n")
}
@ghost_server_list = Hash.new
send_packet_noencrypt(welcome)
test_encryption = Packet.new
test_encryption.packet_type = PacketType::TEST_ENCRYPTION
test_encryption.body = {
test1: "encryption_test123"
}
send_packet(test_encryption)
EM.add_periodic_timer(15) do
puts @stats.to_json
end
end
# Send outbound raw packet. DO NOT SEND INSECURE PAYLOADS WITH THIS
def send_packet_noencrypt(packet)
send_data packet.to_json + "\n"
end
# Send an outbound encrypted packet
def send_packet(packet)
final = @outbound_crypto.encrypt(packet.to_json)
final.gsub!("\n", "\0")
send_data final + "\n"
@stats["packet_outbound_total"] += 1
@stats["packet_outbound_total_size"] += final.size
end
# Forward a local port to remote
def forward_port(packet)
client_port = packet.body["client_port"]
remote_port = packet.body["remote_port"]
@logger.info "Forwarding port #{client_port} -> #{remote_port}"
@logger.debug "Starting GhostServer on port #{remote_port}"
EventMachine.start_server "0.0.0.0", remote_port, GhostServer, self, remote_port, client_port
end
# Handle inbound packets
def handle_packet(packet)
if packet.packet_type == PacketType::CONFIGURE_ADD_PORT
forward_port(packet)
elsif packet.packet_type == PacketType::TCP_FORWARD
decoded = Base64.decode64(packet.body["data"])
if @ghost_server_list.has_key?(packet.body["connection_id"])
@ghost_server_list[packet.body["connection_id"]].send_data(decoded)
else
@logger.warn "Ignoring TCP_FORWARD for #{packet.body["connection_id"]}, no such connection with that ID"
end
elsif packet.packet_type == PacketType::TCP_CLOSE
@ghost_server_list[packet.body["connection_id"]].close_connection(true)
@ghost_server_list.delete(packet.body["connection_id"])
@logger.warn "Closed connection with ID #{packet.body["connection_id"]}"
end
end
def tcp_close(uuid)
if @ghost_server_list.has_key?(uuid)
tcp_close = Packet.new
tcp_close.packet_type = PacketType::TCP_CLOSE
tcp_close.body = {
connection_id: uuid
}
@ghost_server_list[uuid].close_connection(true)
@ghost_server_list.delete(uuid)
send_packet(tcp_close)
else
@logger.error "TCP_CLOSE failed for unknown connection #{uuid}"
end
end
# Handle a new TCP connection to a GhostServer
def tcp_open(ghost_server, local_port)
uuid = SecureRandom.uuid
@stats["connection_outbound_total"] += 1
@ghost_server_list[uuid] = ghost_server
tcp_open = Packet.new
tcp_open.packet_type = PacketType::TCP_OPEN
tcp_open.body = {
connection_id: uuid,
port: local_port
}
send_packet(tcp_open)
@logger.debug "TCP_OPEN with ID #{uuid}"
uuid
end
def tcp_forward(uuid, data)
@stats["connection_inbound_total"] += 1
new_data = data
new_data = Base64.encode64(new_data)
tcp_forward = Packet.new
tcp_forward.packet_type = PacketType::TCP_FORWARD
tcp_forward.body = {
connection_id: uuid,
data: new_data
}
send_packet(tcp_forward)
end
def receive_line(data)
@stats["packet_inbound_total"] += 1
@stats["packet_inbound_total_size"] += data.size
begin
# Decrypt and parse incoming packets
decrypted = @inbound_crypto.decrypt(data)
decrypted.gsub!("\0", "\n")
packet = Packet.new
packet.from_json(decrypted)
# Handle packet
handle_packet(packet)
rescue => error
@logger.error "Packet processing failed, packet dropped. #{error.message}"
end
end
def unbind
end
end
class Server
attr_accessor :server
def initialize(address, port)
@logger = Logger.new(STDOUT)
@address = address
@port = port
end
def listen
@logger.info "Starting blade3 server..."
@logger.info "Binding to address: #{@address}:#{@port}"
EventMachine.run {
EventMachine.start_server @address, @port, Blade3Server
}
end
end