diff --git a/Rakefile b/Rakefile index a132933..bc7a965 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,6 @@ DEBUG = true -SOURCES = %w( flexnbd ioutil readwrite serve util ) +SOURCES = %w( flexnbd ioutil readwrite serve util parse ) OBJECTS = SOURCES.map { |s| "#{s}.o" } LIBS = %w( pthread ) CCFLAGS = %w( -Wall ) diff --git a/flexnbd.c b/flexnbd.c index bc3e2d4..5863c7e 100644 --- a/flexnbd.c +++ b/flexnbd.c @@ -17,89 +17,16 @@ void syntax() "Syntax: flexnbd serve \\\n" " [full path to control socket] \\\n" " [allowed connection addresses ...]\n" - " flexnbd mirror \n" - " flexnbd status \n" " flexnbd read > data\n" " flexnbd write < data\n" " flexnbd write \n" + " flexnbd acl [allowed connection addresses ...]\n" + " flexnbd mirror \n" + " flexnbd status \n" ); exit(1); } -#define IS_IP_VALID_CHAR(x) ( ((x) >= '0' && (x) <= '9' ) || \ - ((x) >= 'a' && (x) <= 'f') || \ - ((x) >= 'A' && (x) <= 'F' ) || \ - (x) == ':' || (x) == '.' \ - ) -int parse_ip_to_sockaddr(struct sockaddr* out, char* src) -{ - char temp[64]; - struct sockaddr_in *v4 = (struct sockaddr_in *) out; - struct sockaddr_in6 *v6 = (struct sockaddr_in6 *) out; - - /* allow user to start with [ and end with any other invalid char */ - { - int i=0, j=0; - if (src[i] == '[') - i++; - for (; i<64 && IS_IP_VALID_CHAR(src[i]); i++) - temp[j++] = src[i]; - temp[j] = 0; - } - - if (temp[0] == '0' && temp[1] == '\0') { - v4->sin_family = AF_INET; - v4->sin_addr.s_addr = INADDR_ANY; - return 1; - } - - if (inet_pton(AF_INET, temp, &v4->sin_addr) == 1) { - out->sa_family = AF_INET; - return 1; - } - - if (inet_pton(AF_INET6, temp, &v6->sin6_addr) == 1) { - out->sa_family = AF_INET6; - return 1; - } - - return 0; -} - -int parse_acl(struct ip_and_mask*** out, int max, char **entries) -{ - int i; - - if (max == 0) { - *out = NULL; - return 0; - } - - for (i = 0; i < max; i++) { -# define MAX_MASK_BITS (outentry->ip.family == AF_INET ? 32 : 128) - int j; - struct ip_and_mask* outentry = (*out)[i]; - - if (parse_ip_to_sockaddr(&outentry->ip.generic, entries[i]) == 0) - return i; - - for (j=0; entries[i][j] && entries[i][j] != '/'; j++) - ; - if (entries[i][j] == '/') { - outentry->mask = atoi(entries[i]+j+1); - if (outentry->mask < 1 || outentry->mask > MAX_MASK_BITS) - return i; - } - else - outentry->mask = MAX_MASK_BITS; -# undef MAX_MASK_BITS - - debug("acl entry %d has mask %d", i, outentry->mask); - } - - return max; -} - void params_serve( struct mode_serve_params* out, char* s_ip_address, diff --git a/ioutil.c b/ioutil.c index 73f8665..7fe74cd 100644 --- a/ioutil.c +++ b/ioutil.c @@ -137,4 +137,19 @@ int splice_via_pipe_loop(int fd_in, int fd_out, size_t len) return spliced < len ? -1 : 0; } - +int read_until_newline(int fd, char* buf, int bufsize) +{ + int cur; + bufsize -=1; + + for (cur=0; cur < bufsize; cur++) { + int result = read(fd, buf+cur, 1); + if (result < 0) + return -1; + if (buf[cur] == 10 || buf[cur] == 13) + break; + } + buf[cur++] = 0; + + return cur; +} diff --git a/ioutil.h b/ioutil.h index 055a057..4837305 100644 --- a/ioutil.h +++ b/ioutil.h @@ -9,6 +9,7 @@ int writeloop(int filedes, const void *buffer, size_t size); int readloop(int filedes, void *buffer, size_t size); int sendfileloop(int out_fd, int in_fd, off64_t *offset, size_t count); int splice_via_pipe_loop(int fd_in, int fd_out, size_t len); +int read_until_newline(int fd, char* buf, int bufsize); #endif diff --git a/params.h b/params.h index 6bff2a9..7b629c5 100644 --- a/params.h +++ b/params.h @@ -4,27 +4,14 @@ #define _GNU_SOURCE #define _LARGEFILE64_SOURCE +#include "parse.h" + #include -#include -#include -#include - -union mysockaddr { - unsigned short family; - struct sockaddr generic; - struct sockaddr_in v4; - struct sockaddr_in6 v6; -}; - -struct ip_and_mask { - union mysockaddr ip; - int mask; -}; struct mode_serve_params { union mysockaddr bind_to; int acl_entries; - struct ip_and_mask** acl; + struct ip_and_mask *acl[0]; char* filename; int tcp_backlog; char* control_socket_name; @@ -54,6 +41,11 @@ struct client_params { char* block_allocation_map; }; +struct control_params { + int socket; + struct mode_serve_params* serve; +}; + union mode_params { struct mode_serve_params serve; struct mode_readwrite_params readwrite; diff --git a/parse.c b/parse.c new file mode 100644 index 0000000..b5e2e86 --- /dev/null +++ b/parse.c @@ -0,0 +1,87 @@ +#include "parse.h" +#include "util.h" + +int atoi(const char *nptr); + +#define IS_IP_VALID_CHAR(x) ( ((x) >= '0' && (x) <= '9' ) || \ + ((x) >= 'a' && (x) <= 'f') || \ + ((x) >= 'A' && (x) <= 'F' ) || \ + (x) == ':' || (x) == '.' \ + ) +int parse_ip_to_sockaddr(struct sockaddr* out, char* src) +{ + char temp[64]; + struct sockaddr_in *v4 = (struct sockaddr_in *) out; + struct sockaddr_in6 *v6 = (struct sockaddr_in6 *) out; + + /* allow user to start with [ and end with any other invalid char */ + { + int i=0, j=0; + if (src[i] == '[') + i++; + for (; i<64 && IS_IP_VALID_CHAR(src[i]); i++) + temp[j++] = src[i]; + temp[j] = 0; + } + + if (temp[0] == '0' && temp[1] == '\0') { + v4->sin_family = AF_INET; + v4->sin_addr.s_addr = INADDR_ANY; + return 1; + } + + if (inet_pton(AF_INET, temp, &v4->sin_addr) == 1) { + out->sa_family = AF_INET; + return 1; + } + + if (inet_pton(AF_INET6, temp, &v6->sin6_addr) == 1) { + out->sa_family = AF_INET6; + return 1; + } + + return 0; +} + +int parse_acl(struct ip_and_mask *out[], int max, char **entries) +{ + int i; + + if (max == 0) { + *out = NULL; + return 0; + } + else { + *out = xmalloc(max * sizeof(struct ip_and_mask)); + debug("acl alloc: %p", *out); + } + + for (i = 0; i < max; i++) { +# define MAX_MASK_BITS (outentry->ip.family == AF_INET ? 32 : 128) + int j; + struct ip_and_mask* outentry = *out+i; + + if (parse_ip_to_sockaddr(&outentry->ip.generic, entries[i]) == 0) + return i; + + for (j=0; entries[i][j] && entries[i][j] != '/'; j++) + ; + if (entries[i][j] == '/') { + outentry->mask = atoi(entries[i]+j+1); + if (outentry->mask < 1 || outentry->mask > MAX_MASK_BITS) + return i; + } + else + outentry->mask = MAX_MASK_BITS; +# undef MAX_MASK_BITS + debug("acl ptr[%d]: %p %d",i, outentry, outentry->mask); + } + + for (i=0; i < max; i++) { + struct ip_and_mask* entry = *out+i; + debug("acl entry %d @ %p has mask %d", i, entry, entry->mask); + } + + return max; +} + diff --git a/parse.h b/parse.h new file mode 100644 index 0000000..6019ec2 --- /dev/null +++ b/parse.h @@ -0,0 +1,24 @@ +#ifndef __PARSE_H +#define __PARSE_H + +#include +#include +#include + +union mysockaddr { + unsigned short family; + struct sockaddr generic; + struct sockaddr_in v4; + struct sockaddr_in6 v6; +}; + +struct ip_and_mask { + union mysockaddr ip; + int mask; +}; + +int parse_ip_to_sockaddr(struct sockaddr* out, char* src); +int parse_acl(struct ip_and_mask *out[], int max, char **entries); + +#endif + diff --git a/serve.c b/serve.c index 80d0d99..937d4bf 100644 --- a/serve.c +++ b/serve.c @@ -3,6 +3,7 @@ #include "ioutil.h" #include "util.h" #include "bitset.h" +#include "parse.h" #include #include @@ -264,6 +265,89 @@ void* client_serve(void* client_uncast) return NULL; } +void control_acl(struct control_params* client) +{ + int acl_entries = 0, parsed; + char** s_acl_entry = NULL; + struct ip_and_mask** acl, **old_acl; + + while (1) { + char entry[64]; + int result = read_until_newline(client->socket, entry, 64); + if (result == -1 || result == 64) + goto done; + if (result == 1) /* blank line terminates */ + break; + s_acl_entry = xrealloc( + s_acl_entry, + ++acl_entries * sizeof(struct s_acl_entry*) + ); + s_acl_entry[acl_entries-1] = strdup(entry); + debug("acl_entry = '%s'", s_acl_entry[acl_entries-1]); + } + + parsed = parse_acl(&acl, acl_entries, s_acl_entry); + if (parsed != acl_entries) { + write(client->socket, "error: ", 7); + write(client->socket, s_acl_entry[parsed], + strlen(s_acl_entry[parsed])); + write(client->socket, "\n", 1); + free(acl); + } + else { + old_acl = client->serve->acl; + client->serve->acl = acl; + client->serve->acl_entries = acl_entries; + free(old_acl); + write(client->socket, "ok\n", 3); + } + +done: if (acl_entries > 0) { + int i; + for (i=0; isocket, command, max), + "Error reading command" + ); + + if (strcmp(command, "acl") == 0) + control_acl(client); + else if (strcmp(command, "mirror") == 0) + control_mirror(client); + else if (strcmp(command, "status") == 0) + control_status(client); + else { + write(client->socket, "error: unknown command\n", 23); + break; + } + } + + close(client->socket); + free(client); + return NULL; +} + static int testmasks[9] = { 0,128,192,224,240,248,252,254,255 }; int is_included_in_acl(int list_length, struct ip_and_mask** list, struct sockaddr* test) @@ -271,26 +355,30 @@ int is_included_in_acl(int list_length, struct ip_and_mask** list, struct sockad int i; for (i=0; i < list_length; i++) { - struct ip_and_mask *entry = list[i]; + struct ip_and_mask *entry = list+i; int testbits; char *raw_address1, *raw_address2; - debug("checking acl entry %d", i); + debug("checking acl entry %d (%d/%d)", i, test->sa_family, entry->ip.family); if (test->sa_family != entry->ip.family) continue; if (test->sa_family == AF_INET) { + debug("it's an AF_INET"); raw_address1 = (char*) &((struct sockaddr_in*) test)->sin_addr; raw_address2 = (char*) &entry->ip.v4.sin_addr; } else if (test->sa_family == AF_INET6) { + debug("it's an AF_INET6"); raw_address1 = (char*) &((struct sockaddr_in6*) test)->sin6_addr; raw_address2 = (char*) &entry->ip.v6.sin6_addr; } + debug("testbits=%d", entry->mask); + for (testbits = entry->mask; testbits > 0; testbits -= 8) { debug("testbits=%d, c1=%d, c2=%d", testbits, raw_address1[0], raw_address2[0]); if (testbits >= 8) { @@ -366,7 +454,7 @@ void serve_open_control_socket(struct mode_serve_params* params) void accept_nbd_client(struct mode_serve_params* params, int client_fd, struct sockaddr* client_address) { - pthread_t client_thread; + pthread_t client_thread; struct client_params* client_params; if (params->acl && @@ -382,18 +470,37 @@ void accept_nbd_client(struct mode_serve_params* params, int client_fd, struct s client_params->block_allocation_map = params->block_allocation_map; - client_thread = pthread_create(&client_thread, NULL, - client_serve, client_params); - SERVER_ERROR_ON_FAILURE(client_thread, - "Failed to create client thread"); + SERVER_ERROR_ON_FAILURE( + pthread_create( + &client_thread, + NULL, + client_serve, + client_params + ), + "Failed to create client thread" + ); /* FIXME: keep track of them? */ /* FIXME: maybe shouldn't be fatal? */ } void accept_control_connection(struct mode_serve_params* params, int client_fd, struct sockaddr* client_address) { - write(client_fd, "hello", 5); - close(client_fd); + pthread_t control_thread; + struct control_params* control_params; + + control_params = xmalloc(sizeof(struct control_params)); + control_params->socket = client_fd; + control_params->serve = params; + + SERVER_ERROR_ON_FAILURE( + pthread_create( + &control_thread, + NULL, + control_serve, + control_params + ), + "Failed to create client thread" + ); } void serve_accept_loop(struct mode_serve_params* params) @@ -402,7 +509,7 @@ void serve_accept_loop(struct mode_serve_params* params) int activity_fd, client_fd; struct sockaddr client_address; fd_set fds; - socklen_t socklen=0; + socklen_t socklen=sizeof(client_address); FD_ZERO(&fds); FD_SET(params->server, &fds);