real http status codes update attack.sh
authorAndy Green <andy.green@linaro.org>
Sun, 10 Nov 2013 23:30:33 +0000 (07:30 +0800)
committerAndy Green <andy.green@linaro.org>
Sun, 10 Nov 2013 23:30:33 +0000 (07:30 +0800)
Signed-off-by: Andy Green <andy.green@linaro.org>
lib/handshake.c
lib/libwebsockets.c
lib/libwebsockets.h
lib/output.c
lib/server.c
test-server/attack.sh
test-server/test-server.c

index f1e7dad..1935c25 100644 (file)
@@ -139,6 +139,7 @@ libwebsocket_read(struct libwebsocket_context *context,
                        memset(&wsi->u, 0, sizeof(wsi->u));
                        wsi->mode = LWS_CONNMODE_HTTP_SERVING_ACCEPTED;
                        wsi->state = WSI_STATE_HTTP;
+                       wsi->u.http.fd = -1;
 
                        /* expose it at the same offset as u.hdr */
                        wsi->u.http.ah = ah;
index 475162b..2cb3ab0 100644 (file)
@@ -217,10 +217,10 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context,
        }
 
 
-       if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED && wsi->u.http.fd) {
+       if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED && wsi->u.http.fd >= 0) {
                lwsl_debug("closing http fd %d\n", wsi->u.http.fd);
                close(wsi->u.http.fd);
-               wsi->u.http.fd = 0;
+               wsi->u.http.fd = -1;
                context->protocols[0].callback(context, wsi,
                        LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
        }
index 54d485a..4c121af 100644 (file)
@@ -127,6 +127,8 @@ LWS_VISIBLE LWS_EXTERN void lwsl_hexdump(void *buf, size_t len);
 
 #endif
 
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
 enum libwebsocket_context_options {
        LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT = 2,
        LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME = 4,
@@ -394,6 +396,34 @@ enum lws_close_status {
        LWS_CLOSE_STATUS_TLS_FAILURE = 1015,
 };
 
+enum http_status {
+       HTTP_STATUS_BAD_REQUEST = 400,
+       HTTP_STATUS_UNAUTHORIZED,
+       HTTP_STATUS_PAYMENT_REQUIRED,
+       HTTP_STATUS_FORBIDDEN,
+       HTTP_STATUS_NOT_FOUND,
+       HTTP_STATUS_METHOD_NOT_ALLOWED,
+       HTTP_STATUS_NOT_ACCEPTABLE,
+       HTTP_STATUS_PROXY_AUTH_REQUIRED,
+       HTTP_STATUS_REQUEST_TIMEOUT,
+       HTTP_STATUS_CONFLICT,
+       HTTP_STATUS_GONE,
+       HTTP_STATUS_LENGTH_REQUIRED,
+       HTTP_STATUS_PRECONDITION_FAILED,
+       HTTP_STATUS_REQ_ENTITY_TOO_LARGE,
+       HTTP_STATUS_REQ_URI_TOO_LONG,
+       HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE,
+       HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE,
+       HTTP_STATUS_EXPECTATION_FAILED,
+
+       HTTP_STATUS_INTERNAL_SERVER_ERROR = 500,
+       HTTP_STATUS_NOT_IMPLEMENTED,
+       HTTP_STATUS_BAD_GATEWAY,
+       HTTP_STATUS_SERVICE_UNAVAILABLE,
+       HTTP_STATUS_GATEWAY_TIMEOUT,
+       HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
+};
+
 struct libwebsocket;
 struct libwebsocket_context;
 /* needed even with extensions disabled for create context */
@@ -940,6 +970,11 @@ LWS_VISIBLE LWS_EXTERN int
 libwebsockets_serve_http_file_fragment(struct libwebsocket_context *context,
                        struct libwebsocket *wsi);
 
+LWS_VISIBLE int libwebsockets_return_http_status(
+               struct libwebsocket_context *context,
+                       struct libwebsocket *wsi, unsigned int code,
+                                                       const char *html_body);
+
 LWS_VISIBLE LWS_EXTERN const struct libwebsocket_protocols *
 libwebsockets_get_protocol(struct libwebsocket *wsi);
 
index 9abe087..5a661b0 100644 (file)
@@ -641,78 +641,3 @@ LWS_VISIBLE int libwebsockets_serve_http_file_fragment(
 
        return 0; /* indicates further processing must be done */
 }
-
-/**
- * libwebsockets_serve_http_file() - Send a file back to the client using http
- * @context:           libwebsockets context
- * @wsi:               Websocket instance (available from user callback)
- * @file:              The file to issue over http
- * @content_type:      The http content type, eg, text/html
- * @other_headers:     NULL or pointer to \0-terminated other header string
- *
- *     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.
- *
- *     Returning <0 indicates error and the wsi should be closed.  Returning
- *     >0 indicates the file was completely sent and the wsi should be closed.
- *     ==0 indicates the file transfer is started and needs more service later,
- *     the wsi should be left alone.
- */
-
-LWS_VISIBLE int libwebsockets_serve_http_file(
-               struct libwebsocket_context *context,
-                       struct libwebsocket *wsi, const char *file,
-                          const char *content_type, const char *other_headers)
-{
-       struct stat stat_buf;
-       unsigned char *p = context->service_buffer;
-       int ret = 0;
-       int n;
-
-       wsi->u.http.fd = open(file, O_RDONLY
-#ifdef WIN32
-                        | _O_BINARY
-#endif
-       );
-
-       if (wsi->u.http.fd < 1) {
-               lwsl_err("Unable to open '%s'\n", file);
-               p += sprintf((char *)p,
-                "HTTP/1.0 400 Bad\x0d\x0aServer: libwebsockets\x0d\x0a\x0d\x0a"
-               );
-               wsi->u.http.fd = 0;
-               /* too small to care about partial, closing anyway */
-               libwebsocket_write(wsi, context->service_buffer,
-                               p - context->service_buffer, LWS_WRITE_HTTP);
-
-               return -1;
-       }
-
-       fstat(wsi->u.http.fd, &stat_buf);
-       wsi->u.http.filelen = stat_buf.st_size;
-       p += sprintf((char *)p,
-"HTTP/1.0 200 OK\x0d\x0aServer: libwebsockets\x0d\x0a""Content-Type: %s\x0d\x0a",
-                                                                 content_type);
-       if (other_headers) {
-               n = strlen(other_headers);
-               memcpy(p, other_headers, n);
-               p += n;
-       }
-       p += sprintf((char *)p,
-               "Content-Length: %u\x0d\x0a\x0d\x0a",
-                                       (unsigned int)stat_buf.st_size);
-
-       ret = libwebsocket_write(wsi, context->service_buffer,
-                                  p - context->service_buffer, LWS_WRITE_HTTP);
-       if (ret != (p - context->service_buffer)) {
-               lwsl_err("_write returned %d from %d\n", ret, (p - context->service_buffer));
-               return -1;
-       }
-
-       wsi->u.http.filepos = 0;
-       wsi->state = WSI_STATE_HTTP_ISSUING_FILE;
-
-       return libwebsockets_serve_http_file_fragment(context, wsi);
-}
-
index bc8b497..7c0421a 100644 (file)
@@ -389,3 +389,142 @@ int lws_server_socket_service(struct libwebsocket_context *context,
        return 0;
 }
 
+
+static const char *err400[] = {
+       "Bad Request",
+       "Unauthorized",
+       "Payment Required",
+       "Forbidden",
+       "Not Found",
+       "Method Not Allowed",
+       "Not Acceptable",
+       "Proxy Auth Required",
+       "Request Timeout",
+       "Conflict",
+       "Gone",
+       "Length Required",
+       "Precondition Failed",
+       "Request Entity Too Large",
+       "Request URI too Long",
+       "Unsupported Media Type",
+       "Requested Range Not Satisfiable",
+       "Expectation Failed"
+};
+
+static const char *err500[] = {
+       "Internal Server Error",
+       "Not Implemented",
+       "Bad Gateway",
+       "Service Unavailable",
+       "Gateway Timeout",
+       "HTTP Version Not Supported"
+};
+
+/**
+ * libwebsockets_return_http_status() - Return simple http status
+ * @context:           libwebsockets context
+ * @wsi:               Websocket instance (available from user callback)
+ * @code:              Status index, eg, 404
+ * @html_body:         User-readable HTML description, or NULL
+ *
+ *     Helper to report HTTP errors back to the client cleanly and
+ *     consistently
+ */
+LWS_VISIBLE int libwebsockets_return_http_status(
+               struct libwebsocket_context *context, struct libwebsocket *wsi,
+                                      unsigned int code, const char *html_body)
+{
+       int n, m;
+       const char *description = "";
+
+       if (!html_body)
+               html_body = "";
+
+       if (code >= 400 && code < (400 + ARRAY_SIZE(err400)))
+               description = err400[code - 400];
+       if (code >= 500 && code < (500 + ARRAY_SIZE(err500)))
+               description = err500[code - 500];
+
+       n = sprintf((char *)context->service_buffer,
+               "HTTP/1.0 %u %s\x0d\x0a"
+               "Server: libwebsockets\x0d\x0a"
+               "Mime-Type: text/html\x0d\x0a\x0d\x0a"
+               "<h1>%u %s</h1>%s",
+               code, description, code, description, html_body);
+
+       lwsl_info((const char *)context->service_buffer);
+
+       m = libwebsocket_write(wsi, context->service_buffer, n, LWS_WRITE_HTTP);
+
+       return m;
+}
+
+/**
+ * libwebsockets_serve_http_file() - Send a file back to the client using http
+ * @context:           libwebsockets context
+ * @wsi:               Websocket instance (available from user callback)
+ * @file:              The file to issue over http
+ * @content_type:      The http content type, eg, text/html
+ * @other_headers:     NULL or pointer to \0-terminated other header string
+ *
+ *     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.
+ *
+ *     Returning <0 indicates error and the wsi should be closed.  Returning
+ *     >0 indicates the file was completely sent and the wsi should be closed.
+ *     ==0 indicates the file transfer is started and needs more service later,
+ *     the wsi should be left alone.
+ */
+
+LWS_VISIBLE int libwebsockets_serve_http_file(
+               struct libwebsocket_context *context,
+                       struct libwebsocket *wsi, const char *file,
+                          const char *content_type, const char *other_headers)
+{
+       struct stat stat_buf;
+       unsigned char *p = context->service_buffer;
+       int ret = 0;
+       int n;
+
+       wsi->u.http.fd = open(file, O_RDONLY
+#ifdef WIN32
+                        | _O_BINARY
+#endif
+       );
+
+       if (wsi->u.http.fd < 1) {
+               lwsl_err("Unable to open '%s'\n", file);
+               libwebsockets_return_http_status(context, wsi,
+                                               HTTP_STATUS_NOT_FOUND, NULL);
+               wsi->u.http.fd = -1;
+               return -1;
+       }
+
+       fstat(wsi->u.http.fd, &stat_buf);
+       wsi->u.http.filelen = stat_buf.st_size;
+       p += sprintf((char *)p,
+"HTTP/1.0 200 OK\x0d\x0aServer: libwebsockets\x0d\x0a""Content-Type: %s\x0d\x0a",
+                                                                 content_type);
+       if (other_headers) {
+               n = strlen(other_headers);
+               memcpy(p, other_headers, n);
+               p += n;
+       }
+       p += sprintf((char *)p,
+               "Content-Length: %u\x0d\x0a\x0d\x0a",
+                                       (unsigned int)stat_buf.st_size);
+
+       ret = libwebsocket_write(wsi, context->service_buffer,
+                                  p - context->service_buffer, LWS_WRITE_HTTP);
+       if (ret != (p - context->service_buffer)) {
+               lwsl_err("_write returned %d from %d\n", ret, (p - context->service_buffer));
+               return -1;
+       }
+
+       wsi->u.http.filepos = 0;
+       wsi->state = WSI_STATE_HTTP_ISSUING_FILE;
+
+       return libwebsockets_serve_http_file_fragment(context, wsi);
+}
+
index 84328ac..c127907 100755 (executable)
@@ -107,7 +107,7 @@ check
 
 echo
 echo "---- good request but http payload coming too (should be ignored and test.html served)"
-echo -e "GET blah HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD........................................" \
+echo -e "GET /test.html HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD........................................" \
        "......................................................................................................................." \
        "......................................................................................................................." \
        "......................................................................................................................." \
@@ -125,10 +125,9 @@ echo -e "GET blah HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD.......................
        "......................................................................................................................." \
         | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check
-size=`stat /tmp/lwscap | grep Size: | tr -s ' ' | cut -d' ' -f3`
-if [ $size -ne 0 ] ; then
-       echo "FAIL: got something back when should have hung up"
-       cat /tmp/lwscap
+diff /tmp/lwscap /usr/share/libwebsockets-test-server/test.html > /dev/null
+if [ $? -ne 0 ] ; then
+       echo "FAIL: got something other than test.html back"
        exit 1
 fi
 
@@ -137,9 +136,8 @@ echo "---- directory attack 1 (/../../../../etc/passwd should be /etc/passswd)"
 rm -f /tmp/lwscap
 echo -e "GET /../../../../etc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check
-size=`stat /tmp/lwscap | grep Size: | tr -s ' ' | cut -d' ' -f3`
-if [ $size -ne 0 ] ; then
-       echo "FAIL: got something back when should have hung up"
+if [ -z "`grep '<h1>403 Forbidden</h1>' /tmp/lwscap`" ] ; then
+       echo "FAIL: should have told forbidden (test server has no dirs)"
        exit 1
 fi
 
@@ -170,9 +168,8 @@ echo "---- directory attack 4 (/blah/.. should be /blah/)"
 rm -f /tmp/lwscap
 echo -e "GET /blah/.. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check
-size=`stat /tmp/lwscap | grep Size: | tr -s ' ' | cut -d' ' -f3`
-if [ $size -ne 0 ] ; then
-       echo "FAIL: got something back when should have hung up"
+if [ -z "`grep '<h1>403 Forbidden</h1>' /tmp/lwscap`" ] ; then
+       echo "FAIL: should have told forbidden (test server has no dirs)"
        exit 1
 fi
 
@@ -181,9 +178,8 @@ echo "---- directory attack 5 (/blah/../ should be /blah/)"
 rm -f /tmp/lwscap
 echo -e "GET /blah/../ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check
-size=`stat /tmp/lwscap | grep Size: | tr -s ' ' | cut -d' ' -f3`
-if [ $size -ne 0 ] ; then
-       echo "FAIL: got something back when should have hung up"
+if [ -z "`grep '<h1>403 Forbidden</h1>' /tmp/lwscap`" ] ; then
+       echo "FAIL: should have told forbidden (test server has no dirs)"
        exit 1
 fi
 
@@ -192,9 +188,8 @@ echo "---- directory attack 6 (/blah/../. should be /blah/)"
 rm -f /tmp/lwscap
 echo -e "GET /blah/../. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check
-size=`stat /tmp/lwscap | grep Size: | tr -s ' ' | cut -d' ' -f3`
-if [ $size -ne 0 ] ; then
-       echo "FAIL: got something back when should have hung up"
+if [ -z "`grep '<h1>403 Forbidden</h1>' /tmp/lwscap`" ] ; then
+       echo "FAIL: should have told forbidden (test server has no dirs)"
        exit 1
 fi
 
@@ -203,9 +198,8 @@ echo "---- directory attack 7 (/%2e%2e%2f../../../etc/passwd should be /etc/pass
 rm -f /tmp/lwscap
 echo -e "GET /%2e%2e%2f../../../etc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check
-size=`stat /tmp/lwscap | grep Size: | tr -s ' ' | cut -d' ' -f3`
-if [ $size -ne 0 ] ; then
-       echo "FAIL: got something back when should have hung up"
+if [ -z "`grep '<h1>403 Forbidden</h1>' /tmp/lwscap`" ] ; then
+       echo "FAIL: should have told forbidden (test server has no dirs)"
        exit 1
 fi
 
@@ -214,13 +208,11 @@ echo "---- directory attack 7 (%2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd should be
 rm -f /tmp/lwscap
 echo -e "GET %2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
 check
-size=`stat /tmp/lwscap | grep Size: | tr -s ' ' | cut -d' ' -f3`
-if [ $size -ne 0 ] ; then
-       echo "FAIL: got something back when should have hung up"
+if [ -z "`grep '<h1>403 Forbidden</h1>' /tmp/lwscap`" ] ; then
+       echo "FAIL: should have told forbidden (test server has no dirs)"
        exit 1
 fi
 
-
 echo
 echo "--- survived"
 kill -2 $CPID
index 35150de..899314c 100644 (file)
@@ -152,6 +152,19 @@ static int callback_http(struct libwebsocket_context *context,
        switch (reason) {
        case LWS_CALLBACK_HTTP:
 
+               if (len < 1) {
+                       libwebsockets_return_http_status(context, wsi,
+                                               HTTP_STATUS_BAD_REQUEST, NULL);
+                       return -1;
+               }
+
+               /* this server has no concept of directories */
+               if (strchr((const char *)in + 1, '/')) {
+                       libwebsockets_return_http_status(context, wsi,
+                                               HTTP_STATUS_FORBIDDEN, NULL);
+                       return -1;
+               }
+
                /* check for the "send a big file by hand" example case */
 
                if (!strcmp((const char *)in, "/leaf.jpg")) {
@@ -217,9 +230,13 @@ static int callback_http(struct libwebsocket_context *context,
                } else /* default file to serve */
                        strcat(buf, "/test.html");
                buf[sizeof(buf) - 1] = '\0';
+
+               /* refuse to serve files we don't understand */
                mimetype = get_mimetype(buf);
                if (!mimetype) {
                        lwsl_err("Unknown mimetype for %s\n", buf);
+                       libwebsockets_return_http_status(context, wsi,
+                                     HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
                        return -1;
                }