diff --git a/Makefile b/Makefile index f1abd3f..3dde5c5 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ build: make clean - gcc src/hostname_resolver.c src/argparse.c src/socket.c src/udp_load_balancer.c -o build/udp_load_balancer + gcc src/hostname_resolver.c src/argparse.c src/socket.c src/udp_load_balancer.c -lpthread -o build/udp_load_balancer install: make build diff --git a/README.md b/README.md index 8720e4a..19e14ad 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,14 @@ Installation ------------ Compile it with ``make build`` or ``make install`` if you want to place the generated binary in ``/usr/local/bin/`` +Features +-------- +Support dynamic resolving of target servers in every 5 seconds. If subsequent resolv fails - use current servers until good resolv. + Usage ----- - udp_load_balancer [-h] [--port PORT] [--servers SERVERS] + udp_load_balancer [-hvd] [--port PORT] [--servers SERVERS] The options are as follows: -h, --help show this help message and exit @@ -23,3 +27,6 @@ Usage Servers list to balance the UDP messages Example: "127.0.0.1:8123, 127.0.0.1:8124" Example: "127.0.0.1:8123, localhost:8124, example.com:8123" + -v, --verbose Be verbose + -d, --debug Debug output enabled + diff --git a/TODO b/TODO new file mode 100644 index 0000000..71e8ac6 --- /dev/null +++ b/TODO @@ -0,0 +1 @@ +* run_resolver(): change servers_list_resolved only when IP adresses have changed for a server diff --git a/src/argparse.c b/src/argparse.c index 1203f8b..5726398 100644 --- a/src/argparse.c +++ b/src/argparse.c @@ -1,15 +1,24 @@ +#include #include #include +#include #include "udp_load_balancer.h" -unsigned int is_help_required(int argc, char **argv) { +unsigned int is_help_required_or_flags_set(int argc, char **argv) { unsigned int i; for (i = 0; i < argc; ++i) { if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { return 1; + } else if ( strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) { + verb_flag = 1; + } else if ( strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) { + version_flag = 1; + } else if ( strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--debug") == 0) { + verb_flag = 1; + debug_flag = 1; } } @@ -100,14 +109,23 @@ char **get_servers_hosts(char **servers_list, int servers_amount) { return servers_hosts; } +void free_servers_hosts(char **servers_hosts, int servers_amount) { + unsigned int i; + for(i = 0; i < servers_amount; i++) { + free(servers_hosts[i]); + } + free(servers_hosts); +} + int *get_servers_ports(char **servers_list, int servers_amount) { - char *server_raw, *port_string; + char *server_raw, *free_pointer, *port_string; int server_port; int *servers_ports = malloc(MAX_SERVERS * sizeof(int*)); unsigned int i, j; for(i = 0; i < servers_amount; i++) { - server_raw = strdup(servers_list[i]); + free_pointer = strdup(servers_list[i]); + server_raw = free_pointer; strsep(&server_raw, ":"); port_string = strsep(&server_raw, ":"); @@ -117,7 +135,12 @@ int *get_servers_ports(char **servers_list, int servers_amount) { } servers_ports[i] = server_port; + free(free_pointer); } return servers_ports; } + +void free_servers_ports(int *servers_ports, int servers_amount) { + free(servers_ports); +} diff --git a/src/argparse.h b/src/argparse.h index e36cab9..12c415f 100644 --- a/src/argparse.h +++ b/src/argparse.h @@ -1,4 +1,4 @@ -unsigned int is_help_required(int argc, char **argv); +unsigned int is_help_required_or_flags_set(int argc, char **argv); unsigned int get_udp_load_balancer_port(int argc, char **argv); @@ -8,4 +8,8 @@ char **get_servers_list(int argc, char **argv); char **get_servers_hosts(char **servers_list, int servers_amount); -int *get_servers_ports(char **servers_list, int servers_amount); \ No newline at end of file +void free_servers_hosts(char **servers_hosts, int servers_amount); + +int *get_servers_ports(char **servers_list, int servers_amount); + +void free_servers_ports(int *servers_ports, int servers_amount); diff --git a/src/hostname_resolver.c b/src/hostname_resolver.c index d915f05..ced5ce2 100644 --- a/src/hostname_resolver.c +++ b/src/hostname_resolver.c @@ -14,7 +14,7 @@ int is_ip(char *address) return result != 0; } -char *get_ip_from_hostname(char *hostname) +char *get_one_ip_from_hostname(char *hostname) { struct hostent *he = gethostbyname(hostname); @@ -27,4 +27,5 @@ char *get_ip_from_hostname(char *hostname) /* return the first one, because one host can have multiple addresses */ return inet_ntoa(*addr_list[0]); -} \ No newline at end of file + +} diff --git a/src/hostname_resolver.h b/src/hostname_resolver.h index b3622bd..63824a6 100644 --- a/src/hostname_resolver.h +++ b/src/hostname_resolver.h @@ -1,3 +1,3 @@ int is_ip(char *address); -char *get_ip_from_hostname(char *hostname); \ No newline at end of file +char *get_one_ip_from_hostname(char *hostname); diff --git a/src/udp_load_balancer.c b/src/udp_load_balancer.c index 148aadc..1fb6894 100644 --- a/src/udp_load_balancer.c +++ b/src/udp_load_balancer.c @@ -9,6 +9,11 @@ #include "socket.h" #include "hostname_resolver.h" +#include +#include +#include + +unsigned int servers_amount_resolved = 0; unsigned int get_servers_amount(char **servers_list) { unsigned int i = 0; @@ -21,36 +26,129 @@ unsigned int get_servers_amount(char **servers_list) { } } -void run_dispatcher(char **servers_list, int socket_) +void* run_resolver( void* call_arg ) { - char message[MAX_PAYLOAD_LENGTH]; - unsigned int message_length; - + char **servers_list = call_arg; + char **servers_list_resolved_tmp; + char **servers_list_resolved_free; + int *servers_ports_resolved_tmp; + int *servers_ports_resolved_free; + servers_list_resolved = NULL; + servers_ports_resolved = NULL; + int first_run = 1; + unsigned int servers_amount_resolved_prev; unsigned int servers_amount = get_servers_amount(servers_list); - char **servers_hosts = get_servers_hosts(servers_list, servers_amount); int *servers_ports = get_servers_ports(servers_list, servers_amount); - unsigned int i; - - for (i = 0; i < servers_amount; i++) { - if (is_ip(servers_hosts[i]) == 0) { - servers_hosts[i] = get_ip_from_hostname(servers_hosts[i]); + while(1) { + servers_amount_resolved_prev = servers_amount_resolved; + servers_list_resolved_tmp = calloc(sizeof (char*), servers_amount); + servers_ports_resolved_tmp = calloc(sizeof (int), servers_amount); + if ( verb_flag ) printf("\n\nBalancing to servers:\n"); + unsigned int i; + unsigned int j = 0; + for (i = 0; i < servers_amount; ++i) { + if ( verb_flag ) printf(" %s\n", servers_list[i]); + if (is_ip(servers_hosts[i]) == 0) { + + struct hostent *he = gethostbyname(servers_hosts[i]); + + if (he == NULL) { + printf("Host \"%s\" not found\n", servers_hosts[i]); + continue; + } + + struct in_addr **addr_list = (struct in_addr **)he->h_addr_list; + + while ( *addr_list ) { + if ( j >= servers_amount ) { + servers_list_resolved_tmp = realloc(servers_list_resolved_tmp, sizeof (char*) * (j+1)); + servers_ports_resolved_tmp = realloc(servers_ports_resolved_tmp, sizeof (int) * (j+1)); + } + servers_list_resolved_tmp[j] = strdup(inet_ntoa(**addr_list)); + servers_ports_resolved_tmp[j] = servers_ports[i]; + if ( verb_flag ) printf(" %s %d\n",servers_list_resolved_tmp[j], servers_ports_resolved_tmp[j]); + j++; + addr_list += 1; + } + } else { + if ( j >= servers_amount ) { + servers_list_resolved_tmp = realloc(servers_list_resolved_tmp, sizeof (char*) * (j+1)); + servers_ports_resolved_tmp = realloc(servers_ports_resolved_tmp, sizeof (int) * (j+1)); + } + servers_list_resolved_tmp[j] = strdup(servers_hosts[i]); + servers_ports_resolved_tmp[j] = servers_ports[i]; + j++; + } + servers_amount_resolved = j; } + if ( j < 1 ) { + printf( "Can't resolve any of servers, waiting for 5sec\n" ); + sleep(5); + continue ; + } + if ( first_run == 0 ) { + pthread_mutex_lock(&resolv_mutex); + } + first_run = 0; + servers_list_resolved_free = servers_list_resolved; + servers_list_resolved = servers_list_resolved_tmp; + servers_ports_resolved_free = servers_ports_resolved; + servers_ports_resolved = servers_ports_resolved_tmp; + pthread_mutex_unlock(&resolv_mutex); + if ( debug_flag ) { + printf("servers_list_resolved array:\n"); + for ( j = 0 ; j < servers_amount_resolved ; j++ ) { + printf(" (%u %s:%d\n)", j, servers_list_resolved_tmp[j], servers_ports_resolved_tmp[j]); + } + } + free_servers_hosts(servers_list_resolved_free,servers_amount_resolved_prev); + free(servers_ports_resolved_free); + + usleep(5000000); // resolve every 5sec } + free_servers_hosts(servers_hosts,servers_amount); + free_servers_ports(servers_ports,servers_amount); +} - while(1) { - for(i = 0; i < servers_amount; i++) { - listen_udp_packet(message, &message_length, socket_); +void* run_dispatcher( void* call_arg ) +{ + dispatcher_arg_struct_t *arg = call_arg; + char message[MAX_PAYLOAD_LENGTH]; + unsigned int message_length; + char srv[16]; + int port; - send_udp_packet(servers_hosts[i], servers_ports[i], message, message_length, socket_); + unsigned int i; + + i = 0; + while(1) { + if ( debug_flag ) printf("while waiting for a resolv_mutex\n"); + listen_udp_packet(message, &message_length, arg->socket_); + if ( debug_flag ) printf("message:%s ", message ); + pthread_mutex_lock(&resolv_mutex); + /* If number of servers' IPs has been decreased by resolver thread - begin from first IP */ + if ( i >= servers_amount_resolved ) { + i = 0; } + /* use srv & port temp variables for call send_udp_packet() outside of pthread_mutex_unlock() */ + strcpy(srv,servers_list_resolved[i]); + port = servers_ports_resolved[i]; + pthread_mutex_unlock(&resolv_mutex); + if ( debug_flag ) printf("%s\n", srv ); + send_udp_packet(srv, port, message, message_length, arg->socket_); + i++; } } +void show_version() { + printf("udp_load_balancer %s\n",UDP_LOAD_BALANCER_VERSION); +} + void show_help() { printf("\n" - "Usage: udp_load_balancer [-h] [--port PORT] [--servers SERVERS]\n" + "Usage: udp_load_balancer [-hvd] [--port PORT] [--servers SERVERS]\n" "\n" "The options are as follows:\n" " -h, --help show this help message and exit\n" @@ -61,16 +159,27 @@ void show_help() { " -s, --servers SERVERS\n" " Servers list to balance the UDP messages\n" " Example: \"127.0.0.1:8123, localhost:8124, example.com:8123\"\n" + " -v, --verbose Be verbose\n" + " -V, --version Show version\n" + " -d, --debug Debug output enabled\n" ); } int main(int argc, char **argv) { - if (is_help_required(argc, argv)) { + verb_flag = 0; + version_flag = 0; + debug_flag = 0; + if (is_help_required_or_flags_set(argc, argv)) { show_help(); exit(0); } + if ( version_flag ) { + show_version(); + exit(0); + } + unsigned int udp_load_balancer_port = get_udp_load_balancer_port(argc, argv); char **servers_list = get_servers_list(argc, argv); @@ -79,30 +188,26 @@ int main(int argc, char **argv) exit(2); } - unsigned int servers_amount = get_servers_amount(servers_list); char *udp_load_balancer_ip = get_udp_load_balancer_host(argc, argv); - printf("Listening on %s:%d", udp_load_balancer_ip, udp_load_balancer_port); + if ( verb_flag )printf("Listening on %s:%d\n", udp_load_balancer_ip, udp_load_balancer_port); if(is_ip(udp_load_balancer_ip) == 0) { - udp_load_balancer_ip = get_ip_from_hostname(udp_load_balancer_ip); - printf(" (%s:%d)", udp_load_balancer_ip, udp_load_balancer_port); + udp_load_balancer_ip = get_one_ip_from_hostname(udp_load_balancer_ip); + if ( verb_flag ) printf(" (%s:%d)\n", udp_load_balancer_ip, udp_load_balancer_port); } - printf("\n\nBalancing to servers:\n"); - char **servers_hosts = get_servers_hosts(servers_list, servers_amount); - int *servers_ports = get_servers_ports(servers_list, servers_amount); - unsigned int i; - for (i = 0; i < servers_amount; ++i) { - printf(" %s", servers_list[i]); - if(is_ip(servers_hosts[i]) == 0) { - printf(" (%s:%d)", get_ip_from_hostname(servers_hosts[i]), servers_ports[i]); - } - printf("\n"); - } + pthread_mutex_init (&resolv_mutex , NULL); + pthread_mutex_lock(&resolv_mutex); // initially lock resolv_mutex - unlock when resolv will be done int socket_ = create_socket(udp_load_balancer_ip, udp_load_balancer_port); - run_dispatcher(servers_list, socket_); + pthread_t dispatcher_thread_id; + pthread_t resolver_thread_id; + dispatcher_arg_struct_t dispatcher_arg_struct = { servers_list , socket_ }; + pthread_create(&resolver_thread_id, NULL, run_resolver, (void *) servers_list); + pthread_create(&dispatcher_thread_id, NULL, run_dispatcher, (void *) &dispatcher_arg_struct); + pthread_join(dispatcher_thread_id, NULL); + if ( verb_flag ) printf("main exit\n"); return 0; } diff --git a/src/udp_load_balancer.h b/src/udp_load_balancer.h index dede040..72266a5 100644 --- a/src/udp_load_balancer.h +++ b/src/udp_load_balancer.h @@ -1,2 +1,25 @@ +#ifndef _DPLB_UDP_LOAD_BALANCER_ +#define _DPLB_UDP_LOAD_BALANCER_ + +#define udp_load_balancer_version 1000000 +#define UDP_LOAD_BALANCER_VERSION "1.0.0" + #define MAX_PAYLOAD_LENGTH 65507 // 65535 bytes − 8 byte UDP header − 20 byte IP header -#define MAX_SERVERS 1024 \ No newline at end of file +#define MAX_SERVERS 1024 + +pthread_mutex_t resolv_mutex; + +int verb_flag; +int version_flag; +int debug_flag; + +char **servers_list_resolved; +int *servers_ports_resolved; + + +typedef struct { + char **servers_list; + int socket_; +} dispatcher_arg_struct_t; + +#endif /* _DPLB_UDP_LOAD_BALANCER_ */