encoding: Accept all exchange types for non IKEv1/IKEv2 major versions
[strongswan.git] / src / swanctl / commands / load_conns.c
1 /*
2 * Copyright (C) 2014 Martin Willi
3 * Copyright (C) 2014 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #define _GNU_SOURCE
17 #include <stdio.h>
18 #include <errno.h>
19 #include <limits.h>
20
21 #include "command.h"
22 #include "swanctl.h"
23
24 /**
25 * Check if we should handle a key as a list of comma separated values
26 */
27 static bool is_list_key(char *key)
28 {
29 char *keys[] = {
30 "local_addrs",
31 "remote_addrs",
32 "proposals",
33 "esp_proposals",
34 "ah_proposals",
35 "local_ts",
36 "remote_ts",
37 "vips",
38 "pools",
39 "groups",
40 };
41 int i;
42
43 for (i = 0; i < countof(keys); i++)
44 {
45 if (strcaseeq(keys[i], key))
46 {
47 return TRUE;
48 }
49 }
50 return FALSE;
51 }
52
53 /**
54 * Check if we should handle a key as a list of comma separated files
55 */
56 static bool is_file_list_key(char *key)
57 {
58 char *keys[] = {
59 "certs",
60 "cacerts",
61 };
62 int i;
63
64 for (i = 0; i < countof(keys); i++)
65 {
66 if (strcaseeq(keys[i], key))
67 {
68 return TRUE;
69 }
70 }
71 return FALSE;
72 }
73
74 /**
75 * Add a vici list from a comma separated string value
76 */
77 static void add_list_key(vici_req_t *req, char *key, char *value)
78 {
79 enumerator_t *enumerator;
80 char *token;
81
82 vici_begin_list(req, key);
83 enumerator = enumerator_create_token(value, ",", " ");
84 while (enumerator->enumerate(enumerator, &token))
85 {
86 vici_add_list_itemf(req, "%s", token);
87 }
88 enumerator->destroy(enumerator);
89 vici_end_list(req);
90 }
91
92 /**
93 * Add a vici list of blobs from a comma separated file list
94 */
95 static void add_file_list_key(vici_req_t *req, char *key, char *value)
96 {
97 enumerator_t *enumerator;
98 chunk_t *map;
99 char *token, buf[PATH_MAX];
100
101 vici_begin_list(req, key);
102 enumerator = enumerator_create_token(value, ",", " ");
103 while (enumerator->enumerate(enumerator, &token))
104 {
105 if (!path_absolute(token))
106 {
107 if (streq(key, "certs"))
108 {
109 snprintf(buf, sizeof(buf), "%s%s%s",
110 SWANCTL_X509DIR, DIRECTORY_SEPARATOR, token);
111 token = buf;
112 }
113 if (streq(key, "cacerts"))
114 {
115 snprintf(buf, sizeof(buf), "%s%s%s",
116 SWANCTL_X509CADIR, DIRECTORY_SEPARATOR, token);
117 token = buf;
118 }
119 }
120
121 map = chunk_map(token, FALSE);
122 if (map)
123 {
124 vici_add_list_item(req, map->ptr, map->len);
125 chunk_unmap(map);
126 }
127 else
128 {
129 fprintf(stderr, "loading certificate '%s' failed: %s\n",
130 token, strerror(errno));
131 }
132 }
133 enumerator->destroy(enumerator);
134 vici_end_list(req);
135 }
136
137 /**
138 * Translate setting key/values from a section into vici key-values/lists
139 */
140 static void add_key_values(vici_req_t *req, settings_t *cfg, char *section)
141 {
142 enumerator_t *enumerator;
143 char *key, *value;
144
145 enumerator = cfg->create_key_value_enumerator(cfg, section);
146 while (enumerator->enumerate(enumerator, &key, &value))
147 {
148 if (is_list_key(key))
149 {
150 add_list_key(req, key, value);
151 }
152 else if (is_file_list_key(key))
153 {
154 add_file_list_key(req, key, value);
155 }
156 else
157 {
158 vici_add_key_valuef(req, key, "%s", value);
159 }
160 }
161 enumerator->destroy(enumerator);
162 }
163
164 /**
165 * Translate a settings section to a vici section
166 */
167 static void add_sections(vici_req_t *req, settings_t *cfg, char *section)
168 {
169 enumerator_t *enumerator;
170 char *name, buf[256];
171
172 enumerator = cfg->create_section_enumerator(cfg, section);
173 while (enumerator->enumerate(enumerator, &name))
174 {
175 vici_begin_section(req, name);
176 snprintf(buf, sizeof(buf), "%s.%s", section, name);
177 add_key_values(req, cfg, buf);
178 add_sections(req, cfg, buf);
179 vici_end_section(req);
180 }
181 enumerator->destroy(enumerator);
182 }
183
184 /**
185 * Load an IKE_SA config with CHILD_SA configs from a section
186 */
187 static bool load_conn(vici_conn_t *conn, settings_t *cfg,
188 char *section, command_format_options_t format)
189 {
190 vici_req_t *req;
191 vici_res_t *res;
192 bool ret = TRUE;
193 char buf[128];
194
195 snprintf(buf, sizeof(buf), "%s.%s", "connections", section);
196
197 req = vici_begin("load-conn");
198
199 vici_begin_section(req, section);
200 add_key_values(req, cfg, buf);
201 add_sections(req, cfg, buf);
202 vici_end_section(req);
203
204 res = vici_submit(req, conn);
205 if (!res)
206 {
207 fprintf(stderr, "load-conn request failed: %s\n", strerror(errno));
208 return FALSE;
209 }
210 if (format & COMMAND_FORMAT_RAW)
211 {
212 vici_dump(res, "load-conn reply", format & COMMAND_FORMAT_PRETTY,
213 stdout);
214 }
215 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
216 {
217 fprintf(stderr, "loading connection '%s' failed: %s\n",
218 section, vici_find_str(res, "", "errmsg"));
219 ret = FALSE;
220 }
221 else
222 {
223 printf("loaded connection '%s'\n", section);
224 }
225 vici_free_res(res);
226 return ret;
227 }
228
229 CALLBACK(list_conn, int,
230 linked_list_t *list, vici_res_t *res, char *name, void *value, int len)
231 {
232 if (streq(name, "conns"))
233 {
234 char *str;
235
236 if (asprintf(&str, "%.*s", len, value) != -1)
237 {
238 list->insert_last(list, str);
239 }
240 }
241 return 0;
242 }
243
244 /**
245 * Create a list of currently loaded connections
246 */
247 static linked_list_t* list_conns(vici_conn_t *conn,
248 command_format_options_t format)
249 {
250 linked_list_t *list;
251 vici_res_t *res;
252
253 list = linked_list_create();
254
255 res = vici_submit(vici_begin("get-conns"), conn);
256 if (res)
257 {
258 if (format & COMMAND_FORMAT_RAW)
259 {
260 vici_dump(res, "get-conns reply", format & COMMAND_FORMAT_PRETTY,
261 stdout);
262 }
263 vici_parse_cb(res, NULL, NULL, list_conn, list);
264 vici_free_res(res);
265 }
266 return list;
267 }
268
269 /**
270 * Remove and free a string from a list
271 */
272 static void remove_from_list(linked_list_t *list, char *str)
273 {
274 enumerator_t *enumerator;
275 char *current;
276
277 enumerator = list->create_enumerator(list);
278 while (enumerator->enumerate(enumerator, &current))
279 {
280 if (streq(current, str))
281 {
282 list->remove_at(list, enumerator);
283 free(current);
284 }
285 }
286 enumerator->destroy(enumerator);
287 }
288
289 /**
290 * Unload a connection by name
291 */
292 static bool unload_conn(vici_conn_t *conn, char *name,
293 command_format_options_t format)
294 {
295 vici_req_t *req;
296 vici_res_t *res;
297 bool ret = TRUE;
298
299 req = vici_begin("unload-conn");
300 vici_add_key_valuef(req, "name", "%s", name);
301 res = vici_submit(req, conn);
302 if (!res)
303 {
304 fprintf(stderr, "unload-conn request failed: %s\n", strerror(errno));
305 return FALSE;
306 }
307 if (format & COMMAND_FORMAT_RAW)
308 {
309 vici_dump(res, "unload-conn reply", format & COMMAND_FORMAT_PRETTY,
310 stdout);
311 }
312 else if (!streq(vici_find_str(res, "no", "success"), "yes"))
313 {
314 fprintf(stderr, "unloading connection '%s' failed: %s\n",
315 name, vici_find_str(res, "", "errmsg"));
316 ret = FALSE;
317 }
318 vici_free_res(res);
319 return ret;
320 }
321
322 static int load_conns(vici_conn_t *conn)
323 {
324 u_int found = 0, loaded = 0, unloaded = 0;
325 command_format_options_t format = COMMAND_FORMAT_NONE;
326 char *arg, *section;
327 enumerator_t *enumerator;
328 linked_list_t *conns;
329 settings_t *cfg;
330
331 while (TRUE)
332 {
333 switch (command_getopt(&arg))
334 {
335 case 'h':
336 return command_usage(NULL);
337 case 'P':
338 format |= COMMAND_FORMAT_PRETTY;
339 /* fall through to raw */
340 case 'r':
341 format |= COMMAND_FORMAT_RAW;
342 continue;
343 case EOF:
344 break;
345 default:
346 return command_usage("invalid --load-conns option");
347 }
348 break;
349 }
350
351 cfg = settings_create(SWANCTL_CONF);
352 if (!cfg)
353 {
354 fprintf(stderr, "parsing '%s' failed\n", SWANCTL_CONF);
355 return EINVAL;
356 }
357
358 conns = list_conns(conn, format);
359
360 enumerator = cfg->create_section_enumerator(cfg, "connections");
361 while (enumerator->enumerate(enumerator, &section))
362 {
363 remove_from_list(conns, section);
364 found++;
365 if (load_conn(conn, cfg, section, format))
366 {
367 loaded++;
368 }
369 }
370 enumerator->destroy(enumerator);
371
372 cfg->destroy(cfg);
373
374 /* unload all connection in daemon, but not in file */
375 while (conns->remove_first(conns, (void**)&section) == SUCCESS)
376 {
377 if (unload_conn(conn, section, format))
378 {
379 unloaded++;
380 }
381 free(section);
382 }
383 conns->destroy(conns);
384
385 if (format & COMMAND_FORMAT_RAW)
386 {
387 return 0;
388 }
389 if (found == 0)
390 {
391 printf("no connections found, %u unloaded\n", unloaded);
392 return 0;
393 }
394 if (loaded == found)
395 {
396 printf("successfully loaded %u connections, %u unloaded\n",
397 loaded, unloaded);
398 return 0;
399 }
400 fprintf(stderr, "loaded %u of %u connections, %u failed to load, "
401 "%u unloaded\n", loaded, found, found - loaded, unloaded);
402 return EINVAL;
403 }
404
405 /**
406 * Register the command.
407 */
408 static void __attribute__ ((constructor))reg()
409 {
410 command_register((command_t) {
411 load_conns, 'c', "load-conns", "(re-)load connection configuration",
412 {"[--raw|--pretty]"},
413 {
414 {"help", 'h', 0, "show usage information"},
415 {"raw", 'r', 0, "dump raw response message"},
416 {"pretty", 'P', 0, "dump raw response message in pretty print"},
417 }
418 });
419 }