#ifndef _BIRD_HEADER #define _BIRD_HEADER 1 #include #include #include #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: "); 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, "<<>>\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: "); 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, "<<>>\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