364 lines
12 KiB
C
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
|