first kinda working version
This commit is contained in:
parent
5df47b17b2
commit
6d5c6040fe
363
bird.h
Normal file
363
bird.h
Normal file
|
@ -0,0 +1,363 @@
|
|||
#ifndef _BIRD_HEADER
|
||||
#define _BIRD_HEADER 1
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "string.h"
|
||||
#include "logging.h"
|
||||
|
||||
#define BIRD_BUF_SIZE 1024
|
||||
|
||||
typedef struct bird_code_s {
|
||||
char code[4];
|
||||
char* message;
|
||||
} bird_code_t;
|
||||
|
||||
|
||||
const bird_code_t bird_success_codes[] = {
|
||||
{"0002", "Reading configuration"},
|
||||
{"0003", "Reconfigured"},
|
||||
{"0004", "Reconfiguration in progress"},
|
||||
{"0005", "Reconfiguration already in progress, queueing"},
|
||||
{"0006", "Reconfiguration ignored, shutting down"},
|
||||
{"0007", "Shutdown ordered"},
|
||||
{"0008", "Already disabled"},
|
||||
{"0009", "Disabled"},
|
||||
{"0010", "Already enabled"},
|
||||
{"0011", "Enabled"},
|
||||
{"0012", "Restarted"},
|
||||
{"0013", "Status report"},
|
||||
{"0014", "Route count"},
|
||||
{"0015", "Reloading"},
|
||||
{"0016", "Access restricted"},
|
||||
};
|
||||
|
||||
const bird_code_t bird_error_codes[] = {
|
||||
{"8000", "Reply too long"},
|
||||
{"8001", "Route not found"},
|
||||
{"8002", "Configuration file error"},
|
||||
{"8003", "No protocols match"},
|
||||
{"8004", "Stopped due to reconfiguration"},
|
||||
{"8005", "Protocol is down => cannot dump"},
|
||||
{"8006", "Reload failed"},
|
||||
{"8007", "Access denied"},
|
||||
|
||||
{"9000", "Command too long"},
|
||||
{"9001", "Parse error"},
|
||||
{"9002", "Invalid symbol type"},
|
||||
};
|
||||
|
||||
const char* code_get_message(const bird_code_t *codes, size_t codes_len, const char code[4]) {
|
||||
for(size_t i = 0; i < codes_len; ++i) {
|
||||
if (memcmp(codes[i].code, code, 4) == 0) {
|
||||
return codes[i].message;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
typedef int bird_connection;
|
||||
|
||||
typedef struct bird_response_s {
|
||||
bool success;
|
||||
string message;
|
||||
} bird_response;
|
||||
|
||||
bool bird_initialize(bird_connection conn);
|
||||
bird_connection bird_connect_inet(const char* host, const char* port);
|
||||
bird_connection bird_connect_uds(const char* path);
|
||||
bird_response bird_command(bird_connection conn, string_view* command);
|
||||
void bird_close(bird_connection conn);
|
||||
void bird_free_response(bird_response res);
|
||||
|
||||
|
||||
|
||||
|
||||
bird_connection bird_connect_inet(const char* host, const char* port) {
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *result, *rp;
|
||||
int conn;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if(0 != getaddrinfo(host, port, &hints, &result)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
||||
conn = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (conn == -1) {
|
||||
continue;
|
||||
}
|
||||
if (connect(conn, rp->ai_addr, rp->ai_addrlen) != -1) {
|
||||
break; // success
|
||||
}
|
||||
close(conn);
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
if (rp == NULL) { // no successful connection
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!bird_initialize(conn)) {
|
||||
bird_close(conn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
bird_connection bird_connect_uds(const char* path) {
|
||||
int conn;
|
||||
struct sockaddr_un addr;
|
||||
|
||||
if (strlen(path) >= sizeof(addr.sun_path)) {
|
||||
log_error("bird connection failed: socket path too long (path is \"%s\")\n", path);
|
||||
return -1;
|
||||
}
|
||||
log_trace("bird socket path is \"%s\"\n", path);
|
||||
|
||||
conn = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (conn == -1) {
|
||||
log_error("bird connection failed: socket(...) returned -1, errno: %d (%s)\n", errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, path);
|
||||
if (connect(conn, (struct sockaddr *)&addr, SUN_LEN(&addr)) == -1) {
|
||||
log_error("bird connection failed: connect(...) returned -1, errno: %d (%s)\n", errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
log_info("bird connection successful\n");
|
||||
if (!bird_initialize(conn)) {
|
||||
bird_close(conn);
|
||||
return -1;
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
bool bird_initialize(bird_connection conn) {
|
||||
// read and discard welcome message
|
||||
char buf[1024];
|
||||
read(conn, buf, 1024);
|
||||
// restrict
|
||||
bird_response res = bird_command(conn, (string_view*)string_from("restrict"));
|
||||
if (!res.success) {
|
||||
string_null_terminate(res.message);
|
||||
log_error("bird restrict failed: %s", res.message);
|
||||
}
|
||||
return res.success;
|
||||
}
|
||||
|
||||
bird_response bird_command_v1(bird_connection conn, string_view* command) {
|
||||
log_info("bird command: %.*s\n", command->len, command->buf);
|
||||
bird_response response;
|
||||
const char *ptr = command->buf;
|
||||
size_t commandlen = command->len;
|
||||
while(commandlen > 0) {
|
||||
int writelen = write(conn, ptr, commandlen);
|
||||
if (writelen < 1) {
|
||||
response.success = false;
|
||||
response.message = string_from("Bird connection problem: <TODO>");
|
||||
return response;
|
||||
}
|
||||
ptr += writelen;
|
||||
commandlen -= writelen;
|
||||
}
|
||||
write(conn, "\n", 1);
|
||||
|
||||
char buf[BIRD_BUF_SIZE];
|
||||
string_view buf_view;
|
||||
buf_view.buf = &(buf[0]);
|
||||
buf_view.len = 0;
|
||||
response.message = string_init(BIRD_BUF_SIZE);
|
||||
string last_line = string_init(0);
|
||||
string data = string_init(0);
|
||||
bool completed = false;
|
||||
|
||||
while(!completed) {
|
||||
log_trace("before read\n");
|
||||
buf_view.len = read(conn, buf, BIRD_BUF_SIZE - 1);
|
||||
buf[BIRD_BUF_SIZE - 1] = '\0';
|
||||
log_trace("read buf: \n=======\n%s=======\n", buf);
|
||||
log_trace("read %d bytes\n", buf_view.len);
|
||||
string_copy(data, (string_view*)last_line);
|
||||
log_trace("copied last_line to data\n");
|
||||
string_concat(data, &buf_view);
|
||||
log_trace("concated buf_view onto data\n");
|
||||
string_view *lines;
|
||||
int num_lines = string_split((string_view*) data, '\n', &lines);
|
||||
log_trace("split data into %d lines\n", num_lines);
|
||||
int actual_num_lines = num_lines;
|
||||
if (buf_view.len == BIRD_BUF_SIZE) {
|
||||
string_copy(last_line, &(lines[num_lines - 1]));
|
||||
actual_num_lines = num_lines - 1;
|
||||
}
|
||||
for(int i = 0; i < actual_num_lines; ++i) {
|
||||
string_view line = lines[i];
|
||||
if (line.len == 0 || string_is_whitespace(&line)) {
|
||||
continue;
|
||||
}
|
||||
if (line.len >= 4) {
|
||||
if (memcmp(line.buf, "0000", 4) == 0) {
|
||||
response.success = true;
|
||||
completed = true;
|
||||
log_trace("received 0000, response complete\n");
|
||||
break;
|
||||
}
|
||||
const char* code_msg;
|
||||
code_msg = code_get_message(bird_success_codes, sizeof(bird_success_codes) / sizeof(bird_code_t), line.buf);
|
||||
if (code_msg != NULL) {
|
||||
response.success = true;
|
||||
string_copy_chars(response.message, code_msg);
|
||||
completed = true;
|
||||
log_trace("received success code, response complete (%s)\n", code_msg);
|
||||
break;
|
||||
}
|
||||
code_msg = code_get_message(bird_error_codes, sizeof(bird_error_codes) / sizeof(bird_code_t), line.buf);
|
||||
if (code_msg != NULL) {
|
||||
response.success = false;
|
||||
string_copy_chars(response.message, code_msg);
|
||||
completed = true;
|
||||
log_trace("received error code, response complete (%s)\n", code_msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
string_view line_part;
|
||||
if (line.buf[0] == '1' || line.buf[0] == '2') {
|
||||
line_part = string_get_view(&line, 5, -1);
|
||||
string_concat(response.message, &line_part);
|
||||
string_append(response.message, "\n");
|
||||
} else if (line.buf[0] == ' ') {
|
||||
line_part = string_get_view(&line, 1, -1);
|
||||
string_concat(response.message, &line_part);
|
||||
string_append(response.message, "\n");
|
||||
} else if (line.buf[0] == '+') {
|
||||
line_part = string_get_view(&line, 1, -1);
|
||||
string_concat(response.message, &line_part);
|
||||
} else {
|
||||
string_append(response.message, "<<<unparsable_string(");
|
||||
string_concat(response.message, &line);
|
||||
string_append(response.message, ")>>>\n");
|
||||
}
|
||||
}
|
||||
free(lines);
|
||||
}
|
||||
string_free(last_line);
|
||||
string_free(data);
|
||||
string_null_terminate(response.message);
|
||||
log_trace("bird response: %s\n", response.message->buf);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bird_response bird_command(bird_connection conn, string_view* command) {
|
||||
log_info("bird command: %.*s\n", command->len, command->buf);
|
||||
bird_response response;
|
||||
const char *ptr = command->buf;
|
||||
size_t commandlen = command->len;
|
||||
while(commandlen > 0) {
|
||||
int writelen = write(conn, ptr, commandlen);
|
||||
if (writelen < 1) {
|
||||
response.success = false;
|
||||
response.message = string_from("Bird connection problem: <TODO>");
|
||||
return response;
|
||||
}
|
||||
ptr += writelen;
|
||||
commandlen -= writelen;
|
||||
}
|
||||
write(conn, "\n", 1);
|
||||
|
||||
char buf[BIRD_BUF_SIZE];
|
||||
string_view buf_view;
|
||||
buf_view.buf = &(buf[0]);
|
||||
buf_view.len = 0;
|
||||
response.message = string_init(BIRD_BUF_SIZE);
|
||||
string last_line = string_init(0);
|
||||
string data = string_init(0);
|
||||
bool completed = false;
|
||||
|
||||
while(!completed) {
|
||||
log_trace("before read\n");
|
||||
buf_view.len = read(conn, buf, BIRD_BUF_SIZE - 1);
|
||||
buf[BIRD_BUF_SIZE - 1] = '\0';
|
||||
log_trace("read buf: \n=======\n%s=======\n", buf);
|
||||
log_trace("read %d bytes\n", buf_view.len);
|
||||
string_copy(data, (string_view*)last_line);
|
||||
log_trace("copied last_line to data\n");
|
||||
string_concat(data, &buf_view);
|
||||
log_trace("concated buf_view onto data\n");
|
||||
string_view *lines;
|
||||
int num_lines = string_split((string_view*) data, '\n', &lines);
|
||||
log_trace("split data into %d lines\n", num_lines);
|
||||
int actual_num_lines = num_lines;
|
||||
if (buf_view.len == BIRD_BUF_SIZE) {
|
||||
string_copy(last_line, &(lines[num_lines - 1]));
|
||||
actual_num_lines = num_lines - 1;
|
||||
}
|
||||
for(int i = 0; i < actual_num_lines; ++i) {
|
||||
string_view line = lines[i];
|
||||
|
||||
if (line.len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
string_view line_content;
|
||||
bool append_newline = true;
|
||||
|
||||
if (line.buf[0] == '0' && line.buf[1] == '0' && line.buf[2] == '0' && line.buf[3] == '0') {
|
||||
completed = true;
|
||||
response.success = true;
|
||||
line_content = string_get_view(&line, 0, 0);
|
||||
} else if (line.buf[0] == '0') {
|
||||
completed = true;
|
||||
response.success = true;
|
||||
line_content = string_get_view(&line, 5, -1);
|
||||
} else if (line.buf[0] == '8' || line.buf[0] == '9') {
|
||||
completed = true;
|
||||
response.success = false;
|
||||
line_content = string_get_view(&line, 5, -1);
|
||||
} else if (line.buf[0] == '1' || line.buf[0] == '2') {
|
||||
line_content = string_get_view(&line, 5, -1);
|
||||
} else if (line.buf[0] == ' ') {
|
||||
line_content = string_get_view(&line, 1, -1);
|
||||
} else if (line.buf[0] == '+') {
|
||||
line_content = string_get_view(&line, 5, -1);
|
||||
append_newline = false;
|
||||
} else {
|
||||
line_content = string_get_view(&line, 0, 0);
|
||||
string_append(response.message, "<<<unparsable_string(");
|
||||
string_concat(response.message, &line);
|
||||
string_append(response.message, ")>>>\n");
|
||||
}
|
||||
string_concat(response.message, &line_content);
|
||||
if (append_newline) {
|
||||
string_append_char(response.message, '\n');
|
||||
}
|
||||
|
||||
}
|
||||
free(lines);
|
||||
}
|
||||
string_free(last_line);
|
||||
string_free(data);
|
||||
string_null_terminate(response.message);
|
||||
log_trace("bird response: %s\n", response.message->buf);
|
||||
return response;
|
||||
}
|
||||
|
||||
void bird_close(bird_connection conn) {
|
||||
close(conn);
|
||||
}
|
||||
|
||||
void bird_free_response(bird_response res) {
|
||||
string_free(res.message);
|
||||
}
|
||||
|
||||
#endif
|
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
1937
httpserver.h
Normal file
1937
httpserver.h
Normal file
File diff suppressed because it is too large
Load diff
40
logging.h
Normal file
40
logging.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef _LOGGING_HEADER
|
||||
#define _LOGGING_HEADER 1
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
// log levels:
|
||||
// 0: no logging
|
||||
// 1: errors
|
||||
// 2: warnings
|
||||
// 3: info
|
||||
// 4: trace
|
||||
|
||||
|
||||
#define log_error(...) log_message(1, __VA_ARGS__)
|
||||
#define log_warning(...) log_message(2, __VA_ARGS__)
|
||||
#define log_info(...) log_message(3, __VA_ARGS__)
|
||||
#ifdef LOG_ENABLE_TRACE
|
||||
#define log_trace(...) log_message(4, __VA_ARGS__)
|
||||
#endif
|
||||
#ifndef LOG_ENABLE_TRACE
|
||||
#define log_trace(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
|
||||
int log_level = 1;
|
||||
|
||||
|
||||
void log_message(int level, const char * format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
if(level <= log_level) {
|
||||
// TODO: format log messages with timestamp, log level and newline at the end
|
||||
vfprintf(stderr, format, args);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
199
main.c
Normal file
199
main.c
Normal file
|
@ -0,0 +1,199 @@
|
|||
#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);
|
||||
}
|
||||
|
||||
|
221
string.h
Normal file
221
string.h
Normal file
|
@ -0,0 +1,221 @@
|
|||
#ifndef _STRING_HEADER
|
||||
#define _STRING_HEADER 1
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
|
||||
#define HTTP_STR_TO_VIEW(http_str) ((string_view){(char*)http_str.buf, (size_t)http_str.len})
|
||||
|
||||
|
||||
typedef struct string_s {
|
||||
char* buf;
|
||||
size_t len;
|
||||
size_t size;
|
||||
} *string;
|
||||
|
||||
typedef struct string_view_s {
|
||||
char* buf;
|
||||
size_t len;
|
||||
} string_view;
|
||||
|
||||
const size_t po2s[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384 };
|
||||
|
||||
|
||||
size_t find_next_size(size_t old_size) {
|
||||
if (old_size >= 16384) {
|
||||
return (old_size / 16384 + 1) * 16384;
|
||||
} else {
|
||||
for(size_t i = 0; i < sizeof(po2s)/sizeof(size_t); ++i) {
|
||||
if (old_size < po2s[i]) {
|
||||
return po2s[i];
|
||||
}
|
||||
}
|
||||
assert(false /*can not reach here */);
|
||||
}
|
||||
}
|
||||
|
||||
string string_init(size_t size) {
|
||||
size_t actual_size = find_next_size(size);
|
||||
string str = malloc(sizeof (struct string_s));
|
||||
assert(str != NULL /* str allocation was successful */);
|
||||
str->len = 0;
|
||||
str->size = actual_size;
|
||||
str->buf = malloc(actual_size);
|
||||
assert(str->buf != NULL /* str->buf allocation was successful */);
|
||||
return str;
|
||||
}
|
||||
|
||||
string string_from(const char * content) {
|
||||
size_t length = strlen(content);
|
||||
size_t size = find_next_size(length);
|
||||
string str = string_init(size);
|
||||
memcpy(str->buf, content, length);
|
||||
str->len = length;
|
||||
return str;
|
||||
}
|
||||
|
||||
void string_free(string str) {
|
||||
free(str->buf);
|
||||
free(str);
|
||||
}
|
||||
|
||||
void string_grow(string str, size_t amount) {
|
||||
size_t new_size = find_next_size(str->size + amount);
|
||||
str->buf = realloc(str->buf, new_size);
|
||||
assert(str->buf != NULL /* str->buf reallocation was successful */);
|
||||
str->size = new_size;
|
||||
}
|
||||
|
||||
void string_append_with_len(string str, const char * content, size_t len) {
|
||||
if (str->len + len + 1 > str->size) {
|
||||
string_grow(str, len + 1);
|
||||
}
|
||||
assert(str->len + len <= str->size /* string is large enough to fit appended content */);
|
||||
memcpy(str->buf + str->len, content, len);
|
||||
str->len += len;
|
||||
}
|
||||
|
||||
void string_append(string str, const char * content) {
|
||||
size_t len = strlen(content);
|
||||
string_append_with_len(str, content, len);
|
||||
}
|
||||
|
||||
void string_append_char(string str, char ch) {
|
||||
string_append_with_len(str, &ch, 1);
|
||||
}
|
||||
|
||||
void string_concat(string str, const string_view *other) {
|
||||
string_append_with_len(str, other->buf, other->len);
|
||||
}
|
||||
|
||||
void string_copy(string str, const string_view *other) {
|
||||
str->len = 0;
|
||||
string_concat(str, other);
|
||||
}
|
||||
|
||||
void string_copy_chars(string str, const char *other) {
|
||||
str->len = 0;
|
||||
string_append(str, other);
|
||||
}
|
||||
|
||||
void string_null_terminate(string str) {
|
||||
if (str->len + 1 > str->size) {
|
||||
string_grow(str, 1);
|
||||
}
|
||||
assert(str->len + 1 <= str->size /* string is large enough to fit null terminator at the end */);
|
||||
str->buf[str->len] = '\0';
|
||||
}
|
||||
|
||||
string_view string_get_view(string_view *str, size_t offset, int length) {
|
||||
string_view view;
|
||||
if (length == -1) {
|
||||
length = str->len - offset;
|
||||
}
|
||||
if (offset < str->len && offset + length - 1 < str->len) {
|
||||
view.buf = str->buf + offset;
|
||||
view.len = length;
|
||||
} else {
|
||||
view.buf = str->buf + str->len - 1;
|
||||
view.len = 0;
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
size_t string_split(string_view *str, char delimiter, string_view **out) {
|
||||
const char* strPtr = str->buf;
|
||||
size_t remainingLength = str->len;
|
||||
const char* newlinePtr;
|
||||
|
||||
string_view *lines = malloc(sizeof(string_view));
|
||||
assert(lines != NULL /* lines allocation was successful */);
|
||||
int nextLineIndex = 0;
|
||||
size_t offset, length;
|
||||
|
||||
while(NULL != (newlinePtr = memchr(strPtr, delimiter, remainingLength))) {
|
||||
offset = strPtr - str->buf;
|
||||
length = newlinePtr - strPtr;
|
||||
lines[nextLineIndex] = string_get_view(str, offset, length);
|
||||
nextLineIndex += 1;
|
||||
strPtr = newlinePtr + 1;
|
||||
remainingLength -= length + 1;
|
||||
lines = realloc(lines, sizeof(string_view) * (nextLineIndex + 1));
|
||||
assert(lines != NULL /* lines allocation was successful */);
|
||||
}
|
||||
offset = strPtr - str->buf;
|
||||
lines[nextLineIndex] = string_get_view(str, offset, -1);
|
||||
|
||||
*out = lines;
|
||||
|
||||
return nextLineIndex + 1;
|
||||
}
|
||||
|
||||
bool string_is_whitespace(string_view *str) {
|
||||
for (size_t i = 0; i < str->len; ++i) {
|
||||
char ch = str->buf[i];
|
||||
if (!isspace(ch)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int hex_char_to_num(char ch) {
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
return ch - '0';
|
||||
}
|
||||
if (ch >= 'a' && ch <= 'f') {
|
||||
return ch - 'a' + 10;
|
||||
}
|
||||
if (ch >= 'A' && ch <= 'F') {
|
||||
return ch - 'A' + 10;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int decode_url_hex(const char * input) {
|
||||
char ch1 = input[0];
|
||||
char ch2 = input[1];
|
||||
int n1 = hex_char_to_num(ch1);
|
||||
int n2 = hex_char_to_num(ch2);
|
||||
if (n1 == -1 || n2 == -1) {
|
||||
return -1;
|
||||
} else {
|
||||
return n1 * 16 + n2;
|
||||
}
|
||||
}
|
||||
|
||||
string string_urldecode(string_view *str) {
|
||||
string result = string_init(str->len);
|
||||
|
||||
int c;
|
||||
|
||||
for(size_t i = 0; i < str->len; ++i) {
|
||||
c = str->buf[i];
|
||||
if (c == '+') {
|
||||
c = ' ';
|
||||
} else if (c == '%') {
|
||||
if (i + 2 < str->len) {
|
||||
c = decode_url_hex(&(str->buf[i + 1]));
|
||||
i += 2;
|
||||
if (c == -1) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
string_append_char(result, (char)c);
|
||||
}
|
||||
return result;
|
||||
|
||||
error:
|
||||
string_free(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue