static void * cache_maintain(void * arg) { /* This code runs as a thread and is responsible for maintaining the * cache. Every 10 seconds it scans 1/6 of the cache, so that in a * minute it will scan the entire cache. It attempts to refresh cache * entries which have expired, but only up to 5 times if they have not * been used in the interim. After that they are removed. */ int entries = HASH_SIZE / 6; int position = 0; for(;;) { int i; time_t now; struct timespec delay; delay.tv_sec = 10; delay.tv_nsec = 0; while(nanosleep(&delay, &delay) < 0 && errno == EINTR) if(debug) printf("Resuming interrupted sleep!\n"); pthread_mutex_lock(&cache_mutex); if(debug) printf("Look over 1/6 of cache...\n"); now = time(NULL); for(i = 0; i < entries; i++) { /* Since we'll potentially be removing entries from the * linked list, we keep a pointer to the previous * element's link to us so that we can update it and use * that to get to the next element if we remove one. */ struct cache_entry ** point = &hash_table[position]; struct cache_entry * scan; while((scan = *point)) { /* GET*ENT entries do not get refreshed here */ if(scan->refreshes == 5 || (now > scan->expire_time && (scan->type == GETPWENT || scan->type == GETGRENT))) /* kill it */ cache_entry_destroy(scan); else if(now > scan->expire_time) { request_header req = {version: NSCD_VERSION, type: scan->type, key_len: scan->key_len}; int r; void * reply; int32_t reply_len; time_t refresh_interval; /* refresh it */ if(debug) printf("Refreshing cache entry for [%s], refreshes %d\n", (char *) scan->key, scan->refreshes); pthread_mutex_unlock(&cache_mutex); r = generate_reply(&req, scan->key, -1, &reply, &reply_len, &refresh_interval); pthread_mutex_lock(&cache_mutex); now = time(NULL); /* while we were refreshing it, it may have * been marked stale and a new copy fetched */ if(scan->refreshes == 5 || r < 0) { /* kill it */ cache_entry_destroy(scan); if(r >= 0) free(reply); } else { free(scan->reply); scan->reply = reply; scan->reply_len = reply_len; scan->expire_time += refresh_interval; scan->refresh_interval = refresh_interval; scan->refreshes++; /* go to next entry */ point = &scan->chain; } } else /* go to next entry */ point = &scan->chain; } if(++position == HASH_SIZE) position = 0; }
static void process_gabn_finddone(isc_task_t *task, isc_event_t *ev) { ns_lwdclient_t *client = ev->ev_arg; isc_eventtype_t evtype; isc_boolean_t claimed; ns_lwdclient_log(50, "find done for task %p, client %p", task, client); evtype = ev->ev_type; isc_event_free(&ev); /* * No more info to be had? If so, we have all the good stuff * right now, so we can render things. */ claimed = ISC_FALSE; if (evtype == DNS_EVENT_ADBNOMOREADDRESSES) { if (NEED_V4(client)) { client->v4find = client->find; claimed = ISC_TRUE; } if (NEED_V6(client)) { client->v6find = client->find; claimed = ISC_TRUE; } if (client->find != NULL) { if (claimed) client->find = NULL; else dns_adb_destroyfind(&client->find); } generate_reply(client); return; } /* * We probably don't need this find anymore. We're either going to * reissue it, or an error occurred. Either way, we're done with * it. */ if ((client->find != client->v4find) && (client->find != client->v6find)) { dns_adb_destroyfind(&client->find); } else { client->find = NULL; } /* * We have some new information we can gather. Run off and fetch * it. */ if (evtype == DNS_EVENT_ADBMOREADDRESSES) { restart_find(client); return; } /* * An error or other strangeness happened. Drop this query. */ cleanup_gabn(client); ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE); }
static void restart_find(ns_lwdclient_t *client) { unsigned int options; isc_result_t result; isc_boolean_t claimed; ns_lwdclient_log(50, "starting find for client %p", client); /* * Issue a find for the name contained in the request. We won't * set the bit that says "anything is good enough" -- we want it * all. */ options = 0; options |= DNS_ADBFIND_WANTEVENT; options |= DNS_ADBFIND_RETURNLAME; /* * Set the bits up here to mark that we want this address family * and that we do not currently have a find pending. We will * set that bit again below if it turns out we will get an event. */ if (NEED_V4(client)) options |= DNS_ADBFIND_INET; if (NEED_V6(client)) options |= DNS_ADBFIND_INET6; find_again: INSIST(client->find == NULL); result = dns_adb_createfind(client->clientmgr->view->adb, client->clientmgr->task, process_gabn_finddone, client, dns_fixedname_name(&client->target_name), dns_rootname, 0, options, 0, dns_fixedname_name(&client->target_name), client->clientmgr->view->dstport, &client->find); /* * Did we get an alias? If so, save it and re-issue the query. */ if (result == DNS_R_ALIAS) { ns_lwdclient_log(50, "found alias, restarting query"); dns_adb_destroyfind(&client->find); cleanup_gabn(client); result = add_alias(client); if (result != ISC_R_SUCCESS) { ns_lwdclient_log(50, "out of buffer space adding alias"); ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE); return; } goto find_again; } ns_lwdclient_log(50, "find returned %d (%s)", result, isc_result_totext(result)); /* * Did we get an error? */ if (result != ISC_R_SUCCESS) { if (client->find != NULL) dns_adb_destroyfind(&client->find); cleanup_gabn(client); ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE); return; } claimed = ISC_FALSE; /* * Did we get our answer to V4 addresses? */ if (NEED_V4(client) && ((client->find->query_pending & DNS_ADBFIND_INET) == 0)) { ns_lwdclient_log(50, "client %p ipv4 satisfied by find %p", client, client->find); claimed = ISC_TRUE; client->v4find = client->find; } /* * Did we get our answer to V6 addresses? */ if (NEED_V6(client) && ((client->find->query_pending & DNS_ADBFIND_INET6) == 0)) { ns_lwdclient_log(50, "client %p ipv6 satisfied by find %p", client, client->find); claimed = ISC_TRUE; client->v6find = client->find; } /* * If we're going to get an event, set our internal pending flag * and return. When we get an event back we'll do the right * thing, basically by calling this function again, perhaps with a * new target name. * * If we have both v4 and v6, and we are still getting an event, * we have a programming error, so die hard. */ if ((client->find->options & DNS_ADBFIND_WANTEVENT) != 0) { ns_lwdclient_log(50, "event will be sent"); INSIST(client->v4find == NULL || client->v6find == NULL); return; } ns_lwdclient_log(50, "no event will be sent"); if (claimed) client->find = NULL; else dns_adb_destroyfind(&client->find); /* * We seem to have everything we asked for, or at least we are * able to respond with things we've learned. */ generate_reply(client); }
/* Return values: * Negative on error * 0 on success with a reusable socket * 1 on success with a non-reusable socket */ static int process_request(int client, uid_t uid, request_header * req, void * key) { struct cache_entry * entry; void * reply; int32_t reply_len; time_t refresh_interval; pthread_mutex_t * extra_mutex = NULL; int r; if(debug) printf("Got request type %d (key = [%s]) from UID %d on FD %d\n", req->type, (char *) key, uid, client); /* first check for control messages */ if(req->type > LASTDBREQ && req->type != GETPWENT && req->type != GETGRENT && req->type != GETAI && req->type != INITGROUPS) { if(req->type == SHUTDOWN) exit(0); if(req->type == GETSTAT) { /* to aid the use of -g in figuring out whether * gnscd is answering queries correctly, grab * the cache mutex and release it to make sure * it's not stuck locked by some thread */ pthread_mutex_lock(&cache_mutex); pthread_mutex_unlock(&cache_mutex); send_stats(client, uid); } if(req->type == INVALIDATE) { /* key is "passwd" "group" or "hosts" */ /* use memcmp not strcmp for security */ /* if it's hosts, call res_init() */ /* NOT IMPLEMENTED */ } return 1; } /* not a control message, so check if the service is disabled */ if(is_disabled(req->type)) { if(debug) printf("Service type %d disabled\n", req->type); r = generate_disabled_reply(req->type, &reply, &reply_len); if(r < 0) return -1; if(write_all(client, reply, reply_len, SHORT_TIMEOUT) != reply_len) return -1; return r; } /* if it is a GET*ENT query, grab the extra mutex */ if(req->type == GETPWENT) extra_mutex = pwent_query_mutex; else if(req->type == GETGRENT) extra_mutex = grent_query_mutex; if(extra_mutex) pthread_mutex_lock(extra_mutex); pthread_mutex_lock(&cache_mutex); r = cache_search(req, key, uid, &entry); if(r < 0 && extra_mutex) { if(debug) printf("Not in the cache, requesting iteration.\n"); /* request it */ r = request_ent_cache(req, key, uid); if(r >= 0) r = cache_search(req, key, uid, &entry); } if(r >= 0) { if(debug) printf("Found it in the cache!\n"); r = entry->close_socket; /* reset the refresh count */ entry->refreshes = 0; if(write_all(client, entry->reply, entry->reply_len, SHORT_TIMEOUT) != entry->reply_len) r = -1; pthread_mutex_unlock(&cache_mutex); if(extra_mutex) pthread_mutex_unlock(extra_mutex); return r; } pthread_mutex_unlock(&cache_mutex); if(debug) printf("Not in the cache.\n"); if(!extra_mutex) /* find it */ r = generate_reply(req, key, uid, &reply, &reply_len, &refresh_interval); else r = -1; if(r >= 0) { int close_socket = r; int add_result = -1; if(write_all(client, reply, reply_len, SHORT_TIMEOUT) != reply_len) { if(debug) printf("Failed to write to client %d\n", client); r = -1; } pthread_mutex_lock(&cache_mutex); /* don't add duplicate entries */ if(cache_search(req, key, uid, &entry) < 0) add_result = cache_add(req, key, uid, reply, reply_len, close_socket, refresh_interval); if(add_result < 0) /* either it was already in the cache or adding it failed */ free(reply); pthread_mutex_unlock(&cache_mutex); } /* if it was a GET*ENT query, release the extra mutex */ if(extra_mutex) pthread_mutex_unlock(extra_mutex); return r; }