vici 5.3.5
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.
- data/lib/vici.rb +595 -0
- metadata +50 -0
data/lib/vici.rb
ADDED
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
##
|
|
2
|
+
# The Vici module implements a native ruby client side library for the
|
|
3
|
+
# strongSwan VICI protocol. The Connection class provides a high-level
|
|
4
|
+
# interface to issue requests or listen for events.
|
|
5
|
+
#
|
|
6
|
+
# Copyright (C) 2014 Martin Willi
|
|
7
|
+
# Copyright (C) 2014 revosec AG
|
|
8
|
+
#
|
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
# in the Software without restriction, including without limitation the rights
|
|
12
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
# furnished to do so, subject to the following conditions:
|
|
15
|
+
#
|
|
16
|
+
# The above copyright notice and this permission notice shall be included in
|
|
17
|
+
# all copies or substantial portions of the Software.
|
|
18
|
+
#
|
|
19
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
25
|
+
# THE SOFTWARE.
|
|
26
|
+
|
|
27
|
+
module Vici
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# Vici specific exception all others inherit from
|
|
31
|
+
class Error < StandardError
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
##
|
|
35
|
+
# Error while parsing a vici message from the daemon
|
|
36
|
+
class ParseError < Error
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# Error while encoding a vici message from ruby data structures
|
|
41
|
+
class EncodeError < Error
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
##
|
|
45
|
+
# Error while exchanging messages over the vici Transport layer
|
|
46
|
+
class TransportError < Error
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
##
|
|
50
|
+
# Generic vici command execution error
|
|
51
|
+
class CommandError < Error
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
##
|
|
55
|
+
# Error if an issued vici command is unknown by the daemon
|
|
56
|
+
class CommandUnknownError < CommandError
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
##
|
|
60
|
+
# Error if a command failed to execute in the daemon
|
|
61
|
+
class CommandExecError < CommandError
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
##
|
|
65
|
+
# Generic vici event handling error
|
|
66
|
+
class EventError < Error
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
##
|
|
70
|
+
# Tried to register to / unregister from an unknown vici event
|
|
71
|
+
class EventUnknownError < EventError
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
##
|
|
75
|
+
# Exception to raise from an event listening closure to stop listening
|
|
76
|
+
class StopEventListening < Exception
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
##
|
|
81
|
+
# The Message class provides the low level encoding and decoding of vici
|
|
82
|
+
# protocol messages. Directly using this class is usually not required.
|
|
83
|
+
class Message
|
|
84
|
+
|
|
85
|
+
SECTION_START = 1
|
|
86
|
+
SECTION_END = 2
|
|
87
|
+
KEY_VALUE = 3
|
|
88
|
+
LIST_START = 4
|
|
89
|
+
LIST_ITEM = 5
|
|
90
|
+
LIST_END = 6
|
|
91
|
+
|
|
92
|
+
def initialize(data = "")
|
|
93
|
+
if data == nil
|
|
94
|
+
@root = Hash.new()
|
|
95
|
+
elsif data.is_a?(Hash)
|
|
96
|
+
@root = data
|
|
97
|
+
else
|
|
98
|
+
@encoded = data
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
##
|
|
103
|
+
# Get the raw byte encoding of an on-the-wire message
|
|
104
|
+
def encoding
|
|
105
|
+
if @encoded == nil
|
|
106
|
+
@encoded = encode(@root)
|
|
107
|
+
end
|
|
108
|
+
@encoded
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
##
|
|
112
|
+
# Get the root element of the parsed ruby data structures
|
|
113
|
+
def root
|
|
114
|
+
if @root == nil
|
|
115
|
+
@root = parse(@encoded)
|
|
116
|
+
end
|
|
117
|
+
@root
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
def encode_name(name)
|
|
123
|
+
[name.length].pack("c") << name
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def encode_value(value)
|
|
127
|
+
if value.class != String
|
|
128
|
+
value = value.to_s
|
|
129
|
+
end
|
|
130
|
+
[value.length].pack("n") << value
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def encode_kv(encoding, key, value)
|
|
134
|
+
encoding << KEY_VALUE << encode_name(key) << encode_value(value)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def encode_section(encoding, key, value)
|
|
138
|
+
encoding << SECTION_START << encode_name(key)
|
|
139
|
+
encoding << encode(value) << SECTION_END
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def encode_list(encoding, key, value)
|
|
143
|
+
encoding << LIST_START << encode_name(key)
|
|
144
|
+
value.each do |item|
|
|
145
|
+
encoding << LIST_ITEM << encode_value(item)
|
|
146
|
+
end
|
|
147
|
+
encoding << LIST_END
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def encode(node)
|
|
151
|
+
encoding = ""
|
|
152
|
+
node.each do |key, value|
|
|
153
|
+
case value.class
|
|
154
|
+
when String, Fixnum, true, false
|
|
155
|
+
encoding = encode_kv(encoding, key, value)
|
|
156
|
+
else
|
|
157
|
+
if value.is_a?(Hash)
|
|
158
|
+
encoding = encode_section(encoding, key, value)
|
|
159
|
+
elsif value.is_a?(Array)
|
|
160
|
+
encoding = encode_list(encoding, key, value)
|
|
161
|
+
else
|
|
162
|
+
encoding = encode_kv(encoding, key, value)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
encoding
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def parse_name(encoding)
|
|
170
|
+
len = encoding.unpack("c")[0]
|
|
171
|
+
name = encoding[1, len]
|
|
172
|
+
return encoding[(1 + len)..-1], name
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def parse_value(encoding)
|
|
176
|
+
len = encoding.unpack("n")[0]
|
|
177
|
+
value = encoding[2, len]
|
|
178
|
+
return encoding[(2 + len)..-1], value
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def parse(encoding)
|
|
182
|
+
stack = [Hash.new]
|
|
183
|
+
list = nil
|
|
184
|
+
while encoding.length != 0 do
|
|
185
|
+
type = encoding.unpack("c")[0]
|
|
186
|
+
encoding = encoding[1..-1]
|
|
187
|
+
case type
|
|
188
|
+
when SECTION_START
|
|
189
|
+
encoding, name = parse_name(encoding)
|
|
190
|
+
stack.push(stack[-1][name] = Hash.new)
|
|
191
|
+
when SECTION_END
|
|
192
|
+
if stack.length() == 1
|
|
193
|
+
raise ParseError, "unexpected section end"
|
|
194
|
+
end
|
|
195
|
+
stack.pop()
|
|
196
|
+
when KEY_VALUE
|
|
197
|
+
encoding, name = parse_name(encoding)
|
|
198
|
+
encoding, value = parse_value(encoding)
|
|
199
|
+
stack[-1][name] = value
|
|
200
|
+
when LIST_START
|
|
201
|
+
encoding, name = parse_name(encoding)
|
|
202
|
+
stack[-1][name] = []
|
|
203
|
+
list = name
|
|
204
|
+
when LIST_ITEM
|
|
205
|
+
raise ParseError, "unexpected list item" if list == nil
|
|
206
|
+
encoding, value = parse_value(encoding)
|
|
207
|
+
stack[-1][list].push(value)
|
|
208
|
+
when LIST_END
|
|
209
|
+
raise ParseError, "unexpected list end" if list == nil
|
|
210
|
+
list = nil
|
|
211
|
+
else
|
|
212
|
+
raise ParseError, "invalid type: #{type}"
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
if stack.length() > 1
|
|
216
|
+
raise ParseError, "unexpected message end"
|
|
217
|
+
end
|
|
218
|
+
stack[0]
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
##
|
|
224
|
+
# The Transport class implements to low level segmentation of packets
|
|
225
|
+
# to the underlying transport stream. Directly using this class is usually
|
|
226
|
+
# not required.
|
|
227
|
+
class Transport
|
|
228
|
+
|
|
229
|
+
CMD_REQUEST = 0
|
|
230
|
+
CMD_RESPONSE = 1
|
|
231
|
+
CMD_UNKNOWN = 2
|
|
232
|
+
EVENT_REGISTER = 3
|
|
233
|
+
EVENT_UNREGISTER = 4
|
|
234
|
+
EVENT_CONFIRM = 5
|
|
235
|
+
EVENT_UNKNOWN = 6
|
|
236
|
+
EVENT = 7
|
|
237
|
+
|
|
238
|
+
##
|
|
239
|
+
# Create a transport layer using a provided socket for communication.
|
|
240
|
+
def initialize(socket)
|
|
241
|
+
@socket = socket
|
|
242
|
+
@events = Hash.new
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
##
|
|
246
|
+
# Receive data from socket, until len bytes read
|
|
247
|
+
def recv_all(len)
|
|
248
|
+
encoding = ""
|
|
249
|
+
while encoding.length < len do
|
|
250
|
+
data = @socket.recv(len - encoding.length)
|
|
251
|
+
if data.empty?
|
|
252
|
+
raise TransportError, "connection closed"
|
|
253
|
+
end
|
|
254
|
+
encoding << data
|
|
255
|
+
end
|
|
256
|
+
encoding
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
##
|
|
260
|
+
# Send data to socket, until all bytes sent
|
|
261
|
+
def send_all(encoding)
|
|
262
|
+
len = 0
|
|
263
|
+
while len < encoding.length do
|
|
264
|
+
len += @socket.send(encoding[len..-1], 0)
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
##
|
|
269
|
+
# Write a packet prefixed by its length over the transport socket. Type
|
|
270
|
+
# specifies the message, the optional label and message get appended.
|
|
271
|
+
def write(type, label, message)
|
|
272
|
+
encoding = ""
|
|
273
|
+
if label
|
|
274
|
+
encoding << label.length << label
|
|
275
|
+
end
|
|
276
|
+
if message
|
|
277
|
+
encoding << message.encoding
|
|
278
|
+
end
|
|
279
|
+
send_all([encoding.length + 1, type].pack("Nc") + encoding)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
##
|
|
283
|
+
# Read a packet from the transport socket. Returns the packet type, and
|
|
284
|
+
# if available in the packet a label and the contained message.
|
|
285
|
+
def read
|
|
286
|
+
len = recv_all(4).unpack("N")[0]
|
|
287
|
+
encoding = recv_all(len)
|
|
288
|
+
type = encoding.unpack("c")[0]
|
|
289
|
+
len = 1
|
|
290
|
+
case type
|
|
291
|
+
when CMD_REQUEST, EVENT_REGISTER, EVENT_UNREGISTER, EVENT
|
|
292
|
+
label = encoding[2, encoding[1].unpack("c")[0]]
|
|
293
|
+
len += label.length + 1
|
|
294
|
+
when CMD_RESPONSE, CMD_UNKNOWN, EVENT_CONFIRM, EVENT_UNKNOWN
|
|
295
|
+
label = nil
|
|
296
|
+
else
|
|
297
|
+
raise TransportError, "invalid message: #{type}"
|
|
298
|
+
end
|
|
299
|
+
if encoding.length == len
|
|
300
|
+
return type, label, Message.new
|
|
301
|
+
end
|
|
302
|
+
return type, label, Message.new(encoding[len..-1])
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def dispatch_event(name, message)
|
|
306
|
+
@events[name].each do |handler|
|
|
307
|
+
handler.call(name, message)
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def read_and_dispatch_event
|
|
312
|
+
type, label, message = read
|
|
313
|
+
p
|
|
314
|
+
if type == EVENT
|
|
315
|
+
dispatch_event(label, message)
|
|
316
|
+
else
|
|
317
|
+
raise TransportError, "unexpected message: #{type}"
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def read_and_dispatch_events
|
|
322
|
+
loop do
|
|
323
|
+
type, label, message = read
|
|
324
|
+
if type == EVENT
|
|
325
|
+
dispatch_event(label, message)
|
|
326
|
+
else
|
|
327
|
+
return type, label, message
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
##
|
|
333
|
+
# Send a command with a given name, and optionally a message. Returns
|
|
334
|
+
# the reply message on success.
|
|
335
|
+
def request(name, message = nil)
|
|
336
|
+
write(CMD_REQUEST, name, message)
|
|
337
|
+
type, label, message = read_and_dispatch_events
|
|
338
|
+
case type
|
|
339
|
+
when CMD_RESPONSE
|
|
340
|
+
return message
|
|
341
|
+
when CMD_UNKNOWN
|
|
342
|
+
raise CommandUnknownError, name
|
|
343
|
+
else
|
|
344
|
+
raise CommandError, "invalid response for #{name}"
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
##
|
|
349
|
+
# Register a handler method for the given event name
|
|
350
|
+
def register(name, handler)
|
|
351
|
+
write(EVENT_REGISTER, name, nil)
|
|
352
|
+
type, label, message = read_and_dispatch_events
|
|
353
|
+
case type
|
|
354
|
+
when EVENT_CONFIRM
|
|
355
|
+
if @events.has_key?(name)
|
|
356
|
+
@events[name] += [handler]
|
|
357
|
+
else
|
|
358
|
+
@events[name] = [handler];
|
|
359
|
+
end
|
|
360
|
+
when EVENT_UNKNOWN
|
|
361
|
+
raise EventUnknownError, name
|
|
362
|
+
else
|
|
363
|
+
raise EventError, "invalid response for #{name} register"
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
##
|
|
368
|
+
# Unregister a handler method for the given event name
|
|
369
|
+
def unregister(name, handler)
|
|
370
|
+
write(EVENT_UNREGISTER, name, nil)
|
|
371
|
+
type, label, message = read_and_dispatch_events
|
|
372
|
+
case type
|
|
373
|
+
when EVENT_CONFIRM
|
|
374
|
+
@events[name] -= [handler]
|
|
375
|
+
when EVENT_UNKNOWN
|
|
376
|
+
raise EventUnknownError, name
|
|
377
|
+
else
|
|
378
|
+
raise EventError, "invalid response for #{name} unregister"
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
##
|
|
385
|
+
# The Connection class provides the high-level interface to monitor, configure
|
|
386
|
+
# and control the IKE daemon. It takes a connected stream-oriented Socket for
|
|
387
|
+
# the communication with the IKE daemon.
|
|
388
|
+
#
|
|
389
|
+
# This class takes and returns ruby objects for the exchanged message data.
|
|
390
|
+
# * Sections get encoded as Hash, containing other sections as Hash, or
|
|
391
|
+
# * Key/Values, where the values are Strings as Hash values
|
|
392
|
+
# * Lists get encoded as Arrays with String values
|
|
393
|
+
# Non-String values that are not a Hash nor an Array get converted with .to_s
|
|
394
|
+
# during encoding.
|
|
395
|
+
class Connection
|
|
396
|
+
|
|
397
|
+
def initialize(socket = nil)
|
|
398
|
+
if socket == nil
|
|
399
|
+
socket = UNIXSocket.new("/var/run/charon.vici")
|
|
400
|
+
end
|
|
401
|
+
@transp = Transport.new(socket)
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
##
|
|
405
|
+
# List matching loaded connections. The provided closure is invoked
|
|
406
|
+
# for each matching connection.
|
|
407
|
+
def list_conns(match = nil, &block)
|
|
408
|
+
call_with_event("list-conns", Message.new(match), "list-conn", &block)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
##
|
|
412
|
+
# List matching active SAs. The provided closure is invoked for each
|
|
413
|
+
# matching SA.
|
|
414
|
+
def list_sas(match = nil, &block)
|
|
415
|
+
call_with_event("list-sas", Message.new(match), "list-sa", &block)
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
##
|
|
419
|
+
# List matching installed policies. The provided closure is invoked
|
|
420
|
+
# for each matching policy.
|
|
421
|
+
def list_policies(match, &block)
|
|
422
|
+
call_with_event("list-policies", Message.new(match), "list-policy",
|
|
423
|
+
&block)
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
##
|
|
427
|
+
# List matching loaded certificates. The provided closure is invoked
|
|
428
|
+
# for each matching certificate definition.
|
|
429
|
+
def list_certs(match = nil, &block)
|
|
430
|
+
call_with_event("list-certs", Message.new(match), "list-cert", &block)
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
##
|
|
434
|
+
# Load a connection into the daemon.
|
|
435
|
+
def load_conn(conn)
|
|
436
|
+
check_success(@transp.request("load-conn", Message.new(conn)))
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
##
|
|
440
|
+
# Unload a connection from the daemon.
|
|
441
|
+
def unload_conn(conn)
|
|
442
|
+
check_success(@transp.request("unload-conn", Message.new(conn)))
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
##
|
|
446
|
+
# Get the names of connections managed by vici.
|
|
447
|
+
def get_conns()
|
|
448
|
+
@transp.request("get-conns").root
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
##
|
|
452
|
+
# Clear all loaded credentials.
|
|
453
|
+
def clear_creds()
|
|
454
|
+
check_success(@transp.request("clear-creds"))
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
##
|
|
458
|
+
# Load a certificate into the daemon.
|
|
459
|
+
def load_cert(cert)
|
|
460
|
+
check_success(@transp.request("load-cert", Message.new(cert)))
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
##
|
|
464
|
+
# Load a private key into the daemon.
|
|
465
|
+
def load_key(key)
|
|
466
|
+
check_success(@transp.request("load-key", Message.new(key)))
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
##
|
|
470
|
+
# Load a shared key into the daemon.
|
|
471
|
+
def load_shared(shared)
|
|
472
|
+
check_success(@transp.request("load-shared", Message.new(shared)))
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
##
|
|
476
|
+
# Load a virtual IP / attribute pool
|
|
477
|
+
def load_pool(pool)
|
|
478
|
+
check_success(@transp.request("load-pool", Message.new(pool)))
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
##
|
|
482
|
+
# Unload a virtual IP / attribute pool
|
|
483
|
+
def unload_pool(pool)
|
|
484
|
+
check_success(@transp.request("unload-pool", Message.new(pool)))
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
##
|
|
488
|
+
# Get the currently loaded pools.
|
|
489
|
+
def get_pools()
|
|
490
|
+
@transp.request("get-pools").root
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
##
|
|
494
|
+
# Initiate a connection. The provided closure is invoked for each log line.
|
|
495
|
+
def initiate(options, &block)
|
|
496
|
+
check_success(call_with_event("initiate", Message.new(options),
|
|
497
|
+
"control-log", &block))
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
##
|
|
501
|
+
# Terminate a connection. The provided closure is invoked for each log line.
|
|
502
|
+
def terminate(options, &block)
|
|
503
|
+
check_success(call_with_event("terminate", Message.new(options),
|
|
504
|
+
"control-log", &block))
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
##
|
|
508
|
+
# Install a shunt/route policy.
|
|
509
|
+
def install(policy)
|
|
510
|
+
check_success(@transp.request("install", Message.new(policy)))
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
##
|
|
514
|
+
# Uninstall a shunt/route policy.
|
|
515
|
+
def uninstall(policy)
|
|
516
|
+
check_success(@transp.request("uninstall", Message.new(policy)))
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
##
|
|
520
|
+
# Reload strongswan.conf settings.
|
|
521
|
+
def reload_settings
|
|
522
|
+
check_success(@transp.request("reload-settings", nil))
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
##
|
|
526
|
+
# Get daemon statistics and information.
|
|
527
|
+
def stats
|
|
528
|
+
@transp.request("stats", nil).root
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
##
|
|
532
|
+
# Get daemon version information
|
|
533
|
+
def version
|
|
534
|
+
@transp.request("version", nil).root
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
##
|
|
538
|
+
# Listen for a set of event messages. This call is blocking, and invokes
|
|
539
|
+
# the passed closure for each event received. The closure receives the
|
|
540
|
+
# event name and the event message as argument. To stop listening, the
|
|
541
|
+
# closure may raise a StopEventListening exception, the only catched
|
|
542
|
+
# exception.
|
|
543
|
+
def listen_events(events, &block)
|
|
544
|
+
self.class.instance_eval do
|
|
545
|
+
define_method(:listen_event) do |label, message|
|
|
546
|
+
block.call(label, message.root)
|
|
547
|
+
end
|
|
548
|
+
end
|
|
549
|
+
events.each do |event|
|
|
550
|
+
@transp.register(event, method(:listen_event))
|
|
551
|
+
end
|
|
552
|
+
begin
|
|
553
|
+
loop do
|
|
554
|
+
@transp.read_and_dispatch_event
|
|
555
|
+
end
|
|
556
|
+
rescue StopEventListening
|
|
557
|
+
ensure
|
|
558
|
+
events.each do |event|
|
|
559
|
+
@transp.unregister(event, method(:listen_event))
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
##
|
|
565
|
+
# Issue a command request, but register for a specific event while the
|
|
566
|
+
# command is active. VICI uses this mechanism to stream potentially large
|
|
567
|
+
# data objects continuously. The provided closure is invoked for all
|
|
568
|
+
# event messages.
|
|
569
|
+
def call_with_event(command, request, event, &block)
|
|
570
|
+
self.class.instance_eval do
|
|
571
|
+
define_method(:call_event) do |label, message|
|
|
572
|
+
block.call(message.root)
|
|
573
|
+
end
|
|
574
|
+
end
|
|
575
|
+
@transp.register(event, method(:call_event))
|
|
576
|
+
begin
|
|
577
|
+
reply = @transp.request(command, request)
|
|
578
|
+
ensure
|
|
579
|
+
@transp.unregister(event, method(:call_event))
|
|
580
|
+
end
|
|
581
|
+
reply
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
##
|
|
585
|
+
# Check if the reply of a command indicates "success", otherwise raise a
|
|
586
|
+
# CommandExecError exception
|
|
587
|
+
def check_success(reply)
|
|
588
|
+
root = reply.root
|
|
589
|
+
if root["success"] != "yes"
|
|
590
|
+
raise CommandExecError, root["errmsg"]
|
|
591
|
+
end
|
|
592
|
+
root
|
|
593
|
+
end
|
|
594
|
+
end
|
|
595
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: vici
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 5.3.5
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Martin Willi
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2016-01-22 00:00:00.000000000 Z
|
|
13
|
+
dependencies: []
|
|
14
|
+
description: ! "\n The strongSwan VICI protocol allows external application to
|
|
15
|
+
monitor,\n configure and control the IKE daemon charon. This ruby gem provides
|
|
16
|
+
a\n native client side implementation of the VICI protocol, well suited to\n
|
|
17
|
+
\ script automated tasks in a relaible way.\n "
|
|
18
|
+
email:
|
|
19
|
+
- martin@strongswan.org
|
|
20
|
+
executables: []
|
|
21
|
+
extensions: []
|
|
22
|
+
extra_rdoc_files: []
|
|
23
|
+
files:
|
|
24
|
+
- lib/vici.rb
|
|
25
|
+
homepage: https://wiki.strongswan.org/projects/strongswan/wiki/Vici
|
|
26
|
+
licenses:
|
|
27
|
+
- MIT
|
|
28
|
+
post_install_message:
|
|
29
|
+
rdoc_options: []
|
|
30
|
+
require_paths:
|
|
31
|
+
- lib
|
|
32
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
34
|
+
requirements:
|
|
35
|
+
- - ! '>='
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '0'
|
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
|
+
none: false
|
|
40
|
+
requirements:
|
|
41
|
+
- - ! '>='
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '0'
|
|
44
|
+
requirements: []
|
|
45
|
+
rubyforge_project:
|
|
46
|
+
rubygems_version: 1.8.23
|
|
47
|
+
signing_key:
|
|
48
|
+
specification_version: 3
|
|
49
|
+
summary: Native ruby interface for strongSwan VICI
|
|
50
|
+
test_files: []
|