From c295d0eb4b11a024d1607f6a80275f372c345c08 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 26 Sep 2007 14:02:21 +0000 Subject: [PATCH] refactored strongswan manager removed buggy request parsing code, use ClearSilvers CGI kit instead fixed CHILD_SA listing in manager (needs better design) using secure XML communication through unix sockets removed images with questionable (non-GPL) license --- .../control/interfaces/stroke_interface.c | 7 +- src/charon/control/interfaces/xml_interface.c | 62 ++-- .../control/interfaces/xml_interface.xml | 51 ++- src/charon/daemon.c | 3 + src/manager/Makefile.am | 30 +- src/manager/controller/auth_controller.c | 41 +-- src/manager/controller/gateway_controller.c | 43 ++- src/manager/controller/status_controller.c | 232 +++++++------ src/manager/gateway.c | 120 ++++++- src/manager/gateway.h | 22 +- src/manager/lib/controller.h | 6 +- src/manager/lib/dict.c | 154 --------- src/manager/lib/dict.h | 73 ---- src/manager/lib/dispatcher.c | 26 +- src/manager/lib/dispatcher.h | 3 +- src/manager/lib/request.c | 323 ++++++++++-------- src/manager/lib/request.h | 64 +++- src/manager/lib/response.c | 233 ------------- src/manager/lib/response.h | 102 ------ src/manager/lib/session.c | 19 +- src/manager/lib/session.h | 4 +- src/manager/lib/template.c | 138 -------- src/manager/lib/template.h | 80 ----- src/manager/main.c | 11 +- src/manager/manager.c | 8 +- src/manager/manager.db | Bin 0 -> 12288 bytes src/manager/sqlite.db | Bin 4096 -> 0 bytes src/manager/templates/error.cs | 3 + src/manager/templates/static/client-left.png | Bin 6649 -> 10228 bytes src/manager/templates/static/client-right.png | Bin 6703 -> 10349 bytes src/manager/templates/static/gateway-left.png | Bin 2441 -> 12206 bytes .../templates/static/gateway-right.png | Bin 2488 -> 12180 bytes src/manager/templates/static/nat.png | Bin 4751 -> 0 bytes src/manager/templates/static/pipe-bad.png | Bin 4445 -> 4905 bytes src/manager/templates/static/pipe-good.png | Bin 4536 -> 322 bytes .../templates/static/pipe-thin-left.png | Bin 0 -> 345 bytes .../templates/static/pipe-thin-right.png | Bin 0 -> 357 bytes src/manager/templates/static/pipe-thin.png | Bin 0 -> 256 bytes src/manager/templates/static/pipe.png | Bin 316 -> 322 bytes src/manager/templates/static/router.png | Bin 5059 -> 3300 bytes src/manager/templates/status/ikesalist.cs | 83 +++-- 41 files changed, 722 insertions(+), 1219 deletions(-) delete mode 100644 src/manager/lib/dict.c delete mode 100644 src/manager/lib/dict.h delete mode 100644 src/manager/lib/response.c delete mode 100644 src/manager/lib/response.h delete mode 100644 src/manager/lib/template.c delete mode 100644 src/manager/lib/template.h create mode 100644 src/manager/manager.db delete mode 100644 src/manager/sqlite.db create mode 100644 src/manager/templates/error.cs delete mode 100644 src/manager/templates/static/nat.png create mode 100644 src/manager/templates/static/pipe-thin-left.png create mode 100644 src/manager/templates/static/pipe-thin-right.png create mode 100644 src/manager/templates/static/pipe-thin.png diff --git a/src/charon/control/interfaces/stroke_interface.c b/src/charon/control/interfaces/stroke_interface.c index 74bfa1aec..3ab6b5700 100755 --- a/src/charon/control/interfaces/stroke_interface.c +++ b/src/charon/control/interfaces/stroke_interface.c @@ -1698,7 +1698,7 @@ interface_t *interface_create() return NULL; } - old = umask(~S_IRWXU); + old = umask(~(S_IRWXU | S_IRWXG)); if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0) { DBG1(DBG_CFG, "could not bind stroke socket: %s", strerror(errno)); @@ -1707,6 +1707,11 @@ interface_t *interface_create() return NULL; } umask(old); + if (chown(socket_addr.sun_path, IPSEC_UID, IPSEC_GID) != 0) + { + DBG1(DBG_CFG, "changing stroke socket permissions failed: %s", + strerror(errno)); + } if (listen(this->socket, 0) < 0) { diff --git a/src/charon/control/interfaces/xml_interface.c b/src/charon/control/interfaces/xml_interface.c index 3946611db..d376c01df 100644 --- a/src/charon/control/interfaces/xml_interface.c +++ b/src/charon/control/interfaces/xml_interface.c @@ -146,23 +146,28 @@ static void write_address(xmlTextWriterPtr writer, char *element, host_t *host) } /** - * write a list of traffic_selector_t + * write a childEnd */ -static void write_ts(xmlTextWriterPtr writer, linked_list_t *list) +static void write_childend(xmlTextWriterPtr writer, child_sa_t *child, bool local) { iterator_t *iterator; + linked_list_t *list; traffic_selector_t *ts; - + xmlTextWriterWriteFormatElement(writer, "spi", "%lx", + child->get_spi(child, local)); + xmlTextWriterStartElement(writer, "networks"); + list = child->get_traffic_selectors(child, local); iterator = list->create_iterator(list, TRUE); while (iterator->iterate(iterator, (void**)&ts)) { - xmlTextWriterStartElement(writer, "net"); + xmlTextWriterStartElement(writer, "network"); xmlTextWriterWriteAttribute(writer, "type", ts->get_type(ts) == TS_IPV4_ADDR_RANGE ? "ipv4" : "ipv6"); xmlTextWriterWriteFormatString(writer, "%R", ts); xmlTextWriterEndElement(writer); } iterator->destroy(iterator); + xmlTextWriterEndElement(writer); } /** @@ -170,12 +175,26 @@ static void write_ts(xmlTextWriterPtr writer, linked_list_t *list) */ static void write_child(xmlTextWriterPtr writer, child_sa_t *child) { + mode_t mode; + encryption_algorithm_t encr; + integrity_algorithm_t int_algo; + size_t encr_len, int_len; + u_int32_t rekey, use_in, use_out, use_fwd; + child_cfg_t *config; + + config = child->get_config(child); + child->get_stats(child, &mode, &encr, &encr_len, &int_algo, &int_len, + &rekey, &use_in, &use_out, &use_fwd); + xmlTextWriterStartElement(writer, "childsa"); + xmlTextWriterWriteFormatElement(writer, "reqid", "%d", child->get_reqid(child)); + xmlTextWriterWriteFormatElement(writer, "childconfig", "%s", + config->get_name(config)); xmlTextWriterStartElement(writer, "local"); - write_ts(writer, child->get_traffic_selectors(child, TRUE)); + write_childend(writer, child, TRUE); xmlTextWriterEndElement(writer); xmlTextWriterStartElement(writer, "remote"); - write_ts(writer, child->get_traffic_selectors(child, FALSE)); + write_childend(writer, child, FALSE); xmlTextWriterEndElement(writer); xmlTextWriterEndElement(writer); } @@ -421,7 +440,8 @@ static job_requeue_t dispatch(private_xml_interface_t *this) return JOB_REQUEUE_DIRECT; } -struct sockaddr_un unix_addr = { AF_UNIX, "/var/run/charon.xml"}; +/** XML unix socket */ +struct sockaddr_un unix_addr = { AF_UNIX, IPSEC_PIDDIR "/charon.xml"}; /** * Implementation of itnerface_t.destroy. @@ -430,7 +450,7 @@ static void destroy(private_xml_interface_t *this) { this->job->cancel(this->job); close(this->socket); - //unlink(unix_addr.sun_path); + unlink(unix_addr.sun_path); free(this); } @@ -440,42 +460,32 @@ static void destroy(private_xml_interface_t *this) interface_t *interface_create() { private_xml_interface_t *this = malloc_thing(private_xml_interface_t); - //mode_t old; - struct sockaddr_in tcp_addr; + mode_t old; this->public.interface.destroy = (void (*)(interface_t*))destroy; /* set up unix socket */ - this->socket = socket(AF_INET, SOCK_STREAM, 0);//socket(AF_UNIX, SOCK_STREAM, 0); + this->socket = socket(AF_UNIX, SOCK_STREAM, 0); if (this->socket == -1) { DBG1(DBG_CFG, "could not create XML socket"); free(this); return NULL; } - - memset(&tcp_addr, 0, sizeof(tcp_addr)); - tcp_addr.sin_family = AF_INET; - tcp_addr.sin_addr.s_addr = INADDR_ANY; - tcp_addr.sin_port = htons(4502); - if (bind(this->socket, (struct sockaddr*)&tcp_addr, sizeof(tcp_addr)) < 0) + + old = umask(~(S_IRWXU | S_IRWXG)); + if (bind(this->socket, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) < 0) { DBG1(DBG_CFG, "could not bind XML socket: %s", strerror(errno)); close(this->socket); free(this); return NULL; } - - /* - old = umask(~S_IRWXU); - if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0) + umask(old); + if (chown(unix_addr.sun_path, IPSEC_UID, IPSEC_GID) != 0) { - DBG1(DBG_CFG, "could not bind XML socket: %s", strerror(errno)); - close(this->socket); - free(this); - return NULL; + DBG1(DBG_CFG, "changing XML socket permissions failed: %s", strerror(errno)); } - umask(old);*/ if (listen(this->socket, 5) < 0) { diff --git a/src/charon/control/interfaces/xml_interface.xml b/src/charon/control/interfaces/xml_interface.xml index 82dff98b4..466d95f34 100644 --- a/src/charon/control/interfaces/xml_interface.xml +++ b/src/charon/control/interfaces/xml_interface.xml @@ -100,17 +100,30 @@ + + + + + + - + - + + + + + + + + - + @@ -131,6 +144,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/charon/daemon.c b/src/charon/daemon.c index 9ef80a626..56b737ad6 100644 --- a/src/charon/daemon.c +++ b/src/charon/daemon.c @@ -245,6 +245,8 @@ static void drop_capabilities(private_daemon_t *this, bool full) keep |= (1< - #include @@ -46,39 +44,34 @@ struct private_auth_controller_t { manager_t *manager; }; -static void login(private_auth_controller_t *this, - request_t *request, response_t *response) +static void login(private_auth_controller_t *this, request_t *request) { - template_t *t = template_create("templates/auth/login.cs"); - t->set(t, "action", "check"); - t->set(t, "title", "Login"); - t->render(t, response); - t->destroy(t); + request->set(request, "action", "check"); + request->set(request, "title", "Login"); + request->render(request, "templates/auth/login.cs"); } -static void check(private_auth_controller_t *this, - request_t *request, response_t *response) +static void check(private_auth_controller_t *this, request_t *request) { char *username, *password; - username = request->get_post_data(request, "username"); - password = request->get_post_data(request, "password"); + username = request->get_query_data(request, "username"); + password = request->get_query_data(request, "password"); if (username && password && this->manager->login(this->manager, username, password)) { - response->redirect(response, "status/ikesalist"); + request->redirect(request, "status/ikesalist"); } else { - response->redirect(response, "auth/login"); + request->redirect(request, "auth/login"); } } -static void logout(private_auth_controller_t *this, - request_t *request, response_t *response) +static void logout(private_auth_controller_t *this, request_t *request) { this->manager->logout(this->manager); - response->redirect(response, "auth/login"); + request->redirect(request, "auth/login"); } /** @@ -93,24 +86,24 @@ static char* get_name(private_auth_controller_t *this) * Implementation of controller_t.handle */ static void handle(private_auth_controller_t *this, - request_t *request, response_t *response, char *action) + request_t *request, char *action) { if (action) { if (streq(action, "login")) { - return login(this, request, response); + return login(this, request); } else if (streq(action, "check")) { - return check(this, request, response); + return check(this, request); } else if (streq(action, "logout")) { - return logout(this, request, response); + return logout(this, request); } } - response->redirect(response, "auth/login"); + request->redirect(request, "auth/login"); } /** @@ -129,7 +122,7 @@ controller_t *auth_controller_create(context_t *context, void *param) private_auth_controller_t *this = malloc_thing(private_auth_controller_t); this->public.controller.get_name = (char*(*)(controller_t*))get_name; - this->public.controller.handle = (void(*)(controller_t*,request_t*,response_t*,char*,char*,char*,char*,char*))handle; + this->public.controller.handle = (void(*)(controller_t*,request_t*,char*,char*,char*,char*,char*))handle; this->public.controller.destroy = (void(*)(controller_t*))destroy; this->manager = (manager_t*)context; diff --git a/src/manager/controller/gateway_controller.c b/src/manager/controller/gateway_controller.c index 1ebb51192..bdc779256 100644 --- a/src/manager/controller/gateway_controller.c +++ b/src/manager/controller/gateway_controller.c @@ -24,8 +24,6 @@ #include "../manager.h" #include "../gateway.h" -#include - #include @@ -48,50 +46,47 @@ struct private_gateway_controller_t { }; -static void list(private_gateway_controller_t *this, - request_t *request, response_t *response) +static void list(private_gateway_controller_t *this, request_t *request) { - template_t *t; enumerator_t *enumerator; char *name, *address; int id, port; - t = template_create("templates/gateway/list.cs"); enumerator = this->manager->create_gateway_enumerator(this->manager); while (enumerator->enumerate(enumerator, &id, &name, &port, &address)) { - t->setf(t, "gateways.%d.name=%s", id, name); + request->setf(request, "gateways.%d.name=%s", id, name); if (port) { - t->setf(t, "gateways.%d.address=tcp://%s:%d", id, address, port); + request->setf(request, "gateways.%d.address=tcp://%s:%d", + id, address, port); } else { - t->setf(t, "gateways.%d.address=unix://%s", id, address); + request->setf(request, "gateways.%d.address=unix://%s", + id, IPSEC_PIDDIR"/charon.xml"); } } enumerator->destroy(enumerator); - t->set(t, "action", "select"); - t->set(t, "title", "Choose gateway"); - t->render(t, response); - t->destroy(t); + request->set(request, "action", "select"); + request->set(request, "title", "Choose gateway"); + request->render(request, "templates/gateway/list.cs"); } -static void _select(private_gateway_controller_t *this, - request_t *request, response_t *response) +static void _select(private_gateway_controller_t *this, request_t *request) { char *id; - id = request->get_post_data(request, "gateway"); + id = request->get_query_data(request, "gateway"); if (id) { if (this->manager->select_gateway(this->manager, atoi(id))) { - response->redirect(response, "status/ikesalist"); + request->redirect(request, "status/ikesalist"); return; } } - response->printf(response, "selecting gateway failed: %s", id); + request->redirect(request, "gateway/list"); } /** @@ -106,24 +101,24 @@ static char* get_name(private_gateway_controller_t *this) * Implementation of controller_t.handle */ static void handle(private_gateway_controller_t *this, - request_t *request, response_t *response, char *action) + request_t *request, char *action) { if (!this->manager->logged_in(this->manager)) { - return response->redirect(response, "auth/login"); + return request->redirect(request, "auth/login"); } if (action) { if (streq(action, "list")) { - return list(this, request, response); + return list(this, request); } else if (streq(action, "select")) { - return _select(this, request, response); + return _select(this, request); } } - response->redirect(response, "gateway/list"); + request->redirect(request, "gateway/list"); } @@ -143,7 +138,7 @@ controller_t *gateway_controller_create(context_t *context, void *param) private_gateway_controller_t *this = malloc_thing(private_gateway_controller_t); this->public.controller.get_name = (char*(*)(controller_t*))get_name; - this->public.controller.handle = (void(*)(controller_t*,request_t*,response_t*,char*,char*,char*,char*,char*))handle; + this->public.controller.handle = (void(*)(controller_t*,request_t*,char*,char*,char*,char*,char*))handle; this->public.controller.destroy = (void(*)(controller_t*))destroy; this->manager = (manager_t*)context; diff --git a/src/manager/controller/status_controller.c b/src/manager/controller/status_controller.c index 70711763a..bcdbd26ea 100644 --- a/src/manager/controller/status_controller.c +++ b/src/manager/controller/status_controller.c @@ -24,7 +24,6 @@ #include "../manager.h" #include "../gateway.h" -#include #include #include @@ -48,124 +47,137 @@ struct private_status_controller_t { manager_t *manager; }; -static void ikesalist(private_status_controller_t *this, - request_t *request, response_t *response) +/** + * read XML of a childsa element and fill template + */ +static void process_childsa(private_status_controller_t *this, char *id, + enumerator_t *e, request_t *r) { - char *str; - gateway_t *gateway; - xml_t *node; - enumerator_t *e1, *e2, *e3, *e4, *e5, *e6, *e7, *e8; - char *name, *value, *id = "", *section; - - gateway = this->manager->select_gateway(this->manager, 0); - str = gateway->request(gateway, "" - "" - "" - "" - ""); - if (str == NULL) - { - response->printf(response, "gateway did not respond"); - return; - } + xml_t *xml; + enumerator_t *e1, *e2; + char *name, *value, *reqid = "", *section = ""; + int num = 0; - node = xml_create(str); - if (node == NULL) + while (e->enumerate(e, &xml, &name, &value)) { - response->printf(response, "parsing XML failed"); - return; - } - - template_t *t = template_create("templates/status/ikesalist.cs"); - - e1 = node->children(node); - while (e1->enumerate(e1, &node, &name, &value)) - { - if (streq(name, "message")) + if (streq(name, "reqid")) { - e2 = node->children(node); - while (e2->enumerate(e2, &node, &name, &value)) + reqid = value; + } + else if (streq(name, "local") || streq(name, "remote")) + { + section = name; + e1 = xml->children(xml); + while (e1->enumerate(e1, &xml, &name, &value)) { - if (streq(name, "query")) + if (streq(name, "networks")) { - e3 = node->children(node); - while (e3->enumerate(e3, &node, &name, &value)) + e2 = xml->children(xml); + while (e2->enumerate(e2, &xml, &name, &value)) { - if (streq(name, "ikesalist")) + if (streq(name, "network")) { - e4 = node->children(node); - while (e4->enumerate(e4, &node, &name, &value)) - { - if (streq(name, "ikesa")) - { - e5 = node->children(node); - while (e5->enumerate(e5, &node, &name, &value)) - { - if (streq(name, "id")) - { - id = value; - } - else if(streq(name, "local") || - streq(name, "remote")) - { - section = name; - e6 = node->children(node); - while (e6->enumerate(e6, &node, &name, &value)) - { - t->setf(t, "ikesas.%s.%s.%s=%s", id, section, name, value); - } - e6->destroy(e6); - } - else if (streq(name, "childsalist")) - { - e6 = node->children(node); - while (e6->enumerate(e6, &node, &name, &value)) - { - if (streq(name, "childsa")) - { - e7 = node->children(node); - while (e7->enumerate(e7, &node, &name, &value)) - { - if (streq(name, "local") || - streq(name, "remote")) - { - section = name; - e8 = node->children(node); - while (e8->enumerate(e8, &node, &name, &value)) - { - t->setf(t, "ikesas.%s.childsas.%s.%s=%s", id, section, name, value); - } - e8->destroy(e8); - } - } - e7->destroy(e7); - } - } - e6->destroy(e6); - } - else - { - t->setf(t, "ikesas.%s.%s=%s", id, name, value); - } - } - e5->destroy(e5); - } - } - e4->destroy(e4); + r->setf(r, "ikesas.%s.childsas.%s.%s.networks.%d=%s", + id, reqid, section, ++num, value); } } - e3->destroy(e3); + e2->destroy(e2); + } + else + { + r->setf(r, "ikesas.%s.childsas.%s.%s.%s=%s", + id, reqid, section, name, value); } } - e2->destroy(e2); + e1->destroy(e1); + } + else + { + r->setf(r, "ikesas.%s.childsas.%s.%s=%s", + id, reqid, name, value); } } - e1->destroy(e1); +} - t->set(t, "title", "IKE SA overview"); - t->render(t, response); - t->destroy(t); - free(str); +/** + * read XML of a ikesa element and fill template + */ +static void process_ikesa(private_status_controller_t *this, + enumerator_t *e, request_t *r) +{ + xml_t *xml; + enumerator_t *e1, *e2; + char *name, *value, *id = "", *section = ""; + + while (e->enumerate(e, &xml, &name, &value)) + { + if (streq(name, "id")) + { + id = value; + } + else if (streq(name, "local") || streq(name, "remote")) + { + section = name; + e1 = xml->children(xml); + while (e1->enumerate(e1, &xml, &name, &value)) + { + r->setf(r, "ikesas.%s.%s.%s=%s", id, section, name, value); + } + e1->destroy(e1); + } + else if (streq(name, "childsalist")) + { + e1 = xml->children(xml); + while (e1->enumerate(e1, &xml, &name, &value)) + { + if (streq(name, "childsa")) + { + e2 = xml->children(xml); + process_childsa(this, id, e2, r); + e2->destroy(e2); + } + } + e1->destroy(e1); + } + else + { + r->setf(r, "ikesas.%s.%s=%s", id, name, value); + } + } +} + +static void ikesalist(private_status_controller_t *this, request_t *r) +{ + gateway_t *gateway; + xml_t *xml; + enumerator_t *e1, *e2; + char *name, *value; + + gateway = this->manager->select_gateway(this->manager, 0); + e1 = gateway->query_ikesalist(gateway); + if (e1 == NULL) + { + r->set(r, "title", "Error"); + r->set(r, "error", "querying the gateway failed"); + r->render(r, "templates/error.cs"); + } + else + { + r->set(r, "title", "IKE SA overview"); + + while (e1->enumerate(e1, &xml, &name, &value)) + { + if (streq(name, "ikesa")) + { + e2 = xml->children(xml); + process_ikesa(this, e2, r); + e2->destroy(e2); + } + } + e1->destroy(e1); + + r->render(r, "templates/status/ikesalist.cs"); + } } /** @@ -180,24 +192,24 @@ static char* get_name(private_status_controller_t *this) * Implementation of controller_t.handle */ static void handle(private_status_controller_t *this, - request_t *request, response_t *response, char *action) + request_t *request, char *action) { if (!this->manager->logged_in(this->manager)) { - return response->redirect(response, "auth/login"); + return request->redirect(request, "auth/login"); } if (this->manager->select_gateway(this->manager, 0) == NULL) { - return response->redirect(response, "gateway/list"); + return request->redirect(request, "gateway/list"); } if (action) { if (streq(action, "ikesalist")) { - return ikesalist(this, request, response); + return ikesalist(this, request); } } - return response->redirect(response, "status/ikesalist"); + return request->redirect(request, "status/ikesalist"); } /** @@ -216,7 +228,7 @@ controller_t *status_controller_create(context_t *context, void *param) private_status_controller_t *this = malloc_thing(private_status_controller_t); this->public.controller.get_name = (char*(*)(controller_t*))get_name; - this->public.controller.handle = (void(*)(controller_t*,request_t*,response_t*,char*,char*,char*,char*,char*))handle; + this->public.controller.handle = (void(*)(controller_t*,request_t*,char*,char*,char*,char*,char*))handle; this->public.controller.destroy = (void(*)(controller_t*))destroy; this->manager = (manager_t*)context; diff --git a/src/manager/gateway.c b/src/manager/gateway.c index b918da26d..5f5a4b477 100644 --- a/src/manager/gateway.c +++ b/src/manager/gateway.c @@ -26,6 +26,10 @@ #include #include #include +#include +#include + +#include typedef struct private_gateway_t private_gateway_t; @@ -45,7 +49,7 @@ struct private_gateway_t { char *name; /** - * connection information + * host to connect using tcp */ host_t *host; @@ -55,22 +59,39 @@ struct private_gateway_t { int fd; }; +struct sockaddr_un unix_addr = { AF_UNIX, IPSEC_PIDDIR "/charon.xml"}; + /** * establish connection to gateway */ static bool connect_(private_gateway_t *this) { + int family, len; + struct sockaddr *addr; + if (this->fd >= 0) { close(this->fd); } - this->fd = socket(AF_INET, SOCK_STREAM, 0); + if (this->host) + { + family = AF_INET; + addr = this->host->get_sockaddr(this->host); + len = *this->host->get_sockaddr_len(this->host); + } + else + { + family = AF_UNIX; + addr = (struct sockaddr*)&unix_addr; + len = sizeof(unix_addr); + } + + this->fd = socket(family, SOCK_STREAM, 0); if (this->fd < 0) { return FALSE; } - if (connect(this->fd, this->host->get_sockaddr(this->host), - *this->host->get_sockaddr_len(this->host)) != 0) + if (connect(this->fd, addr, len) != 0) { close(this->fd); this->fd = -1; @@ -79,7 +100,6 @@ static bool connect_(private_gateway_t *this) return TRUE; } - /** * Implementation of gateway_t.request. */ @@ -119,7 +139,64 @@ static char* request(private_gateway_t *this, char *xml) return strdup(buf); } } + +/** + * Implementation of gateway_t.query_ikesalist. + */ +static enumerator_t* query_ikesalist(private_gateway_t *this) +{ + char *str, *name, *value; + xml_t *xml; + enumerator_t *e1, *e2, *e3, *e4 = NULL; + str = request(this, "" + "" + "" + "" + ""); + if (str == NULL) + { + return NULL; + } + xml = xml_create(str); + if (xml == NULL) + { + return NULL; + } + + e1 = xml->children(xml); + free(str); + while (e1->enumerate(e1, &xml, &name, &value)) + { + if (streq(name, "message")) + { + e2 = xml->children(xml); + while (e2->enumerate(e2, &xml, &name, &value)) + { + if (streq(name, "query")) + { + e3 = xml->children(xml); + while (e3->enumerate(e3, &xml, &name, &value)) + { + if (streq(name, "ikesalist")) + { + e4 = xml->children(xml); + e1->destroy(e1); + e2->destroy(e2); + e3->destroy(e3); + return e4; + } + } + e3->destroy(e3); + } + } + e2->destroy(e2); + } + } + e1->destroy(e1); + return NULL; +} + /** * Implementation of gateway_t.destroy */ @@ -129,25 +206,48 @@ static void destroy(private_gateway_t *this) { close(this->fd); } - this->host->destroy(this->host); + if (this->host) this->host->destroy(this->host); free(this->name); free(this); } -/* - * see header file +/** + * generic constructor */ -gateway_t *gateway_create(char *name, host_t *host) +static private_gateway_t *gateway_create(char *name) { private_gateway_t *this = malloc_thing(private_gateway_t); this->public.request = (char*(*)(gateway_t*, char *xml))request; + this->public.query_ikesalist = (enumerator_t*(*)(gateway_t*))query_ikesalist; this->public.destroy = (void(*)(gateway_t*))destroy; this->name = strdup(name); - this->host = host; + this->host = NULL; this->fd = -1; + return this; +} + +/** + * see header + */ +gateway_t *gateway_create_tcp(char *name, host_t *host) +{ + private_gateway_t *this = gateway_create(name); + + this->host = host; + + return &this->public; +} + +/** + * see header + */ +gateway_t *gateway_create_unix(char *name) +{ + private_gateway_t *this = gateway_create(name); + return &this->public; } diff --git a/src/manager/gateway.h b/src/manager/gateway.h index 7b5d91045..d84f00038 100644 --- a/src/manager/gateway.h +++ b/src/manager/gateway.h @@ -24,6 +24,7 @@ #define GATEWAY_H_ #include +#include typedef struct gateway_t gateway_t; @@ -39,7 +40,14 @@ struct gateway_t { * @return allocated xml response string */ char* (*request)(gateway_t *this, char *xml); - + + /** + * @brief Query the list of IKE_SAs and all its children. + * + * @return enumerator over ikesa XML elements + */ + enumerator_t* (*query_ikesalist)(gateway_t *this); + /** * @brief Destroy a gateway instance. */ @@ -47,12 +55,20 @@ struct gateway_t { }; /** - * @brief Create a gateway instance. + * @brief Create a gateway instance using a TCP connection. * * @param name name of the gateway * @param host gateway connection endpoint * @param */ -gateway_t *gateway_create(char *name, host_t *host); +gateway_t *gateway_create_tcp(char *name, host_t *host); + +/** + * @brief Create a gateway instance using a UNIX socket. + * + * @param name name of the gateway + * @param + */ +gateway_t *gateway_create_unix(char *name); #endif /* GATEWAY_H_ */ diff --git a/src/manager/lib/controller.h b/src/manager/lib/controller.h index fe6177513..5b39f559c 100644 --- a/src/manager/lib/controller.h +++ b/src/manager/lib/controller.h @@ -24,7 +24,6 @@ #define CONTROLLER_H_ #include "request.h" -#include "response.h" #include "context.h" typedef struct controller_t controller_t; @@ -35,7 +34,7 @@ typedef struct controller_t controller_t; * @param request http request * @param response http response */ -typedef void *(*controller_handler_t)(controller_t *this, request_t *request, response_t *response); +typedef void *(*controller_handler_t)(controller_t *this, request_t *request); /** * @brief Constructor function for a controller @@ -66,7 +65,6 @@ struct controller_t { * parameter not found in the request URL is set to NULL. * * @param request HTTP request - * @param response HTTP response * @param p1 first parameter * @param p2 second parameter * @param p3 third parameter @@ -74,7 +72,7 @@ struct controller_t { * @param p5 fifth parameter * @return */ - void (*handle)(controller_t *this, request_t *request, response_t *response, + void (*handle)(controller_t *this, request_t *request, char *a1, char *a2, char *a3, char *a4, char *a5); /** diff --git a/src/manager/lib/dict.c b/src/manager/lib/dict.c deleted file mode 100644 index a5fdd52d5..000000000 --- a/src/manager/lib/dict.c +++ /dev/null @@ -1,154 +0,0 @@ -/** - * @file dict.c - * - * @brief Implementation of dict_t. - * - */ - -/* - * Copyright (C) 2007 Martin Willi - * Hochschule fuer Technik Rapperswil - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See . - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#include "dict.h" - -#include - - -typedef struct private_dict_t private_dict_t; - -/** - * private data of dict - */ -struct private_dict_t { - - /** - * public functions - */ - dict_t public; - - /** - * baaah, we really should have a hashtable for this - */ - linked_list_t *list; - - /** - * key comparator function - */ - bool(*key_comparator)(void*,void*); - - /** - * destructor function for key - */ - void(*key_destructor)(void*); - - /** - * destructor function for value - */ - void(*value_destructor)(void*); -}; - -/** - * key value pair to store entries - */ -typedef struct { - void *key; - void *value; -} key_value_t; - -/** - * Implementation of dict_t.get. - */ -static void* get(private_dict_t *this, void *key) -{ - key_value_t *kv; - iterator_t *iterator; - void *value = NULL; - - iterator = this->list->create_iterator(this->list, TRUE); - while (iterator->iterate(iterator, (void**)&kv)) - { - if (this->key_comparator(kv->key, key)) - { - value = kv->value; - break; - } - } - iterator->destroy(iterator); - return value; -} -/** - * Implementation of dict_t.set. - */ -static void set(private_dict_t *this, void *key, void *value) -{ - /* we don't overwrite, just prepend */ - key_value_t *kv = malloc_thing(key_value_t); - kv->key = key; - kv->value = value; - this->list->insert_first(this->list, kv); -} - - -/** - * comparator for strings - */ -bool dict_streq(void *a, void *b) -{ - return streq(a, b); -} - -/** - * Implementation of dict_t.destroy - */ -static void destroy(private_dict_t *this) -{ - key_value_t *kv; - - while (this->list->remove_last(this->list, (void**)&kv) == SUCCESS) - { - if (this->key_destructor) - { - this->key_destructor(kv->key); - } - if (this->value_destructor) - { - this->value_destructor(kv->value); - } - free(kv); - } - this->list->destroy(this->list); - free(this); -} - -/* - * see header file - */ -dict_t *dict_create(bool(*key_comparator)(void*,void*), - void(*key_destructor)(void*), - void(*value_destructor)(void*)) -{ - private_dict_t *this = malloc_thing(private_dict_t); - - this->public.set = (void(*)(dict_t*, void *key, void *value))set; - this->public.get = (void*(*)(dict_t*, void *key))get; - this->public.destroy = (void(*)(dict_t*))destroy; - - this->list = linked_list_create(); - this->key_comparator = key_comparator; - this->key_destructor = key_destructor; - this->value_destructor = value_destructor; - - return &this->public; -} - diff --git a/src/manager/lib/dict.h b/src/manager/lib/dict.h deleted file mode 100644 index 9a9e9036b..000000000 --- a/src/manager/lib/dict.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @file dict.h - * - * @brief Interface of dict_t. - * - */ - -/* - * Copyright (C) 2007 Martin Willi - * Hochschule fuer Technik Rapperswil - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See . - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#ifndef DICT_H_ -#define DICT_H_ - -#include - -typedef struct dict_t dict_t; - -/** - * @brief Dictionary type, key value stuff. - */ -struct dict_t { - - /** - * @brief Set a value in the dict. - * - * @param key key to set - * @param value value, NULL to unset key - * @return - */ - void (*set)(dict_t *this, void *key, void *value); - - /** - * @brief Get a value form the dict. - * - * @param key key to get value of - * @return assigned value, NULL if not found - */ - void* (*get)(dict_t *this, void *key); - - /** - * @brief Destroy a dict instance. - */ - void (*destroy)(dict_t *this); -}; - -/** - * @brief Key comparator function for strings - */ -bool dict_streq(void *a, void *b); - -/** - * @brief Create a dict instance. - * - * @param free_key TRUE to free() keys on destruction - * @param - */ -dict_t *dict_create(bool(*key_comparator)(void*,void*), - void(*key_destructor)(void*), - void(*value_destructor)(void*)); - -#endif /* DICT_H_ */ diff --git a/src/manager/lib/dispatcher.c b/src/manager/lib/dispatcher.c index db99110c3..5ce5523f8 100644 --- a/src/manager/lib/dispatcher.c +++ b/src/manager/lib/dispatcher.c @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -183,7 +184,6 @@ static void dispatch(private_dispatcher_t *this) while (TRUE) { request_t *request; - response_t *response; session_entry_t *current, *found = NULL; iterator_t *iterator; time_t now; @@ -200,8 +200,11 @@ static void dispatch(private_dispatcher_t *this) } /* prepare */ - response = response_create(&fcgi_req); - request = request_create(&fcgi_req); + request = request_create(&fcgi_req, TRUE); + if (request == NULL) + { + continue; + } sid = request->get_cookie(request, "SID"); now = time(NULL); @@ -244,7 +247,7 @@ static void dispatch(private_dispatcher_t *this) } /* start processing */ - found->session->process(found->session, request, response); + found->session->process(found->session, request); found->used = time(NULL); /* release session */ @@ -255,7 +258,6 @@ static void dispatch(private_dispatcher_t *this) /* cleanup */ request->destroy(request); - response->destroy(response); /* FCGX_FPrintF(fcgi_req.out, "
    "); @@ -323,8 +325,8 @@ static void destroy(private_dispatcher_t *this) /* * see header file */ -dispatcher_t *dispatcher_create(int timeout, context_constructor_t constructor, - void *param) +dispatcher_t *dispatcher_create(char *socket, int timeout, + context_constructor_t constructor, void *param) { private_dispatcher_t *this = malloc_thing(private_dispatcher_t); @@ -343,11 +345,11 @@ dispatcher_t *dispatcher_create(int timeout, context_constructor_t constructor, FCGX_Init(); -#ifdef FCGI_SOCKET - unlink(FCGI_SOCKET); - this->fd = FCGX_OpenSocket(FCGI_SOCKET, 10); -#endif /* FCGI_SOCKET */ - + if (socket) + { + unlink(socket); + this->fd = FCGX_OpenSocket(socket, 10); + } return &this->public; } diff --git a/src/manager/lib/dispatcher.h b/src/manager/lib/dispatcher.h index f46e5f32d..d04705409 100644 --- a/src/manager/lib/dispatcher.h +++ b/src/manager/lib/dispatcher.h @@ -73,11 +73,12 @@ struct dispatcher_t { * The context constructor is invoked to create a session context for * each session. * + * @param socket FastCGI socket path, NULL for dynamic * @param timeout session timeout * @param constructor construction function for session context * @param param parameter to supply to context constructor */ -dispatcher_t *dispatcher_create(int timeout, +dispatcher_t *dispatcher_create(char *socket, int timeout, context_constructor_t constructor, void *param); #endif /* DISPATCHER_H_ */ diff --git a/src/manager/lib/request.c b/src/manager/lib/request.c index 704b25203..e029c0d09 100644 --- a/src/manager/lib/request.c +++ b/src/manager/lib/request.c @@ -24,9 +24,10 @@ #include "request.h" +#include #include - -#include +#include +#include typedef struct private_request_t private_request_t; @@ -41,27 +42,111 @@ struct private_request_t { request_t public; /** - * the associated fcgi request + * FastCGI request object */ FCGX_Request *req; /** - * list of cookies + * ClearSilver CGI Kit context */ - dict_t *cookies; + CGI *cgi; /** - * list of post data + * ClearSilver HDF dataset for this request */ - dict_t *posts; + HDF *hdf; }; + +/** + * thread specific FCGX_Request, used for ClearSilver cgiwrap callbacks. + * ClearSilver cgiwrap is not threadsave, so we use a private + * context for each thread. + */ +__thread FCGX_Request *req; + +/** + * length of param list in req->envp + */ +__thread int req_env_len; + +/** + * fcgiwrap read callback + */ +static int read_cb(void *null, char *buf, int size) +{ + return FCGX_GetStr(buf, size, req->in); +} + +/** + * fcgiwrap writef callback + */ +static int writef_cb(void *null, const char *format, va_list args) +{ + FCGX_VFPrintF(req->out, format, args); + return 0; +} +/** + * fcgiwrap write callback + */ +static int write_cb(void *null, const char *buf, int size) +{ + return FCGX_PutStr(buf, size, req->out); +} + +/** + * fcgiwrap getenv callback + */ +static char *getenv_cb(void *null, const char *key) +{ + char *value; + + value = FCGX_GetParam(key, req->envp); + return value ? strdup(value) : NULL; +} + +/** + * fcgiwrap getenv callback + */ +static int putenv_cb(void *null, const char *key, const char *value) +{ + /* not supported */ + return 1; +} + +/** + * fcgiwrap iterenv callback + */ +static int iterenv_cb(void *null, int num, char **key, char **value) +{ + *key = NULL; + *value = NULL; + + if (num > req_env_len) + { + char *eq; + + eq = strchr(req->envp[num], '='); + if (eq) + { + *key = strndup(req->envp[num], eq - req->envp[num]); + *value = strdup(eq + 1); + } + if (*key == NULL || *value == NULL) + { + free(*key); + free(*value); + return 1; + } + } + return 0; +} /** * Implementation of request_t.get_cookie. */ static char* get_cookie(private_request_t *this, char *name) { - return this->cookies->get(this->cookies, name); + return hdf_get_valuef(this->hdf, "Cookie.%s", name); } /** @@ -76,152 +161,74 @@ static char* get_path(private_request_t *this) /** * Implementation of request_t.get_post_data. */ -static char* get_post_data(private_request_t *this, char *name) +static char* get_query_data(private_request_t *this, char *name) { - return this->posts->get(this->posts, name); + return hdf_get_valuef(this->hdf, "Query.%s", name); } /** - * convert 2 digit hex string to a integer + * Implementation of request_t.add_cookie. */ -static char hex2char(char *hex) +static void add_cookie(private_request_t *this, char *name, char *value) { - static char hexdig[] = "00112233445566778899AaBbCcDdEeFf"; + cgi_cookie_set (this->cgi, name, value, + FCGX_GetParam("SCRIPT_NAME", this->req->envp), + NULL, NULL, 0, 0); +} - return (strchr(hexdig, hex[1]) - hexdig)/2 + - ((strchr(hexdig, hex[0]) - hexdig)/2 * 16); +/** + * Implementation of request_t.redirect. + */ +static void redirect(private_request_t *this, char *location) +{ + FCGX_FPrintF(this->req->out, "Status: 303 See Other\n"); + FCGX_FPrintF(this->req->out, "Location: %s%s%s\n\n", + FCGX_GetParam("SCRIPT_NAME", this->req->envp), + *location == '/' ? "" : "/", location); } /** - * unescape a string up to the delimiter, and return a clone + * Implementation of request_t.get_base. */ -static char *unescape(char **pos, char delimiter) +static char* get_base(private_request_t *this) { - char *ptr, *res, *end, code[3] = {'\0','\0','\0'}; - - if (**pos == '\0') - { - return NULL; - } - ptr = strchr(*pos, delimiter); - if (ptr) - { - res = strndup(*pos, ptr - *pos); - *pos = ptr + 1; - } - else - { - res = strdup(*pos); - *pos = ""; - } - end = res + strlen(res) + 1; - /* replace '+' with ' ' */ - ptr = res; - while ((ptr = strchr(ptr, '+'))) - { - *ptr = ' '; - } - /* replace %HH with its ascii value */ - ptr = res; - while ((ptr = strchr(ptr, '%'))) - { - if (ptr > end - 2) - { - break; - } - strncpy(code, ptr + 1, 2); - *ptr = hex2char(code); - memmove(ptr + 1, ptr + 3, end - (ptr + 3)); - } - return res; + return FCGX_GetParam("SCRIPT_NAME", this->req->envp); } /** - * parse the http POST data + * Implementation of request_t.render. */ -static void parse_post(private_request_t *this) +static void render(private_request_t *this, char *template) { - char buf[4096], *pos, *name, *value; - int len; - - if (!streq(FCGX_GetParam("REQUEST_METHOD", this->req->envp), "POST") || - !streq(FCGX_GetParam("CONTENT_TYPE", this->req->envp), - "application/x-www-form-urlencoded")) - { - return; - } + NEOERR* err; - len = FCGX_GetStr(buf, sizeof(buf) - 1, this->req->in); - if (len != atoi(FCGX_GetParam("CONTENT_LENGTH", this->req->envp))) + err = cgi_display(this->cgi, template); + if (err) { - return; - } - buf[len] = 0; - - pos = buf; - while (TRUE) - { - name = unescape(&pos, '='); - if (name) - { - value = unescape(&pos, '&'); - if (value) - { - this->posts->set(this->posts, name, value); - continue; - } - else - { - free(name); - } - } - break; + cgi_neo_error(this->cgi, err); + nerr_log_error(err); } + return; } /** - * parse the requests cookies + * Implementation of request_t.set. */ -static void parse_cookies(private_request_t *this) +static void set(private_request_t *this, char *key, char *value) { - char *str, *pos; - char *name, *value; - - str = FCGX_GetParam("HTTP_COOKIE", this->req->envp); - while (str) - { - if (*str == ' ') - { - str++; - continue; - } - pos = strchr(str, '='); - if (pos == NULL) - { - break; - } - name = strndup(str, pos - str); - value = NULL; - str = pos + 1; - if (str) - { - pos = strchr(str, ';'); - if (pos) - { - value = strndup(str, pos - str); - } - else - { - value = strdup(str); - } - } - this->cookies->set(this->cookies, name, value); - if (pos == NULL) - { - break; - } - str = pos + 1; - } + hdf_set_value(this->hdf, key, value); +} + +/** + * Implementation of request_t.setf. + */ +static void setf(private_request_t *this, char *format, ...) +{ + va_list args; + + va_start(args, format); + hdf_set_valuevf(this->hdf, format, args); + va_end(args); } /** @@ -229,30 +236,70 @@ static void parse_cookies(private_request_t *this) */ static void destroy(private_request_t *this) { - this->cookies->destroy(this->cookies); - this->posts->destroy(this->posts); + cgi_destroy(&this->cgi); free(this); } /* * see header file */ -request_t *request_create(FCGX_Request *request) +request_t *request_create(FCGX_Request *request, bool debug) { + NEOERR* err; + static bool initialized = FALSE; private_request_t *this = malloc_thing(private_request_t); this->public.get_path = (char*(*)(request_t*))get_path; + this->public.get_base = (char*(*)(request_t*))get_base; + this->public.add_cookie = (void(*)(request_t*, char *name, char *value))add_cookie; this->public.get_cookie = (char*(*)(request_t*,char*))get_cookie; - this->public.get_post_data = (char*(*)(request_t*, char *name))get_post_data; + this->public.get_query_data = (char*(*)(request_t*, char *name))get_query_data; + this->public.redirect = (void(*)(request_t*, char *location))redirect; + this->public.render = (void(*)(request_t*,char*))render; + this->public.set = (void(*)(request_t*, char *, char*))set; + this->public.setf = (void(*)(request_t*, char *format, ...))setf; this->public.destroy = (void(*)(request_t*))destroy; + if (!initialized) + { + cgiwrap_init_emu(NULL, read_cb, writef_cb, write_cb, + getenv_cb, putenv_cb, iterenv_cb); + initialized = TRUE; + } + this->req = request; - this->cookies = dict_create(dict_streq, free, free); - this->posts = dict_create(dict_streq, free, free); + req = request; + req_env_len = 0; + while (req->envp[req_env_len] != NULL) + { + req_env_len++; + } - parse_cookies(this); - parse_post(this); + err = hdf_init(&this->hdf); + if (!err) + { + hdf_set_value(this->hdf, "base", get_base(this)); + hdf_set_value(this->hdf, "Config.NoCache", "true"); + if (!debug) + { + hdf_set_value(this->hdf, "Config.TimeFooter", "0"); + hdf_set_value(this->hdf, "Config.CompressionEnabled", "1"); + hdf_set_value(this->hdf, "Config.WhiteSpaceStrip", "2"); + } - return &this->public; + err = cgi_init(&this->cgi, this->hdf); + if (!err) + { + err = cgi_parse(this->cgi); + if (!err) + { + return &this->public; + } + cgi_destroy(&this->cgi); + } + } + nerr_log_error(err); + free(this); + return NULL; } diff --git a/src/manager/lib/request.h b/src/manager/lib/request.h index 852238a1d..e6fd71e71 100644 --- a/src/manager/lib/request.h +++ b/src/manager/lib/request.h @@ -23,8 +23,8 @@ #ifndef REQUEST_H_ #define REQUEST_H_ - #include +#include typedef struct request_t request_t; @@ -34,6 +34,14 @@ typedef struct request_t request_t; */ struct request_t { + /** + * @brief Add a cookie to the reply (Set-Cookie header). + * + * @param name name of the cookie to set + * @param value value of the cookie + */ + void (*add_cookie)(request_t *this, char *name, char *value); + /** * @brief Get a cookie the client sent in the request. * @@ -50,12 +58,57 @@ struct request_t { char* (*get_path)(request_t *this); /** - * @brief Get a post variable included in the request. + * @brief Get the base path of the application. * - * @param name name of the POST variable + * @return base path + */ + char* (*get_base)(request_t *this); + + /** + * @brief Get a post/get variable included in the request. + * + * @param name name of the POST/GET variable * @return value, NULL if not found */ - char* (*get_post_data)(request_t *this, char *name); + char* (*get_query_data)(request_t *this, char *name); + + /** + * @brief Redirect the client to another location. + * + * @param location location to redirect to + */ + void (*redirect)(request_t *this, char *location); + + /** + * @brief Set a template value. + * + * @param key key to set + * @param value value to set key to + */ + void (*set)(request_t *this, char *key, char *value); + + /** + * @brief Set a template value using format strings. + * + * Format string is in the form "key=value", where printf like format + * substitution occurs over the whole string. + * + * @param format printf like format string + * @param ... variable argument list + */ + void (*setf)(request_t *this, char *format, ...); + + /** + * @brief Render a template. + * + * The render() function additionally sets a HDF variable "base" + * which points to the root of the web application and allows to point to + * other targets without to worry about path location. + * + * @param template clearsilver template file location + * @return rendered template string + */ + void (*render)(request_t *this, char *template); /** * @brief Destroy the request_t. @@ -67,7 +120,8 @@ struct request_t { * @brief Create a request from the fastcgi struct. * * @param request the FCGI request + * @param debug no stripping, no compression, timing information */ -request_t *request_create(FCGX_Request *request); +request_t *request_create(FCGX_Request *request, bool debug); #endif /* REQUEST_H_ */ diff --git a/src/manager/lib/response.c b/src/manager/lib/response.c deleted file mode 100644 index be933792f..000000000 --- a/src/manager/lib/response.c +++ /dev/null @@ -1,233 +0,0 @@ -/** - * @file response.c - * - * @brief Implementation of response_t. - * - */ - -/* - * Copyright (C) 2007 Martin Willi - * Hochschule fuer Technik Rapperswil - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See . - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#include "response.h" - -#include -#include - -#include - -typedef struct { - char *name; - char *value; -} name_value_t; - -/** - * create name value pair - */ -static name_value_t *name_value_create(char *name, char *value) -{ - name_value_t *this = malloc_thing(name_value_t); - - this->name = strdup(name); - this->value = strdup(value); - - return this; -} - -/** - * destroy a name value pair - */ -static void name_value_destroy(name_value_t *this) -{ - free(this->name); - free(this->value); - free(this); -} - -typedef struct private_response_t private_response_t; - -/** - * private data of the task manager - */ -struct private_response_t { - - /** - * public functions - */ - response_t public; - - /** - * the associated fcgi request - */ - FCGX_Request *req; - - /** - * Content type - */ - char *content_type; - - /** - * list of cookies (name_value_t) - */ - linked_list_t *cookies; - - /** - * list of custom headers (name_value_t) - */ - linked_list_t *headers; - - /** - * headers already written? - */ - bool started; -}; - -/** - * write the headers, if not already written - */ -static void write_headers(private_response_t *this) -{ - iterator_t *iterator; - name_value_t *current; - - FCGX_FPrintF(this->req->out, "Content-type: %s\n", this->content_type); - iterator = this->cookies->create_iterator(this->cookies, TRUE); - while (iterator->iterate(iterator, (void**)¤t)) - { - FCGX_FPrintF(this->req->out, "Set-Cookie: %s=%s; path=%s\n", - current->name, current->value, - FCGX_GetParam("SCRIPT_NAME", this->req->envp)); - } - iterator->destroy(iterator); - iterator = this->cookies->create_iterator(this->headers, TRUE); - while (iterator->iterate(iterator, (void**)¤t)) - { - FCGX_FPrintF(this->req->out, "%s: %s\n", - current->name, current->value); - } - iterator->destroy(iterator); - FCGX_PutChar('\n', this->req->out); - this->started = TRUE; -} - -/** - * Implementation of response_t.print. - */ -static void print_(private_response_t *this, char *str) -{ - if (!this->started) - { - write_headers(this); - } - FCGX_PutS(str, this->req->out); -} - -/** - * Implementation of response_t.printf. - */ -static void printf_(private_response_t *this, char *format, ...) -{ - va_list args; - - if (!this->started) - { - write_headers(this); - } - - va_start(args, format); - FCGX_VFPrintF(this->req->out, format, args); - va_end(args); -} - -/** - * Implementation of response_t.add_header. - */ -static void add_header(private_response_t *this, char *name, char *value) -{ - this->headers->insert_last(this->headers, name_value_create(name, value)); -} - -/** - * Implementation of response_t.set_content_type. - */ -static void set_content_type(private_response_t *this, char *type) -{ - free(this->content_type); - this->content_type = strdup(type); -} - -/** - * Implementation of response_t.add_cookie. - */ -static void add_cookie(private_response_t *this, char *name, char *value) -{ - this->cookies->insert_last(this->cookies, name_value_create(name, value)); -} - -/** - * Implementation of response_t.redirect. - */ -static void redirect(private_response_t *this, char *location) -{ - FCGX_FPrintF(this->req->out, "Status: 303 See Other\n"); - FCGX_FPrintF(this->req->out, "Location: %s%s%s\n\n", - FCGX_GetParam("SCRIPT_NAME", this->req->envp), - *location == '/' ? "" : "/", location); -} - - -/** - * Implementation of response_t.get_base. - */ -static char* get_base(private_response_t *this) -{ - return FCGX_GetParam("SCRIPT_NAME", this->req->envp); -} - -/** - * Implementation of response_t.destroy - */ -static void destroy(private_response_t *this) -{ - this->headers->destroy_function(this->headers, (void*)name_value_destroy); - this->cookies->destroy_function(this->cookies, (void*)name_value_destroy); - free(this->content_type); - free(this); -} - -/* - * see header file - */ -response_t *response_create(FCGX_Request *request) -{ - private_response_t *this = malloc_thing(private_response_t); - - this->public.print = (void(*)(response_t*, char *str))print_; - this->public.printf = (void(*)(response_t*, char *format, ...))printf_; - this->public.add_header = (void(*)(response_t*, char *name, char *value))add_header; - this->public.set_content_type = (void(*)(response_t*, char *type))set_content_type; - this->public.add_cookie = (void(*)(response_t*, char *name, char *value))add_cookie; - this->public.redirect = (void(*)(response_t*, char *location))redirect; - this->public.get_base = (char*(*)(response_t*))get_base; - this->public.destroy = (void(*)(response_t*))destroy; - - this->req = request; - this->headers = linked_list_create(); - this->cookies = linked_list_create(); - this->content_type = strdup("text/html"); - this->started = FALSE; - - return &this->public; -} - diff --git a/src/manager/lib/response.h b/src/manager/lib/response.h deleted file mode 100644 index 50d0eacc1..000000000 --- a/src/manager/lib/response.h +++ /dev/null @@ -1,102 +0,0 @@ -/** - * @file response.h - * - * @brief Interface of response_t. - * - */ - -/* - * Copyright (C) 2007 Martin Willi - * Hochschule fuer Technik Rapperswil - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See . - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#ifndef RESPONSE_H_ -#define RESPONSE_H_ - - -#include - -typedef struct response_t response_t; - -/** - * @brief A HTTP response, wraps response functionality around FCGX_Request. - * - */ -struct response_t { - - /** - * @brief Write a string to the client. - * - * @param str string to write - */ - void (*print)(response_t *this, char *str); - - /** - * @brief Write a printf like format string to client. - * - * @param format printf like format string - * @param ... variable argument list - */ - void (*printf)(response_t *this, char *format, ...); - - /** - * @brief Add a custom header to the response. - * - * @param name name of the header - * @param value value of the header - */ - void (*add_header)(response_t *this, char *name, char *value); - - /** - * @brief Set the content type (Content-Type header). - * - * @param type content type (e.g. text/html) - */ - void (*set_content_type)(response_t *this, char *type); - - /** - * @brief Add a cookie to the response (Set-Cookie header). - * - * @param name name of the cookie to set - * @param value value of the cookie - */ - void (*add_cookie)(response_t *this, char *name, char *value); - - /** - * @brief Redirect the client to another location. - * - * @param location location to redirect to - */ - void (*redirect)(response_t *this, char *location); - - /** - * @brief Get the base path of the application. - * - * @return base path - */ - char* (*get_base)(response_t *this); - - /** - * @brief Destroy a response_t. - */ - void (*destroy) (response_t *this); -}; - -/** - * @brief Create a response. - * - * @param request the FCGI request structure - */ -response_t *response_create(FCGX_Request *request); - -#endif /* RESPONSE_H_ */ diff --git a/src/manager/lib/session.c b/src/manager/lib/session.c index 7520c3226..8ffbc7230 100644 --- a/src/manager/lib/session.c +++ b/src/manager/lib/session.c @@ -70,7 +70,7 @@ static void add_controller(private_session_t *this, controller_t *controller) /** * Create a session ID and a cookie */ -static void create_sid(private_session_t *this, response_t *response) +static void create_sid(private_session_t *this, request_t *request) { char buf[16]; chunk_t chunk = chunk_from_buf(buf); @@ -78,15 +78,14 @@ static void create_sid(private_session_t *this, response_t *response) randomizer->get_pseudo_random_bytes(randomizer, sizeof(buf), buf); asprintf(&this->sid, "%#B", &chunk); - response->add_cookie(response, "SID", this->sid); + request->add_cookie(request, "SID", this->sid); randomizer->destroy(randomizer); } /** * Implementation of session_t.process. */ -static void process(private_session_t *this, - request_t *request, response_t *response) +static void process(private_session_t *this, request_t *request) { char *pos, *path, *controller, *action; iterator_t *iterator; @@ -95,7 +94,7 @@ static void process(private_session_t *this, if (this->sid == NULL) { - create_sid(this, response); + create_sid(this, request); } path = request->get_path(request); @@ -125,7 +124,7 @@ static void process(private_session_t *this, { if (streq(current->get_name(current), controller)) { - current->handle(current, request, response, action, NULL, NULL, NULL, NULL); + current->handle(current, request, action, NULL, NULL, NULL, NULL); handled = TRUE; break; } @@ -138,11 +137,7 @@ static void process(private_session_t *this, if (this->controllers->get_first(this->controllers, (void**)¤t) == SUCCESS) { - response->redirect(response, current->get_name(current)); - } - else - { - response->printf(response, "No controllers loaded!\n"); + request->redirect(request, current->get_name(current)); } } } @@ -174,7 +169,7 @@ session_t *session_create(context_t *context) private_session_t *this = malloc_thing(private_session_t); this->public.add_controller = (void(*)(session_t*, controller_t*))add_controller; - this->public.process = (void(*)(session_t*, request_t*,response_t*))process; + this->public.process = (void(*)(session_t*,request_t*))process; this->public.get_sid = (char*(*)(session_t*))get_sid; this->public.destroy = (void(*)(session_t*))destroy; diff --git a/src/manager/lib/session.h b/src/manager/lib/session.h index a66b1a8e2..d18545876 100644 --- a/src/manager/lib/session.h +++ b/src/manager/lib/session.h @@ -24,7 +24,6 @@ #define SESSION_H_ #include "request.h" -#include "response.h" #include "controller.h" typedef struct session_t session_t; @@ -53,9 +52,8 @@ struct session_t { * @brief Process a request in this session. * * @param request request to process - * @param response response to send */ - void (*process)(session_t *this, request_t *request, response_t *response); + void (*process)(session_t *this, request_t *request); /** * @brief Destroy the session_t. diff --git a/src/manager/lib/template.c b/src/manager/lib/template.c deleted file mode 100644 index 36a4d294e..000000000 --- a/src/manager/lib/template.c +++ /dev/null @@ -1,138 +0,0 @@ -/** - * @file template.c - * - * @brief Implementation of template_t. - * - */ - -/* - * Copyright (C) 2007 Martin Willi - * Hochschule fuer Technik Rapperswil - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See . - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#include "template.h" - -#include - -#include - -typedef struct private_template_t private_template_t; - -/** - * private data of the task manager - */ -struct private_template_t { - - /** - * public functions - */ - template_t public; - - /** - * template file - */ - char *file; - - /** - * clearsilver HDF dataset - */ - HDF *hdf; -}; - -/** - * clearsilver cs_render callback function - */ -static NEOERR* render_cb(response_t *response, char *str) -{ - response->print(response, str); - return NULL; -} - -/** - * Implementation of template_t.render. - */ -static void render(private_template_t *this, response_t *response) -{ - NEOERR* err; - CSPARSE *parse; - - hdf_set_value(this->hdf, "base", response->get_base(response)); - - err = cs_init(&parse, this->hdf); - if (!err) - { - err = cs_parse_file(parse, this->file); - if (!err) - { - err = cs_render(parse, response, (CSOUTFUNC)render_cb); - if (!err) - { - cs_destroy(&parse); - return; - } - } - cs_destroy(&parse); - } - nerr_log_error(err); - return; -} - -/** - * Implementation of template_t.set. - */ -static void set(private_template_t *this, char *key, char *value) -{ - hdf_set_value(this->hdf, key, value); -} - -/** - * Implementation of template_t.setf. - */ -static void setf(private_template_t *this, char *format, ...) -{ - va_list args; - - va_start(args, format); - hdf_set_valuevf(this->hdf, format, args); - va_end(args); -} - -/** - * Implementation of template_t.destroy - */ -static void destroy(private_template_t *this) -{ - hdf_destroy(&this->hdf); - free(this->file); - free(this); -} - -/* - * see header file - */ -template_t *template_create(char *file) -{ - private_template_t *this = malloc_thing(private_template_t); - - this->public.render = (void(*)(template_t*,response_t*))render; - this->public.set = (void(*)(template_t*, char *, char*))set; - this->public.setf = (void(*)(template_t*, char *format, ...))setf; - this->public.destroy = (void(*)(template_t*))destroy; - - this->file = strdup(file); - this->hdf = NULL; - - hdf_init(&this->hdf); - return &this->public; -} - diff --git a/src/manager/lib/template.h b/src/manager/lib/template.h deleted file mode 100644 index 6e17177a1..000000000 --- a/src/manager/lib/template.h +++ /dev/null @@ -1,80 +0,0 @@ -/** - * @file template.h - * - * @brief Interface of template_t. - * - */ - -/* - * Copyright (C) 2007 Martin Willi - * Hochschule fuer Technik Rapperswil - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See . - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#ifndef TEMPLATE_H_ -#define TEMPLATE_H_ - -#include "response.h" - -typedef struct template_t template_t; - -/** - * @brief Template engine based on ClearSilver. - * - */ -struct template_t { - - /** - * @brief Set a template value. - * - * @param key key to set - * @param value value to set key to - */ - void (*set)(template_t *this, char *key, char *value); - - /** - * @brief Set a template value using format strings. - * - * Format string is in the form "key=value", where printf like format - * substitution occurs over the whole string. - * - * @param format printf like format string - * @param ... variable argument list - */ - void (*setf)(template_t *this, char *format, ...); - - /** - * @brief Render a template to a response object. - * - * The render() function additionally sets a clearsilver variable "base" - * which points to the root of the web application and allows to point to - * other targets without to worry about path location. - * - * @param response response to render to - * @return rendered template string - */ - void (*render)(template_t *this, response_t *response); - - /** - * @brief Destroy the template_t. - */ - void (*destroy) (template_t *this); -}; - -/** - * @brief Create a template from a file. - * - * @param file template file - */ -template_t *template_create(char *file); - -#endif /* TEMPLATE_H_ */ diff --git a/src/manager/main.c b/src/manager/main.c index abfc52912..45e4b2fd9 100644 --- a/src/manager/main.c +++ b/src/manager/main.c @@ -29,7 +29,7 @@ #include "controller/status_controller.h" #include "controller/gateway_controller.h" -#define DBFILE IPSECDIR "/sqlite.db" +#define DBFILE IPSECDIR "/manager.db" #define SESSION_TIMEOUT 180 #define THREADS 10 @@ -37,6 +37,11 @@ int main (int arc, char *argv[]) { dispatcher_t *dispatcher; database_t *database; + char *socket = NULL; + +#ifdef FCGI_SOCKET + socket = FCGI_SOCKET; +#endif /* FCGI_SOCKET */ database = database_create(DBFILE); if (database == NULL) @@ -45,8 +50,8 @@ int main (int arc, char *argv[]) return 1; } - dispatcher = dispatcher_create(SESSION_TIMEOUT, - (context_constructor_t)manager_create, database); + dispatcher = dispatcher_create(socket, SESSION_TIMEOUT, + (context_constructor_t)manager_create, database); dispatcher->add_controller(dispatcher, status_controller_create, NULL); dispatcher->add_controller(dispatcher, gateway_controller_create, NULL); dispatcher->add_controller(dispatcher, auth_controller_create, NULL); diff --git a/src/manager/manager.c b/src/manager/manager.c index b09d8d339..39c8d995a 100644 --- a/src/manager/manager.c +++ b/src/manager/manager.c @@ -82,12 +82,16 @@ static gateway_t* select_gateway(private_manager_t *this, int select_id) { if (select_id == id) { - if (port != 0) + if (port == 0) + { + this->gateway = gateway_create_unix(name); + } + else { host = host_create_from_string(address, port); if (host) { - this->gateway = gateway_create(name, host); + this->gateway = gateway_create_tcp(name, host); } } break; diff --git a/src/manager/manager.db b/src/manager/manager.db new file mode 100644 index 0000000000000000000000000000000000000000..01c1f27225d80bb88134df2415385ca310ebd75a GIT binary patch literal 12288 zcmeI2PmkO*6u|9dX0k2xfcO){YN^X=+Zky!)7jOuAhe>T+AfDZ0NqL)K*3EsGhvfB zINpCJGziYzxFMu|0zN};dWWGTl^J^`iJzZiKmQW@y=3Or zN87PvWXMHIC0R!egfY5C2tw!s*s5s-1gBDfEBRBiP0(WZPgUndb2zoSRayd;z<)yE zZ1WuSgWX>dd~LD>EPEEZDmY;@~@W+oS(b05I`zlYp=u(Qb$umt{R1m1EQ z7x4Nv4{1VfXYnEG?NiYcds#0WQ<&}D!!&^*z2TfUb#`ui_|o_NwX5CB@aZf6@3_As z_c82jvIH!F87I(mT#T{P#Oi=e{QnTS4`+PXc6gS+{1WhSV{sWPj}M>zJ8RwkmA94q zM`r%tMeg1Cg=0r<2|T|9KEL5k{SCQ)z|JO1z!I2Y0-n>*>xb)MlW~mye}vpe zGdyfNG)rL43A~0E7U8FTh#$(p0m6w-oQrE_{y&EIe{&AV4&M@(Cj!&=f6kAnfo~#w z6Mu&vI2$0^^vnddaLf07%q6{(u)TtbLhqa#A8%gY*(5vH-{0OOx`?#ZA)_fHk{!xb zlGCC%;3Cpu=eE=GmzMBXx}g!3?0_B>)1zB!Zdyv()z-L>B+j5+8byK?McJ&|@>f;RU7KA(-#-pN=ZPr??C`dcezgcMv zf~6e(hNkl&6H>4*br0~UN|oi`7$P|XbIU%e`Btrys-{Xub-oYRCgzzQi|#*&4SHfs zn2M`L8B$|Ysho<+#31KM93B}ebWe_hl*uuVOaY^LAdhlpq+NCtXCoJ-5=)#7s3iOfK<{wY3O4_B`IEdAg{b&cX8)w?jbtbR3Jpa-qJTak9wdJBFEIieM zqUxf_ULNs-%=oP_FC+~^6~!nwnewP~Z&l}6_nl8%XJ>*u&)?X-2AG(moC;a=|4Jd7{YxCWuE= Y#Am><45EZ%$!POwWT=!{P{CH`FIgRnk)GcG){&??wXZO3aA9%g1Zm0#jkL84G)+8PQAZLsbLblCiN6s{033hGC zKg%5>A#M?T!&{SFI0AofV}Vf z)^1h$c}3R@7gDw{+mS0Zi{&sgN@o-BlbF+mBj5=9TLOE4r30m99F2N29%WUS&OgO> zf* +
    + diff --git a/src/manager/templates/static/client-left.png b/src/manager/templates/static/client-left.png index ee4247d9d61491fbf5433599d82298d0202d7484..8a082ff9e53cff2a55d1b62daf436dabde60754e 100644 GIT binary patch literal 10228 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Ⓢ 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 LNkvXXu0mjfC4n`= delta 6701 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 D#F3#c diff --git a/src/manager/templates/static/gateway-left.png b/src/manager/templates/static/gateway-left.png index b5843640646f1a7b79d2e71bb2df33dc91c93cb8..9ddd3624dd00be98b3f3d605562017f769bcdb50 100644 GIT binary patch literal 12206 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 delta 2405 zcmV-r37Yn2_z^|C@n(aK|&nLA*HlDJt3q31%!wYQjvN< z{125X_2FI+mq&U;d+4c;1oU|>m3UDOAU%PIC{Ms4c^|{MSN=U;BI;i_vKGFL&k`O?3m1OLIsZ3+_};yHap=$?P)Z?$C|*1ad3nXp1lBoQi`swE`-BjynOi* zLWq5YkY5}*a^$y&z?O10Nb{a&wD#5 zDWxzB1C&ynJb4m@LIDpRJb)15H=3q>`}pzW-yc7IT-2)+hKvc;RimvO>Gz|w19Dl&<>@23HrU2mB(W6HjpFDZ;{k8^j z{rdIpLZR>@=e)V2qXRc?+(1i9OA*HBG&3-ALeX^{{r&w&r_)HM)39y(tHXy6|L57W zXA@ORGOGOrP1C$2gowAav|wap1c^if08l6tWFZzfITH-SFmU znt%0q@cp6y(A(RK&dyE%fMr>*EDJ%FGX;-CB4}@K_oSHitPasMt>_Y#mzS{}#27Tp}V`g{(xAP zg+ieaI3SIUjW7(um(>G3NCV)vmY0{2%YWroBq4PPq@ke!O-)UK$f_g|zodeCnTp+y zOi%#{m}ouCJ_STc0oh6`uG=gtlgVIdY03BdaPQ0b0Y&#~_Bp>SV=Qp{B7VtN3u$d_ z^|c5qR~OYKkVGPZP$&dSgBQyD+OP2yNH&|T+J5?%_L;kXXD}hem)Te<*^m~J&1UN}?QL#uMssts@AqMvCKeYL>o>~^nuR#; zq15uu$Jeh%zKTZwSkpkZlZX%vl#qs+0{n{; zdkhKk#?)G_ix2`-5tQ0f516)19g@rCJll5UjUzUGlTX4}ZuQrcUtD6=dJlv0F3p^|}}5K^^0c+Pq8z-LQK3+CtN>o2Ht&M`4D zfpj_z%d$WSK`a(SGMOx%)^)p+ojZ3{<-SbQglU?-PE22ex)1^{UcA8MslIE()Nek7Aggu`Jl#_;OZE8mvs>#7cs`tacc z03el0;lzm(NT<`7oScNN>lhmwD|R#n;p|AU4xtqK8ZoXWVu*B8`FtL;v$Mq@Dj%1` zIY*&TKqL|g3=qRG5UbVYcf&AXnkMGv=J5LU>tbbWnkG2sAcO?ssDIlE3(+*_RqSl^ z6-aw~J7#8Pkj-W>K0c0kJdQ%4fSH*Y06;t*Uj>A7UbSAXO1c&E%ggxq+11t@ka#?f z&dyH!{PWLPTwGkyf@2s4dU|?R)giWRV{UHF^Pnz&o}8b@=ubb@ZXMFy-HqnvX1sg% z4ogc*&~+V&L;{_ioqxsWX_YGo%k5okcCTfc zr?jHiX26u(640~-F2qdDFP(hJQb(x)|B~IvQZ+~{nf&`de)!=}3dkk~?@K%D>#qBK zy#cL9=|P*-^)9tzs4OJ7F-HNZV9;b>bw8`!8MI}8*G8`iVt-GVuaDL!_j0DRa&T?i z2Ist3UoggC+xDt!7-Pk2IOoN!SXquFI9;ErLpEU{ z?u^L^$nCNiW6(6MBoKE7r+{oS?R5hwH~XDI+YL>+EPp4EAb|O@VkLHgSrB;#V*U8Wx1_pJu7m)S3tHPs5@u>M&pc3dsPhC zq|5pe3#kN%vXBi1^-_hQ(?T{9L!4__Du!&*Le?q_E6K9fy2iP*uYhc@S}*6aN>%EW zxGX0S6@NpvIEFZjzDny5=kHY(Qe_J%2gD7#?ty5UhJE|?tqUWSN&x`E;V=jx=)k2r#qG1@{KuJkSNl8gbX+8Z9 X+0UAjm!pwz00000NkvXXu0mjfIOTjt diff --git a/src/manager/templates/static/gateway-right.png b/src/manager/templates/static/gateway-right.png index 4b370cbeeca7b65247a038a58170594ac66390a5..0be446827e8d9148198eb5de958331493ff62722 100644 GIT binary patch literal 12180 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 Q`sX3!rB$SAB#eUo57t>-N&o-= delta 2453 zcmV;G32OF~U$_&HJ%0~j000kR0jNKxX#fBK2XskIMF-aj4hk<4!V+JG000R^Nkl4rn_3F zJ{ZUhmFe#K>YVSKs#7(9f`WpAf`WpA!sbH@!%dt%efp1_^WRFojm20j_SYLXZp_p* zA!B1>-{o?-9|7pH@5MCD@j{_+;O5Pn#kwTq%$YO4W{mwCKw{_4o!GZ;9|$4foR`~F z`g=3+{{4I0zkh!pj^hBpzX1G67AevQ866$%AcXvnF}9<H(8u~k9>=Ypcr%s*1 z@bEA+O#`Jga5980QA%ML1_&YO>+1uh6cZB@ux)$ylP6Ei@$vDyUnL>u&Yk;2w+Z4h;ya>}Y0RY8f5sa}=WXXaQ!!V%hI)C=;*@L;cIZRJa1Hhq!2M;zreE9J0 z7fs0J%a?mfrP5D~v6jxxPF%Ti1@U;iOvcABGc<6MVi*Pn2M3YKWRS^Z;5g2=`}gnv z&*R6BCw&Vt{C+^ovd(eN6Y+Q)V`F1TCX)bwQmG^ovCzRCV3?+f^XJcF`}XbNoNKmi z|9{@ss4sx$ivd7?e?Pjqx&Q#SZNs*0gc* zO3~2J08I;SLL>0RkTHh&`FZJ4RZa6snub^`7APSE3Za4_^Wy1rI+X6Kxw#oVJw5eD zh;7>_l}e!_q_MFPrfEuBp|BzdsY^l{8XC~t+#HIiN)RFoO4O@l z>@p%jMM%g%>S^>TLKGAsn`y;$8)fBkIV>$LNxu*8x{M4`3|XVk{jikM(5;Kef>aA> zYipC5gjLIn>XMLTGKoke0z$$kWwO?5q!NpGg6ngX46m#!fIFn>Kg z9m?6>SS*IVzP|bqb?*q)G)>BAtQ2er3(4p6^%?fIw6vh5rA7LESeAvw#YMSM*0-_P zf}a;yNT9Nnn4h1oKT&6lVRCX3nM?+@Z9~&Ew6?Y)l}eR&>w4YE)~#E8 zxi8DIU`d|lkt*t(b3A?e6n|4wQ{|!p&N)7O_<-^8aV#t>pxz*rkhgE&BAd;YkFyvY z97HOWLNpo$r4-MfKbJPiS1AwiJux)a@YkCf%U3D!dH3!e0HC+G7e|g9K_-*I)YKFV z!@$JEM7g3d40}fc(~#DW8~Odh;u<@n#OM=ls#q*yZf>qDqT;wD#(x+}r4pK&nnIl{ zZmxy+T%}j72+lN3SeAusHj5W8UX&l!vMeyhplMntwz>ssAs;@v4Zlz%1gJ2=J32Zr zJ3EVfK96)djYJ}WQmKU5*;xQUB9T}{2xH8*TCOmhE@9mDIa5zGG$39fQ%@uk=<4di zt5>hExVX5Y1jjT@^ndmBtrAnmagfbs0~ht6f(yg%gOT_r9=4~a2Q4ivc=P5BmX?-a z7zUEbB)Yn~%I9fSO9-p^^o{y^nZ@4*^Bf*zfM{Xi@hicKeEB#obkt z)e7SjAs#DoPk)(+j?{I1g%yeE4%@a@k;WKX@tGjaO-QBU>bl88Y9T}Xj)=SC#ON2F zxvL)CZM;_GI1V`HD?anOs=Bt2TFNv<#|oFlIfvsotK5+o?QRXJW+6gDyksj4*`Ui3 zzhEG=Ky>z6Zp}@aXd?)5lc+SLMwjKa5J8A|?;ZVacYjpPLfo!Tmesj zoB-z>jg5`aG!4DIz1X&G+X^>y?AS3d#$cKzG=EJ)XJ;plA3qM@mtXb)fGu0LRJ@m< zttfCpG)=4NnsFGYXP4jywC@3f>C@3g=3jYUPo1J%+ Ti4Gvs00000NkvXXu0mjfA_<_M diff --git a/src/manager/templates/static/nat.png b/src/manager/templates/static/nat.png deleted file mode 100644 index f669b27c2234a5c75f49f16c8673106175b57d7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4751 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 diff --git a/src/manager/templates/static/pipe-bad.png b/src/manager/templates/static/pipe-bad.png index dede311de233a3115ada545f74d26cef7e82d5f8..dce7e836ece72ed3eaf746fc45ae4a0e037faf98 100644 GIT binary patch delta 4889 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 Bi!}fM diff --git a/src/manager/templates/static/pipe-good.png b/src/manager/templates/static/pipe-good.png index 9419fd3f4de9af0030a03ad87639153410ebf7aa..1e254711c715d69b09b57cbdfd2ada6e08604035 100644 GIT binary patch delta 270 zcmdm?e28g+Z9TUz0|U1(Bg3pY5)2Fs>?NMQuI$%2CE3jMkFL`>3luu<>EaktaqI1k z-JDF046YX^US(t1ZMcQUnECbw)@os%YXOdeg4fQ!%UTfHp>MIg<#$5bj3A&sRM7BW zN@x1&ojjIpivzb#Q_PrfGskM0;)@gYx89$ZmuzCooVDEX&^rOqNo delta 4517 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 diff --git a/src/manager/templates/static/pipe-thin-left.png b/src/manager/templates/static/pipe-thin-left.png new file mode 100644 index 0000000000000000000000000000000000000000..6ccc59a1756217d3d46687cbf0003d6179ed6e43 GIT binary patch literal 345 zcmeAS@N?(olHy`uVBq!ia0vp^DIm!7;wQCZem!IyKQatTLtlSvBZEhrGC#(d*Yt2T=#j?%_Tg}a;v;%YO?Q?>3=ESZSuSJSWw8! zstL~uJk0wnbGA#iAJmu}?{Rs_{PUORp4VKuFVdQ I&MBb@09}N8JOBUy literal 0 HcmV?d00001 diff --git a/src/manager/templates/static/pipe-thin-right.png b/src/manager/templates/static/pipe-thin-right.png new file mode 100644 index 0000000000000000000000000000000000000000..1f582d24b07c99b9cb5994a9ee322f046668674d GIT binary patch literal 357 zcmeAS@N?(olHy`uVBq!ia0vp^DImSH$g`&jHkq26n`qeVp=1 z>u~80iMHp@Qq8nAk4>BzIP+ro(c49m?{jba$UiGib-N_nKXK;8^kcc#UAN@=e1E;w zNcrXY=l6v#$?^#b8s>^~rykq2sZDW})~_=bwe|6AH}lO-_5A7kGV|hGzv+%;x7TVV z*A~uJTcx#Ye{;>dvQs@z(u_LK^eoic*Y?opQtjo6tLu-fdX=|pRn;UWGzT{Pf6l)5 WK*r%EkIuM*q&!{yT-G@yGywpZe22*Z literal 0 HcmV?d00001 diff --git a/src/manager/templates/static/pipe-thin.png b/src/manager/templates/static/pipe-thin.png new file mode 100644 index 0000000000000000000000000000000000000000..d25f1b625042470b5e30f0960cff6f36092c0878 GIT binary patch literal 256 zcmeAS@N?(olHy`uVBq!ia0vp^DImY S>TT-{lJa!*b6Mw<&;$S%2~1`H literal 0 HcmV?d00001 diff --git a/src/manager/templates/static/pipe.png b/src/manager/templates/static/pipe.png index 931e1709e19e47ed5f1b31b3af0755835a46dc57..1e254711c715d69b09b57cbdfd2ada6e08604035 100644 GIT binary patch delta 270 zcmdnPbckt!Z9TUz0|U1(Bg3pY5)2Fs>?NMQuI$%2CE3jMkFL`>3luu<>EaktaqI1k z-JDF046YX^US(t1ZMcQUnECbw)@os%YXOdeg4fQ!%UTfHp>MIg<#$5bj3A&sRM7BW zN@x1&ojjIpivzb#Q_PrfGskM0;)@gYx89$ZmuzCooVDEX&^rOqNo?NMQuI$%2`M3;Jw}1U24iq}+>EaktaqI1k zjl72p7+eB*TC^Hm4l=B9JjgM#apUD+7cXX67sW&#zAyJU{-;ELN@4_RLIMJ7=e+)< z=-l^p)0DY9j?HVl0u?hSWM5t}!Q{lplAwAZ=exx!AZPLG5D%#o88*>LY%`a6wkmjY z@-#Wtz2M!sbuE`?^PYMBqA`&YM-(KU$jf~#4P6P;@vk62@l{KXnU)G~(fexArYWb( ttWOJ+IMwW1&TDkuZZnc2fXsbwm}`H?rg1CDD1an9UHx3vIVCVQ0RWn#T^0ZU diff --git a/src/manager/templates/static/router.png b/src/manager/templates/static/router.png index 53114d9e63fe8481b89826aed5429b4a58a80f97..12a68fc90753a80db851e1a0a62d97802366d4e1 100644 GIT binary patch delta 3271 zcmb7H`#;l<_kT^~l9;(omP-i9ePpT37$et7M4MdS<~}0#A$M~rmvR{ulUwAPyDXPV z7Q!$Qky0)RDWi`+;QPz>`Q!QJJf4^Hd_2x8`6aQe%cK*60B}MO1QZlO0RZ5Pc9ZE3vpaoAcURGE)a;lGUen1iSnTtCA}jk0qBzpz zg)YegS6~!`g|Q_wTQ9HbcW%e(fr=HR-pW{qCAVTQ!W9q*mQXs7K7$gpD_e5WZTY>v zGe`2^<8Rc|ZS2rqr_bQCf0-z4AcDRGM$fZqS871q{|}@{WKp_%mxt=?0Sh*@-L@L` zn0>&{scp+B$O#hoXGlVHYlj{5XBi@Uqwe=7R4%5QBzVO)u-ZuU!<#rtW-rNlKD2ZPZM1`MW$Js@g zJpjjeUg+E?p62_V#H7?eIRM?Xi=UYr{BGdp6Iw+2{`bIdWTRch%qGd%^WKVuEbqHv zN=!MS%6)p?#@5$ss)4D9@<@oG3}@WMPVN|C6;j7!#_?D~e#Ne}04INvp36-;;Okkc z=3Fq2OB$(&KIe{ZIsx3i3>s>9tgx!jl+bNaxK?$fAMLRS7ZYdUafuU$#%jA+Nm!et>By_&<9 ze3%xoL4IemD+jgPdcS&dNk0b#!tY^7NZVYMAoNQ_+SIo7xX{1i97q|~BQsAv+a!Q) zi?e*kW@S9%CV+a%B`sJ^hz*k9mJ=(zMLr$}6UK(0?&4xQ!PJg;mq$4JSmB(y`F91t zdpznjue7WyGem*4J*P$wQ(#OfWBXdPs1JlduSOmbOg$xz^zhomg=u8_Pu`n2NT zVNIX8X^4eYUrKu2-mp!Pqv~-xy^FLqbuLK3H1#8roS|yGnyv+3w$%iGP^6Z4U2oQ( z*Tt*@s*mt1aT>I_Z648I#fLpAb$4)VT&8{Y?`UQ5SZa=D?cKwUV*s5CIkd`|lw z_#5*BdAZ3#$Y5MjIRPVkKk2@Su5ML)QCW`0P{WxoZ~4Xd1)l#ZBPA=t4tIwi)K=?5 zEfr&*C}3oHE2;|2pEep)6MjB$I((C6aHC5guGR0eW08%0-f6dSeDDLm`n*vw(RD#u z;{;JNyu@w8!O?xw@xSfpCfmwumwSnm^r?xI{QS0|_m_ULtS}3yv}o<~r~-4Zswy5F zW54#s4-Umq^}E?@tt2(xlM|~(o8%VX*dVm!*|iOg#1%#NpWI>VZC4FUe7u@3UfH`P z0VI@Ht_5?AHa~CbF&Tw(6lv;YFl!0Zx5&=nf02uC9u@mJ4gFVE&xXyW;{C(j>)8(Q)V8mCbg zDKpyK9r~sh5JU_Shx{OP{wP>E6kEiAW-Z zOd|UL$E!vZk`RjPT5c)czvUn~Avc$^l|)TPpfmc4v5V1vD5V?4KUNwwaW zy_XAX3VAW6D-k{2o~_IvcR&`N7cWwQ5#|2M+?|}%f)~zxNxF89Bj!`@U07h-8D5#W zT}^yt-#q?WWlgF5`Bx3+JqsW;?ZG0E?P<|nZ|vns-@YqH7z!GVX2boW&9+T+(015! z;gE?##(EQufi4|Ay7>f@SiF@Gk8n-RQr7T*5^F0xeE`fd78owSn=PNY`0Z)WtTwkF z=J|oQSrZd5 z;1($)mAs#se z9^dd$_Xw$=bP&ibTMuiuB_p5 zq@r=X{n7r|asu&5O(@f*a9HH+vId_dsTie!C_1_Bk~!`(qV}=HyrL@3t2w^8nHoY2 zMxsoq&Gy-zaEFSC)zGDTyg~_?InP533_8=n7mr+X5VptR;=y-2K;4~@s%NE(CG(4m zp*mu-8MhLH*C;Vnfm1E9DCx|OJ9@^|TA`2nL`CvkhOrFuhaRbB78(9mTNg+_E@nL1 zUyJ@xOG(J|rxF}-uA0xdN(>Zz8THWhk3%V@az;79-zW9-Qh#Yk>P4@9>g$Uoi#ns_ z$NgOn*dfaP+mgT0Ovn7THS)*x#&IvLr9cEZ{l=%80)Ha?+{3~MVI$!ZJt1q9Ska2J zXUo?#|5NEKcvGg)UP}x92&I#(x1+o-cxUq5en6q2~4fczkWRP2gco6+@$pd5|~ zEb7oDk4HVc`k?!;l`vzZU3lD;H{-KZ$@@}#oxhjLVaZ>XR~@0?A>{x9U%RZKPE2mC z-RFymrP=Y_a<@Wxcz6tg%q%QCq~g;8+u~g6bsGGCSv(r`Wbcs^6TeF8Z5Xow9PYl= zx?VIhCp939j{U3?0OKUrC#8Z>n25RA`CJ-E_ISwZPl@B3rc-D3l;>Vyl3&yk!3 zFP);B^qo;xYNhaLu|Lh;$!YLtJ}@{)T%d7u?yq;eUhdQq9V7;P8)gHB{AA zsZ0*aagrz#zZdtySNy4E37^Ci5!PCJVyMCDyokEGdhG3sq?4%5u7+tJk*caQCGTbK zVc6Y&T(1^hLM#U&TJ7wsTWt1EHL2E2d`zhUv!xr!JO{^*be%1`@ODe`y0s8AQ2@O7 zZ%0Pm{6ie>)ablSIQySLsH>F_M-H~4>J+%g(PM}Rn{~n)cH09Wkh_p3XSwr@>zVxN z`xt}UtA6~_Bxfy&yf9~SY*4p%JQ%fELhac5MbTSS8qAD~SqS+9x^lQ}6S@D>KSrtZ z*@0glc_W`+y6m$-WRiGvV~Zp7hl;gNOYzzPJY%iERX+X^eKpkbA7||f=>%IW)~2_= z?+#L5U(wewd2&uS7=<~Ftj7CqU9V2KUd=%dlfuwR2q82#%HZECsQ16A{$GM*^@rtR ztTEFMaQi%tS87Pb1os<9bZWmF(WjN49!|v~I?KBfGsPuQNGTJa$cb>C_R3d87`)IN zUZ^~u3IBaR%Mvt;!>hm$M3+-kEO$XUdNLXJ`@%c5G zT)mD;O8t@_+k-PDHo4(Cl^r)Oh0cf5EXbL6k15f1lV#+ZD+f?QeEm39bfpdzO7C;^|aO3AKC%b z%hK#Xanx?%parFU^MjklfzXQAs&thyEjYoZ&hCNV9BWh6zvp0b!@GI)Bv7)JpSY(J z^_TYe=g-QzZ|!0FTz35`th>Y3$j0gOsw#8c&CfSSQH|4kye}y_8BtTH&j1n&G$k1u qBNvo0wm?m0Oi*^#{|zXCCHf9`LQX1c$L8o!0Zh;qhP9|$asLArI1x+$ delta 5044 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 diff --git a/src/manager/templates/status/ikesalist.cs b/src/manager/templates/status/ikesalist.cs index d329d070a..05c3268b6 100644 --- a/src/manager/templates/status/ikesalist.cs +++ b/src/manager/templates/status/ikesalist.cs @@ -2,7 +2,7 @@

    - []: + IKE # []: <->

    @@ -10,38 +10,16 @@
    - - - - - - - - - - - - + + - + + + - - - - - + + + + + + + + +
    - + + -
    - - - : - - -
    - -

    - -
    @@ -49,9 +27,13 @@ +


    + +
    - + @@ -65,11 +47,15 @@ - + +


    + +
    @@ -77,26 +63,37 @@ - -

    - +
    +

    IPsec # []:

    - - : - - - : - -
    + +

    + +
    +


    +
    + +


    +
    + +

    + +