vici 5.3.5
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|