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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +19 -0
  3. data/README.md +29 -0
  4. data/bin/nova +8 -0
  5. data/lib/generator/template/new_install/galaxy/some_star.rb +3 -0
  6. data/lib/generator/template/new_install/supernova.yml +16 -0
  7. data/lib/nova.rb +49 -0
  8. data/lib/nova/cli.rb +62 -0
  9. data/lib/nova/commands/server.rb +71 -0
  10. data/lib/nova/common.rb +17 -0
  11. data/lib/nova/common/event_handler.rb +165 -0
  12. data/lib/nova/common/event_handler/event.rb +147 -0
  13. data/lib/nova/common/features.rb +93 -0
  14. data/lib/nova/common/features/feature.rb +65 -0
  15. data/lib/nova/common/metadata.rb +65 -0
  16. data/lib/nova/common/metadata/data.rb +171 -0
  17. data/lib/nova/common/star_management.rb +164 -0
  18. data/lib/nova/constructor.rb +84 -0
  19. data/lib/nova/exceptions.rb +16 -0
  20. data/lib/nova/project.rb +199 -0
  21. data/lib/nova/remote.rb +10 -0
  22. data/lib/nova/remote/fake.rb +51 -0
  23. data/lib/nova/remote/fake/commands.rb +44 -0
  24. data/lib/nova/remote/fake/file_system.rb +76 -0
  25. data/lib/nova/remote/fake/operating_system.rb +52 -0
  26. data/lib/nova/remote/fake/platform.rb +89 -0
  27. data/lib/nova/star.rb +25 -0
  28. data/lib/nova/starbound.rb +14 -0
  29. data/lib/nova/starbound/client.rb +59 -0
  30. data/lib/nova/starbound/encryptor.rb +134 -0
  31. data/lib/nova/starbound/encryptors.rb +13 -0
  32. data/lib/nova/starbound/encryptors/openssl.rb +122 -0
  33. data/lib/nova/starbound/encryptors/plaintext.rb +64 -0
  34. data/lib/nova/starbound/encryptors/rbnacl.rb +67 -0
  35. data/lib/nova/starbound/protocol.rb +81 -0
  36. data/lib/nova/starbound/protocol/encryption.rb +48 -0
  37. data/lib/nova/starbound/protocol/exceptions.rb +38 -0
  38. data/lib/nova/starbound/protocol/messages.rb +116 -0
  39. data/lib/nova/starbound/protocol/packet.rb +267 -0
  40. data/lib/nova/starbound/protocol/socket.rb +231 -0
  41. data/lib/nova/starbound/server.rb +182 -0
  42. data/lib/nova/version.rb +5 -0
  43. data/spec/constructor_spec.rb +20 -0
  44. data/spec/local_spec.rb +26 -0
  45. data/spec/nova_spec.rb +7 -0
  46. data/spec/spec_helper.rb +19 -0
  47. data/spec/star/some_type.rb +27 -0
  48. data/spec/star_spec.rb +107 -0
  49. data/spec/starbound/encryptor_spec.rb +33 -0
  50. data/spec/starbound/openssl_encryptor_spec.rb +80 -0
  51. data/spec/starbound/packet_spec.rb +61 -0
  52. data/spec/starbound/plaintext_encryptor_spec.rb +27 -0
  53. data/spec/starbound/protocol_spec.rb +163 -0
  54. data/spec/starbound/rbnacl_encryptor_spec.rb +70 -0
  55. 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