diff options
Diffstat (limited to 'src/charon/control/interfaces/xml_interface.c')
-rw-r--r-- | src/charon/control/interfaces/xml_interface.c | 200 |
1 files changed, 199 insertions, 1 deletions
diff --git a/src/charon/control/interfaces/xml_interface.c b/src/charon/control/interfaces/xml_interface.c index e570f2543..992377436 100644 --- a/src/charon/control/interfaces/xml_interface.c +++ b/src/charon/control/interfaces/xml_interface.c @@ -24,8 +24,22 @@ #include "xml_interface.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <libxml/xmlreader.h> +#include <libxml/xmlwriter.h> + #include <library.h> #include <daemon.h> +#include <processing/jobs/callback_job.h> + +static struct sockaddr_un socket_addr = { AF_UNIX, "/var/run/charon.xml"}; typedef struct private_xml_interface_t private_xml_interface_t; @@ -39,14 +53,166 @@ struct private_xml_interface_t { * Public part of xml_t object. */ xml_interface_t public; + + /** + * XML unix socket fd + */ + int socket; + + /** + * job accepting stroke messages + */ + callback_job_t *job; }; +/** + * process a getRequest message + */ +static void process_get(xmlTextReaderPtr reader, xmlTextWriterPtr writer) +{ + if (/* <GetResponse> */ + xmlTextWriterStartElement(writer, "GetResponse") < 0 || + /* <Status Code="200"><Message/></Status> */ + xmlTextWriterStartElement(writer, "Status") < 0 || + xmlTextWriterWriteAttribute(writer, "Code", "200") < 0 || + xmlTextWriterStartElement(writer, "Message") < 0 || + xmlTextWriterEndElement(writer) < 0 || + xmlTextWriterEndElement(writer) < 0 || + /* <ConnectionList/> */ + xmlTextWriterStartElement(writer, "ConnectionList") < 0 || + xmlTextWriterEndElement(writer) < 0 || + /* </GetResponse> */ + xmlTextWriterEndElement(writer) < 0) + { + DBG1(DBG_CFG, "error writing XML document (GetResponse)"); + } +} + +/** + * read from a opened connection and process it + */ +static job_requeue_t process(int *fdp) +{ + int oldstate, fd = *fdp; + char buffer[4096]; + size_t len; + xmlTextReaderPtr reader; + xmlTextWriterPtr writer; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + len = read(fd, buffer, sizeof(buffer)); + pthread_setcancelstate(oldstate, NULL); + if (len <= 0) + { + close(fd); + DBG2(DBG_CFG, "SMP XML connection closed"); + return JOB_REQUEUE_NONE; + } + + reader = xmlReaderForMemory(buffer, len, NULL, NULL, 0); + if (reader == NULL) + { + DBG1(DBG_CFG, "opening SMP XML reader failed"); + return JOB_REQUEUE_FAIR;; + } + + writer = xmlNewTextWriter(xmlOutputBufferCreateFd(fd, NULL)); + if (writer == NULL) + { + xmlFreeTextReader(reader); + DBG1(DBG_CFG, "opening SMP XML writer failed"); + return JOB_REQUEUE_FAIR;; + } + + /* create the standard message parts */ + if (xmlTextWriterStartDocument(writer, NULL, NULL, NULL) < 0 || + /* <SMPMessage xmlns="http://www.strongswan.org/smp/1.0"> */ + xmlTextWriterStartElement(writer, "SMPMessage") < 0 || + xmlTextWriterWriteAttribute(writer, "xmlns", + "http://www.strongswan.org/smp/1.0") < 0 || + /* <Body> */ + xmlTextWriterStartElement(writer, "Body") < 0) + { + xmlFreeTextReader(reader); + xmlFreeTextWriter(writer); + DBG1(DBG_CFG, "creating SMP XML message failed"); + return JOB_REQUEUE_FAIR;; + } + + while (TRUE) + { + switch (xmlTextReaderRead(reader)) + { + case 1: + { + if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) + { + if (streq(xmlTextReaderConstName(reader), "GetRequest")) + { + process_get(reader, writer); + break; + } + } + continue; + } + case 0: + /* end of XML */ + break; + default: + DBG1(DBG_CFG, "parsing SMP XML message failed"); + break; + } + xmlFreeTextReader(reader); + break; + } + /* write </Body></SMPMessage> and close document */ + if (xmlTextWriterEndDocument(writer) < 0) + { + DBG1(DBG_CFG, "completing SMP XML message failed"); + } + xmlFreeTextWriter(writer); + + /* write a newline to indicate end of xml */ + write(fd, "\n", 1); + return JOB_REQUEUE_FAIR;; +} + +/** + * accept from XML socket and create jobs to process connections + */ +static job_requeue_t dispatch(private_xml_interface_t *this) +{ + struct sockaddr_un strokeaddr; + int oldstate, fd, *fdp, strokeaddrlen = sizeof(strokeaddr); + callback_job_t *job; + + /* wait for connections, but allow thread to terminate */ + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + fd = accept(this->socket, (struct sockaddr *)&strokeaddr, &strokeaddrlen); + pthread_setcancelstate(oldstate, NULL); + + if (fd < 0) + { + DBG1(DBG_CFG, "accepting SMP XML socket failed: %s", strerror(errno)); + sleep(1); + return JOB_REQUEUE_FAIR;; + } + + fdp = malloc_thing(int); + *fdp = fd; + job = callback_job_create((callback_job_cb_t)process, fdp, free, this->job); + charon->processor->queue_job(charon->processor, (job_t*)job); + + return JOB_REQUEUE_DIRECT; +} /** * Implementation of itnerface_t.destroy. */ static void destroy(private_xml_interface_t *this) { + this->job->cancel(this->job); + unlink(socket_addr.sun_path); free(this); } @@ -56,8 +222,40 @@ 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; - this->public.interface.destroy = (void (*)(xml_interface_t*))destroy; + this->public.interface.destroy = (void (*)(interface_t*))destroy; + + /* set up unix socket */ + this->socket = socket(AF_UNIX, SOCK_STREAM, 0); + if (this->socket == -1) + { + DBG1(DBG_CFG, "could not create XML socket"); + free(this); + return NULL; + } + + old = umask(~S_IRWXU); + if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0) + { + DBG1(DBG_CFG, "could not bind XML socket: %s", strerror(errno)); + close(this->socket); + free(this); + return NULL; + } + umask(old); + + if (listen(this->socket, 0) < 0) + { + DBG1(DBG_CFG, "could not listen on XML socket: %s", strerror(errno)); + close(this->socket); + free(this); + return NULL; + } + + this->job = callback_job_create((callback_job_cb_t)dispatch, this, NULL, NULL); + charon->processor->queue_job(charon->processor, (job_t*)this->job); return &this->public.interface; } + |