bird-lg/main.c
2021-12-10 15:11:40 +01:00

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