mirror of
https://github.com/Netflix/dial-reference.git
synced 2026-06-08 02:49:58 +00:00
Reject HTTP requests with an invalid (e.g. negative) Content-Length header value.
Make sure to allocate enough memory in buffers for the operations they are used for, and to check/enforce buffer sizes when performing those memory operations. Properly allocate and free memory. Make a best effort at allocating memory for the network hardware address (remove code for Apple platforms). Try to consume all of the remaining content if a valid Content-Length header was provided. Check for success when attempting to acquire mutexes.
This commit is contained in:
@@ -44,46 +44,79 @@ void set_dial_data_dir(const char *data_dir) {
|
||||
static char* getAppPath(char *app_name) {
|
||||
size_t name_size = strlen(app_name) + sizeof(dial_data_dir) + 1;
|
||||
char* filename = (char*) malloc(name_size);
|
||||
if (filename == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
filename[0] = 0;
|
||||
strncat(filename, dial_data_dir, name_size);
|
||||
strncat(filename, app_name, name_size - sizeof(dial_data_dir));
|
||||
return filename;
|
||||
}
|
||||
|
||||
void store_dial_data(char app_name[], DIALData *data) {
|
||||
void store_dial_data(char *app_name, DIALData *data) {
|
||||
char* filename = getAppPath(app_name);
|
||||
if (filename == NULL) {
|
||||
printf("Cannot open DIAL data output file, out-of-memory.");
|
||||
exit(1);
|
||||
}
|
||||
FILE *f = fopen(filename, "w");
|
||||
free(filename); filename = NULL;
|
||||
if (f == NULL) {
|
||||
printf("Cannot open DIAL data output file: %s\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
free(filename);
|
||||
for (DIALData *first = data; first != NULL; first = first->next) {
|
||||
fprintf(f, "%s %s\n", first->key, first->value);
|
||||
// truncate because we have limits on length when retrieving.
|
||||
fprintf(f, "%.*s %.*s\n", DIAL_KEY_OR_VALUE_MAX_LEN, first->key, DIAL_KEY_OR_VALUE_MAX_LEN, first->value);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
DIALData *retrieve_dial_data(char *app_name) {
|
||||
char* filename = getAppPath(app_name);
|
||||
if (filename == NULL) {
|
||||
return NULL; // no dial data found, that's fine
|
||||
}
|
||||
FILE *f = fopen(filename, "r");
|
||||
free(filename);
|
||||
free(filename); filename = NULL;
|
||||
if (f == NULL) {
|
||||
return NULL; // no dial data found, that's fine
|
||||
}
|
||||
DIALData *result = NULL;
|
||||
char key[256];
|
||||
char value[256];
|
||||
while (fscanf(f, "%255s %255s\n", key, value) != EOF) {
|
||||
char key[DIAL_KEY_OR_VALUE_MAX_LEN + 1] = {0,};
|
||||
char value[DIAL_KEY_OR_VALUE_MAX_LEN + 1] = {0,};
|
||||
int err = 0;
|
||||
while (fscanf(f, "%" DIAL_KEY_OR_VALUE_MAX_LEN_STR "s %" DIAL_KEY_OR_VALUE_MAX_LEN_STR "s\n", key, value) != EOF) {
|
||||
DIALData *newNode = (DIALData *) malloc(sizeof(DIALData));
|
||||
newNode->key = (char *) calloc(1, strlen(key));
|
||||
if (newNode == NULL) {
|
||||
err = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
newNode->key = (char *) calloc(strlen(key) + 1, sizeof(char));
|
||||
if (newNode->key == NULL) {
|
||||
err = 1;
|
||||
free(newNode); newNode = NULL;
|
||||
break;
|
||||
}
|
||||
strncpy(newNode->key, key, strlen(key));
|
||||
newNode->value = (char *) calloc(1, strlen(value));
|
||||
|
||||
newNode->value = (char *) calloc(strlen(value) + 1, sizeof(char));
|
||||
if (newNode->value == NULL) {
|
||||
err = 1;
|
||||
free(newNode->key); newNode->key = NULL;
|
||||
free(newNode); newNode = NULL;
|
||||
break;
|
||||
}
|
||||
strncpy(newNode->value, value, strlen(value));
|
||||
newNode->next = result;
|
||||
result = newNode;
|
||||
}
|
||||
fclose(f);
|
||||
if (err) {
|
||||
free_dial_data(&result);
|
||||
result = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -92,10 +125,10 @@ void free_dial_data(DIALData **dialData)
|
||||
DIALData *curNode=NULL;
|
||||
while (*dialData != NULL) {
|
||||
curNode = *dialData;
|
||||
*dialData =curNode->next;
|
||||
*dialData = curNode->next;
|
||||
|
||||
free(curNode->key);
|
||||
free(curNode->value);
|
||||
free(curNode);
|
||||
free(curNode->key); curNode->key = NULL;
|
||||
free(curNode->value); curNode->value = NULL;
|
||||
free(curNode); curNode = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,12 +66,32 @@ struct DIALServer_ {
|
||||
pthread_mutex_t mux;
|
||||
};
|
||||
|
||||
static void ds_lock(DIALServer *ds) {
|
||||
pthread_mutex_lock(&ds->mux);
|
||||
/**
|
||||
* Acquire the DIAL server mutex.
|
||||
*
|
||||
* @return 1 if acquisition succeeded, 0 if it failed.
|
||||
*/
|
||||
static int ds_lock(DIALServer *ds) {
|
||||
int err = 0;
|
||||
if ((err = pthread_mutex_lock(&ds->mux)) != 0) {
|
||||
printf("Unable to acquire DS mutex: [%d]", err);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ds_unlock(DIALServer *ds) {
|
||||
pthread_mutex_unlock(&ds->mux);
|
||||
/**
|
||||
* Release the DIAL server mutex.
|
||||
*
|
||||
* @return 1 if release succeeded, 0 if it failed.
|
||||
*/
|
||||
static int ds_unlock(DIALServer *ds) {
|
||||
int err = 0;
|
||||
if ((err = pthread_mutex_unlock(&ds->mux)) != 0) {
|
||||
printf("Unable to release DS mutex: [%d]", err);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// finds an app and returns a pointer to the previous element's next pointer
|
||||
@@ -88,11 +108,25 @@ static DIALApp **find_app(DIALServer *ds, const char *app_name) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void url_decode_xml_encode(char *dst, char *src, size_t src_size) {
|
||||
/**
|
||||
* URL-unescape the string, then XML-escape it.
|
||||
*
|
||||
* @param dst the destination XML-escaped string. Must be large enough for
|
||||
* the resulting string (technically as much as 6x the raw string
|
||||
* length based on the implementation in url_lib.c).
|
||||
* @param src the URL-escaped string.
|
||||
* @param src_size size of the URL-escaped string, including trailing NULL.
|
||||
* @return true if successful, false if out-of-memory.
|
||||
*/
|
||||
static int url_decode_xml_encode(char *dst, char *src, size_t src_size) {
|
||||
char *url_decoded_key = (char *) malloc(src_size + 1);
|
||||
if (url_decoded_key == NULL) {
|
||||
return 0;
|
||||
}
|
||||
urldecode(url_decoded_key, src, src_size);
|
||||
xmlencode(dst, url_decoded_key, 2 * src_size);
|
||||
free(url_decoded_key);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -117,19 +151,21 @@ static void handle_app_start(struct mg_connection *conn,
|
||||
const struct mg_request_info *request_info,
|
||||
const char *app_name,
|
||||
const char *origin_header) {
|
||||
char additional_data_param[256] = {0, };
|
||||
char additional_data_param[DIAL_MAX_ADDITIONALURL] = {0, };
|
||||
char body[DIAL_MAX_PAYLOAD + sizeof(additional_data_param) + 2] = {0, };
|
||||
DIALApp *app;
|
||||
DIALServer *ds = request_info->user_data;
|
||||
int body_size;
|
||||
|
||||
ds_lock(ds);
|
||||
if (!ds_lock(ds)) {
|
||||
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||
return;
|
||||
}
|
||||
app = *find_app(ds, app_name);
|
||||
if (!app) {
|
||||
mg_send_http_error(conn, 404, "Not Found", "Not Found");
|
||||
} else {
|
||||
body_size = mg_read(conn, body, sizeof(body));
|
||||
// NUL-terminate it just in case
|
||||
body_size = mg_read(conn, body, sizeof(body) - 1);
|
||||
if (body_size > DIAL_MAX_PAYLOAD) {
|
||||
mg_send_http_error(conn, 413, "413 Request Entity Too Large",
|
||||
"413 Request Entity Too Large");
|
||||
@@ -144,7 +180,7 @@ static void handle_app_start(struct mg_connection *conn,
|
||||
|
||||
if (app->useAdditionalData) {
|
||||
// Construct additionalDataUrl=http://host:port/apps/app_name/dial_data
|
||||
sprintf(additional_data_param,
|
||||
snprintf(additional_data_param, DIAL_MAX_ADDITIONALURL,
|
||||
"additionalDataUrl=http%%3A%%2F%%2Flocalhost%%3A%d%%2Fapps%%2F%s%%2Fdial_data%%3F",
|
||||
dial_port, app_name);
|
||||
}
|
||||
@@ -170,6 +206,8 @@ static void handle_app_start(struct mg_connection *conn,
|
||||
mg_send_http_error(conn, 403, "Forbidden", "Forbidden");
|
||||
} else if (app->state == kDIALStatusErrorUnauth) {
|
||||
mg_send_http_error(conn, 401, "Unauthorized", "Unauthorized");
|
||||
} else if (app->state == kDIALStatusErrorNotImplemented) {
|
||||
mg_send_http_error(conn, 501, "Not Implemented", "Not Implemented");
|
||||
} else {
|
||||
mg_send_http_error(conn, 503, "Service Unavailable",
|
||||
"Service Unavailable");
|
||||
@@ -195,7 +233,10 @@ static void handle_app_status(struct mg_connection *conn,
|
||||
free(clientVersionStr);
|
||||
}
|
||||
|
||||
ds_lock(ds);
|
||||
if (!ds_lock(ds)) {
|
||||
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||
return;
|
||||
}
|
||||
app = *find_app(ds, app_name);
|
||||
if (!app) {
|
||||
mg_send_http_error(conn, 404, "Not Found", "Not Found");
|
||||
@@ -208,23 +249,45 @@ static void handle_app_status(struct mg_connection *conn,
|
||||
char *p = dial_data;
|
||||
|
||||
for (DIALData* first = app->dial_data; first != NULL; first = first->next) {
|
||||
p = smartstrcat(p, " <", end - p);
|
||||
p = smartstrncpy(p, " <", end - p);
|
||||
size_t key_length = strlen(first->key);
|
||||
char *encoded_key = (char *) malloc(2 * key_length + 1);
|
||||
url_decode_xml_encode(encoded_key, first->key, key_length);
|
||||
if (encoded_key == NULL) {
|
||||
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||
ds_unlock(ds);
|
||||
return;
|
||||
}
|
||||
if (!url_decode_xml_encode(encoded_key, first->key, key_length)) {
|
||||
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||
free(encoded_key); encoded_key = NULL;
|
||||
ds_unlock(ds);
|
||||
return;
|
||||
};
|
||||
|
||||
size_t value_length = strlen(first->value);
|
||||
char *encoded_value = (char *) malloc(2 * value_length + 1);
|
||||
url_decode_xml_encode(encoded_value, first->value, value_length);
|
||||
if (encoded_value == NULL) {
|
||||
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||
free(encoded_key); encoded_key = NULL;
|
||||
ds_unlock(ds);
|
||||
return;
|
||||
}
|
||||
if (!url_decode_xml_encode(encoded_value, first->value, value_length)) {
|
||||
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||
free(encoded_value); encoded_value = NULL;
|
||||
free(encoded_key); encoded_key = NULL;
|
||||
ds_unlock(ds);
|
||||
return;
|
||||
}
|
||||
|
||||
p = smartstrcat(p, encoded_key, end - p);
|
||||
p = smartstrcat(p, ">", end - p);
|
||||
p = smartstrcat(p, encoded_value, end - p);
|
||||
p = smartstrcat(p, "</", end - p);
|
||||
p = smartstrcat(p, encoded_key, end - p);
|
||||
p = smartstrcat(p, ">", end - p);
|
||||
free(encoded_key);
|
||||
free(encoded_value);
|
||||
p = smartstrncpy(p, encoded_key, end - p);
|
||||
p = smartstrncpy(p, ">", end - p);
|
||||
p = smartstrncpy(p, encoded_value, end - p);
|
||||
p = smartstrncpy(p, "</", end - p);
|
||||
p = smartstrncpy(p, encoded_key, end - p);
|
||||
p = smartstrncpy(p, ">", end - p);
|
||||
free(encoded_key); encoded_key = NULL;
|
||||
free(encoded_value); encoded_value = NULL;
|
||||
}
|
||||
|
||||
app->state = app->callbacks.status_cb(ds, app_name, app->run_id, &canStop,
|
||||
@@ -284,7 +347,10 @@ static void handle_app_stop(struct mg_connection *conn,
|
||||
DIALServer *ds = request_info->user_data;
|
||||
int canStop = 0;
|
||||
|
||||
ds_lock(ds);
|
||||
if (!ds_lock(ds)) {
|
||||
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||
return;
|
||||
}
|
||||
|
||||
// Special handling for system app
|
||||
if (strcmp(app_name, "system") == 0) {
|
||||
@@ -321,7 +387,10 @@ static void handle_app_hide(struct mg_connection *conn,
|
||||
DIALServer *ds = request_info->user_data;
|
||||
int canStop = 0;
|
||||
|
||||
ds_lock(ds);
|
||||
if (!ds_lock(ds)) {
|
||||
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||
return;
|
||||
}
|
||||
app = *find_app(ds, app_name);
|
||||
|
||||
// update the application state
|
||||
@@ -335,17 +404,17 @@ static void handle_app_hide(struct mg_connection *conn,
|
||||
} else {
|
||||
// not implemented in reference
|
||||
DIALStatus status = app->callbacks.hide_cb(ds, app_name, app->run_id, app->callback_data);
|
||||
if (status!=kDIALStatusHide){
|
||||
if (status != kDIALStatusHide){
|
||||
fprintf(stderr, "Hide not implemented for reference.\n");
|
||||
mg_send_http_error(conn, 501, "Not Implemented",
|
||||
"Not Implemented");
|
||||
}else{
|
||||
app->state = kDIALStatusHide;
|
||||
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Access-Control-Allow-Origin: %s\r\n"
|
||||
"\r\n",
|
||||
origin_header);
|
||||
} else {
|
||||
app->state = kDIALStatusHide;
|
||||
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Access-Control-Allow-Origin: %s\r\n"
|
||||
"\r\n",
|
||||
origin_header);
|
||||
}
|
||||
}
|
||||
ds_unlock(ds);
|
||||
@@ -361,7 +430,10 @@ static void handle_dial_data(struct mg_connection *conn,
|
||||
DIALApp *app;
|
||||
DIALServer *ds = request_info->user_data;
|
||||
|
||||
ds_lock(ds);
|
||||
if (!ds_lock(ds)) {
|
||||
mg_send_http_error(conn, 500, "500 Internal Server Error", "500 Internal Server Error");
|
||||
return;
|
||||
}
|
||||
app = *find_app(ds, app_name);
|
||||
if (!app) {
|
||||
mg_send_http_error(conn, 404, "Not Found", "Not Found");
|
||||
@@ -371,21 +443,22 @@ static void handle_dial_data(struct mg_connection *conn,
|
||||
int nread;
|
||||
if (!use_payload) {
|
||||
if (request_info->query_string) {
|
||||
int qs_len = strlen(request_info->query_string);
|
||||
if (qs_len > DIAL_DATA_MAX_PAYLOAD) {
|
||||
mg_send_http_error(conn, 413, "413 Request Entity Too Large",
|
||||
"413 Request Entity Too Large");
|
||||
ds_unlock(ds);
|
||||
return;
|
||||
}
|
||||
strncpy(body, request_info->query_string, DIAL_DATA_MAX_PAYLOAD);
|
||||
nread = strlen(body);
|
||||
nread = qs_len;
|
||||
} else {
|
||||
nread = 0;
|
||||
}
|
||||
} else {
|
||||
nread = mg_read(conn, body, DIAL_DATA_MAX_PAYLOAD);
|
||||
body[nread] = '\0';
|
||||
}
|
||||
if (nread > DIAL_DATA_MAX_PAYLOAD) {
|
||||
mg_send_http_error(conn, 413, "413 Request Entity Too Large",
|
||||
"413 Request Entity Too Large");
|
||||
ds_unlock(ds);
|
||||
return;
|
||||
}
|
||||
body[nread] = '\0';
|
||||
|
||||
if (isBadPayload(body, nread)) {
|
||||
mg_send_http_error(conn, 400, "400 Bad Request", "400 Bad Request");
|
||||
@@ -398,6 +471,7 @@ static void handle_dial_data(struct mg_connection *conn,
|
||||
|
||||
app->dial_data = parse_params(body);
|
||||
store_dial_data(app->name, app->dial_data);
|
||||
free_dial_data(&app->dial_data);
|
||||
|
||||
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
||||
"Access-Control-Allow-Origin: %s\r\n"
|
||||
@@ -418,7 +492,7 @@ static int ends_with(const char *str, const char *suffix) {
|
||||
}
|
||||
|
||||
|
||||
// str contains a white space separated list of strings (only supports SPACE characters for now)
|
||||
// str contains a white space separated list of strings (only supports SPACE characters for now)
|
||||
static int ends_with_in_list (const char *str, const char *list) {
|
||||
if (!str || !list)
|
||||
return 0;
|
||||
@@ -447,12 +521,12 @@ static int ends_with_in_list (const char *str, const char *list) {
|
||||
substring[copyLength] = '\0';
|
||||
//printf("found %s \n", substring);
|
||||
if (ends_with(str, substring)) {
|
||||
free(substring);
|
||||
free(substring); substring = NULL;
|
||||
return 1;
|
||||
}
|
||||
scanPointer = scanPointer + copyLength + 1; // assumption: only 1 character
|
||||
}
|
||||
free(substring);
|
||||
free(substring); substring = NULL;
|
||||
return ends_with(str, scanPointer);
|
||||
}
|
||||
|
||||
@@ -471,7 +545,10 @@ static int is_allowed_origin(DIALServer* ds, char * origin, const char * app_nam
|
||||
return 1;
|
||||
}
|
||||
|
||||
ds_lock(ds);
|
||||
if (!ds_lock(ds)) {
|
||||
// If we can't check, fail in favor of safety.
|
||||
return 0;
|
||||
}
|
||||
DIALApp *app;
|
||||
int result = 0;
|
||||
for (app = ds->apps; app != NULL; app = app->next) {
|
||||
@@ -530,11 +607,16 @@ static void *request_handler(enum mg_event event, struct mg_connection *conn,
|
||||
fprintf(stderr, "Origin %s, Host: %s\n", origin_header, host_header);
|
||||
if (event == MG_NEW_REQUEST) {
|
||||
// URL ends with run
|
||||
if (!strncmp(request_info->uri + strlen(request_info->uri) - 4, RUN_URI,
|
||||
strlen(RUN_URI))) {
|
||||
char app_name[256] = {0, }; // assuming the application name is not over 256 chars.
|
||||
strncpy(app_name, request_info->uri + strlen(APPS_URI),
|
||||
((strlen(request_info->uri) - 4) - (sizeof(APPS_URI) - 1)));
|
||||
if (strlen(request_info->uri) > strlen(RUN_URI) + strlen(APPS_URI)
|
||||
&& !strncmp(request_info->uri + strlen(request_info->uri) - strlen(RUN_URI), RUN_URI, strlen(RUN_URI)))
|
||||
{
|
||||
// Maximum app name length of 255 characters.
|
||||
char app_name[256] = {0, };
|
||||
int appname_len = strlen(request_info->uri) - strlen(RUN_URI) - strlen(APPS_URI);
|
||||
if (appname_len > 255) {
|
||||
appname_len = 255;
|
||||
}
|
||||
strncpy(app_name, request_info->uri + strlen(APPS_URI), appname_len);
|
||||
|
||||
if (!strcmp(request_info->request_method, "OPTIONS")) {
|
||||
return options_response(ds, conn, host_header, origin_header, app_name, "DELETE, OPTIONS");
|
||||
@@ -542,7 +624,8 @@ static void *request_handler(enum mg_event event, struct mg_connection *conn,
|
||||
|
||||
// DELETE non-empty app name
|
||||
if (app_name[0] != '\0'
|
||||
&& !strcmp(request_info->request_method, "DELETE")) {
|
||||
&& !strcmp(request_info->request_method, "DELETE"))
|
||||
{
|
||||
if (host_header && is_allowed_origin(ds, origin_header, app_name)) {
|
||||
handle_app_stop(conn, request_info, app_name, origin_header);
|
||||
} else {
|
||||
@@ -555,10 +638,12 @@ static void *request_handler(enum mg_event event, struct mg_connection *conn,
|
||||
}
|
||||
}
|
||||
// URI starts with "/apps/" and is followed by an app name
|
||||
else if (!strncmp(request_info->uri, APPS_URI, sizeof(APPS_URI) - 1)
|
||||
&& !strchr(request_info->uri + strlen(APPS_URI), '/')) {
|
||||
else if (strlen(request_info->uri) > strlen(APPS_URI)
|
||||
&& !strncmp(request_info->uri, APPS_URI, strlen(APPS_URI))
|
||||
&& !strchr(request_info->uri + strlen(APPS_URI), '/'))
|
||||
{
|
||||
const char *app_name;
|
||||
app_name = request_info->uri + sizeof(APPS_URI) - 1;
|
||||
app_name = request_info->uri + strlen(APPS_URI);
|
||||
|
||||
if (!strcmp(request_info->request_method, "OPTIONS")) {
|
||||
return options_response(ds, conn, host_header, origin_header, app_name, "GET, POST, OPTIONS");
|
||||
@@ -576,27 +661,29 @@ static void *request_handler(enum mg_event event, struct mg_connection *conn,
|
||||
} else if (!strcmp(request_info->request_method, "GET")) {
|
||||
handle_app_status(conn, request_info, app_name, origin_header);
|
||||
} else {
|
||||
mg_send_http_error(conn, 501, "Not Implemented",
|
||||
"Not Implemented");
|
||||
mg_send_http_error(conn, 501, "Not Implemented", "Not Implemented");
|
||||
}
|
||||
}
|
||||
// URI that ends with HIDE_URI
|
||||
else if (!strncmp(request_info->uri + strlen(request_info->uri) - strlen(HIDE_URI), HIDE_URI,
|
||||
strlen(HIDE_URI))) {
|
||||
char app_name[256] = {0, }; // assuming the application name is not over 256 chars.
|
||||
strncpy(app_name, request_info->uri + strlen(APPS_URI),
|
||||
((strlen(request_info->uri) - strlen(RUN_URI) - strlen(HIDE_URI)) - (sizeof(APPS_URI) - 1)));
|
||||
else if (strlen(request_info->uri) > strlen(HIDE_URI) + strlen(RUN_URI) + strlen(APPS_URI)
|
||||
&& !strncmp(request_info->uri + strlen(request_info->uri) - strlen(HIDE_URI), HIDE_URI, strlen(HIDE_URI)))
|
||||
{
|
||||
// Maximum app name length of 255 characters.
|
||||
char app_name[256] = {0, };
|
||||
int appname_len = strlen(request_info->uri) - strlen(RUN_URI) - strlen(HIDE_URI) - strlen(APPS_URI);
|
||||
if (appname_len > 255) {
|
||||
appname_len = 255;
|
||||
}
|
||||
strncpy(app_name, request_info->uri + strlen(APPS_URI), appname_len);
|
||||
|
||||
if (!strcmp(request_info->request_method, "OPTIONS")) {
|
||||
return options_response(ds, conn, host_header, origin_header, app_name, "POST, OPTIONS");
|
||||
}
|
||||
|
||||
if (app_name[0] != '\0'
|
||||
&& !strcmp(request_info->request_method, "POST")) {
|
||||
if (app_name[0] != '\0' && !strcmp(request_info->request_method, "POST")) {
|
||||
handle_app_hide(conn, request_info, app_name, origin_header);
|
||||
}else{
|
||||
mg_send_http_error(conn, 501, "Not Implemented",
|
||||
"Not Implemented");
|
||||
} else {
|
||||
mg_send_http_error(conn, 501, "Not Implemented", "Not Implemented");
|
||||
}
|
||||
}
|
||||
// URI is of the form */app_name/dial_data
|
||||
@@ -607,18 +694,20 @@ static void *request_handler(enum mg_event event, struct mg_connection *conn,
|
||||
inet_ntop(addr->sin_family, &addr->sin_addr, laddr, sizeof(laddr));
|
||||
if ( !strncmp(laddr, gLocalhost, strlen(gLocalhost)) ) {
|
||||
char *app_name = parse_app_name(request_info->uri);
|
||||
if (app_name == NULL) {
|
||||
mg_send_http_error(conn, 500, "Internal Error", "Internal Error");
|
||||
} else {
|
||||
if (!strcmp(request_info->request_method, "OPTIONS")) {
|
||||
void *ret = options_response(ds, conn, host_header, origin_header, app_name, "POST, OPTIONS");
|
||||
free(app_name);
|
||||
return ret;
|
||||
}
|
||||
int use_payload = strcmp(request_info->request_method, "POST") ? 0 : 1;
|
||||
handle_dial_data(conn, request_info, app_name, origin_header,
|
||||
use_payload);
|
||||
|
||||
if (!strcmp(request_info->request_method, "OPTIONS")) {
|
||||
void *ret = options_response(ds, conn, host_header, origin_header, app_name, "POST, OPTIONS");
|
||||
free(app_name);
|
||||
return ret;
|
||||
}
|
||||
int use_payload =
|
||||
strcmp(request_info->request_method, "POST") ? 0 : 1;
|
||||
handle_dial_data(conn, request_info, app_name, origin_header,
|
||||
use_payload);
|
||||
|
||||
free(app_name);
|
||||
} else {
|
||||
// If the request is not from local host, return an error
|
||||
mg_send_http_error(conn, 403, "Forbidden", "Forbidden");
|
||||
@@ -636,12 +725,19 @@ static void *request_handler(enum mg_event event, struct mg_connection *conn,
|
||||
|
||||
DIALServer *DIAL_create() {
|
||||
DIALServer *ds = calloc(1, sizeof(DIALServer));
|
||||
pthread_mutex_init(&ds->mux, NULL);
|
||||
if (ds == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (pthread_mutex_init(&ds->mux, NULL) != 0) {
|
||||
free(ds); ds = NULL;
|
||||
return NULL;
|
||||
}
|
||||
return ds;
|
||||
}
|
||||
|
||||
void DIAL_start(DIALServer *ds) {
|
||||
int DIAL_start(DIALServer *ds) {
|
||||
ds->ctx = mg_start(&request_handler, ds, DIAL_PORT);
|
||||
return (ds->ctx != NULL);
|
||||
}
|
||||
|
||||
void DIAL_stop(DIALServer *ds) {
|
||||
@@ -663,51 +759,60 @@ int DIAL_register_app(DIALServer *ds, const char *app_name,
|
||||
int useAdditionalData,
|
||||
const char* corsAllowedOrigin) {
|
||||
DIALApp **ptr, *app;
|
||||
int ret;
|
||||
|
||||
ds_lock(ds);
|
||||
if (!ds_lock(ds)) {
|
||||
return -1;
|
||||
}
|
||||
ptr = find_app(ds, app_name);
|
||||
if (*ptr != NULL) { // app already registered
|
||||
ret = 0;
|
||||
ds_unlock(ds);
|
||||
return 0;
|
||||
} else {
|
||||
app = malloc(sizeof(DIALApp));
|
||||
if (app == NULL) {
|
||||
ds_unlock(ds);
|
||||
return -1;
|
||||
}
|
||||
app->callbacks = *callbacks;
|
||||
app->name = strdup(app_name);
|
||||
if (app->name == NULL) {
|
||||
free(app); app = NULL;
|
||||
ds_unlock(ds);
|
||||
return -1;
|
||||
}
|
||||
app->next = *ptr;
|
||||
app->state = kDIALStatusStopped;
|
||||
app->callback_data = user_data;
|
||||
app->dial_data = retrieve_dial_data(app->name);
|
||||
app->useAdditionalData = useAdditionalData;
|
||||
app->corsAllowedOrigin[0] = '\0';
|
||||
if (corsAllowedOrigin &&
|
||||
strlen(corsAllowedOrigin) < sizeof(app->corsAllowedOrigin)) {
|
||||
strcpy(app->corsAllowedOrigin, corsAllowedOrigin);
|
||||
if (corsAllowedOrigin && strlen(corsAllowedOrigin) < sizeof(app->corsAllowedOrigin)) {
|
||||
strcpy(app->corsAllowedOrigin, corsAllowedOrigin);
|
||||
}
|
||||
*ptr = app;
|
||||
ret = 1;
|
||||
ds_unlock(ds);
|
||||
return 1;
|
||||
}
|
||||
ds_unlock(ds);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int DIAL_unregister_app(DIALServer *ds, const char *app_name) {
|
||||
DIALApp **ptr, *app;
|
||||
int ret;
|
||||
|
||||
ds_lock(ds);
|
||||
if (!ds_lock(ds)) {
|
||||
return -1;
|
||||
}
|
||||
ptr = find_app(ds, app_name);
|
||||
if (*ptr == NULL) { // no such app
|
||||
ret = 0;
|
||||
ds_unlock(ds);
|
||||
return 0;
|
||||
} else {
|
||||
app = *ptr;
|
||||
*ptr = app->next;
|
||||
free(app->name);
|
||||
free(app);
|
||||
ret = 1;
|
||||
free(app->name); app->name = NULL;
|
||||
free(app); app = NULL;
|
||||
ds_unlock(ds);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ds_unlock(ds);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char * DIAL_get_payload(DIALServer *ds, const char *app_name) {
|
||||
|
||||
@@ -121,7 +121,7 @@ DIALServer *DIAL_create();
|
||||
*
|
||||
* @param[in] ds DIAL server handle
|
||||
*/
|
||||
void DIAL_start(DIALServer *ds);
|
||||
int DIAL_start(DIALServer *ds);
|
||||
|
||||
/*
|
||||
* Stop the DIAL server.
|
||||
|
||||
@@ -112,19 +112,19 @@ int isAppRunning( char *pzName, char *pzCommandPattern ) {
|
||||
struct dirent* procEntry;
|
||||
while((procEntry=readdir(proc_fd)) != NULL) {
|
||||
if( doesMatch( "^[0-9][0-9]*$", procEntry->d_name ) ) {
|
||||
char exePath[64] = {0,};
|
||||
char exePath[384] = {0,};
|
||||
char link[256] = {0,};
|
||||
char cmdlinePath[64] = {0,};
|
||||
char cmdlinePath[384] = {0,};
|
||||
char buffer[1024] = {0,};
|
||||
int len;
|
||||
sprintf( exePath, "/proc/%s/exe", procEntry->d_name);
|
||||
sprintf( cmdlinePath, "/proc/%s/cmdline", procEntry->d_name);
|
||||
snprintf( exePath, sizeof(exePath), "/proc/%s/exe", procEntry->d_name);
|
||||
snprintf( cmdlinePath, sizeof(cmdlinePath), "/proc/%s/cmdline", procEntry->d_name);
|
||||
|
||||
if( (len = readlink( exePath, link, sizeof(link)-1)) != -1 ) {
|
||||
char executable[256] = {0,};
|
||||
strcat( executable, pzName );
|
||||
strncpy( executable, pzName, sizeof(executable) - 2 );
|
||||
strcat( executable, "$" );
|
||||
// TODO: Make this search for EOL to prevent false positivies
|
||||
// TODO: Make this search for EOL to prevent false positives
|
||||
if( !doesMatch( executable, link ) ) {
|
||||
continue;
|
||||
}
|
||||
@@ -147,9 +147,9 @@ int isAppRunning( char *pzName, char *pzCommandPattern ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int d_name = atoi(procEntry->d_name);
|
||||
closedir(proc_fd);
|
||||
return atoi(procEntry->d_name);
|
||||
return d_name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,14 +202,14 @@ static DIALStatus youtube_start(DIALServer *ds, const char *appname,
|
||||
printf("\n\n ** LAUNCH YouTube ** with payload %s\n\n", payload);
|
||||
|
||||
char url[512] = {0,}, data[512] = {0,};
|
||||
if (strlen(payload) && strlen(additionalDataUrl)){
|
||||
sprintf( url, "https://www.youtube.com/tv?%s&%s", payload, additionalDataUrl);
|
||||
}else if (strlen(payload)){
|
||||
sprintf( url, "https://www.youtube.com/tv?%s", payload);
|
||||
}else{
|
||||
sprintf( url, "https://www.youtube.com/tv");
|
||||
if (strlen(payload) && strlen(additionalDataUrl)) {
|
||||
snprintf( url, sizeof(url), "https://www.youtube.com/tv?%s&%s", payload, additionalDataUrl);
|
||||
} else if (strlen(payload)) {
|
||||
snprintf( url, sizeof(url), "https://www.youtube.com/tv?%s", payload);
|
||||
} else {
|
||||
snprintf( url, sizeof(url), "https://www.youtube.com/tv");
|
||||
}
|
||||
sprintf( data, "--user-data-dir=%s/.config/google-chrome-dial", getenv("HOME") );
|
||||
snprintf( data, sizeof(data), "--user-data-dir=%s/.config/google-chrome-dial", getenv("HOME") );
|
||||
|
||||
const char * const youtube_args[] = { spAppYouTubeExecutable,
|
||||
spYouTubePS3UserAgent,
|
||||
@@ -268,13 +268,18 @@ static void setValue( char * pSource, char dest[] )
|
||||
static void setDataDir(char *pData)
|
||||
{
|
||||
setValue( spNfDataDir, spDataDir );
|
||||
strcat(spDataDir, pData);
|
||||
strncat(spDataDir, pData, sizeof(spDataDir) - 1);
|
||||
}
|
||||
|
||||
void runDial(void)
|
||||
{
|
||||
DIALServer *ds;
|
||||
ds = DIAL_create();
|
||||
if (ds == NULL) {
|
||||
printf("Unable to create DIAL server.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
struct DIALAppCallbacks cb_nf;
|
||||
cb_nf.start_cb = netflix_start;
|
||||
cb_nf.hide_cb = netflix_hide;
|
||||
@@ -283,16 +288,20 @@ void runDial(void)
|
||||
struct DIALAppCallbacks cb_yt = {youtube_start, youtube_hide, youtube_stop, youtube_status};
|
||||
struct DIALAppCallbacks cb_system = {system_start, system_hide, NULL, system_status};
|
||||
|
||||
DIAL_register_app(ds, "Netflix", &cb_nf, NULL, 1, ".netflix.com");
|
||||
DIAL_register_app(ds, "YouTube", &cb_yt, NULL, 1, ".youtube.com");
|
||||
DIAL_register_app(ds, "system", &cb_system, NULL, 1, "");
|
||||
DIAL_start(ds);
|
||||
|
||||
gDialPort = DIAL_get_port(ds);
|
||||
printf("launcher listening on gDialPort %d\n", gDialPort);
|
||||
run_ssdp(gDialPort, spFriendlyName, spModelName, spUuid);
|
||||
|
||||
DIAL_stop(ds);
|
||||
if (DIAL_register_app(ds, "Netflix", &cb_nf, NULL, 1, ".netflix.com") == -1 ||
|
||||
DIAL_register_app(ds, "YouTube", &cb_yt, NULL, 1, ".youtube.com") == -1 ||
|
||||
DIAL_register_app(ds, "system", &cb_system, NULL, 1, "") == -1)
|
||||
{
|
||||
printf("Unable to register DIAL applications.\n");
|
||||
} else if (!DIAL_start(ds)) {
|
||||
printf("Unable to start DIAL master listening thread.\n");
|
||||
} else {
|
||||
gDialPort = DIAL_get_port(ds);
|
||||
printf("launcher listening on gDialPort %d\n", gDialPort);
|
||||
run_ssdp(gDialPort, spFriendlyName, spModelName, spUuid);
|
||||
|
||||
DIAL_stop(ds);
|
||||
}
|
||||
free(ds);
|
||||
}
|
||||
|
||||
@@ -317,11 +326,11 @@ static void processOption( int index, char * pOption )
|
||||
setValue( pOption, spUuid );
|
||||
break;
|
||||
case 5:
|
||||
if (strcmp(pOption, "on")==0){
|
||||
if (strcmp(pOption, "on")==0) {
|
||||
wakeOnWifiLan=true;
|
||||
}else if (strcmp(pOption, "off")==0){
|
||||
} else if (strcmp(pOption, "off") == 0) {
|
||||
wakeOnWifiLan=false;
|
||||
}else{
|
||||
} else {
|
||||
fprintf(stderr, "Option %s is not valid for %s",
|
||||
pOption, WAKE_OPTION_LONG);
|
||||
exit(1);
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
#ifndef BUFSIZ
|
||||
#define BUFSIZ 4096
|
||||
#endif
|
||||
#ifndef REASON_SIZ
|
||||
/* REASON_SIZ must be non-trivially smaller than BUFSIZ. */
|
||||
#define REASON_SIZ 2048
|
||||
#endif
|
||||
|
||||
#define MAX_REQUEST_SIZE 4096
|
||||
#define NUM_THREADS 4
|
||||
@@ -214,33 +218,12 @@ static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen,
|
||||
// Skip the characters until one of the delimiters characters found.
|
||||
// 0-terminate resulting word. Skip the delimiter and following whitespaces if any.
|
||||
// Advance pointer to buffer to the next word. Return found 0-terminated word.
|
||||
// Delimiters can be quoted with quotechar.
|
||||
static char *skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar) {
|
||||
static char *skip_quoted(char **buf, const char *delimiters, const char *whitespace) {
|
||||
char *p, *begin_word, *end_word, *end_whitespace;
|
||||
|
||||
begin_word = *buf;
|
||||
end_word = begin_word + strcspn(begin_word, delimiters);
|
||||
|
||||
/* Check for quotechar */
|
||||
if (end_word > begin_word) {
|
||||
p = end_word - 1;
|
||||
while (*p == quotechar) {
|
||||
/* If there is anything beyond end_word, copy it */
|
||||
if (*end_word == '\0') {
|
||||
*p = '\0';
|
||||
break;
|
||||
} else {
|
||||
size_t end_off = strcspn(end_word + 1, delimiters);
|
||||
memmove (p, end_word, end_off + 1);
|
||||
p += end_off; /* p must correspond to end_word - 1 */
|
||||
end_word += end_off + 1;
|
||||
}
|
||||
}
|
||||
for (p++; p < end_word; p++) {
|
||||
*p = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (*end_word == '\0') {
|
||||
*buf = end_word;
|
||||
} else {
|
||||
@@ -259,7 +242,7 @@ static char *skip_quoted(char **buf, const char *delimiters, const char *whitesp
|
||||
// Simplified version of skip_quoted without quote char
|
||||
// and whitespace == delimiters
|
||||
static char *skip(char **buf, const char *delimiters) {
|
||||
return skip_quoted(buf, delimiters, delimiters, 0);
|
||||
return skip_quoted(buf, delimiters, delimiters);
|
||||
}
|
||||
|
||||
|
||||
@@ -296,7 +279,14 @@ void mg_send_http_error(struct mg_connection *conn, int status,
|
||||
|
||||
/* Errors 1xx, 204 and 304 MUST NOT send a body */
|
||||
if (status > 199 && status != 204 && status != 304) {
|
||||
len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason);
|
||||
// If reason is really long, buf will have been completely filled up.
|
||||
// Then buf[len] = '\n' will replace the trailing NULL with '\n' and kill buf
|
||||
// as a string. Plus the next call to mg_vsnprintf() will be writing to an
|
||||
// invalid location and negative (gigantic because size_t is unsigned) size.
|
||||
//
|
||||
// Given how this function is called, the safest thing to do is probably to
|
||||
// require reason remains relatively short and will get truncated.
|
||||
len = mg_snprintf(conn, buf, REASON_SIZ, "Error %d: %s", status, reason);
|
||||
cry(conn, "%s", buf);
|
||||
buf[len++] = '\n';
|
||||
|
||||
@@ -332,13 +322,11 @@ static int start_thread(struct mg_context *ctx, mg_thread_func_t func,
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int set_non_blocking_mode(SOCKET sock) {
|
||||
static void set_non_blocking_mode(SOCKET sock) {
|
||||
int flags;
|
||||
|
||||
flags = fcntl(sock, F_GETFL, 0);
|
||||
(void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write data to the IO channel - opened file descriptor, socket or SSL
|
||||
@@ -350,8 +338,8 @@ static int64_t push(FILE *fp, SOCKET sock, const char *buf, int64_t len) {
|
||||
sent = 0;
|
||||
while (sent < len) {
|
||||
|
||||
/* How many bytes we send in this iteration */
|
||||
k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);
|
||||
/* How many bytes should we send in this iteration */
|
||||
k = ((len - sent) > INT_MAX) ? INT_MAX : (int) (len - sent);
|
||||
|
||||
if (fp != NULL) {
|
||||
n = fwrite(buf + sent, 1, (size_t)k, fp);
|
||||
@@ -458,9 +446,10 @@ static size_t url_decode(const char *src, size_t src_len, char *dst,
|
||||
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
|
||||
|
||||
for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
|
||||
if (src[i] == '%' &&
|
||||
if (src[i] == '%' && (i + 2 < src_len) &&
|
||||
isxdigit(* (const unsigned char *) (src + i + 1)) &&
|
||||
isxdigit(* (const unsigned char *) (src + i + 2))) {
|
||||
isxdigit(* (const unsigned char *) (src + i + 2)))
|
||||
{
|
||||
a = tolower(* (const unsigned char *) (src + i + 1));
|
||||
b = tolower(* (const unsigned char *) (src + i + 2));
|
||||
dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
|
||||
@@ -490,6 +479,7 @@ static int get_request_len(const char *buf, int buflen) {
|
||||
// Control characters are not allowed but >=128 is.
|
||||
if (!isprint(* (const unsigned char *) s) && *s != '\r' &&
|
||||
*s != '\n' && * (const unsigned char *) s < 128) {
|
||||
// FIXME: Why doesn't this immediately return as malformed?
|
||||
len = -1;
|
||||
} else if (s[0] == '\n' && s[1] == '\n') {
|
||||
len = (int) (s - buf) + 2;
|
||||
@@ -529,7 +519,7 @@ static void parse_http_headers(char **buf, struct mg_request_info *ri) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) {
|
||||
ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0);
|
||||
ri->http_headers[i].name = skip_quoted(buf, ":", " ");
|
||||
ri->http_headers[i].value = skip(buf, "\r\n");
|
||||
if (ri->http_headers[i].name[0] == '\0')
|
||||
break;
|
||||
@@ -637,6 +627,8 @@ static int set_ports_option(struct mg_context *ctx, int port) {
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 500 * 1000;
|
||||
|
||||
// TODO This code calls close(INVALID_SOCKET), even though close expects positive file descriptors.
|
||||
// Not sure what the behavior is as a result, might be fine.
|
||||
if ((ctx->local_socket = socket(PF_INET, SOCK_STREAM, 6)) == INVALID_SOCKET ||
|
||||
setsockopt(ctx->local_socket, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) != 0 ||
|
||||
setsockopt(ctx->local_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0 ||
|
||||
@@ -702,22 +694,28 @@ static void close_connection(struct mg_connection *conn) {
|
||||
}
|
||||
|
||||
static void discard_current_request_from_buffer(struct mg_connection *conn) {
|
||||
int buffered_len, body_len;
|
||||
int buffered_len;
|
||||
int64_t remaining = 0, pull_len = 0, count = 0;
|
||||
char discard_buffer[16384];
|
||||
|
||||
buffered_len = conn->data_len - conn->request_len;
|
||||
assert(buffered_len >= 0);
|
||||
|
||||
if (conn->content_len == -1) {
|
||||
body_len = 0;
|
||||
} else if (conn->content_len < (int64_t) buffered_len) {
|
||||
body_len = (int) conn->content_len;
|
||||
} else {
|
||||
body_len = buffered_len;
|
||||
// If there was no specified Content-Length header, then there's no way for
|
||||
// us to know how much more data needs to be discarded.
|
||||
//
|
||||
// Otherwise keep pull()-ing content until we consume the full content length.
|
||||
if (conn->content_len != -1 && conn->consumed_content < conn->content_len) {
|
||||
remaining = conn->content_len - conn->consumed_content;
|
||||
while (remaining > 0) {
|
||||
pull_len = (remaining < sizeof(discard_buffer)) ? remaining : sizeof(discard_buffer);
|
||||
count = mg_read(conn, discard_buffer, pull_len);
|
||||
if (count <= 0) {
|
||||
break;
|
||||
}
|
||||
remaining -= count;
|
||||
}
|
||||
}
|
||||
|
||||
conn->data_len -= conn->request_len + body_len;
|
||||
memmove(conn->buf, conn->buf + conn->request_len + body_len,
|
||||
(size_t) conn->data_len);
|
||||
}
|
||||
|
||||
static void process_new_connection(struct mg_connection *conn) {
|
||||
@@ -731,7 +729,7 @@ static void process_new_connection(struct mg_connection *conn) {
|
||||
conn->request_len = read_request(conn->client.sock,
|
||||
conn->buf, conn->buf_size, &conn->data_len);
|
||||
}
|
||||
assert(conn->data_len >= conn->request_len && conn->request_len > 1);
|
||||
assert(conn->data_len >= conn->request_len);
|
||||
if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
|
||||
mg_send_http_error(conn, 413, "Request Too Large", "");
|
||||
return;
|
||||
@@ -739,7 +737,7 @@ static void process_new_connection(struct mg_connection *conn) {
|
||||
return; // Remote end closed the connection
|
||||
}
|
||||
|
||||
// Nul-terminate the request cause parse_http_request() uses sscanf
|
||||
// Null-terminate the request because parse_http_request() uses sscanf
|
||||
conn->buf[conn->request_len - 1] = '\0';
|
||||
if (!parse_http_request(conn->buf, ri)) {
|
||||
// Do not put garbage in the access log, just send it back to the client
|
||||
@@ -752,6 +750,11 @@ static void process_new_connection(struct mg_connection *conn) {
|
||||
// Request is valid, handle it
|
||||
cl = get_header(ri, "Content-Length");
|
||||
conn->content_len = cl == NULL ? -1 : strtoll(cl, NULL, 10);
|
||||
if (cl != NULL && conn->content_len < 0) {
|
||||
mg_send_http_error(conn, 400, "Bad Request",
|
||||
"Invalid Content-Length header value: [%s]", cl);
|
||||
return;
|
||||
}
|
||||
conn->birth_time = time(NULL);
|
||||
handle_request(conn);
|
||||
discard_current_request_from_buffer(conn);
|
||||
@@ -800,9 +803,6 @@ static void worker_thread(struct mg_context *ctx) {
|
||||
// required in the DIAL specification.
|
||||
int buf_size = MAX_REQUEST_SIZE;
|
||||
|
||||
#ifndef __APPLE__
|
||||
pthread_setname_np( pthread_self(), __func__);
|
||||
#endif
|
||||
conn = (struct mg_connection *) calloc(1, sizeof(*conn) + buf_size);
|
||||
conn->buf_size = buf_size;
|
||||
conn->buf = (char *) (conn + 1);
|
||||
@@ -820,16 +820,22 @@ static void worker_thread(struct mg_context *ctx) {
|
||||
|
||||
// Fill in local IP info
|
||||
socklen_t addr_len = sizeof(conn->request_info.local_addr);
|
||||
getsockname(conn->client.sock,
|
||||
(struct sockaddr *) &conn->request_info.local_addr, &addr_len);
|
||||
|
||||
process_new_connection(conn);
|
||||
if (getsockname(conn->client.sock, (struct sockaddr *) &conn->request_info.local_addr, &addr_len) != 0) {
|
||||
// Something went wrong.
|
||||
mg_send_http_error(conn, 500, "Internal Server Error", "");
|
||||
} else {
|
||||
process_new_connection(conn);
|
||||
}
|
||||
|
||||
close_connection(conn);
|
||||
}
|
||||
free(conn);
|
||||
|
||||
// Signal master that we're done with connection and exiting
|
||||
// Signal master that we're done with connection and exiting.
|
||||
//
|
||||
// It is possible that we fail to acquire the mutex and then num_threads will
|
||||
// end up decrementing incorrectly which may cause the server to hang
|
||||
// indefinitely while trying to shutdown. But we'll try our best.
|
||||
(void) pthread_mutex_lock(&ctx->mutex);
|
||||
ctx->num_threads--;
|
||||
(void) pthread_cond_signal(&ctx->cond);
|
||||
@@ -839,13 +845,25 @@ static void worker_thread(struct mg_context *ctx) {
|
||||
DEBUG_TRACE(("exiting"));
|
||||
}
|
||||
|
||||
// Master thread adds accepted socket to a queue
|
||||
static void produce_socket(struct mg_context *ctx, const struct socket *sp) {
|
||||
(void) pthread_mutex_lock(&ctx->mutex);
|
||||
/**
|
||||
* Copy an accepted socket onto the queue. Blocks if the queue is full. This
|
||||
* function is called from the master thread.
|
||||
*
|
||||
* @param ctx Mongoose context.
|
||||
* @param sp the socket.
|
||||
* @return true if successful, false if there was a mutex error.
|
||||
*/
|
||||
static int produce_socket(struct mg_context *ctx, const struct socket *sp) {
|
||||
if (pthread_mutex_lock(&ctx->mutex) != 0) {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// If the queue is full, wait
|
||||
while (ctx->sq_head - ctx->sq_tail >= (int) ARRAY_SIZE(ctx->queue)) {
|
||||
(void) pthread_cond_wait(&ctx->sq_empty, &ctx->mutex);
|
||||
if (pthread_cond_wait(&ctx->sq_empty, &ctx->mutex) != 0) {
|
||||
(void) pthread_mutex_unlock(&ctx->mutex);
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
assert(ctx->sq_head - ctx->sq_tail < (int) ARRAY_SIZE(ctx->queue));
|
||||
|
||||
@@ -854,17 +872,16 @@ static void produce_socket(struct mg_context *ctx, const struct socket *sp) {
|
||||
ctx->sq_head++;
|
||||
DEBUG_TRACE(("queued socket %d", sp->sock));
|
||||
|
||||
// Nothing to do if there is an error on signal. But if we fail to unlock
|
||||
// then we're in a bad state.
|
||||
(void) pthread_cond_signal(&ctx->sq_full);
|
||||
(void) pthread_mutex_unlock(&ctx->mutex);
|
||||
return (pthread_mutex_unlock(&ctx->mutex) == 0);
|
||||
}
|
||||
|
||||
|
||||
static void master_thread(struct mg_context *ctx) {
|
||||
struct socket accepted;
|
||||
|
||||
#ifndef __APPLE__
|
||||
pthread_setname_np( pthread_self(), __func__);
|
||||
#endif
|
||||
socklen_t sock_len = sizeof(accepted.local_addr);
|
||||
memcpy(&accepted.local_addr, &ctx->local_address, sock_len);
|
||||
|
||||
@@ -877,7 +894,10 @@ static void master_thread(struct mg_context *ctx) {
|
||||
if (accepted.sock != INVALID_SOCKET) {
|
||||
// Put accepted socket structure into the queue.
|
||||
DEBUG_TRACE(("accepted socket %d", accepted.sock));
|
||||
produce_socket(ctx, &accepted);
|
||||
// If the socket fails, trigger stop and try to exit gracefully.
|
||||
if (!produce_socket(ctx, &accepted)) {
|
||||
ctx->stop_flag = 1;
|
||||
};
|
||||
}
|
||||
}
|
||||
DEBUG_TRACE(("stopping workers"));
|
||||
@@ -886,14 +906,18 @@ static void master_thread(struct mg_context *ctx) {
|
||||
close_all_listening_sockets(ctx);
|
||||
|
||||
// Wakeup workers that are waiting for connections to handle.
|
||||
pthread_cond_broadcast(&ctx->sq_full);
|
||||
// Nothing we can do if there is an error.
|
||||
(void) pthread_cond_broadcast(&ctx->sq_full);
|
||||
|
||||
// Wait until all threads finish
|
||||
(void) pthread_mutex_lock(&ctx->mutex);
|
||||
while (ctx->num_threads > 0) {
|
||||
(void) pthread_cond_wait(&ctx->cond, &ctx->mutex);
|
||||
// Wait until all threads finish.
|
||||
// If we cannot acquire the lock, we're in a bad state so skip this and
|
||||
// just try to clean up and shut down.
|
||||
if (pthread_mutex_lock(&ctx->mutex) == 0) {
|
||||
while (ctx->num_threads > 0) {
|
||||
(void) pthread_cond_wait(&ctx->cond, &ctx->mutex);
|
||||
}
|
||||
(void) pthread_mutex_unlock(&ctx->mutex);
|
||||
}
|
||||
(void) pthread_mutex_unlock(&ctx->mutex);
|
||||
|
||||
// All threads exited, no sync is needed. Destroy mutex and condvars
|
||||
(void) pthread_mutex_destroy(&ctx->mutex);
|
||||
@@ -925,10 +949,13 @@ void mg_stop(struct mg_context *ctx) {
|
||||
|
||||
struct mg_context *mg_start(mg_callback_t user_callback, void *user_data, int port) {
|
||||
struct mg_context *ctx;
|
||||
|
||||
int retval;
|
||||
|
||||
// Allocate context and initialize reasonable general case defaults.
|
||||
// TODO(lsm): do proper error handling here.
|
||||
ctx = (struct mg_context *) calloc(1, sizeof(*ctx));
|
||||
if (ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ctx->user_callback = user_callback;
|
||||
ctx->user_data = user_data;
|
||||
|
||||
@@ -937,15 +964,26 @@ struct mg_context *mg_start(mg_callback_t user_callback, void *user_data, int po
|
||||
return NULL;
|
||||
}
|
||||
// Ignore SIGPIPE signal, so if browser cancels the request, it
|
||||
// won't kill the whole process.
|
||||
(void) signal(SIGPIPE, SIG_IGN);
|
||||
(void) pthread_mutex_init(&ctx->mutex, NULL);
|
||||
(void) pthread_cond_init(&ctx->cond, NULL);
|
||||
(void) pthread_cond_init(&ctx->sq_empty, NULL);
|
||||
(void) pthread_cond_init(&ctx->sq_full, NULL);
|
||||
// won't kill the whole
|
||||
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
|
||||
free_context(ctx);
|
||||
return NULL;
|
||||
};
|
||||
if (pthread_mutex_init(&ctx->mutex, NULL) != 0 ||
|
||||
pthread_cond_init(&ctx->cond, NULL) != 0 ||
|
||||
pthread_cond_init(&ctx->sq_empty, NULL) != 0 ||
|
||||
pthread_cond_init(&ctx->sq_full, NULL) != 0)
|
||||
{
|
||||
free_context(ctx);
|
||||
return NULL;
|
||||
};
|
||||
|
||||
// Start master (listening) thread
|
||||
start_thread(ctx, (mg_thread_func_t) master_thread, ctx);
|
||||
retval = start_thread(ctx, (mg_thread_func_t) master_thread, ctx);
|
||||
if (retval != 0) {
|
||||
free_context(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Start worker threads
|
||||
for (int i = 0; i < NUM_THREADS; i++) {
|
||||
|
||||
@@ -14,8 +14,14 @@ extern char *spAppNetflix;
|
||||
extern char spNetflix[];
|
||||
static char *defaultLaunchParam = "source_type=12";
|
||||
|
||||
// Adding 20 bytes for prepended source_type for Netflix
|
||||
static char sQueryParam[DIAL_MAX_PAYLOAD+DIAL_MAX_ADDITIONALURL+40];
|
||||
// Adding 40 bytes for defaultLaunchParam, plus additional characters that
|
||||
// may get added.
|
||||
//
|
||||
// dial_server.c ensures the payload is <= DIAL_MAX_PAYLOAD but since we
|
||||
// URL-encode it, the total string length may triple.
|
||||
//
|
||||
// dial_server.c ensures the additional data URL is <= DIAL_MAX_ADDITIONALURL.
|
||||
static char sQueryParam[3 * DIAL_MAX_PAYLOAD + DIAL_MAX_ADDITIONALURL + 40];
|
||||
|
||||
int isAppRunning( char *pzName, char *pzCommandPattern );
|
||||
int shouldRelaunch(DIALServer *pServer, const char *pAppName, const char *args );
|
||||
@@ -34,21 +40,31 @@ DIALStatus netflix_start(DIALServer *ds, const char *appname,
|
||||
|
||||
// construct the payload to determine if it has changed from the previous launch
|
||||
memset( sQueryParam, 0, sizeof(sQueryParam) );
|
||||
strcat( sQueryParam, defaultLaunchParam );
|
||||
if(strlen(payload))
|
||||
strncpy( sQueryParam, defaultLaunchParam, sizeof(sQueryParam) - 1);
|
||||
if(strlen(payload))
|
||||
{
|
||||
char * pUrlEncodedParams;
|
||||
pUrlEncodedParams = url_encode( payload );
|
||||
if( pUrlEncodedParams ){
|
||||
strcat( sQueryParam, "&dial=");
|
||||
strcat( sQueryParam, pUrlEncodedParams );
|
||||
free( pUrlEncodedParams );
|
||||
if (strlen(sQueryParam) + sizeof("&dial=") + strlen(pUrlEncodedParams) < sizeof(sQueryParam)) {
|
||||
strcat( sQueryParam, "&dial=" );
|
||||
strcat( sQueryParam, pUrlEncodedParams );
|
||||
free( pUrlEncodedParams );
|
||||
} else {
|
||||
free( pUrlEncodedParams );
|
||||
return kDIALStatusError;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(strlen(additionalDataUrl)){
|
||||
strcat(sQueryParam, "&");
|
||||
strcat(sQueryParam, additionalDataUrl);
|
||||
if (strlen(sQueryParam) + sizeof("&") + strlen(additionalDataUrl) < sizeof(sQueryParam)) {
|
||||
strcat(sQueryParam, "&");
|
||||
strcat(sQueryParam, additionalDataUrl);
|
||||
} else {
|
||||
return kDIALStatusError;
|
||||
}
|
||||
}
|
||||
|
||||
printf("appPid = %s, shouldRelaunch = %s queryParams = %s\n",
|
||||
|
||||
@@ -35,13 +35,6 @@
|
||||
#include "mongoose.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_dl.h>
|
||||
#endif
|
||||
|
||||
|
||||
// TODO: Partners should define this port
|
||||
#define SSDP_PORT (56790)
|
||||
static char gBuf[4096];
|
||||
@@ -85,8 +78,7 @@ static const char ssdp_reply[] = "HTTP/1.1 200 OK\r\n"
|
||||
static const char wakeup_header[] = "WAKEUP: MAC=%s;Timeout=%d\r\n";
|
||||
#define STR_TIMEOUTLEN 6 /* Longest is 32767 */
|
||||
#define HW_ADDRSTRLEN 18
|
||||
static char ip_addr[INET_ADDRSTRLEN] = "127.0.0.1";
|
||||
static char hw_addr[HW_ADDRSTRLEN] = "00:00:00:00:00:00";
|
||||
static char ip_addr[INET6_ADDRSTRLEN] = "127.0.0.1";
|
||||
static int dial_port = 0;
|
||||
static int my_port = 0;
|
||||
static char friendly_name[256];
|
||||
@@ -115,35 +107,24 @@ static void *request_handler(enum mg_event event,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
static void get_local_address() {
|
||||
struct ifaddrs* iflist;
|
||||
char *if_name= "en0";
|
||||
char buf[INET6_ADDRSTRLEN];
|
||||
|
||||
if (getifaddrs(&iflist) == 0) {
|
||||
for (struct ifaddrs* cur = iflist; cur; cur = cur->ifa_next) {
|
||||
if (strcmp(cur->ifa_name, if_name) == 0) {
|
||||
if ((cur->ifa_addr->sa_family == AF_LINK) && cur->ifa_addr) {
|
||||
unsigned char mac[6];
|
||||
struct sockaddr_dl* sdl = (struct sockaddr_dl*)cur->ifa_addr;
|
||||
memcpy(mac, LLADDR(sdl), sdl->sdl_alen);
|
||||
sprintf(hw_addr, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
}
|
||||
|
||||
if (cur->ifa_addr->sa_family == AF_INET) {
|
||||
void *tmp = &((struct sockaddr_in *)cur->ifa_addr)->sin_addr;
|
||||
strcpy(ip_addr, inet_ntop(cur->ifa_addr->sa_family, tmp, buf, sizeof(buf)));
|
||||
}
|
||||
}
|
||||
}
|
||||
freeifaddrs(iflist);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void get_local_address() {
|
||||
/**
|
||||
* Returns the local hardware address (e.g. MAC address). On macOS the "en0"
|
||||
* interface is used. On other platforms the first non-loopback interface is
|
||||
* used.
|
||||
*
|
||||
* As a side-effect, the local global ip_addr is also populated.
|
||||
*
|
||||
* (Are these choices of interface really the right ones? Seems risky for
|
||||
* multi-homed systems.)
|
||||
*
|
||||
* @return the local hardware address or NULL if it does not exist, cannot
|
||||
* be retrieved, or out-of-memory. The caller must free the returned
|
||||
* memory.
|
||||
*/
|
||||
static char * get_local_address() {
|
||||
struct ifconf ifc;
|
||||
char buf[4096];
|
||||
char * hw_addr = NULL;
|
||||
int s, i;
|
||||
if (-1 == (s = socket(AF_INET, SOCK_DGRAM, 0))) {
|
||||
perror("socket");
|
||||
@@ -160,8 +141,8 @@ static void get_local_address() {
|
||||
exit(1);
|
||||
}
|
||||
for (i = 0; i < ifc.ifc_len/sizeof(ifc.ifc_req[0]); i++) {
|
||||
strcpy(ip_addr,
|
||||
inet_ntoa(((struct sockaddr_in *)(&ifc.ifc_req[i].ifr_addr))->sin_addr));
|
||||
strncpy(ip_addr,
|
||||
inet_ntoa(((struct sockaddr_in *)(&ifc.ifc_req[i].ifr_addr))->sin_addr), sizeof(ip_addr) - 1);
|
||||
if (0 > ioctl(s, SIOCGIFFLAGS, &ifc.ifc_req[i])) {
|
||||
perror("SIOCGIFFLAGS");
|
||||
exit(1);
|
||||
@@ -174,6 +155,14 @@ static void get_local_address() {
|
||||
perror("SIOCGIFHWADDR");
|
||||
exit(1);
|
||||
}
|
||||
// FIXME: How do I figure out what type of interface this is in order to
|
||||
// cast the struct sockaddr ifr_hraddr to the proper type and extract the
|
||||
// hardware address?
|
||||
//
|
||||
// Make sure this is correct for the target device and platform.
|
||||
hw_addr = (char*)malloc(HW_ADDRSTRLEN + 1);
|
||||
if (hw_addr == NULL)
|
||||
break;
|
||||
sprintf(hw_addr, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
(unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[0],
|
||||
(unsigned char)ifc.ifc_req[i].ifr_hwaddr.sa_data[1],
|
||||
@@ -184,14 +173,14 @@ static void get_local_address() {
|
||||
break;
|
||||
}
|
||||
close(s);
|
||||
return hw_addr;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void handle_mcast() {
|
||||
static void handle_mcast(char *hw_addr) {
|
||||
int s, one = 1, bytes;
|
||||
socklen_t addrlen;
|
||||
struct sockaddr_in saddr;
|
||||
struct ip_mreq mreq;
|
||||
struct sockaddr_in saddr = {0};
|
||||
struct ip_mreq mreq = {0};
|
||||
char wakeup_buf[sizeof(wakeup_header) + HW_ADDRSTRLEN + STR_TIMEOUTLEN] = {0, };
|
||||
char send_buf[sizeof(ssdp_reply) + INET_ADDRSTRLEN + 256 + 256 + sizeof(wakeup_buf)] = {0,};
|
||||
int send_size;
|
||||
@@ -208,11 +197,7 @@ static void handle_mcast() {
|
||||
exit(1);
|
||||
}
|
||||
saddr.sin_family = AF_INET;
|
||||
#ifdef __APPLE__
|
||||
saddr.sin_addr.s_addr = INADDR_ANY;
|
||||
#else
|
||||
saddr.sin_addr.s_addr = inet_addr("239.255.255.250");
|
||||
#endif
|
||||
saddr.sin_port = htons(1900);
|
||||
|
||||
if (-1 == bind(s, (struct sockaddr *)&saddr, sizeof(saddr))) {
|
||||
@@ -261,32 +246,42 @@ static void handle_mcast() {
|
||||
}
|
||||
|
||||
void run_ssdp(int port, const char *pFriendlyName, const char * pModelName, const char *pUuid) {
|
||||
char *hw_addr = NULL;
|
||||
struct sockaddr sa;
|
||||
socklen_t len = sizeof(sa);
|
||||
if(pFriendlyName) {
|
||||
strncpy(friendly_name, pFriendlyName, sizeof(friendly_name));
|
||||
friendly_name[255] = '\0';
|
||||
friendly_name[sizeof(friendly_name) - 1] = '\0';
|
||||
} else {
|
||||
strcpy(friendly_name, "DIAL server sample");
|
||||
}
|
||||
if(pModelName) {
|
||||
strncpy(model_name, pModelName, sizeof(model_name));
|
||||
uuid[255] = '\0';
|
||||
model_name[sizeof(model_name) - 1] = '\0';
|
||||
} else {
|
||||
strcpy(model_name, "deadbeef-dead-beef-dead-beefdeadbeef");
|
||||
}
|
||||
if(pUuid) {
|
||||
strncpy(uuid, pUuid, sizeof(uuid));
|
||||
uuid[255] = '\0';
|
||||
uuid[sizeof(uuid) - 1] = '\0';
|
||||
} else {
|
||||
strcpy(uuid, "deadbeef-dead-beef-dead-beefdeadbeef");
|
||||
}
|
||||
dial_port = port;
|
||||
get_local_address();
|
||||
ctx = mg_start(&request_handler, NULL, SSDP_PORT);
|
||||
if (mg_get_listen_addr(ctx, &sa, &len)) {
|
||||
my_port = ntohs(((struct sockaddr_in *)&sa)->sin_port);
|
||||
hw_addr = get_local_address();
|
||||
if (hw_addr == NULL) {
|
||||
printf("Unable to retrieve hardware address.");
|
||||
return;
|
||||
}
|
||||
printf("SSDP listening on %s:%d\n", ip_addr, my_port);
|
||||
handle_mcast();
|
||||
ctx = mg_start(&request_handler, NULL, SSDP_PORT);
|
||||
if (ctx == NULL) {
|
||||
printf("Unable to start SSDP master listening thread.");
|
||||
} else {
|
||||
if (mg_get_listen_addr(ctx, &sa, &len)) {
|
||||
my_port = ntohs(((struct sockaddr_in *)&sa)->sin_port);
|
||||
}
|
||||
printf("SSDP listening on %s:%d\n", ip_addr, my_port);
|
||||
handle_mcast(hw_addr);
|
||||
}
|
||||
free(hw_addr); hw_addr = NULL;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ DIALStatus system_start(DIALServer *ds, const char *appname, const char *payload
|
||||
if (strlen(spSleepPassword) != 0) {
|
||||
|
||||
/* Look for key */
|
||||
char *key_value;
|
||||
if ( (key_value = strchr(query_string, '&')) == '\0' ) {
|
||||
char *key_value = strchr(query_string, '&');
|
||||
if ( key_value == NULL || *key_value == '\0' ) {
|
||||
return kDIALStatusErrorForbidden; // No key specified.
|
||||
}
|
||||
|
||||
|
||||
@@ -30,12 +30,16 @@
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
char* smartstrcat(char* dest, char* src, size_t max_chars) {
|
||||
size_t copied = 0;
|
||||
while ((*(dest++) = *(src++)) && copied++ < max_chars) {
|
||||
static const char * unknown_str = "unknown";
|
||||
|
||||
char* smartstrncpy(char* dest, char* src, size_t max_chars) {
|
||||
size_t copied;
|
||||
for (copied = 0; copied < max_chars; copied++, dest++, src++) {
|
||||
*dest = *src;
|
||||
if (*dest == 0)
|
||||
break;
|
||||
}
|
||||
// In case we over-stepped the size, end the string nicely.
|
||||
*(--dest) = '\0';
|
||||
*dest = 0;
|
||||
return dest;
|
||||
}
|
||||
|
||||
@@ -89,23 +93,33 @@ void xmlencode(char *dst, const char *src, size_t max_size) {
|
||||
while (*src && current_size < max_size) {
|
||||
switch (*src) {
|
||||
case '&':
|
||||
dst = smartstrcat(dst, "&", max_size - current_size);
|
||||
if (current_size + 5 >= max_size)
|
||||
break;
|
||||
dst = smartstrncpy(dst, "&", max_size - current_size);
|
||||
current_size += 5;
|
||||
break;
|
||||
case '\"':
|
||||
dst = smartstrcat(dst, """, max_size - current_size);
|
||||
if (current_size + 6 >= max_size)
|
||||
break;
|
||||
dst = smartstrncpy(dst, """, max_size - current_size);
|
||||
current_size += 6;
|
||||
break;
|
||||
case '\'':
|
||||
dst = smartstrcat(dst, "'", max_size - current_size);
|
||||
if (current_size + 6 >= max_size)
|
||||
break;
|
||||
dst = smartstrncpy(dst, "'", max_size - current_size);
|
||||
current_size += 6;
|
||||
break;
|
||||
case '<':
|
||||
dst = smartstrcat(dst, "<", max_size - current_size);
|
||||
if (current_size + 4 >= max_size)
|
||||
break;
|
||||
dst = smartstrncpy(dst, "<", max_size - current_size);
|
||||
current_size += 4;
|
||||
break;
|
||||
case '>':
|
||||
dst = smartstrcat(dst, ">", max_size - current_size);
|
||||
if (current_size + 4 >= max_size)
|
||||
break;
|
||||
dst = smartstrncpy(dst, ">", max_size - current_size);
|
||||
current_size += 4;
|
||||
break;
|
||||
default:
|
||||
@@ -119,18 +133,34 @@ void xmlencode(char *dst, const char *src, size_t max_size) {
|
||||
}
|
||||
|
||||
char *parse_app_name(const char *uri) {
|
||||
char *unknown = NULL;
|
||||
if (uri == NULL) {
|
||||
return "unknown";
|
||||
unknown = (char*)calloc(strlen(unknown_str) + 1, sizeof(char));
|
||||
if (unknown == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
strncpy(unknown, unknown_str, strlen(unknown_str) + 1);
|
||||
return unknown;
|
||||
}
|
||||
char *slash = strrchr(uri, '/');
|
||||
if (slash == NULL || slash == uri) {
|
||||
return "unknown";
|
||||
unknown = (char*)calloc(strlen(unknown_str) + 1, sizeof(char));
|
||||
if (unknown == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
strncpy(unknown, unknown_str, strlen(unknown_str) + 1);
|
||||
return unknown;
|
||||
}
|
||||
char *begin = slash;
|
||||
while ((begin != uri) && (*--begin != '/'))
|
||||
;
|
||||
begin++; // skip the slash
|
||||
char *result = (char *) calloc(1, slash - begin+1);
|
||||
if (*begin == '/') {
|
||||
begin++; // skip the slash
|
||||
}
|
||||
char *result = (char *) calloc(slash - begin+1, sizeof(char));
|
||||
if (result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
strncpy(result, begin, slash - begin);
|
||||
result[slash-begin]='\0';
|
||||
return result;
|
||||
@@ -151,6 +181,9 @@ char *parse_param(char *query_string, char *param_name) {
|
||||
end++;
|
||||
int result_size = end - start;
|
||||
char *result = malloc(result_size + 1);
|
||||
if (result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
result[0] = '\0';
|
||||
strncpy(result, start, result_size);
|
||||
result[result_size] = '\0';
|
||||
@@ -165,20 +198,46 @@ DIALData *parse_params(char * query_string) {
|
||||
query_string++; // skip leading question mark
|
||||
}
|
||||
DIALData *result = NULL;
|
||||
int err = 0;
|
||||
char *query_string_dup = strdup(query_string);
|
||||
char * name_value = strtok(query_string_dup, "&");
|
||||
while (name_value != NULL) {
|
||||
DIALData *tmp = (DIALData *) malloc(sizeof(DIALData));
|
||||
if (tmp == NULL) {
|
||||
err = 1;
|
||||
break;
|
||||
}
|
||||
size_t name_value_length = strlen(name_value);
|
||||
tmp->key = (char *) malloc(name_value_length);
|
||||
tmp->value = (char *) malloc(name_value_length);
|
||||
sscanf(name_value, "%[^=]=%s", tmp->key, tmp->value);
|
||||
tmp->key = (char *) calloc(name_value_length + 1, sizeof(char));
|
||||
if (tmp->key == NULL) {
|
||||
free(tmp); tmp = NULL;
|
||||
err = 1;
|
||||
break;
|
||||
}
|
||||
tmp->value = (char *) calloc(name_value_length + 1, sizeof(char));
|
||||
if (tmp->value == NULL) {
|
||||
free(tmp->key); tmp->key = NULL;
|
||||
free(tmp); tmp = NULL;
|
||||
err = 1;
|
||||
break;
|
||||
}
|
||||
int match = sscanf(name_value, "%[^=]=%s", tmp->key, tmp->value);
|
||||
if (match != 2) {
|
||||
free(tmp->value); tmp->value = NULL;
|
||||
free(tmp->key); tmp->key = NULL;
|
||||
free(tmp); tmp = NULL;
|
||||
err = 1;
|
||||
break;
|
||||
}
|
||||
tmp->next = result;
|
||||
result = tmp;
|
||||
|
||||
name_value = strtok(NULL, "&"); // read next token
|
||||
}
|
||||
free(query_string_dup);
|
||||
if (err) {
|
||||
free_dial_data(&result); result = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -194,7 +253,7 @@ char from_hex(char ch) {
|
||||
/* Converts an integer value to its hex character*/
|
||||
char to_hex(char code) {
|
||||
static char hex[] = "0123456789abcdef";
|
||||
return hex[code & 15];
|
||||
return hex[code & 15];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
* Concatenate a maxim of max_chars characters from src into dest,
|
||||
* and return a pointer to the last character in dest.
|
||||
*/
|
||||
char* smartstrcat(char* dest, char* src, size_t max_chars);
|
||||
char* smartstrncpy(char* dest, char* src, size_t max_chars);
|
||||
|
||||
int urldecode(char *dst, const char *src, size_t max_size);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user