Silly bug fixes, added ACL support, added parser for read/write requests.
This commit is contained in:
254
flexnbd.c
254
flexnbd.c
@@ -82,17 +82,17 @@ void error(int consult_errno, int close_socket, const char* format, ...)
|
|||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
|
|
||||||
if (pthread_equal(pthread_self(), server_thread_id))
|
if (pthread_equal(pthread_self(), server_thread_id))
|
||||||
pthread_exit((void*) 1);
|
|
||||||
else
|
|
||||||
exit(1);
|
exit(1);
|
||||||
|
else
|
||||||
|
pthread_exit((void*) 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef DEBUG
|
#ifndef DEBUG
|
||||||
# define debug(msg, ...)
|
# define debug(msg, ...)
|
||||||
#else
|
#else
|
||||||
# include <sys/times.h>
|
# include <sys/times.h>
|
||||||
# define debug(msg, ...) fprintf(stderr, "% 4d % 4d: " msg "\n" , \
|
# define debug(msg, ...) fprintf(stderr, "%08x %4d: " msg "\n" , \
|
||||||
(int) pthread_self(), (int) times(NULL), ##__VA_ARGS__)
|
(int) pthread_self(), (int) clock(), ##__VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define CLIENT_ERROR(msg, ...) \
|
#define CLIENT_ERROR(msg, ...) \
|
||||||
@@ -112,22 +112,37 @@ void* xmalloc(size_t size)
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
union mysockaddr {
|
||||||
|
unsigned short family;
|
||||||
|
struct sockaddr generic;
|
||||||
|
struct sockaddr_in v4;
|
||||||
|
struct sockaddr_in6 v6;
|
||||||
|
};
|
||||||
|
|
||||||
struct ip_and_mask {
|
struct ip_and_mask {
|
||||||
/* FIXME */
|
union mysockaddr ip;
|
||||||
|
int mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mode_serve_params {
|
struct mode_serve_params {
|
||||||
union { struct sockaddr generic;
|
union mysockaddr bind_to;
|
||||||
struct sockaddr_in v4;
|
int acl_entries;
|
||||||
struct sockaddr_in6 v6; } bind_to;
|
struct ip_and_mask** acl;
|
||||||
struct ip_and_mask** acl;
|
char* filename;
|
||||||
char* filename;
|
int tcp_backlog;
|
||||||
int tcp_backlog;
|
|
||||||
|
|
||||||
int server;
|
int server;
|
||||||
int threads;
|
int threads;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct mode_readwrite_params {
|
||||||
|
int writeNotRead;
|
||||||
|
union mysockaddr connect_to;
|
||||||
|
off64_t from;
|
||||||
|
off64_t len;
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
struct client_params {
|
struct client_params {
|
||||||
int socket;
|
int socket;
|
||||||
char* filename;
|
char* filename;
|
||||||
@@ -139,6 +154,7 @@ struct client_params {
|
|||||||
|
|
||||||
union mode_params {
|
union mode_params {
|
||||||
struct mode_serve_params serve;
|
struct mode_serve_params serve;
|
||||||
|
struct mode_readwrite_params readwrite;
|
||||||
};
|
};
|
||||||
|
|
||||||
int writeloop(int filedes, const void *buffer, size_t size)
|
int writeloop(int filedes, const void *buffer, size_t size)
|
||||||
@@ -316,9 +332,56 @@ void* client_serve(void* client_uncast)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME */
|
/* FIXME */
|
||||||
int is_included_in_acl(struct ip_and_mask** list, struct sockaddr* test)
|
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)
|
||||||
{
|
{
|
||||||
return 1;
|
int i;
|
||||||
|
|
||||||
|
for (i=0; i < list_length; i++) {
|
||||||
|
struct ip_and_mask *entry = list[i];
|
||||||
|
int testbits;
|
||||||
|
char *raw_address1, *raw_address2;
|
||||||
|
|
||||||
|
debug("checking acl entry %d", i);
|
||||||
|
|
||||||
|
if (test->sa_family != entry->ip.family)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (test->sa_family == 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) {
|
||||||
|
raw_address1 = (char*)
|
||||||
|
&((struct sockaddr_in6*) test)->sin6_addr;
|
||||||
|
raw_address2 = (char*) &entry->ip.v6.sin6_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (raw_address1[0] != raw_address2[0])
|
||||||
|
goto no_match;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((raw_address1[0] & testmasks[testbits%8]) !=
|
||||||
|
(raw_address2[0] & testmasks[testbits%8]) )
|
||||||
|
goto no_match;
|
||||||
|
}
|
||||||
|
|
||||||
|
raw_address1++;
|
||||||
|
raw_address2++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
no_match: ;
|
||||||
|
debug("no match");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serve_open_socket(struct mode_serve_params* params)
|
void serve_open_socket(struct mode_serve_params* params)
|
||||||
@@ -354,7 +417,7 @@ void serve_accept_loop(struct mode_serve_params* params)
|
|||||||
SERVER_ERROR_ON_FAILURE(client_socket, "accept() failed");
|
SERVER_ERROR_ON_FAILURE(client_socket, "accept() failed");
|
||||||
|
|
||||||
if (params->acl &&
|
if (params->acl &&
|
||||||
!is_included_in_acl(params->acl, &client_address)) {
|
!is_included_in_acl(params->acl_entries, params->acl, &client_address)) {
|
||||||
write(client_socket, "Access control error", 20);
|
write(client_socket, "Access control error", 20);
|
||||||
close(client_socket);
|
close(client_socket);
|
||||||
continue;
|
continue;
|
||||||
@@ -373,21 +436,110 @@ void serve_accept_loop(struct mode_serve_params* params)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void serve(struct mode_serve_params* params)
|
void do_serve(struct mode_serve_params* params)
|
||||||
{
|
{
|
||||||
serve_open_socket(params);
|
serve_open_socket(params);
|
||||||
serve_accept_loop(params);
|
serve_accept_loop(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void do_read(struct mode_readwrite_params* params)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_write(struct mode_readwrite_params* params)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("temp='%s'", temp);
|
||||||
|
|
||||||
|
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(
|
void params_serve(
|
||||||
struct mode_serve_params* out,
|
struct mode_serve_params* out,
|
||||||
char* s_ip_address,
|
char* s_ip_address,
|
||||||
char* s_port,
|
char* s_port,
|
||||||
char* s_file
|
char* s_file,
|
||||||
|
int acl_entries,
|
||||||
|
char** s_acl_entries
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
int parsed;
|
||||||
|
|
||||||
out->tcp_backlog = 10; /* does this need to be settable? */
|
out->tcp_backlog = 10; /* does this need to be settable? */
|
||||||
out->acl = NULL; /* ignore for now */
|
|
||||||
|
|
||||||
if (s_ip_address == NULL)
|
if (s_ip_address == NULL)
|
||||||
SERVER_ERROR("No IP address supplied");
|
SERVER_ERROR("No IP address supplied");
|
||||||
@@ -396,18 +548,16 @@ void params_serve(
|
|||||||
if (s_file == NULL)
|
if (s_file == NULL)
|
||||||
SERVER_ERROR("No filename supplied");
|
SERVER_ERROR("No filename supplied");
|
||||||
|
|
||||||
if (s_ip_address[0] == '0' && s_ip_address[1] == '\0') {
|
if (parse_ip_to_sockaddr(&out->bind_to.generic, s_ip_address) == 0)
|
||||||
out->bind_to.v4.sin_family = AF_INET;
|
SERVER_ERROR("Couldn't parse server address '%s' (use 0 if "
|
||||||
out->bind_to.v4.sin_addr.s_addr = INADDR_ANY;
|
"you want to bind to all IPs)", s_ip_address);
|
||||||
}
|
|
||||||
else if (inet_pton(AF_INET, s_ip_address, &out->bind_to.v4) == 0) {
|
out->acl_entries = acl_entries;
|
||||||
}
|
parsed = parse_acl(&out->acl, acl_entries, s_acl_entries);
|
||||||
else if (inet_pton(AF_INET6, s_ip_address, &out->bind_to.v6) == 0) {
|
if (parsed != acl_entries)
|
||||||
}
|
SERVER_ERROR("Bad ACL entry '%s'", s_acl_entries[parsed]);
|
||||||
else {
|
|
||||||
SERVER_ERROR("Couldn't understand address '%%' "
|
debug("%p %d", out->acl, out->acl_entries);
|
||||||
"(use 0 if you don't care)", s_ip_address);
|
|
||||||
}
|
|
||||||
|
|
||||||
out->bind_to.v4.sin_port = atoi(s_port);
|
out->bind_to.v4.sin_port = atoi(s_port);
|
||||||
if (out->bind_to.v4.sin_port < 0 || out->bind_to.v4.sin_port > 65535)
|
if (out->bind_to.v4.sin_port < 0 || out->bind_to.v4.sin_port > 65535)
|
||||||
@@ -417,14 +567,56 @@ void params_serve(
|
|||||||
out->filename = s_file;
|
out->filename = s_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void params_readwrite(
|
||||||
|
struct mode_readwrite_params* out,
|
||||||
|
char* s_ip_address,
|
||||||
|
char* s_port,
|
||||||
|
char* s_from,
|
||||||
|
char* s_length
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (s_ip_address == NULL)
|
||||||
|
SERVER_ERROR("No IP address supplied");
|
||||||
|
if (s_port == NULL)
|
||||||
|
SERVER_ERROR("No port number supplied");
|
||||||
|
if (s_from == NULL);
|
||||||
|
SERVER_ERROR("No from supplied");
|
||||||
|
if (s_length == NULL);
|
||||||
|
SERVER_ERROR("No length supplied");
|
||||||
|
|
||||||
|
if (parse_ip_to_sockaddr(&out->connect_to.generic, s_ip_address) == 0)
|
||||||
|
SERVER_ERROR("Couldn't parse connection address '%s'");
|
||||||
|
|
||||||
|
out->from = atol(s_from);
|
||||||
|
out->len = atol(s_length);
|
||||||
|
}
|
||||||
|
|
||||||
void mode(char* mode, int argc, char **argv)
|
void mode(char* mode, int argc, char **argv)
|
||||||
{
|
{
|
||||||
union mode_params params;
|
union mode_params params;
|
||||||
|
|
||||||
if (strcmp(mode, "serve") == 0) {
|
if (strcmp(mode, "serve") == 0) {
|
||||||
if (argc >= 3) {
|
if (argc >= 3) {
|
||||||
params_serve(¶ms.serve, argv[0], argv[1], argv[2]);
|
params_serve(¶ms.serve, argv[0], argv[1], argv[2], argc-3, argv+3);
|
||||||
serve(¶ms.serve);
|
do_serve(¶ms.serve);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
syntax();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(mode, "read") == 0 ) {
|
||||||
|
if (argc != 4) {
|
||||||
|
params_readwrite(¶ms.readwrite, argv[0], argv[1], argv[2], argv[3]);
|
||||||
|
do_read(¶ms.readwrite);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
syntax();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(mode, "write") == 0 ) {
|
||||||
|
if (argc != 4) {
|
||||||
|
params_readwrite(¶ms.readwrite, argv[0], argv[1], argv[2], argv[3]);
|
||||||
|
do_write(¶ms.readwrite);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
syntax();
|
syntax();
|
||||||
|
Reference in New Issue
Block a user