summaryrefslogtreecommitdiff
path: root/examples/xkms-server.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/xkms-server.c')
-rw-r--r--examples/xkms-server.c839
1 files changed, 839 insertions, 0 deletions
diff --git a/examples/xkms-server.c b/examples/xkms-server.c
new file mode 100644
index 00000000..188d5c73
--- /dev/null
+++ b/examples/xkms-server.c
@@ -0,0 +1,839 @@
+/**
+ * XML Security Library example: simple XKMS server
+ *
+ * Starts XKMS server on specified port.
+ *
+ * Usage:
+ * ./xkms-server [--port <port>] [--format plain|soap-1.1|soap-1.2] <keys-file>
+ *
+ * Example:
+ * ./xkms-server --port 8080 --format soap-1.1 keys.xml
+ *
+ * This is free software; see Copyright file in the source
+ * distribution for preciese wording.
+ *
+ * Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com>
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#ifdef XMLSEC_NO_XKMS
+
+int main(int argc, char** argv) {
+ fprintf(stderr, "ERROR: XKMS is disabled.\n");
+ return 1;
+}
+
+#else /* XMLSEC_NO_XKMS */
+
+#include <libxml/tree.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+
+#ifndef XMLSEC_NO_XSLT
+#include <libxslt/xslt.h>
+#include <libxslt/security.h>
+#endif /* XMLSEC_NO_XSLT */
+
+#include <xmlsec/xmlsec.h>
+#include <xmlsec/xmltree.h>
+#include <xmlsec/buffer.h>
+#include <xmlsec/xkms.h>
+#include <xmlsec/crypto.h>
+
+#ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
+#include <xmlsec/app.h>
+#endif /* XMLSEC_CRYPTO_DYNAMIC_LOADING */
+
+#ifdef UNIX_SOCKETS
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <signal.h>
+#else /* UNIX_SOCKETS */
+#ifdef WIN32_SOCKETS
+#include <windows.h>
+#include <winsock.h>
+#else /* WIN32_SOCKETS */
+#error "Your operating system is not supported"
+#endif /* WIN32_SOCKETS */
+#endif /* UNIX_SOCKETS */
+
+#define DEFAULT_PORT 1234
+#define PENDING_QUEUE_SIZE 100
+
+#define LOG_LEVEL_SILENT 0
+#define LOG_LEVEL_INFO 1
+#define LOG_LEVEL_DATA 2
+#define LOG_LEVEL_DEBUG 3
+
+#ifdef UNIX_SOCKETS
+static int sockfd = -1;
+#endif /* UNIX_SOCKETS */
+
+#ifdef WIN32_SOCKETS
+static SOCKET sockfd = -1;
+#endif /* WIN32_SOCKETS */
+
+static int finished = 0;
+static int log_level = LOG_LEVEL_INFO;
+
+static int init_server(unsigned short port);
+static void stop_server();
+static void int_signal_handler(int sig_num);
+static const xmlChar* my_strnstr(const xmlChar* str, xmlSecSize strLen, const xmlChar* tmpl, xmlSecSize tmplLen);
+
+static int handle_connection(int fd, xmlSecXkmsServerCtxPtr xkmsCtx, xmlSecXkmsServerFormat format);
+static int read_request(int fd, const char* in_ip, xmlSecBufferPtr buffer);
+static int send_response(int fd, const char* in_ip, int resp_code,
+ const char* body, int body_size);
+
+static char usage[] = "[--port <port>] [--format plain|soap-1.1|soap-1.2] <keys-file>";
+static char http_header[] =
+ "HTTP/1.0 %d\n"
+ "Server: XML Security Library: Simple XKMS Server/1.0\n"
+ "Content-length: %d\n"
+ "\n";
+static char http_503[] =
+ "Error 503 - Service Unavailable\n";
+
+int main(int argc, char** argv) {
+ int argpos;
+ unsigned short port = DEFAULT_PORT;
+#ifndef XMLSEC_NO_XSLT
+ xsltSecurityPrefsPtr xsltSecPrefs = NULL;
+#endif /* XMLSEC_NO_XSLT */
+ xmlSecKeysMngrPtr mngr = NULL;
+ xmlSecXkmsServerCtxPtr xkmsCtx = NULL;
+ xmlSecXkmsServerFormat format = xmlSecXkmsServerFormatPlain;
+ int ret;
+
+ fprintf(stdout, "Log: server is starting up\n");
+
+ /* Init libxml and libxslt libraries */
+ xmlInitParser();
+ LIBXML_TEST_VERSION
+ xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
+ xmlSubstituteEntitiesDefault(1);
+#ifndef XMLSEC_NO_XSLT
+ xmlIndentTreeOutput = 1;
+#endif /* XMLSEC_NO_XSLT */
+
+ /* Init libxslt */
+#ifndef XMLSEC_NO_XSLT
+ /* disable everything */
+ xsltSecPrefs = xsltNewSecurityPrefs();
+ xsltSetSecurityPrefs(xsltSecPrefs, XSLT_SECPREF_READ_FILE, xsltSecurityForbid);
+ xsltSetSecurityPrefs(xsltSecPrefs, XSLT_SECPREF_WRITE_FILE, xsltSecurityForbid);
+ xsltSetSecurityPrefs(xsltSecPrefs, XSLT_SECPREF_CREATE_DIRECTORY, xsltSecurityForbid);
+ xsltSetSecurityPrefs(xsltSecPrefs, XSLT_SECPREF_READ_NETWORK, xsltSecurityForbid);
+ xsltSetSecurityPrefs(xsltSecPrefs, XSLT_SECPREF_WRITE_NETWORK, xsltSecurityForbid);
+ xsltSetDefaultSecurityPrefs(xsltSecPrefs);
+#endif /* XMLSEC_NO_XSLT */
+
+ /* Init xmlsec library */
+ if(xmlSecInit() < 0) {
+ fprintf(stderr, "Error %d: xmlsec initialization failed.\n", errno);
+ return(-1);
+ }
+
+ /* Check loaded library version */
+ if(xmlSecCheckVersion() != 1) {
+ fprintf(stderr, "Error %d: loaded xmlsec library version is not compatible.\n", errno);
+ return(-1);
+ }
+
+ /* Load default crypto engine if we are supporting dynamic
+ * loading for xmlsec-crypto libraries. Use the crypto library
+ * name ("openssl", "nss", etc.) to load corresponding
+ * xmlsec-crypto library.
+ */
+#ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
+ if(xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
+ fprintf(stderr, "Error %d: unable to load default xmlsec-crypto library. Make sure\n"
+ "that you have it installed and check shared libraries path\n"
+ "(LD_LIBRARY_PATH) envornment variable.\n", errno);
+ return(-1);
+ }
+#endif /* XMLSEC_CRYPTO_DYNAMIC_LOADING */
+
+ /* Init crypto library */
+ if(xmlSecCryptoAppInit(NULL) < 0) {
+ fprintf(stderr, "Error %d: crypto initialization failed.\n", errno);
+ return(-1);
+ }
+
+ /* Init xmlsec-crypto library */
+ if(xmlSecCryptoInit() < 0) {
+ fprintf(stderr, "Error %d: xmlsec-crypto initialization failed.\n", errno);
+ return(-1);
+ }
+
+ /* Create and initialize keys manager */
+ mngr = xmlSecKeysMngrCreate();
+ if(mngr == NULL) {
+ fprintf(stderr, "Error %d: failed to create keys manager.\n", errno);
+ goto done;
+ }
+ if(xmlSecCryptoAppDefaultKeysMngrInit(mngr) < 0) {
+ fprintf(stderr, "Error %d: failed to initialize keys manager.\n", errno);
+ goto done;
+ }
+
+ /* Create XKMS server context */
+ xkmsCtx = xmlSecXkmsServerCtxCreate(mngr);
+ if(xkmsCtx == NULL) {
+ fprintf(stderr, "Error %d: XKMS server context initialization failed\n", errno);
+ goto done;
+ }
+
+ /* Process input parameters */
+ for(argpos = 1; (argpos < argc) && (argv[argpos][0] == '-'); argpos++) {
+ if((strcmp(argv[argpos], "--port") == 0) || (strcmp(argv[argpos], "-p") == 0)) {
+ argpos++;
+ port = atoi(argv[argpos]);
+ if(port == 0) {
+ fprintf(stderr, "Error %d: invalid port number \"%s\".\nUsage: %s %s\n", errno, argv[argpos], argv[0], usage);
+ goto done;
+ }
+ } else if((strcmp(argv[argpos], "--format") == 0) || (strcmp(argv[argpos], "-f") == 0)) {
+ argpos++;
+ format = xmlSecXkmsServerFormatFromString(BAD_CAST argv[argpos]);
+ if(format == xmlSecXkmsServerFormatUnknown) {
+ fprintf(stderr, "Error %d: invalid format \"%s\".\nUsage: %s %s\n", errno, argv[argpos], argv[0], usage);
+ goto done;
+ }
+ } else if((strcmp(argv[argpos], "--log-level") == 0) || (strcmp(argv[argpos], "-l") == 0)) {
+ argpos++;
+ log_level = atoi(argv[argpos]);
+ } else {
+ fprintf(stderr, "Error %d: unknown parameter \"%s\".\nUsage: %s %s\n", errno, argv[argpos], argv[0], usage);
+ goto done;
+ }
+ }
+ if(argpos >= argc) {
+ fprintf(stderr, "Error %d: keys file is not specified.\nUsage: %s %s\n", errno, argv[0], usage);
+ goto done;
+ }
+
+ /* Load keys */
+ for(; argpos < argc; argpos++) {
+ if(xmlSecCryptoAppDefaultKeysMngrLoad(mngr, argv[argpos]) < 0) {
+ fprintf(stderr, "Error %d: failed to load xml keys file \"%s\".\nUsage: %s %s\n", errno, argv[argpos], argv[0], usage);
+ goto done;
+ }
+ if(log_level >= LOG_LEVEL_INFO) {
+ fprintf(stdout, "Log: loaded keys from \"%s\"\n", argv[argpos]);
+ }
+ }
+
+ /* Startup TCP server */
+ if(init_server(port) < 0) {
+ fprintf(stderr, "Error, errno: server initialization failed\n", errno);
+ goto done;
+ }
+ assert(sockfd != -1);
+
+ /* main loop: accept connections and process requests */
+ while(finished == 0) {
+ fd_set fds;
+ struct timeval timeout;
+
+ /* Set up polling using select() */
+ FD_ZERO(&fds);
+ FD_SET(sockfd, &fds);
+ memset(&timeout, 0, sizeof(timeout));
+ timeout.tv_sec = 1;
+ ret = select(sockfd + 1, &fds, NULL, NULL, &timeout);
+ if((ret <= 0) || !FD_ISSET(sockfd, &fds)) {
+ /* error, timed out or not our socket: try again */
+ continue;
+ }
+
+ if(handle_connection(sockfd, xkmsCtx, format) < 0) {
+ fprintf(stderr, "Error %d: unable to accept incomming connection\n");
+ goto done;
+ }
+ }
+
+done:
+ if(log_level >= LOG_LEVEL_INFO) {
+ fprintf(stdout, "Log: server is shutting down\n");
+ }
+
+ /* Shutdown TCP server */
+ stop_server();
+
+ /* Destroy xkms server context */
+ if(xkmsCtx != NULL) {
+ xmlSecXkmsServerCtxDestroy(xkmsCtx);
+ xkmsCtx = NULL;
+ }
+
+ /* Destroy keys manager */
+ if(mngr != NULL) {
+ xmlSecKeysMngrDestroy(mngr);
+ mngr = NULL;
+ }
+
+ /* Shutdown xmlsec-crypto library */
+ xmlSecCryptoShutdown();
+
+ /* Shutdown crypto library */
+ xmlSecCryptoAppShutdown();
+
+ /* Shutdown xmlsec library */
+ xmlSecShutdown();
+
+ /* Shutdown libxslt/libxml */
+#ifndef XMLSEC_NO_XSLT
+ xsltFreeSecurityPrefs(xsltSecPrefs);
+ xsltCleanupGlobals();
+#endif /* XMLSEC_NO_XSLT */
+ xmlCleanupParser();
+
+ fprintf(stdout, "Log: server is down, bye!\n");
+ return(0);
+}
+
+/**
+ * init_server:
+ * @port: the server'xmlSecBufferGetData(buffer) TCP port number.
+ *
+ * Starts up a TCP server listening on given @port.
+ *
+ * Returns 0 on success or a negative value if an error occurs.
+ */
+static int
+init_server(unsigned short port) {
+#ifdef WIN32_SOCKETS
+ WSADATA data;
+#endif /* WIN32_SOCKETS */
+ struct sockaddr_in saddr;
+ int flags;
+
+#ifdef WIN32_SOCKETS
+ if(WSAStartup(MAKEWORD(1,1), &data)) {
+ fprintf(stderr, "Error %d: WSAStartup() failed\n", errno);
+ return(-1);
+ }
+#endif /* WIN32_SOCKETS */
+
+ /* create socket */
+ sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+#ifdef UNIX_SOCKETS
+ if(sockfd == -1) {
+#endif /* UNIX_SOCKETS */
+
+#ifdef WIN32_SOCKETS
+ if(sockfd == INVALID_SOCKET) {
+#endif /* WIN32_SOCKETS */
+
+ fprintf(stderr, "Error %d: socket() failed\n", errno);
+ return(-1);
+ }
+
+ /* enable reuse of address */
+ flags = 1;
+ if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&flags, sizeof(flags)) != 0) {
+ fprintf(stderr, "Error %d: setsockopt(SO_REUSEADDR) failed\n", errno);
+ return(-1);
+ }
+
+#ifdef UNIX_SOCKETS
+ /* set non-blocking */
+ flags = fcntl(sockfd, F_GETFL);
+ if(flags < 0) {
+ fprintf(stderr, "Error %d: fcntl(F_GETFL) failed\n", errno);
+ return(-1);
+ }
+ if(fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) {
+ fprintf(stderr, "Error %d: fcntl(F_SETFL) failed\n", errno);
+ return(-1);
+ }
+#endif /* UNIX_SOCKETS */
+
+ /* preset socket structure for socket binding */
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(port);
+ saddr.sin_addr.s_addr = INADDR_ANY;
+ if(bind(sockfd, (struct sockaddr *)&saddr, sizeof(struct sockaddr)) != 0) {
+ fprintf(stderr, "Error %d: bind() failed\n", errno);
+ return(-1);
+ }
+
+ /* prepare for listening */
+ if(listen(sockfd, PENDING_QUEUE_SIZE) != 0) {
+ fprintf(stderr, "Error %d: listen() failed\n", errno);
+ return(-1);
+ }
+
+#ifdef UNIX_SOCKETS
+ /* setup SIGINT handler that will stop the server */
+ signal(SIGINT, int_signal_handler);
+#endif /* UNIX_SOCKETS */
+
+ if(log_level >= LOG_LEVEL_INFO) {
+ fprintf(stdout, "Log: server is ready and listening on port %d\n", port);
+ }
+ return(0);
+}
+
+/**
+ * stop_server:
+ *
+ * Shuts down TCP server.
+ */
+static void
+stop_server() {
+#ifdef UNIX_SOCKETS
+ if(sockfd != -1) {
+ shutdown(sockfd, SHUT_RDWR);
+ close(sockfd);
+ sockfd = -1;
+ }
+#endif /* UNIX_SOCKETS */
+
+#ifdef WIN32_SOCKETS
+ if(sockfd != -1) {
+ close(sockfd);
+ sockfd = -1;
+ }
+#endif /* WIN32_SOCKETS */
+ if(log_level >= LOG_LEVEL_INFO) {
+ fprintf(stdout, "Log: server is shutted down\n");
+ }
+}
+
+/**
+ * int_signal_handler:
+ * @sig_num: the signal number.
+ *
+ * Unix's Ctrl-C signal handler that stops the server.
+ */
+static void
+int_signal_handler(int sig_num) {
+ if(log_level >= LOG_LEVEL_INFO) {
+ fprintf(stdout, "Log: server is asked to shutdown\n");
+ }
+ finished = 1;
+}
+
+/**
+ * handle_connection:
+ * @sockfd: the server's socket.
+ * @xkmsCtx: the template XKMS server context.
+ * @format: the expected format of XKMS requests.
+ *
+ * Establishs a connection, forks a child process (onUnix), reads the request,
+ * processes it and writes back the response.
+ *
+ * Returns 0 on success or a negative value if an error occurs.
+ */
+static int
+handle_connection(int sockfd, xmlSecXkmsServerCtxPtr xkmsCtx, xmlSecXkmsServerFormat format) {
+#ifdef UNIX_SOCKETS
+ int fd = -1;
+#endif /* UNIX_SOCKETS */
+
+#ifdef WIN32_SOCKETS
+ SOCKET fd = -1;
+#endif /* WIN32_SOCKETS */
+
+ int in_child_process = 0;
+ struct sockaddr_in saddr;
+ int saddr_size;
+ xmlSecXkmsServerCtxPtr xkmsCtx2 = NULL;
+ xmlSecBufferPtr buffer = NULL;
+ xmlDocPtr inDoc = NULL;
+ xmlDocPtr outDoc = NULL;
+ xmlNodePtr result = NULL;
+ xmlOutputBufferPtr output = NULL;
+ int resp_ready = 0;
+ int ret;
+
+ assert(sockfd != -1);
+ assert(xkmsCtx != NULL);
+
+ /* Get the socket connection */
+ saddr_size = sizeof(struct sockaddr_in);
+ fd = accept(sockfd, (struct sockaddr *)&saddr, &saddr_size);
+
+#ifdef UNIX_SOCKETS
+ if(sockfd == -1) {
+#endif /* UNIX_SOCKETS */
+
+#ifdef WIN32_SOCKETS
+ if(sockfd == INVALID_SOCKET) {
+#endif /* WIN32_SOCKETS */
+
+ fprintf(stderr, "Error %d: accept() failed\n", errno);
+ return(-1);
+ }
+ if(log_level >= LOG_LEVEL_INFO) {
+ fprintf(stdout, "Log [%s]: got connection\n", inet_ntoa(saddr.sin_addr));
+ }
+
+ /* Create a copy of XKMS server context */
+ xkmsCtx2 = xmlSecXkmsServerCtxCreate(NULL);
+ if(xkmsCtx2 == NULL) {
+ fprintf(stderr, "Error %d [%s]: a copy of XKMS server context initialization failed\n", errno, inet_ntoa(saddr.sin_addr));
+ goto done;
+ }
+ if(xmlSecXkmsServerCtxCopyUserPref(xkmsCtx2, xkmsCtx) < 0) {
+ fprintf(stderr, "Error %d [%s]: XKMS server context copy failed\n", errno, inet_ntoa(saddr.sin_addr));
+ goto done;
+ }
+
+#ifdef UNIX_SOCKETS
+ /* on Unix we use child process to process requests */
+ if(fork()) {
+ /* parent process */
+ return(0);
+ }
+
+ /* child process */
+ in_child_process = 1;
+ close(sockfd); /* we don't need listening socket */
+#endif /* UNIX_SOCKETS */
+
+ buffer = xmlSecBufferCreate(0);
+ if(buffer == NULL) {
+ fprintf(stderr, "Error %d [%s]: xmlSecBufferCreate() failed\n", errno, inet_ntoa(saddr.sin_addr));
+ goto done;
+ }
+
+ /* read input request */
+ ret = read_request(fd, inet_ntoa(saddr.sin_addr), buffer);
+ if(ret < 0) {
+ fprintf(stderr, "Error %d [%s]: read_request() failed\n", errno, inet_ntoa(saddr.sin_addr));
+ goto done;
+ }
+
+ /* parse request */
+ inDoc = xmlParseMemory(xmlSecBufferGetData(buffer), xmlSecBufferGetSize(buffer) );
+ if((inDoc == NULL) || (xmlDocGetRootElement(inDoc) == NULL)) {
+ fprintf(stderr, "Error %d [%s]: failed to parse request\n", errno, inet_ntoa(saddr.sin_addr));
+ goto done;
+ }
+ xmlSecBufferEmpty(buffer);
+
+ /* prepare result document */
+ outDoc = xmlNewDoc(BAD_CAST "1.0");
+ if(outDoc == NULL) {
+ fprintf(stderr, "Error %d [%s]: failed to create result doc\n", errno, inet_ntoa(saddr.sin_addr));
+ goto done;
+ }
+
+ result = xmlSecXkmsServerCtxProcess(xkmsCtx2, xmlDocGetRootElement(inDoc), format, outDoc);
+ if(result == NULL) {
+ fprintf(stderr, "Error %d [%s]: failed to process xkms server request\n", errno, inet_ntoa(saddr.sin_addr));
+ goto done;
+ }
+
+ /* apppend returned result node to the output document */
+ xmlDocSetRootElement(outDoc, result);
+
+ /* create LibXML2 output buffer */
+ output = xmlSecBufferCreateOutputBuffer(buffer);
+ if(output == NULL) {
+ fprintf(stderr, "Error %d [%s]: xmlSecBufferCreateOutputBuffer() failed\n", errno, inet_ntoa(saddr.sin_addr));
+ goto done;
+ }
+ xmlNodeDumpOutput(output, result->doc, result, 0, 0, NULL);
+
+ xmlOutputBufferClose(output); output = NULL;
+ resp_ready = 1;
+done:
+ /* send back response */
+ if((resp_ready == 1) && (xmlSecBufferGetData(buffer) != NULL)) {
+ ret = send_response(fd, inet_ntoa(saddr.sin_addr), 200, xmlSecBufferGetData(buffer), xmlSecBufferGetSize(buffer));
+ if(log_level >= LOG_LEVEL_INFO) {
+ fprintf(stdout, "Log [%s]: processed request\n", inet_ntoa(saddr.sin_addr));
+ }
+ } else if(fd >= 0) {
+ ret = send_response(fd, inet_ntoa(saddr.sin_addr), 503, http_503, strlen(http_503));
+ if(log_level >= LOG_LEVEL_INFO) {
+ fprintf(stdout, "Log [%s]: failed to process request\n", inet_ntoa(saddr.sin_addr));
+ }
+ } else {
+ ret = -1;
+ }
+ if(ret < 0) {
+ fprintf(stderr, "Error %d [%s]: send_response() failed\n", errno, inet_ntoa(saddr.sin_addr));
+ }
+
+ /* cleanup */
+ if(output != NULL) {
+ xmlOutputBufferClose(output);
+ output = NULL;
+ }
+
+ if(outDoc != NULL) {
+ xmlFreeDoc(outDoc);
+ outDoc = NULL;
+ }
+
+ if(inDoc != NULL) {
+ xmlFreeDoc(inDoc);
+ inDoc = NULL;
+ }
+
+ if(buffer != NULL) {
+ xmlSecBufferDestroy(buffer);
+ buffer = NULL;
+ }
+
+ if(xkmsCtx2 != NULL) {
+ xmlSecXkmsServerCtxDestroy(xkmsCtx2);
+ xkmsCtx2 = NULL;
+ }
+
+ if(fd >= 0) {
+#ifdef UNIX_SOCKETS
+ shutdown(fd, SHUT_RDWR);
+ close(fd);
+#endif /* UNIX_SCOKETS */
+
+#ifdef WIN32_SOCKETS
+ close(fd);
+#endif /* WIN32_SCOKETS */
+
+ fd = -1;
+ }
+
+ if(in_child_process) {
+ exit(0);
+ }
+ return(0);
+}
+
+/**
+ * read_request:
+ * @fd: the request's socket.
+ * @in_ip: the request's IP address (for logging).
+ * @buffer: the output buffer.
+ *
+ * Reads the request from socket @fd and stores it in the @buffer.
+ *
+ * Returns 0 on success or a negative value if an error occurs.
+ */
+static int
+read_request(int fd, const char* in_ip, xmlSecBufferPtr buffer) {
+ char buf[1024];
+ const xmlChar* s;
+ const xmlChar* p;
+ int nread;
+ int length = 0;
+ int found = 0;
+ int counter;
+
+ assert(fd != -1);
+ assert(in_ip != NULL);
+ assert(buffer);
+
+ /* first read the http headers */
+ counter = 5;
+ while(my_strnstr(xmlSecBufferGetData(buffer), xmlSecBufferGetSize(buffer), BAD_CAST "\r\n\r\n", 4) == NULL) {
+ nread = recv(fd, buf, sizeof(buf), 0);
+ if(nread < 0) {
+ fprintf(stderr, "Error %d [%s]: read() failed\n", errno, in_ip);
+ return(-1);
+ }
+
+ if((nread > 0) && (xmlSecBufferAppend(buffer, buf, nread) < 0)) {
+ fprintf(stderr, "Error %d [%s]: xmlSecBufferAppend(%d) failed\n", errno, in_ip, nread);
+ return(-1);
+ }
+
+ if(nread < sizeof(buffer)) {
+ counter--;
+ if(counter <= 0) {
+ break;
+ }
+ }
+ }
+
+ if(xmlSecBufferGetData(buffer) == NULL) {
+ fprintf(stderr, "Error %d [%s]: no bytes read\n", errno, in_ip);
+ return(-1);
+ }
+
+ if(log_level >= LOG_LEVEL_DEBUG) {
+ xmlSecBufferAppend(buffer, BAD_CAST "\0", 1);
+ fprintf(stdout, "Debug [%s]: request headers:\n%s\n", in_ip, xmlSecBufferGetData(buffer));
+ xmlSecBufferRemoveTail(buffer, 1);
+ }
+
+ /* Parse the request and extract the body. We expect the request to look
+ * like this:
+ * POST <path> HTTP/1.x\r\n
+ * <header1>\r\n
+ * <header2>\r\n
+ * ...
+ * <headerN>\r\n
+ * \r\n
+ * <body>
+ */
+
+ /* analyze the first line */
+ p = my_strnstr(xmlSecBufferGetData(buffer), xmlSecBufferGetSize(buffer), BAD_CAST "\r\n", 2);
+ if(p == NULL) {
+ fprintf(stderr, "Error %d [%s]: there is no HTTP header\n", errno, in_ip);
+ return(-1);
+ }
+ if(xmlStrncasecmp(xmlSecBufferGetData(buffer), BAD_CAST "POST ", 5) != 0) {
+ fprintf(stderr, "Error %d [%s]: not a POST request\n", errno, in_ip);
+ return(-1);
+ }
+ /* "POST " + " HTTP/1.x" == 14 */
+ s = xmlSecBufferGetData(buffer);
+ if(p - s <= 14) {
+ fprintf(stderr, "Error %d [%s]: first line has bad length\n", errno, in_ip);
+ return(-1);
+ }
+ if((xmlStrncasecmp(p - 9, BAD_CAST " HTTP/1.0", 9) != 0) &&
+ (xmlStrncasecmp(p - 9, BAD_CAST " HTTP/1.1", 9) != 0)) {
+
+ fprintf(stderr, "Error %d [%s]: first line does not end with \" HTTP/1.x\"\n", errno, in_ip);
+ return(-1);
+ }
+ if(xmlSecBufferRemoveHead(buffer, p - xmlSecBufferGetData(buffer) + 2) < 0) {
+ fprintf(stderr, "Error %d [%s]: failed to skip first line\n", errno, in_ip);
+ return(-1);
+ }
+
+ /* now skip all the headers (i.e. everything until empty line) */
+ found = 0;
+ while(!found) {
+ p = my_strnstr(xmlSecBufferGetData(buffer), xmlSecBufferGetSize(buffer), BAD_CAST "\r\n", 2);
+ if(p == NULL) {
+ fprintf(stderr, "Error %d [%s]: there is no HTTP body\n", errno, in_ip);
+ return(-1);
+ }
+
+ if(p == xmlSecBufferGetData(buffer)) {
+ found = 1;
+ } else if(xmlStrncasecmp(xmlSecBufferGetData(buffer), BAD_CAST "Content-length: ", 16) == 0) {
+ length = atoi(xmlSecBufferGetData(buffer) + 16);
+ }
+
+ if(xmlSecBufferRemoveHead(buffer, p - xmlSecBufferGetData(buffer) + 2) < 0) {
+ fprintf(stderr, "Error %d [%s]: failed to skip header line\n", errno, in_ip);
+ return(-1);
+ }
+ }
+
+ /* remove the trailing \0 we added */
+ xmlSecBufferRemoveTail(buffer, 1);
+
+ /* now read the body */
+ counter = 5;
+ while(xmlSecBufferGetSize(buffer) < length) {
+ nread = recv(fd, buf, sizeof(buf), 0);
+ if(nread < 0) {
+ fprintf(stderr, "Error %d [%s]: read() failed\n", errno, in_ip);
+ return(-1);
+ }
+
+ if((nread > 0) && (xmlSecBufferAppend(buffer, buf, nread) < 0)) {
+ fprintf(stderr, "Error %d [%s]: xmlSecBufferAppend(%d) failed\n", errno, in_ip, nread);
+ return(-1);
+ }
+ if(nread < sizeof(buffer)) {
+ counter--;
+ if(counter <= 0) {
+ break;
+ }
+ }
+ }
+ if(log_level >= LOG_LEVEL_INFO) {
+ fprintf(stdout, "Log [%s]: body size is %d bytes\n", in_ip, xmlSecBufferGetSize(buffer));
+ }
+ if(log_level >= LOG_LEVEL_DATA) {
+ xmlSecBufferAppend(buffer, BAD_CAST "\0", 1);
+ fprintf(stdout, "Log [%s]: request body:\n%s\n", in_ip, xmlSecBufferGetData(buffer));
+ xmlSecBufferRemoveTail(buffer, 1);
+ }
+ return(0);
+}
+
+/**
+ * send_response:
+ * @fd: the request's socket.
+ * @in_ip: the request's IP address (for logging).
+ * @resp_code: the HTTP response code.
+ * @body: the response body.
+ * @body_len: the response body length.
+ *
+ * Writes HTTP response headers and @body to the @socket.
+ *
+ * Returns 0 on success or a negative value if an error occurs.
+ */
+static int
+send_response(int fd, const char* in_ip, int resp_code, const char* body, int body_size) {
+ char header[sizeof(http_header) + 100];
+
+ assert(fd != -1);
+ assert(in_ip != NULL);
+ assert(resp_code > 0);
+ assert(body != NULL);
+
+ /* prepare and send http header */
+ sprintf(header, http_header, resp_code, body_size);
+ if(send(fd, header, strlen(header), 0) == -1) {
+ fprintf(stderr, "Error %d [%s]: send(header) failed\n", errno, in_ip);
+ return(-1);
+ }
+
+ if(log_level >= LOG_LEVEL_DATA) {
+ xmlChar* tmp = xmlStrndup(body, body_size);
+ fprintf(stdout, "Log [%s]: response is\n%s\n", in_ip, tmp);
+ xmlFree(tmp);
+ }
+
+ /* send body */
+ if(send(fd, body, body_size, 0) == -1) {
+ fprintf(stderr, "Error %d [%s]: send(body) failed\n", errno, in_ip);
+ return(-1);
+ }
+
+ return(0);
+}
+
+/**
+ * my_strnstr:
+ * @str: the soruce string.
+ * @strLen: the source string length.
+ * @tmpl: the template string.
+ * @tmplLen: the template string length.
+ *
+ * Searches for the first occurence of @tmpl in @str.
+ *
+ * Returns pointer to the first occurence of @tmpl in @str or NULL if it is not found.
+ */
+static const xmlChar*
+my_strnstr(const xmlChar* str, xmlSecSize strLen, const xmlChar* tmpl, xmlSecSize tmplLen) {
+ xmlSecSize pos;
+
+ if((str == NULL) || (tmpl == NULL)) {
+ return(NULL);
+ }
+ for(pos = 0; pos + tmplLen <= strLen; pos++) {
+ if(xmlStrncmp(str + pos, tmpl, tmplLen) == 0) {
+ return(str + pos);
+ }
+ }
+
+ return(NULL);
+}
+
+#endif /* XMLSEC_NO_XKMS */
+