bird-lg/bird.h

364 lines
12 KiB
C

#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