mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
real http status codes update attack.sh
Signed-off-by: Andy Green <andy.green@linaro.org>
This commit is contained in:
parent
6f42910987
commit
4e7a13314d
7 changed files with 210 additions and 101 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
75
lib/output.c
75
lib/output.c
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
139
lib/server.c
139
lib/server.c
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue