200 lines
6.8 KiB
C
200 lines
6.8 KiB
C
#define HTTPSERVER_IMPL
|
|
|
|
#define LOG_ENABLE_TRACE 1
|
|
|
|
#include <stdbool.h>
|
|
#include "httpserver.h"
|
|
#include <stdlib.h>
|
|
#include "favicon.h"
|
|
#include "string.h"
|
|
#include "bird.h"
|
|
#include "logging.h"
|
|
|
|
|
|
#define TRACEROUTE "traceroute -%s -A -q1 -N32 -w1 -m15 %s"
|
|
|
|
#define ERROR_NO_BIRD_SOCKET_CONFIGURED "no bird socket configured"
|
|
#define ERROR_BIRD_CONNECTION_FAILED "connection to bird daemon failed"
|
|
|
|
|
|
const char * config_ipv4_source;
|
|
const char * config_ipv6_source;
|
|
const char * bird_socket_path;
|
|
const char * bird6_socket_path;
|
|
|
|
const char * security_shared_secret;
|
|
string_view * security_access_list;
|
|
size_t security_access_list_length;
|
|
|
|
|
|
|
|
int request_target_is(http_request_t* request, char const * target) {
|
|
http_string_t url = http_request_path(request);
|
|
int len = strlen(target);
|
|
return len == url.len && memcmp(url.buf, target, url.len) == 0;
|
|
}
|
|
|
|
int request_is_get(http_request_t* request) {
|
|
http_string_t method = http_request_method(request);
|
|
return method.len == 3 && memcmp(method.buf, "GET", method.len) == 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void handle_traceroute(http_request_t* request, http_response_t* response, bool ipv6, string_view* query) {
|
|
|
|
http_response_header(response, "Content-Type", "text/plain");
|
|
|
|
// TODO: use strings
|
|
|
|
FILE *fp;
|
|
char buf[256];
|
|
|
|
char* command = malloc(sizeof(TRACEROUTE) + query->len);
|
|
assert(command != NULL /* command allocation was successful */);
|
|
char* destination = malloc(query->len + 1);
|
|
assert(command != NULL /* destination allocation was successful */);
|
|
memcpy(destination, query->buf, query->len);
|
|
destination[query->len] = '\0';
|
|
|
|
sprintf(command, TRACEROUTE, ipv6 ? "6" : "4", destination);
|
|
|
|
/* Open the command for reading. */
|
|
fp = popen(command, "r");
|
|
if (fp == NULL) {
|
|
http_response_status(response, 500);
|
|
http_response_body(response, "traceroute failed", sizeof("traceroute failed") - 1);
|
|
http_respond(request, response);
|
|
} else {
|
|
string str = string_init(0);
|
|
/* Read the output a line at a time - output it. */
|
|
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
|
string_append(str, buf);
|
|
}
|
|
|
|
/* close */
|
|
pclose(fp);
|
|
|
|
http_response_status(response, 200);
|
|
http_response_body(response, str->buf, str->len);
|
|
|
|
http_respond(request, response);
|
|
string_free(str);
|
|
}
|
|
}
|
|
|
|
void handle_bird(http_request_t* request, http_response_t* response, bool ipv6, string_view* query) {
|
|
http_response_header(response, "Content-Type", "text/plain");
|
|
|
|
const char * socket_path = ipv6 ? bird6_socket_path : bird_socket_path;
|
|
if (socket_path == NULL) {
|
|
http_response_status(response, 500);
|
|
http_response_body(response, ERROR_NO_BIRD_SOCKET_CONFIGURED, sizeof(ERROR_NO_BIRD_SOCKET_CONFIGURED) - 1);
|
|
http_respond(request, response);
|
|
} else {
|
|
bird_connection bird = bird_connect_uds(socket_path);
|
|
if (bird == -1) {
|
|
http_response_status(response, 500);
|
|
http_response_body(response, ERROR_BIRD_CONNECTION_FAILED, sizeof(ERROR_BIRD_CONNECTION_FAILED) - 1);
|
|
http_respond(request, response);
|
|
} else {
|
|
bird_response bird_res = bird_command(bird, query);
|
|
// todo: use status? depends whether the lg main application expects an error status code when command failed
|
|
http_response_status(response, 200);
|
|
http_response_body(response, bird_res.message->buf, bird_res.message->len);
|
|
http_respond(request, response);
|
|
bird_free_response(bird_res);
|
|
}
|
|
}
|
|
}
|
|
|
|
void handle_favicon(http_request_t* request, http_response_t* response) {
|
|
http_response_status(response, 200);
|
|
http_response_header(response, "Content-Type", "image/x-icon");
|
|
http_response_body(response, favicon_ico, favicon_ico_len);
|
|
http_respond(request, response);
|
|
}
|
|
|
|
void handle_request(http_request_t* request) {
|
|
http_response_t* response = http_response_init();
|
|
|
|
|
|
if (!request_is_get(request)) {
|
|
http_response_status(response, 405);
|
|
http_respond(request, response);
|
|
} else {
|
|
if (request_target_is(request, "/favicon.ico")) {
|
|
handle_favicon(request, response);
|
|
} else {
|
|
http_string_t query = http_request_query(request, "q");
|
|
string query_decoded = string_urldecode(&HTTP_STR_TO_VIEW(query));
|
|
if (query_decoded == NULL) {
|
|
http_response_status(response, 400);
|
|
http_respond(request, response);
|
|
} else {
|
|
if (request_target_is(request, "/traceroute")) {
|
|
handle_traceroute(request, response, false, (string_view*)query_decoded);
|
|
} else if (request_target_is(request, "/traceroute6")) {
|
|
handle_traceroute(request, response, true, (string_view*)query_decoded);
|
|
} else if (request_target_is(request, "/bird")) {
|
|
handle_bird(request, response, false, (string_view*)query_decoded);
|
|
} else if (request_target_is(request, "/bird6")) {
|
|
handle_bird(request, response, true, (string_view*)query_decoded);
|
|
} else {
|
|
http_response_status(response, 404);
|
|
http_respond(request, response);
|
|
}
|
|
string_free(query_decoded);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
const char * log_level_str = getenv("LOG_LEVEL");
|
|
if (log_level_str != NULL) {
|
|
log_level = atoi(log_level_str);
|
|
}
|
|
|
|
const char * bind_ip = getenv("BIND_IP");
|
|
const char * bind_port = getenv("BIND_PORT");
|
|
bird_socket_path = getenv("BIRD_SOCKET");
|
|
bird6_socket_path = getenv("BIRD6_SOCKET");
|
|
|
|
config_ipv4_source = getenv("IPV4_SOURCE");
|
|
config_ipv6_source = getenv("IPV6_SOURCE");
|
|
|
|
char * access_list = getenv("ACCESS_LIST");
|
|
security_shared_secret = getenv("SHARED_SECRET");
|
|
|
|
if (access_list != NULL) {
|
|
string_view access_list_str = (string_view) {access_list, strlen(access_list)};
|
|
security_access_list_length = string_split(&access_list_str, ',', &security_access_list); // don't care about freeing this since it's needed until the end of the program
|
|
}
|
|
|
|
|
|
if (bind_ip == NULL) {
|
|
bind_ip = "0.0.0.0";
|
|
}
|
|
if (bind_port == NULL) {
|
|
bind_port = "5000";
|
|
}
|
|
|
|
if (bird6_socket_path == NULL && bird_socket_path != NULL) {
|
|
bird6_socket_path = bird_socket_path;
|
|
} else if (bird_socket_path == NULL && bird6_socket_path != NULL) {
|
|
bird_socket_path = bird6_socket_path;
|
|
}
|
|
|
|
int port = atoi(bind_port);
|
|
// TODO: implement binding to an IP
|
|
|
|
http_server_t* server = http_server_init(port, handle_request);
|
|
http_server_listen(server);
|
|
}
|
|
|
|
|