From 93a4491ac04c5930534e9ce8c3a37bbff663a935 Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Sat, 26 Oct 2013 18:29:20 +0200 Subject: add handling for offline hosts and bad requests --- pacredir.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 117 insertions(+), 36 deletions(-) diff --git a/pacredir.c b/pacredir.c index 3805623..e4ce8ce 100644 --- a/pacredir.c +++ b/pacredir.c @@ -7,14 +7,11 @@ * This is an example code skeleton provided by vim-skeleton. */ -#ifdef HAVE_CONFIG_H -#include -#endif - #include #include #include #include +#include #include #include @@ -33,11 +30,19 @@ "404 Not Found: %s" #define PORT 7077 #define SERVICE "_pacserve._tcp" +#define BADTIME 60 * 10 -/* is there any other way than storing things in global struct? */ +/* hosts */ struct hosts { + /* host name */ char * host; + /* http port */ uint16_t port; + /* unix timestamp of last bad request */ + __time_t bad; + /* true if host is offline */ + uint8_t offline; + /* pointer to next struct element */ struct hosts * next; }; @@ -45,7 +50,9 @@ struct hosts { struct hosts * hosts = NULL; static AvahiSimplePoll *simple_poll = NULL; -static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface, AVAHI_GCC_UNUSED AvahiProtocol protocol, +/*** resolve_callback_new *** + * Called whenever a service has been resolved successfully or timed out */ +static void resolve_callback_new(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface, AVAHI_GCC_UNUSED AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host, const AvahiAddress *address, AVAHI_GCC_UNUSED uint16_t port, AVAHI_GCC_UNUSED AvahiStringList *txt, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, AVAHI_GCC_UNUSED void* userdata) { @@ -53,8 +60,6 @@ static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIn assert(r); - /* Called whenever a service has been resolved successfully or timed out */ - switch (event) { case AVAHI_RESOLVER_FAILURE: fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", @@ -62,16 +67,12 @@ static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIn break; case AVAHI_RESOLVER_FOUND: { - // char a[AVAHI_ADDRESS_STR_MAX]; - - // avahi_address_snprint(a, sizeof(a), address); - // fprintf(stderr, "%s %s\n", host, a); - while (tmphosts->host != NULL) { if (strcmp(tmphosts->host, host) == 0) { # if defined DEBUG printf("Host is already in the list: %s\n", host); # endif + tmphosts->offline = 0; goto out; } tmphosts = tmphosts->next; @@ -79,10 +80,47 @@ static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIn printf("Adding host: %s, port %d\n", host, port); tmphosts->host = strdup(host); tmphosts->port = port; + tmphosts->bad = 0; + tmphosts->offline = 0; tmphosts->next = realloc(tmphosts->next, sizeof(struct hosts)); tmphosts = tmphosts->next; tmphosts->host = NULL; tmphosts->next = NULL; + + break; + } + } + +out: + avahi_service_resolver_free(r); +} + +/*** resolve_callback_remove *** + * Called whenever a service has been resolved successfully or timed out */ +static void resolve_callback_remove(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface, AVAHI_GCC_UNUSED AvahiProtocol protocol, + AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host, const AvahiAddress *address, + AVAHI_GCC_UNUSED uint16_t port, AVAHI_GCC_UNUSED AvahiStringList *txt, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void* userdata) { + struct hosts * tmphosts = hosts; + + assert(r); + + switch (event) { + case AVAHI_RESOLVER_FAILURE: + fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", + name, type, domain, avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))); + break; + + case AVAHI_RESOLVER_FOUND: { + while (tmphosts->host != NULL) { + if (strcmp(tmphosts->host, host) == 0) { + printf("Marking host offline: %s\n", host); + tmphosts->offline = 1; + goto out; + } + tmphosts = tmphosts->next; + } + break; } } @@ -90,14 +128,14 @@ out: avahi_service_resolver_free(r); } +/*** browse_callback *** + * Called whenever a new services becomes available on the LAN or is removed from the LAN */ static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, void* userdata) { AvahiClient *c = userdata; assert(b); - /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ - switch (event) { case AVAHI_BROWSER_FAILURE: @@ -106,46 +144,48 @@ static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, Avah return; case AVAHI_BROWSER_NEW: - // fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); +# if defined DEBUG + fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); +# endif /* We ignore the returned resolver object. In the callback * function we free it. If the server is terminated before * the callback function is called the server will free * the resolver for us. */ - if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c))) + if (avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback_new, c) == NULL) fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c))); break; case AVAHI_BROWSER_REMOVE: - // fprintf(stderr, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain); +# if defined DEBUG + fprintf(stderr, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain); +# endif - /* TODO: remove host */ - // if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c))) - // fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c))); + if (avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback_remove, c) == NULL) + fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c))); break; case AVAHI_BROWSER_ALL_FOR_NOW: case AVAHI_BROWSER_CACHE_EXHAUSTED: - // fprintf(stderr, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); break; } } +/*** client_callback ***/ static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) { assert(c); - /* Called whenever the client or server state changes */ - if (state == AVAHI_CLIENT_FAILURE) { fprintf(stderr, "Server connection failure: %s\n", avahi_strerror(avahi_client_errno(c))); avahi_simple_poll_quit(simple_poll); } } -int get_content(const char * host, const uint16_t port, const char * url) { +/*** get_http_code ***/ +int get_http_code(const char * host, const uint16_t port, const char * url) { CURL *curl; CURLcode res; unsigned int http_code; @@ -164,7 +204,6 @@ int get_content(const char * host, const uint16_t port, const char * url) { /* get it! */ if (curl_easy_perform(curl) != CURLE_OK) { fprintf(stderr, "Could not connect to server %s on port %d.\n", host, port); - /* TODO remove or disable */ return -1; } @@ -173,8 +212,6 @@ int get_content(const char * host, const uint16_t port, const char * url) { return -1; } - // printf("%s: %d\n", url, http_code); - /* always cleanup */ curl_easy_cleanup(curl); } @@ -185,6 +222,8 @@ int get_content(const char * host, const uint16_t port, const char * url) { return http_code; } +/*** ahc_echo *** + * called whenever a http request is received */ static int ahc_echo(void * cls, struct MHD_Connection * connection, const char * uri, const char * method, const char * version, const char * upload_data, size_t * upload_data_size, void ** ptr) { static int dummy; @@ -195,6 +234,7 @@ static int ahc_echo(void * cls, struct MHD_Connection * connection, const char * char * url = NULL, * page; const char * basename; int http_code = 0; + struct timeval tv; /* we want to filename, not the path */ basename = uri; @@ -215,13 +255,22 @@ static int ahc_echo(void * cls, struct MHD_Connection * connection, const char * /* try to find a server */ while (tmphosts->host != NULL) { + gettimeofday(&tv, NULL); + + /* skip host if offline or had a bad request within last BADTIME seconds */ + if (tmphosts->offline == 1 || tmphosts->bad + BADTIME > tv.tv_sec) { + tmphosts = tmphosts->next; + continue; + } + url = malloc(10 + strlen(tmphosts->host) + 5 + strlen(basename)); sprintf(url, "http://%s:%d/%s", tmphosts->host, tmphosts->port, basename); printf("Trying %s\n", url); - if ((http_code = get_content(tmphosts->host, tmphosts->port, url)) == MHD_HTTP_OK) { + if ((http_code = get_http_code(tmphosts->host, tmphosts->port, url)) == MHD_HTTP_OK) break; - } + else if (http_code == -1) + tmphosts->bad = tv.tv_sec; tmphosts = tmphosts->next; } @@ -235,7 +284,7 @@ static int ahc_echo(void * cls, struct MHD_Connection * connection, const char * ret = MHD_add_response_header(response, "Location", url); ret = MHD_queue_response(connection, MHD_HTTP_TEMPORARY_REDIRECT, response); } else { - printf("File %s Not found, giving up.\n", basename); + printf("File %s not found, giving up.\n", basename); page = malloc(strlen(PAGE404) + strlen(basename) + 1); sprintf(page, PAGE404, basename); response = MHD_create_response_from_data(strlen(page), (void*) page, MHD_NO, MHD_NO); @@ -250,12 +299,31 @@ static int ahc_echo(void * cls, struct MHD_Connection * connection, const char * return ret; } +/*** sigterm_callback ***/ +void sigterm_callback(int signal) { + avahi_simple_poll_quit(simple_poll); +} + +/*** sighup_callback ***/ +void sighup_callback(int signal) { + struct hosts * tmphosts = hosts; + + printf("Received SIGHUP, marking all hosts offline.\n"); + + while (tmphosts->host != NULL) { + tmphosts->offline = 1; + tmphosts = tmphosts->next; + } +} + +/*** main ***/ int main(int argc, char ** argv) { AvahiClient *client = NULL; AvahiServiceBrowser *sb = NULL; int error; int ret = 1; struct MHD_Daemon * mhd; + struct hosts * tmphosts; printf("Starting pacredir/" VERSION "\n"); @@ -263,24 +331,26 @@ int main(int argc, char ** argv) { hosts = malloc(sizeof(struct hosts)); hosts->host = NULL; hosts->port = 0; + hosts->bad = 0; + hosts->offline = 0; hosts->next = NULL; - /* Allocate main loop object */ + /* allocate main loop object */ if (!(simple_poll = avahi_simple_poll_new())) { fprintf(stderr, "Failed to create simple poll object.\n"); goto fail; } - /* Allocate a new client */ + /* allocate a new client */ client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0, client_callback, NULL, &error); - /* Check wether creating the client object succeeded */ + /* check wether creating the client object succeeded */ if (!client) { fprintf(stderr, "Failed to create client: %s\n", avahi_strerror(error)); goto fail; } - /* Create the service browser */ + /* create the service browser */ if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE, NULL, 0, browse_callback, client))) { fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client))); goto fail; @@ -290,8 +360,12 @@ int main(int argc, char ** argv) { fprintf(stderr, "Could not start daemon on port %d.\n", PORT); return EXIT_FAILURE; } + + /* register signal callbacks */ + signal(SIGTERM, sigterm_callback); + signal(SIGHUP, sighup_callback); - /* Run the main loop */ + /* run the main loop */ avahi_simple_poll_loop(simple_poll); MHD_stop_daemon(mhd); @@ -301,6 +375,13 @@ int main(int argc, char ** argv) { fail: /* Cleanup things */ + while(hosts->host != NULL) { + free(hosts->host); + tmphosts = hosts->next; + free(hosts); + hosts = tmphosts; + } + if (sb) avahi_service_browser_free(sb); -- cgit v1.2.3-54-g00ecf