nova 0.0.2
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 +19 -0
- data/README.md +29 -0
- data/bin/nova +8 -0
- data/lib/generator/template/new_install/galaxy/some_star.rb +3 -0
- data/lib/generator/template/new_install/supernova.yml +16 -0
- data/lib/nova.rb +49 -0
- data/lib/nova/cli.rb +62 -0
- data/lib/nova/commands/server.rb +71 -0
- data/lib/nova/common.rb +17 -0
- data/lib/nova/common/event_handler.rb +165 -0
- data/lib/nova/common/event_handler/event.rb +147 -0
- data/lib/nova/common/features.rb +93 -0
- data/lib/nova/common/features/feature.rb +65 -0
- data/lib/nova/common/metadata.rb +65 -0
- data/lib/nova/common/metadata/data.rb +171 -0
- data/lib/nova/common/star_management.rb +164 -0
- data/lib/nova/constructor.rb +84 -0
- data/lib/nova/exceptions.rb +16 -0
- data/lib/nova/project.rb +199 -0
- data/lib/nova/remote.rb +10 -0
- data/lib/nova/remote/fake.rb +51 -0
- data/lib/nova/remote/fake/commands.rb +44 -0
- data/lib/nova/remote/fake/file_system.rb +76 -0
- data/lib/nova/remote/fake/operating_system.rb +52 -0
- data/lib/nova/remote/fake/platform.rb +89 -0
- data/lib/nova/star.rb +25 -0
- data/lib/nova/starbound.rb +14 -0
- data/lib/nova/starbound/client.rb +59 -0
- data/lib/nova/starbound/encryptor.rb +134 -0
- data/lib/nova/starbound/encryptors.rb +13 -0
- data/lib/nova/starbound/encryptors/openssl.rb +122 -0
- data/lib/nova/starbound/encryptors/plaintext.rb +64 -0
- data/lib/nova/starbound/encryptors/rbnacl.rb +67 -0
- data/lib/nova/starbound/protocol.rb +81 -0
- data/lib/nova/starbound/protocol/encryption.rb +48 -0
- data/lib/nova/starbound/protocol/exceptions.rb +38 -0
- data/lib/nova/starbound/protocol/messages.rb +116 -0
- data/lib/nova/starbound/protocol/packet.rb +267 -0
- data/lib/nova/starbound/protocol/socket.rb +231 -0
- data/lib/nova/starbound/server.rb +182 -0
- data/lib/nova/version.rb +5 -0
- data/spec/constructor_spec.rb +20 -0
- data/spec/local_spec.rb +26 -0
- data/spec/nova_spec.rb +7 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/star/some_type.rb +27 -0
- data/spec/star_spec.rb +107 -0
- data/spec/starbound/encryptor_spec.rb +33 -0
- data/spec/starbound/openssl_encryptor_spec.rb +80 -0
- data/spec/starbound/packet_spec.rb +61 -0
- data/spec/starbound/plaintext_encryptor_spec.rb +27 -0
- data/spec/starbound/protocol_spec.rb +163 -0
- data/spec/starbound/rbnacl_encryptor_spec.rb +70 -0
- metadata +166 -0
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'packed_struct'
|
2
|
+
|
3
|
+
module Nova
|
4
|
+
module Starbound
|
5
|
+
class Protocol
|
6
|
+
|
7
|
+
# Handles serialization and deserialization of packets.
|
8
|
+
class Packet
|
9
|
+
|
10
|
+
extend PackedStruct
|
11
|
+
|
12
|
+
struct_layout :packet do
|
13
|
+
little uint32 size
|
14
|
+
little uint32 packet_id
|
15
|
+
little int packet_type
|
16
|
+
little string nonce[24]
|
17
|
+
|
18
|
+
string body[size]
|
19
|
+
null
|
20
|
+
end
|
21
|
+
|
22
|
+
struct_layout :response do
|
23
|
+
little uint32 size
|
24
|
+
little uint32 packet_response_id
|
25
|
+
little int packet_response_type
|
26
|
+
little int packet_type
|
27
|
+
little string nonce[24]
|
28
|
+
|
29
|
+
string body[size]
|
30
|
+
null
|
31
|
+
end
|
32
|
+
|
33
|
+
struct_layout :enc_type do
|
34
|
+
little int index
|
35
|
+
end
|
36
|
+
|
37
|
+
# A list of the types of packets in existance, and their
|
38
|
+
# packet_type codes.
|
39
|
+
Type = {
|
40
|
+
:nul => 0x00,
|
41
|
+
|
42
|
+
# handshake
|
43
|
+
:protocol_version => 0x01,
|
44
|
+
:encryption_options => 0x02,
|
45
|
+
:public_key => 0x03,
|
46
|
+
|
47
|
+
# other stuff
|
48
|
+
:standard_error => 0x04,
|
49
|
+
:close => 0x05,
|
50
|
+
|
51
|
+
# content
|
52
|
+
:echo => 0x06
|
53
|
+
}.freeze
|
54
|
+
|
55
|
+
# Used internally to check the types of packets when
|
56
|
+
# unpacking.
|
57
|
+
Structs = {
|
58
|
+
:packet => 0x00,
|
59
|
+
:response => 0x01
|
60
|
+
}.freeze
|
61
|
+
|
62
|
+
# For checking why the protocol was closed.
|
63
|
+
CloseReasons = {
|
64
|
+
:none => 0x00,
|
65
|
+
:shutdown => 0x01
|
66
|
+
}.freeze
|
67
|
+
|
68
|
+
# Provides access to the {Type} constant.
|
69
|
+
#
|
70
|
+
# @return [Hash]
|
71
|
+
def self.types
|
72
|
+
Type
|
73
|
+
end
|
74
|
+
|
75
|
+
# Builds a packet from a given body.
|
76
|
+
#
|
77
|
+
# @param type [Symbol] the type of packet it is. See {Type}.
|
78
|
+
# @param body [String] the body of the packet.
|
79
|
+
# @param others [Hash] the data to pass to the struct. See
|
80
|
+
# the packet struct definition to see what keys are allowed.
|
81
|
+
# @return [Packet] the packet data.
|
82
|
+
def self.build(type, body, others = {})
|
83
|
+
packet_data = {
|
84
|
+
:packet_type => Packet.types[type],
|
85
|
+
:body => body,
|
86
|
+
:size => body.bytesize
|
87
|
+
}.merge(others)
|
88
|
+
|
89
|
+
# Packet.struct[:packet].pack(packet_data)
|
90
|
+
Packet.new(:packet, packet_data)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Builds a response from a given body. Doesn't increment the
|
94
|
+
# packet id, as a response doesn't have a packet id.
|
95
|
+
#
|
96
|
+
# @param type [Symbol] the type of packet. See {Type}.
|
97
|
+
# @param body [String] the body of the packet.
|
98
|
+
# @param packet_data [Hash<Symbol, Numeric>] the packet data
|
99
|
+
# that this is a response to.
|
100
|
+
# @param others [Hash] the data to pass to the struct. See
|
101
|
+
# the response struct definition to see what keys are
|
102
|
+
# allowed.
|
103
|
+
# @option packet_data [Numeric] :packet_id the packet id this
|
104
|
+
# response is a response to.
|
105
|
+
# @option packet_data [Numeric] :packet_type the packet type
|
106
|
+
# this response is a response to.
|
107
|
+
# @return [Packet] the packet data.
|
108
|
+
def self.build_response(type, body, packet_data = {}, others = {})
|
109
|
+
response_data = {
|
110
|
+
:packet_response_id => packet_data[:packet_id] ||
|
111
|
+
packet_data[:packet_response_id],
|
112
|
+
:packet_response_type => packet_data[:packet_type],
|
113
|
+
:packet_type => Packet.types[type],
|
114
|
+
:body => body,
|
115
|
+
:size => body.bytesize
|
116
|
+
}.merge(others)
|
117
|
+
|
118
|
+
# Packet.struct[:response].pack(response_data)
|
119
|
+
Packet.new(:response, response_data)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Unpacks a struct from a socket.
|
123
|
+
#
|
124
|
+
# @param sock [#read, #seek] the socket to read from.
|
125
|
+
# @raise [NoStructError] if it can't determine the struct
|
126
|
+
# type.
|
127
|
+
# @return [Packet]
|
128
|
+
def self.from_socket(sock)
|
129
|
+
# we're gonna read one byte to see what type of packet it
|
130
|
+
# is, a response or a regular packet.
|
131
|
+
struct_type_num = sock.read(1)
|
132
|
+
|
133
|
+
struct_type = Structs.key(
|
134
|
+
struct_type_num.unpack("c").first)
|
135
|
+
|
136
|
+
unless struct_type
|
137
|
+
raise NoStructError,
|
138
|
+
"Undefined struct type #{struct_type_num.inspect}"
|
139
|
+
end
|
140
|
+
|
141
|
+
data = Packet.struct[struct_type].unpack_from_socket(sock)
|
142
|
+
Packet.new(struct_type, data)
|
143
|
+
end
|
144
|
+
|
145
|
+
# The type of struct this packet is. See {Structs}.
|
146
|
+
#
|
147
|
+
# @return [Symbol]
|
148
|
+
attr_reader :struct
|
149
|
+
|
150
|
+
# The data in this packet.
|
151
|
+
#
|
152
|
+
# @see []
|
153
|
+
# @return [Hash]
|
154
|
+
attr_reader :data
|
155
|
+
|
156
|
+
# Initialize the packet.
|
157
|
+
#
|
158
|
+
# @param struct [Symbol] the type of struct.
|
159
|
+
# @param data [Hash] the packet data.
|
160
|
+
def initialize(struct, data)
|
161
|
+
@struct = struct
|
162
|
+
@data = data
|
163
|
+
end
|
164
|
+
|
165
|
+
# Turn this packet into a string.
|
166
|
+
#
|
167
|
+
# @return [String]
|
168
|
+
def to_s
|
169
|
+
@_cache ||= [Structs[@struct]].pack("c") +
|
170
|
+
Packet.struct[@struct].pack(@data)
|
171
|
+
end
|
172
|
+
|
173
|
+
alias_method :to_str, :to_s
|
174
|
+
|
175
|
+
# Pretty inspect.
|
176
|
+
#
|
177
|
+
# @return [String]
|
178
|
+
def inspect
|
179
|
+
"#<#{self.class.name}:#{@struct}:#{@data}>"
|
180
|
+
end
|
181
|
+
|
182
|
+
# Forwards to the data key :packet_id, or if that doesn't
|
183
|
+
# exist, +:packet_response_id+.
|
184
|
+
#
|
185
|
+
# @return [Numeric]
|
186
|
+
def id
|
187
|
+
@data[:packet_id] || @data[:packet_response_id]
|
188
|
+
end
|
189
|
+
|
190
|
+
# The type of packet this is. Checks {Packet.types} before
|
191
|
+
# returning just the number.
|
192
|
+
#
|
193
|
+
# @return [Symbol, Numeric]
|
194
|
+
def type
|
195
|
+
Packet.types.key(@data[:packet_type]) || @data[:packet_type]
|
196
|
+
end
|
197
|
+
|
198
|
+
# Sets the body and the size for this packet.
|
199
|
+
#
|
200
|
+
# @param body [String] the new body.
|
201
|
+
# @return [void]
|
202
|
+
def body=(body)
|
203
|
+
data[:body] = body
|
204
|
+
data[:size] = body.bytesize
|
205
|
+
end
|
206
|
+
|
207
|
+
# Checks this packet for the expected type.
|
208
|
+
#
|
209
|
+
# @raise [UnacceptablePacketError] if the type doesn't match.
|
210
|
+
def expect(type)
|
211
|
+
if self.type != type
|
212
|
+
raise UnacceptablePacketError,
|
213
|
+
"Expected packet to be of type #{type}, " +
|
214
|
+
"got #{self.type} instead"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Copies the data over to the new packet. This is used when
|
219
|
+
# #clone or #dup is copied on the instance.
|
220
|
+
#
|
221
|
+
# @api private
|
222
|
+
# @return [void]
|
223
|
+
def initialize_copy(other)
|
224
|
+
other.data = self.data.clone
|
225
|
+
end
|
226
|
+
|
227
|
+
# Forwards requests on this packet of unkown methds to the
|
228
|
+
# data hash.
|
229
|
+
#
|
230
|
+
# @return [Object]
|
231
|
+
def method_missing(method, *args, &block)
|
232
|
+
if @data.respond_to?(method)
|
233
|
+
@data.public_send(method, *args, &block)
|
234
|
+
elsif @data.key?(method)
|
235
|
+
@data[method]
|
236
|
+
elsif @data.key?(key = :"packet_#{method}")
|
237
|
+
@data[key]
|
238
|
+
else
|
239
|
+
super
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
# Defined so ruby knows we're doing #method_missing magic.
|
244
|
+
#
|
245
|
+
# @param method [Symbol] the method to check for.
|
246
|
+
# @param include_all [Boolean] whether or not to include
|
247
|
+
# private and protected methods.
|
248
|
+
# @return [Boolean]
|
249
|
+
def respond_to_missing?(method, include_all = false)
|
250
|
+
@data.respond_to?(method, include_all) || @data.key?(method)
|
251
|
+
end
|
252
|
+
|
253
|
+
protected
|
254
|
+
|
255
|
+
# Sets the data to the given argument.
|
256
|
+
#
|
257
|
+
# @api private
|
258
|
+
# @param data [Hash] the data to set to.
|
259
|
+
# @return [void]
|
260
|
+
def data=(data)
|
261
|
+
@data = data
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Nova
|
4
|
+
module Starbound
|
5
|
+
class Protocol
|
6
|
+
|
7
|
+
# Handles sending data on the socket.
|
8
|
+
module Socket
|
9
|
+
|
10
|
+
# The socket to write data to.
|
11
|
+
#
|
12
|
+
# @return [#read, #write]
|
13
|
+
attr_accessor :socket
|
14
|
+
|
15
|
+
# The queue that holds messages.
|
16
|
+
#
|
17
|
+
# @return [Queue]
|
18
|
+
attr_reader :queue
|
19
|
+
|
20
|
+
# The current packet id.
|
21
|
+
#
|
22
|
+
# @return [Numeric]
|
23
|
+
attr_reader :current_packet_id
|
24
|
+
|
25
|
+
# Sends out a regular packet with the given type and body.
|
26
|
+
#
|
27
|
+
# @param packet_type [Symbol] the packet type of this packet.
|
28
|
+
# See {Packet::Type} for a list of packet types.
|
29
|
+
# @param body [String] the body of the packet.
|
30
|
+
# @param others [Hash] other data to pass to the packet.
|
31
|
+
# @return [Packet] the sent packet.
|
32
|
+
def send(packet_type, body, others = {})
|
33
|
+
others = others.merge(:packet_id => current_packet_id)
|
34
|
+
@current_packet_id += 1
|
35
|
+
write_packet Packet.build(packet_type, body, others)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sends out a response packet with the given type and body.
|
39
|
+
#
|
40
|
+
# @param packet [Packet] the packet to respond to.
|
41
|
+
# @param type [Symbol] the packet tpye of this packet. See
|
42
|
+
# {Packet::Type} for a list of packet types.
|
43
|
+
# @param body [String] the body of this packet.
|
44
|
+
# @param others [Hash] the other data to pass to the packet.
|
45
|
+
# @return [Packet] the sent packet.
|
46
|
+
def respond_to(packet, type, body, others = {})
|
47
|
+
write_packet Packet.build_response(type, body, packet,
|
48
|
+
others)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Writes the given packet to the socket, after handling the
|
52
|
+
# encryption of the packet.
|
53
|
+
#
|
54
|
+
# @return [Packet] the sent packet.
|
55
|
+
def write_packet(packet)
|
56
|
+
new_packet = encryption_provider.encrypt(packet)
|
57
|
+
|
58
|
+
socket.write new_packet
|
59
|
+
socket.flush
|
60
|
+
|
61
|
+
new_packet
|
62
|
+
end
|
63
|
+
|
64
|
+
# Waits for a packet and returns it. If it's threaded, or if
|
65
|
+
# there's data in the queue, it
|
66
|
+
# waits for a message in the queue and pops off the top one.
|
67
|
+
# If it's not, it just reads from the socket.
|
68
|
+
#
|
69
|
+
# @raise [RemoteClosedError] if it recieves a +:close+ packet.
|
70
|
+
# @param read_check [Boolean] whether or not to check the read
|
71
|
+
# list for the packet.
|
72
|
+
# @return [Packet]
|
73
|
+
def read(read_check = true)
|
74
|
+
pack = if @read.length > 0 && read_check
|
75
|
+
@read.pop
|
76
|
+
elsif threaded?
|
77
|
+
queue.pop
|
78
|
+
else
|
79
|
+
wait_for_socket
|
80
|
+
end
|
81
|
+
|
82
|
+
if pack && pack.type == :close
|
83
|
+
raise RemoteClosedError
|
84
|
+
end
|
85
|
+
|
86
|
+
pack
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns whether or not this instance of the protocol uses
|
90
|
+
# threads to handle reading.
|
91
|
+
#
|
92
|
+
# @return [Boolean]
|
93
|
+
def threaded?
|
94
|
+
@threaded
|
95
|
+
end
|
96
|
+
|
97
|
+
# Whether or not this is actually running.
|
98
|
+
#
|
99
|
+
# @return [Boolean]
|
100
|
+
def run?
|
101
|
+
@run
|
102
|
+
end
|
103
|
+
|
104
|
+
# Initializes the socket.
|
105
|
+
def initialize
|
106
|
+
@threaded = @options.fetch(:threaded, true)
|
107
|
+
@run = true
|
108
|
+
@queue = Queue.new
|
109
|
+
@read = []
|
110
|
+
@current_packet_id = 0
|
111
|
+
end
|
112
|
+
|
113
|
+
# The thread that is filling the queue with packet data. If
|
114
|
+
# it doesn't exist, it creates one, if this is threaded.
|
115
|
+
#
|
116
|
+
# @return [nil, Thread] nil if this is not threaded, Thread
|
117
|
+
# otherwise.
|
118
|
+
def thread
|
119
|
+
return unless threaded?
|
120
|
+
|
121
|
+
@_thread ||= Thread.start do
|
122
|
+
while run?
|
123
|
+
packet = wait_for_socket
|
124
|
+
|
125
|
+
queue << packet
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Stores callbacks for when packets arrive.
|
131
|
+
#
|
132
|
+
# @param data [Hash<(Symbol, Symbol)>] should consist of only
|
133
|
+
# one key-value pair, with the key being the struct that
|
134
|
+
# represents the packet, and the type being the type of
|
135
|
+
# packet.
|
136
|
+
# @yieldparam packet [Packet] the packet.
|
137
|
+
def on(data, &block)
|
138
|
+
struct = data.keys.first
|
139
|
+
type = data.values.first
|
140
|
+
|
141
|
+
callbacks[type].push :struct => struct,
|
142
|
+
:type => type, :block => block
|
143
|
+
end
|
144
|
+
|
145
|
+
# The callbacks that have been defined on this protocol.
|
146
|
+
#
|
147
|
+
# @return [Hash<Symbol, Object>] the symbol is the type of
|
148
|
+
# packet.
|
149
|
+
def callbacks
|
150
|
+
@_callbacks ||= Hash.new { |h, k| h[k] = [] }
|
151
|
+
end
|
152
|
+
|
153
|
+
# Runs a callback.
|
154
|
+
#
|
155
|
+
# @param struct [Symbol] the struct that the callback is for.
|
156
|
+
# @param type [Symbol] the type of callback this is for.
|
157
|
+
# @return [Array<Object>] all of the results from running the
|
158
|
+
# callbacks.
|
159
|
+
def run_callback(struct, type, *args)
|
160
|
+
callbacks[type].select { |c|
|
161
|
+
c[:struct] == struct }.map do |c|
|
162
|
+
c[:block].call(*args)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Keep looping until we stop running. Reads packets
|
167
|
+
# continuously until we do stop.
|
168
|
+
#
|
169
|
+
# @return [void]
|
170
|
+
def loop
|
171
|
+
while run?
|
172
|
+
read
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Finds the response to the given packet.
|
177
|
+
#
|
178
|
+
# @param packet [Packet]
|
179
|
+
# @return [nil, Packet] nil if it was told to stop running
|
180
|
+
# before it could find a response.
|
181
|
+
def response_to(packet)
|
182
|
+
response = nil
|
183
|
+
|
184
|
+
while response.nil? && run?
|
185
|
+
temp = read(false)
|
186
|
+
|
187
|
+
if temp.struct == :response && temp.response_id == packet.id
|
188
|
+
response = temp
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
response
|
193
|
+
end
|
194
|
+
|
195
|
+
# Closes down the socket.
|
196
|
+
#
|
197
|
+
# @return [void]
|
198
|
+
def close
|
199
|
+
@run = false
|
200
|
+
|
201
|
+
socket.close
|
202
|
+
end
|
203
|
+
|
204
|
+
private
|
205
|
+
|
206
|
+
# Blocks the thread until data is available, or this is
|
207
|
+
# signaled to stop running.
|
208
|
+
#
|
209
|
+
# @return [nil, Packet] nil when it was told to stop running
|
210
|
+
# before data was given, Packet otherwise.
|
211
|
+
def wait_for_socket
|
212
|
+
packet = nil
|
213
|
+
|
214
|
+
while packet.nil? && run?
|
215
|
+
|
216
|
+
out = IO.select [socket], nil, nil, 0.1
|
217
|
+
next unless out
|
218
|
+
|
219
|
+
packet = encryption_provider.decrypt(
|
220
|
+
Packet.from_socket(socket))
|
221
|
+
|
222
|
+
run_callback packet.struct, packet.type, packet
|
223
|
+
end
|
224
|
+
|
225
|
+
packet
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|