From 7c212ccf3c97b17dc79d60100cdff3416c3ac51f Mon Sep 17 00:00:00 2001 From: Andy Green Date: Mon, 8 Nov 2010 20:20:42 +0000 Subject: [PATCH] break-out-lib-sources.patch Signed-off-by: Andy Green --- lib/Makefile.am | 11 +- lib/Makefile.in | 29 +- lib/handshake.c | 237 +++++++++++++ lib/libwebsockets.c | 805 +------------------------------------------- lib/parsers.c | 484 ++++++++++++++++++++++++++ lib/private-libwebsockets.h | 148 ++++++++ test-server/test-server.c | 9 +- 7 files changed, 914 insertions(+), 809 deletions(-) create mode 100644 lib/handshake.c create mode 100644 lib/parsers.c create mode 100644 lib/private-libwebsockets.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 771fee7..78ced38 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,11 +1,18 @@ lib_LTLIBRARIES=libwebsockets.la include_HEADERS=libwebsockets.h -dist_libwebsockets_la_SOURCES=libwebsockets.c md5.c libwebsockets.h +dist_libwebsockets_la_SOURCES=libwebsockets.c \ + handshake.c \ + parsers.c \ + md5.c \ + libwebsockets.h \ + private-libwebsockets.h libwebsockets_la_CFLAGS=-rdynamic -fPIC -c libwebsockets_la_LDFLAGS=-version-info 0:1 all-local: ../scripts/kernel-doc -html \ libwebsockets.c \ - ../test-server/test-server.c > ../libwebsockets-api-doc.html + parsers.c \ + ../test-server/test-server.c \ + > ../libwebsockets-api-doc.html diff --git a/lib/Makefile.in b/lib/Makefile.in index ad6368d..493db5b 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -71,6 +71,7 @@ am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)" LTLIBRARIES = $(lib_LTLIBRARIES) libwebsockets_la_LIBADD = dist_libwebsockets_la_OBJECTS = libwebsockets_la-libwebsockets.lo \ + libwebsockets_la-handshake.lo libwebsockets_la-parsers.lo \ libwebsockets_la-md5.lo libwebsockets_la_OBJECTS = $(dist_libwebsockets_la_OBJECTS) libwebsockets_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ @@ -207,7 +208,13 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ lib_LTLIBRARIES = libwebsockets.la include_HEADERS = libwebsockets.h -dist_libwebsockets_la_SOURCES = libwebsockets.c md5.c libwebsockets.h +dist_libwebsockets_la_SOURCES = libwebsockets.c \ + handshake.c \ + parsers.c \ + md5.c \ + libwebsockets.h \ + private-libwebsockets.h + libwebsockets_la_CFLAGS = -rdynamic -fPIC -c libwebsockets_la_LDFLAGS = -version-info 0:1 all: all-am @@ -284,8 +291,10 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-handshake.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-libwebsockets.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-md5.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libwebsockets_la-parsers.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -315,6 +324,20 @@ libwebsockets_la-libwebsockets.lo: libwebsockets.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -c -o libwebsockets_la-libwebsockets.lo `test -f 'libwebsockets.c' || echo '$(srcdir)/'`libwebsockets.c +libwebsockets_la-handshake.lo: handshake.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -MT libwebsockets_la-handshake.lo -MD -MP -MF $(DEPDIR)/libwebsockets_la-handshake.Tpo -c -o libwebsockets_la-handshake.lo `test -f 'handshake.c' || echo '$(srcdir)/'`handshake.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_la-handshake.Tpo $(DEPDIR)/libwebsockets_la-handshake.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='handshake.c' object='libwebsockets_la-handshake.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -c -o libwebsockets_la-handshake.lo `test -f 'handshake.c' || echo '$(srcdir)/'`handshake.c + +libwebsockets_la-parsers.lo: parsers.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -MT libwebsockets_la-parsers.lo -MD -MP -MF $(DEPDIR)/libwebsockets_la-parsers.Tpo -c -o libwebsockets_la-parsers.lo `test -f 'parsers.c' || echo '$(srcdir)/'`parsers.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_la-parsers.Tpo $(DEPDIR)/libwebsockets_la-parsers.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='parsers.c' object='libwebsockets_la-parsers.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -c -o libwebsockets_la-parsers.lo `test -f 'parsers.c' || echo '$(srcdir)/'`parsers.c + libwebsockets_la-md5.lo: md5.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libwebsockets_la_CFLAGS) $(CFLAGS) -MT libwebsockets_la-md5.lo -MD -MP -MF $(DEPDIR)/libwebsockets_la-md5.Tpo -c -o libwebsockets_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libwebsockets_la-md5.Tpo $(DEPDIR)/libwebsockets_la-md5.Plo @@ -555,7 +578,9 @@ uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES all-local: ../scripts/kernel-doc -html \ libwebsockets.c \ - ../test-server/test-server.c > ../libwebsockets-api-doc.html + parsers.c \ + ../test-server/test-server.c \ + > ../libwebsockets-api-doc.html # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/lib/handshake.c b/lib/handshake.c new file mode 100644 index 0000000..caebd61 --- /dev/null +++ b/lib/handshake.c @@ -0,0 +1,237 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +void md5(const unsigned char *input, int ilen, unsigned char output[16]); + +static int interpret_key(const char *key, unsigned int *result) +{ + char digits[20]; + int digit_pos = 0; + const char *p = key; + int spaces = 0; + + while (*p) { + if (isdigit(*p)) { + if (digit_pos == sizeof(digits) - 1) + return -1; + digits[digit_pos++] = *p; + } + p++; + } + digits[digit_pos] = '\0'; + if (!digit_pos) + return -2; + + while (*key) { + if (*key == ' ') + spaces++; + key++; + } + + if (!spaces) + return -3; + + *result = atol(digits) / spaces; + + return 0; +} + +/* + * We have to take care about parsing because the headers may be split + * into multiple fragments. They may contain unknown headers with arbitrary + * argument lengths. So, we parse using a single-character at a time state + * machine that is completely independent of packet size. + */ + +int +libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len) +{ + size_t n; + char *p; + unsigned int key1, key2; + unsigned char sum[16]; + char *response; + + switch (wsi->state) { + case WSI_STATE_HTTP: + wsi->state = WSI_STATE_HTTP_HEADERS; + wsi->parser_state = WSI_TOKEN_NAME_PART; + /* fallthru */ + case WSI_STATE_HTTP_HEADERS: + + debug("issuing %d bytes to parser\n", (int)len); +#ifdef DEBUG + fwrite(buf, 1, len, stderr); +#endif + for (n = 0; n< len; n++) + libwebsocket_parse(wsi, *buf++); + + if (wsi->parser_state != WSI_PARSING_COMPLETE) + break; + + /* is this websocket protocol or normal http 1.0? */ + + if (!wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len || + !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len) { + if (wsi->callback) + (wsi->callback)(wsi, LWS_CALLBACK_HTTP, + &wsi->user_space[0], + NULL, 0); + wsi->state = WSI_STATE_HTTP; + return 0; + } + + /* Websocket - confirm we have all the necessary pieces */ + + if (!wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len || + !wsi->utf8_token[WSI_TOKEN_HOST].token_len || + !wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len || + !wsi->utf8_token[WSI_TOKEN_KEY1].token_len || + !wsi->utf8_token[WSI_TOKEN_KEY2].token_len) + /* completed header processing, but missing some bits */ + goto bail; + + /* create the response packet */ + + /* make a buffer big enough for everything */ + + response = malloc(256 + + wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len + + wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len + + wsi->utf8_token[WSI_TOKEN_HOST].token_len + + wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len + + wsi->utf8_token[WSI_TOKEN_GET_URI].token_len + + wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len); + if (!response) { + fprintf(stderr, "Out of memory for response buffer\n"); + goto bail; + } + + p = response; + strcpy(p, "HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a" + "Upgrade: WebSocket\x0d\x0a"); + p += strlen("HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a" + "Upgrade: WebSocket\x0d\x0a"); + strcpy(p, "Connection: Upgrade\x0d\x0a" + "Sec-WebSocket-Origin: "); + p += strlen("Connection: Upgrade\x0d\x0a" + "Sec-WebSocket-Origin: "); + strcpy(p, wsi->utf8_token[WSI_TOKEN_ORIGIN].token); + p += wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len; +#ifdef LWS_OPENSSL_SUPPORT + if (use_ssl) { + strcpy(p, "\x0d\x0aSec-WebSocket-Location: wss://"); + p += strlen("\x0d\x0aSec-WebSocket-Location: wss://"); + } else { +#endif + strcpy(p, "\x0d\x0aSec-WebSocket-Location: ws://"); + p += strlen("\x0d\x0aSec-WebSocket-Location: ws://"); +#ifdef LWS_OPENSSL_SUPPORT + } +#endif + strcpy(p, wsi->utf8_token[WSI_TOKEN_HOST].token); + p += wsi->utf8_token[WSI_TOKEN_HOST].token_len; + strcpy(p, wsi->utf8_token[WSI_TOKEN_GET_URI].token); + p += wsi->utf8_token[WSI_TOKEN_GET_URI].token_len; + + if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) { + strcpy(p, "\x0d\x0aSec-WebSocket-Protocol: "); + p += strlen("\x0d\x0aSec-WebSocket-Protocol: "); + strcpy(p, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token); + p += wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len; + } + + strcpy(p, "\x0d\x0a\x0d\x0a"); + p += strlen("\x0d\x0a\x0d\x0a"); + + /* convert the two keys into 32-bit integers */ + + if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY1].token, &key1)) + goto bail; + if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY2].token, &key2)) + goto bail; + + /* lay them out in network byte order (MSB first */ + + sum[0] = key1 >> 24; + sum[1] = key1 >> 16; + sum[2] = key1 >> 8; + sum[3] = key1; + sum[4] = key2 >> 24; + sum[5] = key2 >> 16; + sum[6] = key2 >> 8; + sum[7] = key2; + + /* follow them with the challenge token we were sent */ + + memcpy(&sum[8], wsi->utf8_token[WSI_TOKEN_CHALLENGE].token, 8); + + /* + * compute the md5sum of that 16-byte series and use as our + * payload after our headers + */ + + md5(sum, 16, (unsigned char *)p); + p += 16; + + /* it's complete: go ahead and send it */ + + debug("issuing response packet %d len\n", + (int)(p - response)); +#ifdef DEBUG + fwrite(response, 1, p - response, stderr); +#endif + n = libwebsocket_write(wsi, (unsigned char *)response, p - response, + LWS_WRITE_HTTP); + if (n < 0) { + fprintf(stderr, "ERROR writing to socket"); + goto bail; + } + + /* alright clean up and set ourselves into established state */ + + free(response); + wsi->state = WSI_STATE_ESTABLISHED; + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + + /* notify user code that we're ready to roll */ + + if (wsi->callback) + wsi->callback(wsi, LWS_CALLBACK_ESTABLISHED, + &wsi->user_space[0], NULL, 0); + break; + + case WSI_STATE_ESTABLISHED: + if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) + goto bail; + break; + default: + break; + } + + return 0; + +bail: + libwebsocket_close_and_free_session(wsi); + return -1; +} diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index ac20738..6db4d07 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -19,161 +19,20 @@ * MA 02110-1301 USA */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include +#include "private-libwebsockets.h" #ifdef LWS_OPENSSL_SUPPORT -#include -#include -#include - SSL_CTX *ssl_ctx; int use_ssl; #endif -//#define DEBUG - -#include "libwebsockets.h" -#ifdef DEBUG -#define debug(format, args...) \ - fprintf(stderr, format , ## args) -#else -#define debug(format, args...) -#endif - -void md5(const unsigned char *input, int ilen, unsigned char output[16]); -static int +extern int libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len); -#define MAX_CLIENTS 100 -#define LWS_MAX_HEADER_NAME_LENGTH 64 -#define LWS_MAX_HEADER_LEN 4096 -#define LWS_INITIAL_HDR_ALLOC 256 -#define LWS_ADDITIONAL_HDR_ALLOC 64 - - - -enum lws_connection_states { - WSI_STATE_HTTP, - WSI_STATE_HTTP_HEADERS, - WSI_STATE_DEAD_SOCKET, - WSI_STATE_ESTABLISHED -}; - -enum lws_token_indexes { - WSI_TOKEN_GET_URI, - WSI_TOKEN_HOST, - WSI_TOKEN_CONNECTION, - WSI_TOKEN_KEY1, - WSI_TOKEN_KEY2, - WSI_TOKEN_PROTOCOL, - WSI_TOKEN_UPGRADE, - WSI_TOKEN_ORIGIN, - WSI_TOKEN_CHALLENGE, - - /* always last real token index*/ - WSI_TOKEN_COUNT, - /* parser state additions */ - WSI_TOKEN_NAME_PART, - WSI_TOKEN_SKIPPING, - WSI_TOKEN_SKIPPING_SAW_CR, - WSI_PARSING_COMPLETE -}; - -enum lws_rx_parse_state { - LWS_RXPS_NEW, - - LWS_RXPS_SEEN_76_FF, - LWS_RXPS_PULLING_76_LENGTH, - - LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED -}; - - -struct lws_tokens { - char * token; - int token_len; -}; - - -/* - * This is totally opaque to code using the library. It's exported as a - * forward-reference pointer-only declaration; the user can use the pointer with - * other APIs to get information out of it. - */ - -struct libwebsocket { - int (*callback)(struct libwebsocket *, - enum libwebsocket_callback_reasons reason, void *, void *, size_t); - - enum lws_connection_states state; - - char name_buffer[LWS_MAX_HEADER_NAME_LENGTH]; - int name_buffer_pos; - int current_alloc_len; - enum lws_token_indexes parser_state; - struct lws_tokens utf8_token[WSI_TOKEN_COUNT]; - int ietf_spec_revision; - - int sock; - - enum lws_rx_parse_state lws_rx_parse_state; - size_t rx_packet_length; - -#ifdef LWS_OPENSSL_SUPPORT - char m_fOccupied; - struct sockaddr_in m_addr; - int m_addrlen; - - SSL *ssl; - - // these are valid if it is a POST - - char m_fOngoingPost; - int m_nSessionID; - - time_t m_timeStarted; - long long m_llTransferred; - long long m_llSizeIfKnown; - char m_szTitle[PATH_MAX]; - char m_szStatus[PATH_MAX]; -#endif - - /* last */ - char user_space[0]; -}; - - -const struct lws_tokens lws_tokens[WSI_TOKEN_COUNT] = { - { "GET ", 4 }, - { "Host:", 5 }, - { "Connection:", 11 }, - { "Sec-WebSocket-Key1:", 19 }, - { "Sec-WebSocket-Key2:", 19 }, - { "Sec-WebSocket-Protocol:", 23 }, - { "Upgrade:", 8 }, - { "Origin:", 7 }, - { "\x0d\x0a", 2 }, -}; -static void +void libwebsocket_close_and_free_session(struct libwebsocket *wsi) { int n = wsi->state; @@ -568,662 +427,4 @@ fatal: return 0; } -/** - * libwebsocket_get_uri() - Return the URI path being requested - * @wsi: Websocket instance - * - * The user code can find out the local path being opened from this - * call, it's valid on HTTP or established websocket connections. - * If the client opened the connection with "http://127.0.0.1/xyz/abc.d" - * then this call will return a pointer to "/xyz/abc.d" - */ - -const char * libwebsocket_get_uri(struct libwebsocket *wsi) -{ - if (wsi->utf8_token[WSI_TOKEN_GET_URI].token) - return wsi->utf8_token[WSI_TOKEN_GET_URI].token; - - return NULL; -} - -static int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) -{ - int n; - - switch (wsi->parser_state) { - case WSI_TOKEN_GET_URI: - case WSI_TOKEN_HOST: - case WSI_TOKEN_CONNECTION: - case WSI_TOKEN_KEY1: - case WSI_TOKEN_KEY2: - case WSI_TOKEN_PROTOCOL: - case WSI_TOKEN_UPGRADE: - case WSI_TOKEN_ORIGIN: - case WSI_TOKEN_CHALLENGE: - - debug("WSI_TOKEN_(%d) '%c'\n", wsi->parser_state, c); - - /* collect into malloc'd buffers */ - /* optional space swallow */ - if (!wsi->utf8_token[wsi->parser_state].token_len && c == ' ') - break; - - /* special case space terminator for get-uri */ - if (wsi->parser_state == WSI_TOKEN_GET_URI && c == ' ') { - wsi->utf8_token[wsi->parser_state].token[ - wsi->utf8_token[wsi->parser_state].token_len] = '\0'; - wsi->parser_state = WSI_TOKEN_SKIPPING; - break; - } - - /* allocate appropriate memory */ - if (wsi->utf8_token[wsi->parser_state].token_len == - wsi->current_alloc_len - 1) { - /* need to extend */ - wsi->current_alloc_len += LWS_ADDITIONAL_HDR_ALLOC; - if (wsi->current_alloc_len >= LWS_MAX_HEADER_LEN) { - /* it's waaay to much payload, fail it */ - strcpy(wsi->utf8_token[wsi->parser_state].token, - "!!! Length exceeded maximum supported !!!"); - wsi->parser_state = WSI_TOKEN_SKIPPING; - break; - } - wsi->utf8_token[wsi->parser_state].token = - realloc(wsi->utf8_token[wsi->parser_state].token, - wsi->current_alloc_len); - } - - /* bail at EOL */ - if (wsi->parser_state != WSI_TOKEN_CHALLENGE && c == '\x0d') { - wsi->utf8_token[wsi->parser_state].token[ - wsi->utf8_token[wsi->parser_state].token_len] = '\0'; - wsi->parser_state = WSI_TOKEN_SKIPPING_SAW_CR; - break; - } - - wsi->utf8_token[wsi->parser_state].token[ - wsi->utf8_token[wsi->parser_state].token_len++] = c; - - /* special payload limiting */ - if (wsi->parser_state == WSI_TOKEN_CHALLENGE && - wsi->utf8_token[wsi->parser_state].token_len == 8) { - debug("Setting WSI_PARSING_COMPLETE\n"); - wsi->parser_state = WSI_PARSING_COMPLETE; - break; - } - - break; - - /* collecting and checking a name part */ - case WSI_TOKEN_NAME_PART: - debug("WSI_TOKEN_NAME_PART '%c'\n", c); - - if (wsi->name_buffer_pos == sizeof(wsi->name_buffer) - 1) { - /* name bigger than we can handle, skip until next */ - wsi->parser_state = WSI_TOKEN_SKIPPING; - break; - } - wsi->name_buffer[wsi->name_buffer_pos++] = c; - wsi->name_buffer[wsi->name_buffer_pos] = '\0'; - - for (n = 0; n < WSI_TOKEN_COUNT; n++) { - if (wsi->name_buffer_pos != lws_tokens[n].token_len) - continue; - if (strcmp(lws_tokens[n].token, wsi->name_buffer)) - continue; - debug("known hdr '%s'\n", wsi->name_buffer); - wsi->parser_state = WSI_TOKEN_GET_URI + n; - wsi->current_alloc_len = LWS_INITIAL_HDR_ALLOC; - wsi->utf8_token[wsi->parser_state].token = - malloc(wsi->current_alloc_len); - wsi->utf8_token[wsi->parser_state].token_len = 0; - n = WSI_TOKEN_COUNT; - } - - /* colon delimiter means we just don't know this name */ - - if (wsi->parser_state == WSI_TOKEN_NAME_PART && c == ':') { - debug("skipping unknown header '%s'\n", - wsi->name_buffer); - wsi->parser_state = WSI_TOKEN_SKIPPING; - break; - } - - /* don't look for payload when it can just be http headers */ - - if (wsi->parser_state == WSI_TOKEN_CHALLENGE && - !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len) { - /* they're HTTP headers, not websocket upgrade! */ - debug("Setting WSI_PARSING_COMPLETE " - "from http headers\n"); - wsi->parser_state = WSI_PARSING_COMPLETE; - } - break; - - /* skipping arg part of a name we didn't recognize */ - case WSI_TOKEN_SKIPPING: - debug("WSI_TOKEN_SKIPPING '%c'\n", c); - if (c == '\x0d') - wsi->parser_state = WSI_TOKEN_SKIPPING_SAW_CR; - break; - case WSI_TOKEN_SKIPPING_SAW_CR: - debug("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c); - if (c == '\x0a') - wsi->parser_state = WSI_TOKEN_NAME_PART; - else - wsi->parser_state = WSI_TOKEN_SKIPPING; - wsi->name_buffer_pos = 0; - break; - /* we're done, ignore anything else */ - case WSI_PARSING_COMPLETE: - debug("WSI_PARSING_COMPLETE '%c'\n", c); - break; - - default: /* keep gcc happy */ - break; - } - - return 0; -} - -static int interpret_key(const char *key, unsigned int *result) -{ - char digits[20]; - int digit_pos = 0; - const char *p = key; - int spaces = 0; - - while (*p) { - if (isdigit(*p)) { - if (digit_pos == sizeof(digits) - 1) - return -1; - digits[digit_pos++] = *p; - } - p++; - } - digits[digit_pos] = '\0'; - if (!digit_pos) - return -2; - - while (*key) { - if (*key == ' ') - spaces++; - key++; - } - - if (!spaces) - return -3; - - *result = atol(digits) / spaces; - - return 0; -} - -static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) -{ - int n; - unsigned char buf[2]; - - switch (wsi->lws_rx_parse_state) { - case LWS_RXPS_NEW: - - switch (wsi->ietf_spec_revision) { - /* Firefox 4.0b6 likes this as of 30 Oct */ - case 76: - if (c == 0xff) - wsi->lws_rx_parse_state = LWS_RXPS_SEEN_76_FF; - break; - case 0: - break; - } - break; - case LWS_RXPS_SEEN_76_FF: - if (c) - break; - - debug("Seen that client is requesting " - "a v76 close, sending ack\n"); - buf[0] = 0xff; - buf[1] = 0; - n = libwebsocket_write(wsi, buf, 2, LWS_WRITE_HTTP); - if (n < 0) { - fprintf(stderr, "ERROR writing to socket"); - return -1; - } - debug(" v76 close ack sent, server closing skt\n"); - /* returning < 0 will get it closed in parent */ - return -1; - - case LWS_RXPS_PULLING_76_LENGTH: - break; - case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED: - break; - } - - return 0; -} - -static int libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi, - unsigned char *buf, size_t len) -{ - int n; - - fprintf(stderr, "received %d byte packet\n", (int)len); - for (n = 0; n < len; n++) - fprintf(stderr, "%02X ", buf[n]); - fprintf(stderr, "\n"); - - /* let the rx protocol state machine have as much as it needs */ - - n = 0; - while (wsi->lws_rx_parse_state != - LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED && n < len) - if (libwebsocket_rx_sm(wsi, buf[n++]) < 0) - return -1; - - if (n != len && wsi->callback) - wsi->callback(wsi, LWS_CALLBACK_RECEIVE, &wsi->user_space[0], - &buf[n], len - n); - - return -0; -} - -/* - * We have to take care about parsing because the headers may be split - * into multiple fragments. They may contain unknown headers with arbitrary - * argument lengths. So, we parse using a single-character at a time state - * machine that is completely independent of packet size. - */ - -static int -libwebsocket_read(struct libwebsocket *wsi, unsigned char * buf, size_t len) -{ - size_t n; - char *p; - unsigned int key1, key2; - unsigned char sum[16]; - char *response; - - switch (wsi->state) { - case WSI_STATE_HTTP: - wsi->state = WSI_STATE_HTTP_HEADERS; - wsi->parser_state = WSI_TOKEN_NAME_PART; - /* fallthru */ - case WSI_STATE_HTTP_HEADERS: - - debug("issuing %d bytes to parser\n", (int)len); -#ifdef DEBUG - fwrite(buf, 1, len, stderr); -#endif - for (n = 0; n< len; n++) - libwebsocket_parse(wsi, *buf++); - - if (wsi->parser_state != WSI_PARSING_COMPLETE) - break; - - /* is this websocket protocol or normal http 1.0? */ - - if (!wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len || - !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len) { - if (wsi->callback) - (wsi->callback)(wsi, LWS_CALLBACK_HTTP, - &wsi->user_space[0], - NULL, 0); - wsi->state = WSI_STATE_HTTP; - return 0; - } - - /* Websocket - confirm we have all the necessary pieces */ - - if (!wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len || - !wsi->utf8_token[WSI_TOKEN_HOST].token_len || - !wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len || - !wsi->utf8_token[WSI_TOKEN_KEY1].token_len || - !wsi->utf8_token[WSI_TOKEN_KEY2].token_len) - /* completed header processing, but missing some bits */ - goto bail; - - /* create the response packet */ - - /* make a buffer big enough for everything */ - - response = malloc(256 + - wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len + - wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len + - wsi->utf8_token[WSI_TOKEN_HOST].token_len + - wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len + - wsi->utf8_token[WSI_TOKEN_GET_URI].token_len + - wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len); - if (!response) { - fprintf(stderr, "Out of memory for response buffer\n"); - goto bail; - } - - p = response; - strcpy(p, "HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a" - "Upgrade: WebSocket\x0d\x0a"); - p += strlen("HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a" - "Upgrade: WebSocket\x0d\x0a"); - strcpy(p, "Connection: Upgrade\x0d\x0a" - "Sec-WebSocket-Origin: "); - p += strlen("Connection: Upgrade\x0d\x0a" - "Sec-WebSocket-Origin: "); - strcpy(p, wsi->utf8_token[WSI_TOKEN_ORIGIN].token); - p += wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len; -#ifdef LWS_OPENSSL_SUPPORT - if (use_ssl) { - strcpy(p, "\x0d\x0aSec-WebSocket-Location: wss://"); - p += strlen("\x0d\x0aSec-WebSocket-Location: wss://"); - } else { -#endif - strcpy(p, "\x0d\x0aSec-WebSocket-Location: ws://"); - p += strlen("\x0d\x0aSec-WebSocket-Location: ws://"); -#ifdef LWS_OPENSSL_SUPPORT - } -#endif - strcpy(p, wsi->utf8_token[WSI_TOKEN_HOST].token); - p += wsi->utf8_token[WSI_TOKEN_HOST].token_len; - strcpy(p, wsi->utf8_token[WSI_TOKEN_GET_URI].token); - p += wsi->utf8_token[WSI_TOKEN_GET_URI].token_len; - - if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) { - strcpy(p, "\x0d\x0aSec-WebSocket-Protocol: "); - p += strlen("\x0d\x0aSec-WebSocket-Protocol: "); - strcpy(p, wsi->utf8_token[WSI_TOKEN_PROTOCOL].token); - p += wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len; - } - - strcpy(p, "\x0d\x0a\x0d\x0a"); - p += strlen("\x0d\x0a\x0d\x0a"); - - /* convert the two keys into 32-bit integers */ - - if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY1].token, &key1)) - goto bail; - if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY2].token, &key2)) - goto bail; - - /* lay them out in network byte order (MSB first */ - - sum[0] = key1 >> 24; - sum[1] = key1 >> 16; - sum[2] = key1 >> 8; - sum[3] = key1; - sum[4] = key2 >> 24; - sum[5] = key2 >> 16; - sum[6] = key2 >> 8; - sum[7] = key2; - - /* follow them with the challenge token we were sent */ - - memcpy(&sum[8], wsi->utf8_token[WSI_TOKEN_CHALLENGE].token, 8); - - /* - * compute the md5sum of that 16-byte series and use as our - * payload after our headers - */ - - md5(sum, 16, (unsigned char *)p); - p += 16; - - /* it's complete: go ahead and send it */ - - debug("issuing response packet %d len\n", - (int)(p - response)); -#ifdef DEBUG - fwrite(response, 1, p - response, stderr); -#endif - n = libwebsocket_write(wsi, (unsigned char *)response, p - response, - LWS_WRITE_HTTP); - if (n < 0) { - fprintf(stderr, "ERROR writing to socket"); - goto bail; - } - - /* alright clean up and set ourselves into established state */ - - free(response); - wsi->state = WSI_STATE_ESTABLISHED; - wsi->lws_rx_parse_state = LWS_RXPS_NEW; - - /* notify user code that we're ready to roll */ - - if (wsi->callback) - wsi->callback(wsi, LWS_CALLBACK_ESTABLISHED, - &wsi->user_space[0], NULL, 0); - break; - - case WSI_STATE_ESTABLISHED: - if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) - goto bail; - break; - default: - break; - } - - return 0; - -bail: - libwebsocket_close_and_free_session(wsi); - return -1; -} - -/** - * libwebsocket_write() - Apply protocol then write data to client - * @wsi: Websocket instance (available from user callback) - * @buf: The data to send. For data being sent on a websocket - * connection (ie, not default http), this buffer MUST have - * LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE the pointer - * and an additional LWS_SEND_BUFFER_POST_PADDING bytes valid - * in the buffer after (buf + len). This is so the protocol - * header and trailer data can be added in-situ. - * @len: Count of the data bytes in the payload starting from buf - * @protocol: Use LWS_WRITE_HTTP to reply to an http connection, and one - * of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate - * data on a websockets connection. Remember to allow the extra - * bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT - * are used. - * - * This function provides the way to issue data back to the client - * for both http and websocket protocols. - * - * In the case of sending using websocket protocol, be sure to allocate - * valid storage before and after buf as explained above. This scheme - * allows maximum efficiency of sending data and protocol in a single - * packet while not burdening the user code with any protocol knowledge. - */ - -int libwebsocket_write(struct libwebsocket * wsi, unsigned char *buf, - size_t len, enum libwebsocket_write_protocol protocol) -{ - int n; - int pre = 0; - int post = 0; - unsigned int shift = 7; - - if (protocol == LWS_WRITE_HTTP) - goto send_raw; - - /* websocket protocol, either binary or text */ - - if (wsi->state != WSI_STATE_ESTABLISHED) - return -1; - - switch (wsi->ietf_spec_revision) { - /* chrome likes this as of 30 Oct */ - /* Firefox 4.0b6 likes this as of 30 Oct */ - case 76: - if (protocol == LWS_WRITE_BINARY) { - /* in binary mode we send 7-bit used length blocks */ - pre = 1; - while (len & (127 << shift)) { - pre++; - shift += 7; - } - n = 0; - shift -= 7; - while (shift >= 0) { - if (shift) - buf[0 - pre + n] = - ((len >> shift) & 127) | 0x80; - else - buf[0 - pre + n] = - ((len >> shift) & 127); - n++; - shift -= 7; - } - break; - } - - /* frame type = text, length-free spam mode */ - - buf[-1] = 0; - buf[len] = 0xff; /* EOT marker */ - pre = 1; - post = 1; - break; - - case 0: - buf[-9] = 0xff; -#if defined __LP64__ - buf[-8] = len >> 56; - buf[-7] = len >> 48; - buf[-6] = len >> 40; - buf[-5] = len >> 32; -#else - buf[-8] = 0; - buf[-7] = 0; - buf[-6] = 0; - buf[-5] = 0; -#endif - buf[-4] = len >> 24; - buf[-3] = len >> 16; - buf[-2] = len >> 8; - buf[-1] = len; - pre = 9; - break; - - /* just an unimplemented spec right now apparently */ - case 2: - n = 4; /* text */ - if (protocol == LWS_WRITE_BINARY) - n = 5; /* binary */ - if (len < 126) { - buf[-2] = n; - buf[-1] = len; - pre = 2; - } else { - if (len < 65536) { - buf[-4] = n; - buf[-3] = 126; - buf[-2] = len >> 8; - buf[-1] = len; - pre = 4; - } else { - buf[-10] = n; - buf[-9] = 127; -#if defined __LP64__ - buf[-8] = (len >> 56) & 0x7f; - buf[-7] = len >> 48; - buf[-6] = len >> 40; - buf[-5] = len >> 32; -#else - buf[-8] = 0; - buf[-7] = 0; - buf[-6] = 0; - buf[-5] = 0; -#endif - buf[-4] = len >> 24; - buf[-3] = len >> 16; - buf[-2] = len >> 8; - buf[-1] = len; - pre = 10; - } - } - break; - } - -#if 0 - for (n = 0; n < (len + pre + post); n++) - fprintf(stderr, "%02X ", buf[n - pre]); - - fprintf(stderr, "\n"); -#endif - -send_raw: -#ifdef LWS_OPENSSL_SUPPORT - if (use_ssl) { - n = SSL_write(wsi->ssl, buf - pre, len + pre + post); - if (n < 0) { - fprintf(stderr, "ERROR writing to socket"); - return -1; - } - } else { -#endif - n = send(wsi->sock, buf - pre, len + pre + post, 0); - if (n < 0) { - fprintf(stderr, "ERROR writing to socket"); - return -1; - } -#ifdef LWS_OPENSSL_SUPPORT - } -#endif -// fprintf(stderr, "written %d bytes to client\n", (int)len); - - return 0; -} - - -/** - * libwebsockets_serve_http_file() - Send a file back to the client using http - * @wsi: Websocket instance (available from user callback) - * @file: The file to issue over http - * @content_type: The http content type, eg, text/html - * - * This function is intended to be called from the callback in response - * to http requests from the client. It allows the callback to issue - * local files down the http link in a single step. - */ - -int libwebsockets_serve_http_file(struct libwebsocket *wsi, const char * file, - const char * content_type) -{ - int fd; - struct stat stat; - char buf[512]; - char *p = buf; - int n; - - fd = open(file, O_RDONLY); - if (fd < 1) { - p += sprintf(p, "HTTP/1.0 400 Bad\x0d\x0a" - "Server: libwebsockets\x0d\x0a" - "\x0d\x0a" - ); - libwebsocket_write(wsi, (unsigned char *)buf, p - buf, - LWS_WRITE_HTTP); - - return -1; - } - - fstat(fd, &stat); - p += sprintf(p, "HTTP/1.0 200 OK\x0d\x0a" - "Server: libwebsockets\x0d\x0a" - "Content-Type: %s\x0d\x0a" - "Content-Length: %u\x0d\x0a" - "\x0d\x0a", content_type, (unsigned int)stat.st_size); - - libwebsocket_write(wsi, (unsigned char *)buf, p - buf, LWS_WRITE_HTTP); - - n = 1; - while (n > 0) { - n = read(fd, buf, 512); - libwebsocket_write(wsi, (unsigned char *)buf, n, - LWS_WRITE_HTTP); - } - - close(fd); - - return 0; -} diff --git a/lib/parsers.c b/lib/parsers.c new file mode 100644 index 0000000..e52e443 --- /dev/null +++ b/lib/parsers.c @@ -0,0 +1,484 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +const struct lws_tokens lws_tokens[WSI_TOKEN_COUNT] = { + { "GET ", 4 }, + { "Host:", 5 }, + { "Connection:", 11 }, + { "Sec-WebSocket-Key1:", 19 }, + { "Sec-WebSocket-Key2:", 19 }, + { "Sec-WebSocket-Protocol:", 23 }, + { "Upgrade:", 8 }, + { "Origin:", 7 }, + { "\x0d\x0a", 2 }, +}; + +/** + * libwebsocket_get_uri() - Return the URI path being requested + * @wsi: Websocket instance + * + * The user code can find out the local path being opened from this + * call, it's valid on HTTP or established websocket connections. + * If the client opened the connection with "http://127.0.0.1/xyz/abc.d" + * then this call will return a pointer to "/xyz/abc.d" + */ + +const char * libwebsocket_get_uri(struct libwebsocket *wsi) +{ + if (wsi->utf8_token[WSI_TOKEN_GET_URI].token) + return wsi->utf8_token[WSI_TOKEN_GET_URI].token; + + return NULL; +} + + +int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) +{ + int n; + + switch (wsi->parser_state) { + case WSI_TOKEN_GET_URI: + case WSI_TOKEN_HOST: + case WSI_TOKEN_CONNECTION: + case WSI_TOKEN_KEY1: + case WSI_TOKEN_KEY2: + case WSI_TOKEN_PROTOCOL: + case WSI_TOKEN_UPGRADE: + case WSI_TOKEN_ORIGIN: + case WSI_TOKEN_CHALLENGE: + + debug("WSI_TOKEN_(%d) '%c'\n", wsi->parser_state, c); + + /* collect into malloc'd buffers */ + /* optional space swallow */ + if (!wsi->utf8_token[wsi->parser_state].token_len && c == ' ') + break; + + /* special case space terminator for get-uri */ + if (wsi->parser_state == WSI_TOKEN_GET_URI && c == ' ') { + wsi->utf8_token[wsi->parser_state].token[ + wsi->utf8_token[wsi->parser_state].token_len] = '\0'; + wsi->parser_state = WSI_TOKEN_SKIPPING; + break; + } + + /* allocate appropriate memory */ + if (wsi->utf8_token[wsi->parser_state].token_len == + wsi->current_alloc_len - 1) { + /* need to extend */ + wsi->current_alloc_len += LWS_ADDITIONAL_HDR_ALLOC; + if (wsi->current_alloc_len >= LWS_MAX_HEADER_LEN) { + /* it's waaay to much payload, fail it */ + strcpy(wsi->utf8_token[wsi->parser_state].token, + "!!! Length exceeded maximum supported !!!"); + wsi->parser_state = WSI_TOKEN_SKIPPING; + break; + } + wsi->utf8_token[wsi->parser_state].token = + realloc(wsi->utf8_token[wsi->parser_state].token, + wsi->current_alloc_len); + } + + /* bail at EOL */ + if (wsi->parser_state != WSI_TOKEN_CHALLENGE && c == '\x0d') { + wsi->utf8_token[wsi->parser_state].token[ + wsi->utf8_token[wsi->parser_state].token_len] = '\0'; + wsi->parser_state = WSI_TOKEN_SKIPPING_SAW_CR; + break; + } + + wsi->utf8_token[wsi->parser_state].token[ + wsi->utf8_token[wsi->parser_state].token_len++] = c; + + /* special payload limiting */ + if (wsi->parser_state == WSI_TOKEN_CHALLENGE && + wsi->utf8_token[wsi->parser_state].token_len == 8) { + debug("Setting WSI_PARSING_COMPLETE\n"); + wsi->parser_state = WSI_PARSING_COMPLETE; + break; + } + + break; + + /* collecting and checking a name part */ + case WSI_TOKEN_NAME_PART: + debug("WSI_TOKEN_NAME_PART '%c'\n", c); + + if (wsi->name_buffer_pos == sizeof(wsi->name_buffer) - 1) { + /* name bigger than we can handle, skip until next */ + wsi->parser_state = WSI_TOKEN_SKIPPING; + break; + } + wsi->name_buffer[wsi->name_buffer_pos++] = c; + wsi->name_buffer[wsi->name_buffer_pos] = '\0'; + + for (n = 0; n < WSI_TOKEN_COUNT; n++) { + if (wsi->name_buffer_pos != lws_tokens[n].token_len) + continue; + if (strcmp(lws_tokens[n].token, wsi->name_buffer)) + continue; + debug("known hdr '%s'\n", wsi->name_buffer); + wsi->parser_state = WSI_TOKEN_GET_URI + n; + wsi->current_alloc_len = LWS_INITIAL_HDR_ALLOC; + wsi->utf8_token[wsi->parser_state].token = + malloc(wsi->current_alloc_len); + wsi->utf8_token[wsi->parser_state].token_len = 0; + n = WSI_TOKEN_COUNT; + } + + /* colon delimiter means we just don't know this name */ + + if (wsi->parser_state == WSI_TOKEN_NAME_PART && c == ':') { + debug("skipping unknown header '%s'\n", + wsi->name_buffer); + wsi->parser_state = WSI_TOKEN_SKIPPING; + break; + } + + /* don't look for payload when it can just be http headers */ + + if (wsi->parser_state == WSI_TOKEN_CHALLENGE && + !wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len) { + /* they're HTTP headers, not websocket upgrade! */ + debug("Setting WSI_PARSING_COMPLETE " + "from http headers\n"); + wsi->parser_state = WSI_PARSING_COMPLETE; + } + break; + + /* skipping arg part of a name we didn't recognize */ + case WSI_TOKEN_SKIPPING: + debug("WSI_TOKEN_SKIPPING '%c'\n", c); + if (c == '\x0d') + wsi->parser_state = WSI_TOKEN_SKIPPING_SAW_CR; + break; + case WSI_TOKEN_SKIPPING_SAW_CR: + debug("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c); + if (c == '\x0a') + wsi->parser_state = WSI_TOKEN_NAME_PART; + else + wsi->parser_state = WSI_TOKEN_SKIPPING; + wsi->name_buffer_pos = 0; + break; + /* we're done, ignore anything else */ + case WSI_PARSING_COMPLETE: + debug("WSI_PARSING_COMPLETE '%c'\n", c); + break; + + default: /* keep gcc happy */ + break; + } + + return 0; +} + + +static int libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) +{ + int n; + unsigned char buf[2]; + + switch (wsi->lws_rx_parse_state) { + case LWS_RXPS_NEW: + + switch (wsi->ietf_spec_revision) { + /* Firefox 4.0b6 likes this as of 30 Oct */ + case 76: + if (c == 0xff) + wsi->lws_rx_parse_state = LWS_RXPS_SEEN_76_FF; + break; + case 0: + break; + } + break; + case LWS_RXPS_SEEN_76_FF: + if (c) + break; + + debug("Seen that client is requesting " + "a v76 close, sending ack\n"); + buf[0] = 0xff; + buf[1] = 0; + n = libwebsocket_write(wsi, buf, 2, LWS_WRITE_HTTP); + if (n < 0) { + fprintf(stderr, "ERROR writing to socket"); + return -1; + } + debug(" v76 close ack sent, server closing skt\n"); + /* returning < 0 will get it closed in parent */ + return -1; + + case LWS_RXPS_PULLING_76_LENGTH: + break; + case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED: + break; + } + + return 0; +} + +int libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi, + unsigned char *buf, size_t len) +{ + int n; + + fprintf(stderr, "received %d byte packet\n", (int)len); + for (n = 0; n < len; n++) + fprintf(stderr, "%02X ", buf[n]); + fprintf(stderr, "\n"); + + /* let the rx protocol state machine have as much as it needs */ + + n = 0; + while (wsi->lws_rx_parse_state != + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED && n < len) + if (libwebsocket_rx_sm(wsi, buf[n++]) < 0) + return -1; + + if (n != len && wsi->callback) + wsi->callback(wsi, LWS_CALLBACK_RECEIVE, &wsi->user_space[0], + &buf[n], len - n); + + return -0; +} + + + +/** + * libwebsocket_write() - Apply protocol then write data to client + * @wsi: Websocket instance (available from user callback) + * @buf: The data to send. For data being sent on a websocket + * connection (ie, not default http), this buffer MUST have + * LWS_SEND_BUFFER_PRE_PADDING bytes valid BEFORE the pointer + * and an additional LWS_SEND_BUFFER_POST_PADDING bytes valid + * in the buffer after (buf + len). This is so the protocol + * header and trailer data can be added in-situ. + * @len: Count of the data bytes in the payload starting from buf + * @protocol: Use LWS_WRITE_HTTP to reply to an http connection, and one + * of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate + * data on a websockets connection. Remember to allow the extra + * bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT + * are used. + * + * This function provides the way to issue data back to the client + * for both http and websocket protocols. + * + * In the case of sending using websocket protocol, be sure to allocate + * valid storage before and after buf as explained above. This scheme + * allows maximum efficiency of sending data and protocol in a single + * packet while not burdening the user code with any protocol knowledge. + */ + +int libwebsocket_write(struct libwebsocket * wsi, unsigned char *buf, + size_t len, enum libwebsocket_write_protocol protocol) +{ + int n; + int pre = 0; + int post = 0; + unsigned int shift = 7; + + if (protocol == LWS_WRITE_HTTP) + goto send_raw; + + /* websocket protocol, either binary or text */ + + if (wsi->state != WSI_STATE_ESTABLISHED) + return -1; + + switch (wsi->ietf_spec_revision) { + /* chrome likes this as of 30 Oct */ + /* Firefox 4.0b6 likes this as of 30 Oct */ + case 76: + if (protocol == LWS_WRITE_BINARY) { + /* in binary mode we send 7-bit used length blocks */ + pre = 1; + while (len & (127 << shift)) { + pre++; + shift += 7; + } + n = 0; + shift -= 7; + while (shift >= 0) { + if (shift) + buf[0 - pre + n] = + ((len >> shift) & 127) | 0x80; + else + buf[0 - pre + n] = + ((len >> shift) & 127); + n++; + shift -= 7; + } + break; + } + + /* frame type = text, length-free spam mode */ + + buf[-1] = 0; + buf[len] = 0xff; /* EOT marker */ + pre = 1; + post = 1; + break; + + case 0: + buf[-9] = 0xff; +#if defined __LP64__ + buf[-8] = len >> 56; + buf[-7] = len >> 48; + buf[-6] = len >> 40; + buf[-5] = len >> 32; +#else + buf[-8] = 0; + buf[-7] = 0; + buf[-6] = 0; + buf[-5] = 0; +#endif + buf[-4] = len >> 24; + buf[-3] = len >> 16; + buf[-2] = len >> 8; + buf[-1] = len; + pre = 9; + break; + + /* just an unimplemented spec right now apparently */ + case 2: + n = 4; /* text */ + if (protocol == LWS_WRITE_BINARY) + n = 5; /* binary */ + if (len < 126) { + buf[-2] = n; + buf[-1] = len; + pre = 2; + } else { + if (len < 65536) { + buf[-4] = n; + buf[-3] = 126; + buf[-2] = len >> 8; + buf[-1] = len; + pre = 4; + } else { + buf[-10] = n; + buf[-9] = 127; +#if defined __LP64__ + buf[-8] = (len >> 56) & 0x7f; + buf[-7] = len >> 48; + buf[-6] = len >> 40; + buf[-5] = len >> 32; +#else + buf[-8] = 0; + buf[-7] = 0; + buf[-6] = 0; + buf[-5] = 0; +#endif + buf[-4] = len >> 24; + buf[-3] = len >> 16; + buf[-2] = len >> 8; + buf[-1] = len; + pre = 10; + } + } + break; + } + +#if 0 + for (n = 0; n < (len + pre + post); n++) + fprintf(stderr, "%02X ", buf[n - pre]); + + fprintf(stderr, "\n"); +#endif + +send_raw: +#ifdef LWS_OPENSSL_SUPPORT + if (use_ssl) { + n = SSL_write(wsi->ssl, buf - pre, len + pre + post); + if (n < 0) { + fprintf(stderr, "ERROR writing to socket"); + return -1; + } + } else { +#endif + n = send(wsi->sock, buf - pre, len + pre + post, 0); + if (n < 0) { + fprintf(stderr, "ERROR writing to socket"); + return -1; + } +#ifdef LWS_OPENSSL_SUPPORT + } +#endif +// fprintf(stderr, "written %d bytes to client\n", (int)len); + + return 0; +} + + +/** + * libwebsockets_serve_http_file() - Send a file back to the client using http + * @wsi: Websocket instance (available from user callback) + * @file: The file to issue over http + * @content_type: The http content type, eg, text/html + * + * This function is intended to be called from the callback in response + * to http requests from the client. It allows the callback to issue + * local files down the http link in a single step. + */ + +int libwebsockets_serve_http_file(struct libwebsocket *wsi, const char * file, + const char * content_type) +{ + int fd; + struct stat stat; + char buf[512]; + char *p = buf; + int n; + + fd = open(file, O_RDONLY); + if (fd < 1) { + p += sprintf(p, "HTTP/1.0 400 Bad\x0d\x0a" + "Server: libwebsockets\x0d\x0a" + "\x0d\x0a" + ); + libwebsocket_write(wsi, (unsigned char *)buf, p - buf, + LWS_WRITE_HTTP); + + return -1; + } + + fstat(fd, &stat); + p += sprintf(p, "HTTP/1.0 200 OK\x0d\x0a" + "Server: libwebsockets\x0d\x0a" + "Content-Type: %s\x0d\x0a" + "Content-Length: %u\x0d\x0a" + "\x0d\x0a", content_type, (unsigned int)stat.st_size); + + libwebsocket_write(wsi, (unsigned char *)buf, p - buf, LWS_WRITE_HTTP); + + n = 1; + while (n > 0) { + n = read(fd, buf, 512); + libwebsocket_write(wsi, (unsigned char *)buf, n, + LWS_WRITE_HTTP); + } + + close(fd); + + return 0; +} diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h new file mode 100644 index 0000000..4009064 --- /dev/null +++ b/lib/private-libwebsockets.h @@ -0,0 +1,148 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef LWS_OPENSSL_SUPPORT +#include +#include +#include +#endif + +#include "libwebsockets.h" + +//#define DEBUG + + +#ifdef DEBUG +#define debug(format, args...) \ + fprintf(stderr, format , ## args) +#else +#define debug(format, args...) +#endif + +#ifdef LWS_OPENSSL_SUPPORT +extern SSL_CTX *ssl_ctx; +extern int use_ssl; +#endif + + +#define MAX_CLIENTS 100 +#define LWS_MAX_HEADER_NAME_LENGTH 64 +#define LWS_MAX_HEADER_LEN 4096 +#define LWS_INITIAL_HDR_ALLOC 256 +#define LWS_ADDITIONAL_HDR_ALLOC 64 + + + +enum lws_connection_states { + WSI_STATE_HTTP, + WSI_STATE_HTTP_HEADERS, + WSI_STATE_DEAD_SOCKET, + WSI_STATE_ESTABLISHED +}; + +enum lws_token_indexes { + WSI_TOKEN_GET_URI, + WSI_TOKEN_HOST, + WSI_TOKEN_CONNECTION, + WSI_TOKEN_KEY1, + WSI_TOKEN_KEY2, + WSI_TOKEN_PROTOCOL, + WSI_TOKEN_UPGRADE, + WSI_TOKEN_ORIGIN, + WSI_TOKEN_CHALLENGE, + + /* always last real token index*/ + WSI_TOKEN_COUNT, + /* parser state additions */ + WSI_TOKEN_NAME_PART, + WSI_TOKEN_SKIPPING, + WSI_TOKEN_SKIPPING_SAW_CR, + WSI_PARSING_COMPLETE +}; + +enum lws_rx_parse_state { + LWS_RXPS_NEW, + + LWS_RXPS_SEEN_76_FF, + LWS_RXPS_PULLING_76_LENGTH, + + LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED +}; + + +struct lws_tokens { + char * token; + int token_len; +}; + + +/* + * This is totally opaque to code using the library. It's exported as a + * forward-reference pointer-only declaration; the user can use the pointer with + * other APIs to get information out of it. + */ + +struct libwebsocket { + int (*callback)(struct libwebsocket *, + enum libwebsocket_callback_reasons reason, void *, void *, size_t); + + enum lws_connection_states state; + + char name_buffer[LWS_MAX_HEADER_NAME_LENGTH]; + int name_buffer_pos; + int current_alloc_len; + enum lws_token_indexes parser_state; + struct lws_tokens utf8_token[WSI_TOKEN_COUNT]; + int ietf_spec_revision; + + int sock; + + enum lws_rx_parse_state lws_rx_parse_state; + size_t rx_packet_length; + +#ifdef LWS_OPENSSL_SUPPORT + SSL *ssl; +#endif + + /* last */ + char user_space[0]; +}; + +extern void +libwebsocket_close_and_free_session(struct libwebsocket *wsi); diff --git a/test-server/test-server.c b/test-server/test-server.c index 81be54a..755d932 100644 --- a/test-server/test-server.c +++ b/test-server/test-server.c @@ -104,7 +104,8 @@ static int websocket_callback(struct libwebsocket * wsi, */ case LWS_CALLBACK_SEND: n = sprintf(p, "%d", pss->number++); - n = libwebsocket_write(wsi, (unsigned char *)p, n, LWS_WRITE_TEXT); + n = libwebsocket_write(wsi, (unsigned char *)p, n, + LWS_WRITE_TEXT); if (n < 0) { fprintf(stderr, "ERROR writing to socket"); exit(1); @@ -163,8 +164,10 @@ static struct option options[] = { int main(int argc, char **argv) { int n = 0; - const char * cert_path = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem"; - const char * key_path = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem"; + const char * cert_path = + LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem"; + const char * key_path = + LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem"; fprintf(stderr, "libwebsockets test server\n" "Copyright 2010 Andy Green " -- 2.7.4