summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2017-10-28 00:03:18 +0200
committerSimon Rettberg2017-10-28 00:03:18 +0200
commitdc6ba44ea66c471305908a8053d7de56d705e499 (patch)
treee38ca2ac8c67523c0d3e5145e4f050831f16c0a6
parent[SERVER] Use picohttpparser from h2o (diff)
downloaddnbd3-dc6ba44ea66c471305908a8053d7de56d705e499.tar.gz
dnbd3-dc6ba44ea66c471305908a8053d7de56d705e499.tar.xz
dnbd3-dc6ba44ea66c471305908a8053d7de56d705e499.zip
[SERVER] Add function to parse x-www-form-urlencoded strings
Use it to properly parse RPC queries. Will also come in handy when parsing POST body for calls that actually trigger any actions in the server (reload, alt-servers, ...)
-rw-r--r--src/server/picohttpparser/picohttpparser.h1
-rw-r--r--src/server/rpc.c71
-rw-r--r--src/server/urldecode.c61
-rw-r--r--src/server/urldecode.h19
4 files changed, 126 insertions, 26 deletions
diff --git a/src/server/picohttpparser/picohttpparser.h b/src/server/picohttpparser/picohttpparser.h
index 582a1b8..b315795 100644
--- a/src/server/picohttpparser/picohttpparser.h
+++ b/src/server/picohttpparser/picohttpparser.h
@@ -44,7 +44,6 @@ struct string {
size_t l;
};
-
/* contains name and value of a header (name == NULL if is a continuing line
* of a multiline header */
struct phr_header {
diff --git a/src/server/rpc.c b/src/server/rpc.c
index 480f5bb..6b958be 100644
--- a/src/server/rpc.c
+++ b/src/server/rpc.c
@@ -7,6 +7,7 @@
#include "../shared/sockhelper.h"
#include "fileutil.h"
#include "picohttpparser/picohttpparser.h"
+#include "urldecode.h"
#include <jansson.h>
#include <sys/types.h>
@@ -32,8 +33,9 @@ static int aclCount = 0;
static dnbd3_access_rule_t aclRules[MAX_ACLS];
static json_int_t randomRunId;
-static bool handleStatus(int sock, struct string *path, int permissions);
+static bool handleStatus(int sock, int permissions, struct field *fields, size_t fields_num);
static bool sendReply(int sock, const char *status, const char *ctype, const char *payload, ssize_t plen, int keepAlive);
+static void parsePath(struct string *path, struct string *file, struct field *getv, size_t *getc);
static int getacl(dnbd3_host_t *host);
static void addacl(int argc, char **argv, void *data);
static void loadAcl();
@@ -48,7 +50,7 @@ void rpc_sendStatsJson(int sock, dnbd3_host_t* host, const void* data, const int
sendReply( sock, "403 Forbidden", "text/plain", "Access denied", -1, HTTP_CLOSE );
return;
}
- char headerBuf[1000];
+ char headerBuf[3000];
if ( dataLen > 0 ) {
// We call this function internally with a maximum data len of sizeof(dnbd3_request_t) so no bounds checking
memcpy( headerBuf, data, dataLen );
@@ -89,12 +91,17 @@ void rpc_sendStatsJson(int sock, dnbd3_host_t* host, const void* data, const int
} while ( true );
if ( method.s != NULL && path.s != NULL ) {
// Handle stuff
+ struct string file;
+ struct field getv[10];
+ size_t getc = 10;
+ parsePath( &path, &file, getv, &getc );
if ( method.s && method.s[0] == 'P' ) {
// POST only methods
+
}
// Don't care if GET or POST
- if ( STRSTART( path, "/query" ) ) {
- ok = handleStatus( sock, &path, permissions );
+ if ( STRCMP( file, "/query" ) ) {
+ ok = handleStatus( sock, permissions, getv, getc );
} else {
ok = sendReply( sock, "404 Not found", "text/plain", "Nothing", -1, HTTP_KEEPALIVE );
}
@@ -109,33 +116,27 @@ void rpc_sendStatsJson(int sock, dnbd3_host_t* host, const void* data, const int
} while (true);
}
-static bool handleStatus(int sock, struct string *path, int permissions)
+static bool handleStatus(int sock, int permissions, struct field *fields, size_t fields_num)
{
bool ok;
bool stats = false, images = false, clients = false, space = false;
- if ( strstr( path->s, "stats" ) != NULL ) {
- if ( !(permissions & ACL_STATS) ) {
- return sendReply( sock, "403 Forbidden", "text/plain", "No permission to access statistics", -1, HTTP_KEEPALIVE );
- }
- stats = true;
+#define SETVAR(var) if ( !var && STRCMP(fields[i].value, #var) ) var = true
+ for (size_t i = 0; i < fields_num; ++i) {
+ if ( !STRCMP( fields[i].name, "q" ) ) continue;
+ SETVAR(stats);
+ else SETVAR(space);
+ else SETVAR(images);
+ else SETVAR(clients);
}
- if ( strstr( path->s, "space" ) != NULL ) {
- if ( !(permissions & ACL_STATS) ) {
- return sendReply( sock, "403 Forbidden", "text/plain", "No permission to access statistics", -1, HTTP_KEEPALIVE );
- }
- space = true;
+#undef SETVAR
+ if ( ( stats || space ) && !(permissions & ACL_STATS) ) {
+ return sendReply( sock, "403 Forbidden", "text/plain", "No permission to access statistics", -1, HTTP_KEEPALIVE );
}
- if ( strstr( path->s, "images" ) != NULL ) {
- if ( !(permissions & ACL_IMAGE_LIST) ) {
- return sendReply( sock, "403 Forbidden", "text/plain", "No permission to access image list", -1, HTTP_KEEPALIVE );
- }
- images = true;
+ if ( images && !(permissions & ACL_IMAGE_LIST) ) {
+ return sendReply( sock, "403 Forbidden", "text/plain", "No permission to access image list", -1, HTTP_KEEPALIVE );
}
- if ( strstr(path->s, "clients" ) != NULL ) {
- if ( !(permissions & ACL_CLIENT_LIST) ) {
- return sendReply( sock, "403 Forbidden", "text/plain", "No permission to access client list", -1, HTTP_KEEPALIVE );
- }
- clients = true;
+ if ( clients && !(permissions & ACL_CLIENT_LIST) ) {
+ return sendReply( sock, "403 Forbidden", "text/plain", "No permission to access client list", -1, HTTP_KEEPALIVE );
}
// Call this first because it will update the total bytes sent counter
json_t *jsonClients = NULL;
@@ -197,10 +198,30 @@ static bool sendReply(int sock, const char *status, const char *ctype, const cha
// Wait for flush
shutdown( sock, SHUT_WR );
while ( read( sock, buffer, sizeof buffer ) > 0 );
+ return false;
}
return true;
}
+static void parsePath(struct string *path, struct string *file, struct field *getv, size_t *getc)
+{
+ size_t i = 0;
+ while ( i < path->l && path->s[i] != '?' ) ++i;
+ if ( i == path->l ) {
+ *getc = 0;
+ *file = *path;
+ return;
+ }
+ file->s = path->s;
+ file->l = i;
+ ++i;
+ path->s += i;
+ path->l -= i;
+ urldecode( path, getv, getc );
+ path->s -= i;
+ path->l += i;
+}
+
static int getacl(dnbd3_host_t *host)
{
if ( aclCount == 0 ) return 0x7fffff; // For now compat mode - no rules defined == all access
diff --git a/src/server/urldecode.c b/src/server/urldecode.c
new file mode 100644
index 0000000..4553097
--- /dev/null
+++ b/src/server/urldecode.c
@@ -0,0 +1,61 @@
+#include "urldecode.h"
+#include <stdlib.h>
+#include <ctype.h>
+
+#define hex2int(a) do { \
+ if ( a >= 'a' ) { \
+ a = (char)(a - ( 'a' - 'A' - 10 )); \
+ } else if ( a > 'F' ) { \
+ goto normie; \
+ } else if ( a >= 'A' ) { \
+ a = (char)(a - ( 'A' - 10 )); \
+ } else if ( a < '0' || a > '9' ) { \
+ goto normie; \
+ } else { \
+ a = (char)(a - '0'); \
+ } \
+} while (0)
+
+void urldecode(struct string* str, struct field *out, size_t *out_num)
+{
+ char *src = (char*)str->s;
+ char *dst = src;
+ const char * const end = str->s + str->l;
+ char a, b;
+ size_t max_out = *out_num;
+ *out_num = 0;
+ do {
+ if ( *out_num == max_out ) return;
+ out->name.s = dst;
+ while ( src < end && *src != '=' ) {
+ *dst++ = *src++;
+ }
+ if ( src == end ) return;
+ out->name.l = (size_t)( dst - out->name.s );
+ ++src;
+ out->value.s = ++dst;
+ while ( src < end && *src != '&' ) {
+ if ( *src == '%' && src + 2 < end ) {
+ if ( src[1] > 'f' || src[2] > 'f' ) goto normie;
+ a = src[1];
+ hex2int(a);
+ b = src[2];
+ hex2int(b);
+ *dst++ = (char)( (16 * a) + b );
+ src += 3;
+ } else if (*src == '+') {
+ *dst++ = (char)' ';
+ ++src;
+ } else {
+ normie:;
+ *dst++ = *src++;
+ }
+ }
+ out->value.l = (size_t)( dst - out->value.s );
+ out++;
+ (*out_num)++;
+ if ( src++ >= end ) return;
+ ++dst;
+ } while ( 1 );
+}
+
diff --git a/src/server/urldecode.h b/src/server/urldecode.h
new file mode 100644
index 0000000..e27f8f8
--- /dev/null
+++ b/src/server/urldecode.h
@@ -0,0 +1,19 @@
+#ifndef _URLENCODE_H_
+#define _URLENCODE_H_
+
+#include "picohttpparser/picohttpparser.h"
+
+struct field {
+ struct string name;
+ struct string value;
+};
+
+/**
+ * decode given x-form-urlencoded string. Breaks constness rules by
+ * casting the const char* s from str to char* and modifying it, then
+ * populating out with pointers into it, so make sure the memory
+ * is actually writable.
+ */
+void urldecode(struct string* str, struct field *out, size_t *out_num);
+
+#endif