1
0
Fork 0
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:
Andy Green 2013-11-11 07:30:33 +08:00
parent 6f42910987
commit 4e7a13314d
7 changed files with 210 additions and 101 deletions

View 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;

View 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);
}

View 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);

View 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);
}

View 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);
}

View file

@ -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

View 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;
}