diff options
Diffstat (limited to 'examples/xkms-server.c')
-rw-r--r-- | examples/xkms-server.c | 839 |
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 */ + |