/* * The DotSlash module for Apache web server * * (c) Columbia University, 2004-2006, All Rights Reserved. * Author: Weibin Zhao */ #include "mod_dots.h" module AP_MODULE_DECLARE_DATA dots_module; extern module AP_MODULE_DECLARE_DATA cache_module; extern scoreboard *ap_scoreboard_image; extern const char* ap_server_root; /*------------- file size for the redirected urls -----------------*/ static int lookup_rurls(char *path) { int i; redirect_urls_t *rurls = &(ptable->rurls); for (i=0; inumber; i++) { if (strcmp(rurls->path[i], path) == 0) return rurls->fsize[i]; } return 0; } static void enter_rurls(char *path, int fsize) { redirect_urls_t *rurls = &(ptable->rurls); int number = rurls->number; if (number < MAXREDIRECTURL) { strcpy(rurls->path[number], path); rurls->fsize[number] = fsize; rurls->number++; } } /*------------------------------------------------------------- * The Dostd -- Mod_dots interface for accessing shared memory *-------------------------------------------------------------*/ /* * Obtain a rescue server from existing rescue_list * Need to check client IP ** NO redirect for rescue proxy request */ static int get_rescserv(request_rec *r, char *client_ip, char **resc_serv, int *resc_port, int is_dynamic) { float min_lf = -1, new_cpu_lf = 0, new_link_lf = 0; /* load factor */ dots_pnode_t *p, *q = NULL; int fsize = 1; /* the file size of redirected URL */ struct stat buf; if (client_ip == NULL) return A_BadReq; // find the one with the minimum loading_factor p = ptable->pused; while (p != NULL) { // p->type must be C_Rescue if (p->type == C_Rescue && p->set_done == 1) { if (strcmp(p->resc_ip, client_ip) == 0) return A_Reject; if (is_dynamic && (min_lf == -1 || p->cpu_lf < min_lf)) { q = p; min_lf = p->cpu_lf; } else if (!is_dynamic && (min_lf == -1 || p->link_lf < min_lf)) { q = p; min_lf = p->link_lf; } } p = p->next; } if (q == NULL || q->link_lf >= 1 || q->cpu_lf >= 1) { apr_proc_mutex_lock(ptable_mutex); ptable->no_resc_cap = 1; apr_proc_mutex_unlock(ptable_mutex); return A_NotFound; } // do we need to allocate more rescue capacity if ((q->link_lf > dots_gamma || q->cpu_lf > dots_gamma) && ptable->need_resc_cap == 0) { apr_proc_mutex_lock(ptable_mutex); ptable->need_resc_cap = 1; apr_proc_mutex_unlock(ptable_mutex); } // accounting for the redirect if (is_dynamic) { apr_proc_mutex_lock(ptable_mutex); q->cpu_lf = q->cpu_lf + q->cpu_lw; apr_proc_mutex_unlock(ptable_mutex); } else { if ((fsize=lookup_rurls(r->uri)) == 0) { /* not in rtable */ ap_core_translate(r); stat(r->filename, &buf); fsize = buf.st_size; apr_proc_mutex_lock(ptable_mutex); enter_rurls(r->uri, fsize); apr_proc_mutex_unlock(ptable_mutex); } apr_proc_mutex_lock(ptable_mutex); q->link_lf = q->link_lf + q->link_lw * fsize; apr_proc_mutex_unlock(ptable_mutex); } *resc_serv = q->resc_serv; *resc_port = q->resc_port; return A_OK; } /* * Map a query (host WITHOUT port) to an origin server (host:port) * The query can be a rescue server alias, or an origin server name * (in the latter case no mapping is performed) */ static int map_origserv(const char *query, char **orig_serv, char **orig_ip, int *orig_port, int *qcache_ttl) { dots_pnode_t *p; int rcode = A_NotFound; if (query == NULL) return A_BadReq; p = ptable->pused; while (p != NULL) { if ((p->type == C_Origin || p->type == C_PastOrig) && (strcmp(p->resc_serv,query)==0 || strcmp(p->orig_serv,query)==0)){ if (p->type == C_Origin) rcode = A_OK; else rcode = A_Expired; *orig_serv = p->orig_serv; *orig_ip = p->orig_ip; *orig_port = p->orig_port; *qcache_ttl = p->qcache_ttl; break; } else p = p->next; } return rcode; } /*------------------------------------------------------------- * The URL table (utable) for cache control *-------------------------------------------------------------*/ static void reset_utable() { int i; utable->uused = NULL; utable->ufree = (dots_unode_t *) &utable->heap[0]; for (i=0; iheap[i].next = (dots_unode_t *) &(utable->heap[i+1]); } else { utable->heap[i].next = NULL; } } } /* * Try to get a node from "free", and add it to "used" * Return: new node pointer if OK * NULL on error */ static dots_unode_t *alloc_unode(char *url) { dots_unode_t *p = NULL; if (utable->ufree != NULL) { p = utable->ufree; utable->ufree = utable->ufree->next; p->next = utable->uused; utable->uused = p; strcpy(p->url, url); p->done = 0; p->nwait = 0; pthread_mutex_init(&p->mutex, NULL); pthread_cond_init(&p->cond, NULL); } return p; } /* * Try to move node "p" from "used" to "free" * Return: 0 if OK * -1 on error */ static int release_unode(dots_unode_t *p) { dots_unode_t *q; int rval = 0; if (p == NULL) return rval; /* nothing to release */ if (p == utable->uused) { utable->uused = p->next; } else { q = utable->uused; while (q != NULL && q->next != p) q = q->next; if (q == NULL) rval = -1; else q->next = p->next; } p->next = utable->ufree; utable->ufree = p; return rval; } static dots_unode_t *find_unode(char *url) { dots_unode_t *p = utable->uused; while (p != NULL) { if (strcmp(p->url, url) == 0) return p; p = p->next; } return NULL; } /*------------------------------------------------------ * The Mod_dots hooks *------------------------------------------------------*/ static void dots_init_parameters() { if (CommunityType == 0) CommunityType = Open_Community; if (DotsdPidFileSet == 0) { sprintf(DotsdPidFile, "%s/logs/dotsd.pid", ap_server_root); } if (ScriptRootSet == 0) { sprintf(ScriptRoot, "%s/htdocs/_dots_script", ap_server_root); } if (ddns_script_set == 0) { sprintf(ddns_script, "%s/bin/dots_ddns", ap_server_root); } if (MaxDataRate == 0) MaxDataRate = Default_MaxDataRate; if (MaxDreqRate == 0) MaxDreqRate = Default_MaxDreqRate; if (ControlInterval == 0) ControlInterval = Default_ControlInterval; if (RescueIdleTTL == 0) RescueIdleTTL = Default_RescueIdleTTL; if (OriginIdleTTL == 0) OriginIdleTTL = Default_OriginIdleTTL; if (PastOriginTTL == 0) PastOriginTTL = Default_PastOriginTTL; if (KeepAliveTTL == 0) KeepAliveTTL = Default_KeepAliveTTL; if (DotsdPort == 0) DotsdPort = Default_DotsdPort; if (SrvlocServerSet == 0) strcpy(SrvlocServer, "NULL"); if (SrvlocPort == 0) SrvlocPort = Default_SrvlocPort; if (DiscoverTTL == 0) DiscoverTTL = Default_DiscoverTTL; if (RegisterTTL == 0) RegisterTTL = Default_RegisterTTL; if (RedirectOverhead == 0) RedirectOverhead = Default_RedirectOverhead; if (AccountingScale == 0) AccountingScale = Default_AccountingScale; if (dots_alpha == 0) dots_alpha = Default_alpha; if (dots_gamma == 0) dots_gamma = Default_gamma; if (cpu_upper_thd == 0) cpu_upper_thd = Default_cpu_upper_thd; if (cpu_lower_thd == 0) cpu_lower_thd = Default_cpu_lower_thd; if (cpu_lower_thd >= cpu_upper_thd) { cpu_upper_thd = Default_cpu_upper_thd; cpu_lower_thd = Default_cpu_lower_thd; } if (link_upper_thd == 0) link_upper_thd = Default_link_upper_thd; if (link_lower_thd == 0) link_lower_thd = Default_link_lower_thd; if (link_lower_thd >= link_upper_thd) { link_upper_thd = Default_link_upper_thd; link_lower_thd = Default_link_lower_thd; } if (alias_pool_size == 0) alias_pool_size = Default_alias_pool_size; if (QCacheServerSet == 0) strcpy(QCacheServer, "NULL"); if (QCacheTTL == 0) QCacheTTL = 60; cpu_optload = (cpu_upper_thd + cpu_lower_thd) * 0.5; link_optload = (link_upper_thd + link_lower_thd) * 0.5; if (dns_tsig_set == 1) { if (my_tsigkey_set == 0 || my_tsigsecret_set == 0) { ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL, "Error: please configure TSIG key and secret"); return; } } else { // clear all TSIG settings my_tsigkey_set = 0; strcpy(my_tsigkey, "NULL"); my_tsigsecret_set = 0; strcpy(my_tsigsecret, "NULL"); } } void reset_ometer() { ometer->num_reply = 0; ometer->num_redirect = 0; ometer->num_rescue = 0; ometer->num_proxy = 0; ometer->reply_bytes = 0; ometer->redi_msg_size = 0; ometer->redi_msg_body = 0; ometer->redi_msg_alen = 0; ometer->cpu_load = 0; ometer->link_load = 0; } static void dots_init_others(server_rec *s) { int i; char cmd[1024]; /* clean up script directory */ sprintf(cmd, "rm -rf %s/*", ScriptRoot); system(cmd); #if 0 /* init my_hostname, get FQDN, uname(2) also OK */ gethostname(my_fullname, MAXNAME); #endif /* * my_fullname is used for DNS-RR (add/delete IP addresses for my_fullname) * my_aliasbase is used for constructing aliases for HTTP redirects * You can define my_fullname and my_aliasname in different domains * In our experiments, they are both in the dot-slash.net. domain. * my_fullname is derived from the "ServerName" directive. * Also, the "ServerName" directive must specify port number. * my_aliasbase is defined by DotsAliasBase; if undefined, * my_aliasbase is regarded the same as my_fullname. */ // Set FQDN (my_fullname) for the web server sprintf(my_fullname, s->server_hostname); i = strlen(my_fullname); while (i>0 && my_fullname[i-1] == '.') { my_fullname[i-1] = 0; // remove trailing dots i = strlen(my_fullname); } ApachePort = s->port; // Set my_aliasbase if not set if (my_aliasbase_set == 0) strcpy(my_aliasbase, my_fullname); // Set my_hostname (with the domain part) i = 0; while (my_fullname[i]) { if (my_fullname[i] == '.') { my_hostname[i] = 0; break; } else my_hostname[i] = my_fullname[i]; i++; } // You should give a static name if DNS-RR is enable if (dns_rr_set == 1 && my_staticname_set == 0) { ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL, "You should give a static name if DNS-RR is enabled"); } // Set my_staticname if not set if (my_staticname_set == 0) strcpy(my_staticname, my_fullname); } static void init_qcstat() { int i; for (i=0; iqcstat[i]=0; } } /* * (1) Initialize shared memory and mutex * (2) Calculate derived parameters based on configurable parameters */ static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { apr_status_t rv; int i, pid; dots_init_parameters(); pid = getpid(); sprintf(shm_fname, "/tmp/dots_shm.%d", pid); sprintf(ometer_mutex_fname, "/tmp/dots_ometer_mutex.%d", pid); sprintf(ptable_mutex_fname, "/tmp/dots_ptable_mutex.%d", pid); sprintf(htable_mutex_fname, "/tmp/dots_htable_mutex.%d", pid); sprintf(qcstat_mutex_fname, "/tmp/dots_qcstat_mutex.%d", pid); rv = apr_shm_create(&dots_shm, sizeof(dots_shm_t), shm_fname, p); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, "unable to create shared memory for mod_dots"); return HTTP_INTERNAL_SERVER_ERROR; } shmptr = (dots_shm_t *) apr_shm_baseaddr_get(dots_shm); if (shmptr == NULL) { ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL, "unable to attach shared memory for mod_dots"); return HTTP_INTERNAL_SERVER_ERROR; } shmptr->dotsd_running = 0; shmptr->alias_alloc_num = alias_alloc_num; shmptr->alias_reg_num = alias_reg_num; shmptr->srvloc_port = SrvlocPort; strcpy(shmptr->srvloc_server, SrvlocServer); ometer = (dots_ometer_t *) &(shmptr->ometer); reset_ometer(); ptable = (dots_ptable_t *) &(shmptr->ptable); init_ptable(); init_qcstat(); ctable = (dots_ctable_t *) &(shmptr->ctable); reset_ctable(); utable = (dots_utable_t *) malloc(sizeof(dots_utable_t)); reset_utable(); rv = apr_proc_mutex_create(&ometer_mutex, ometer_mutex_fname, APR_LOCK_DEFAULT, p); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, "unable to create ometer mutex for mod_dots"); return HTTP_INTERNAL_SERVER_ERROR; } rv = apr_proc_mutex_create(&ptable_mutex, ptable_mutex_fname, APR_LOCK_DEFAULT, p); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, "unable to create ptable mutex for mod_dots"); return HTTP_INTERNAL_SERVER_ERROR; } rv = apr_proc_mutex_create(&htable_mutex, htable_mutex_fname, APR_LOCK_DEFAULT, p); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, "unable to create htable mutex for mod_dots"); return HTTP_INTERNAL_SERVER_ERROR; } rv = apr_proc_mutex_create(&qcstat_mutex, qcstat_mutex_fname, APR_LOCK_DEFAULT, p); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, "unable to create qcstat mutex for mod_dots"); return HTTP_INTERNAL_SERVER_ERROR; } pthread_mutex_init(&ctable_mutex, NULL); pthread_mutex_init(&utable_mutex, NULL); dots_init_others(s); return DECLINED; } /* * Initialize mutex for each child process */ static void child_init(apr_pool_t *p, server_rec *s) { apr_status_t rv; int fd, pid, need_start_dotsd = 0; struct timeval tp; rv = apr_proc_mutex_child_init(&ometer_mutex, ometer_mutex_fname, p); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, "child_init: ometer_mutex error"); } rv = apr_proc_mutex_child_init(&ptable_mutex, ptable_mutex_fname, p); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, "child_init: ptable_mutex error"); } rv = apr_proc_mutex_child_init(&htable_mutex, htable_mutex_fname, p); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, "child_init: htable_mutex error"); } rv = apr_proc_mutex_child_init(&qcstat_mutex, qcstat_mutex_fname, p); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, "child_init: qcstat_mutex error"); } /* * Fork twice to start dotsd() */ apr_proc_mutex_lock(ptable_mutex); if (shmptr->dotsd_running == 0) { shmptr->dotsd_running = 1; need_start_dotsd = 1; } apr_proc_mutex_unlock(ptable_mutex); if (need_start_dotsd == 1) { if ((pid = fork()) < 0) { ap_log_error(APLOG_MARK, APLOG_CRIT, pid, NULL, "fork error for the first child"); } if (pid == 0) { // first child if ((pid = fork()) < 0) { ap_log_error(APLOG_MARK, APLOG_CRIT, pid, NULL, "fork error for the second child"); } if (pid == 0) { // second child dotsd(DotsdPidFile); } exit(0); // terminate the first child here } // we are the parent of the first child if (waitpid(pid, NULL, 0) != pid) { // wait for the first child ap_log_error(APLOG_MARK, APLOG_CRIT, pid, NULL, "waitpid error for the first child"); } } gettimeofday(&tp, NULL); srand48(tp.tv_sec); } static int is_dynamic(char *pathname) { int len; char *p; if (pathname == NULL) return 0; len = strlen(pathname); if (len <= 4) return 0; p = pathname + len - 4; if (strncmp(p, ".php", 4) == 0) return 1; p = strstr(pathname, ".php?"); if (p != NULL) return 1; return 0; } /* * (1) Allocate space for pre-request information for dots_module * (2) Redirect current request if load > highmark * (3) Perform cache control for reverse proxy */ static int post_read_request(request_rec *r) { const char *cache_control; apr_status_t rv; int rcode; double prob; char *resc_serv; int resc_port; cache_request_rec *cache; dots_request_rec *dots_req; dots_unode_t *p; dots_req = (dots_request_rec *) ap_get_module_config(r->request_config, &dots_module); if (dots_req == NULL) { dots_req = apr_pcalloc(r->pool, sizeof(dots_request_rec)); dots_req->is_reset = 0; /* not reset */ dots_req->is_redirect = 0; /* not HTTP redirect */ dots_req->is_rescue = 0; /* not rescue */ dots_req->is_dynamic = 0; /* not dynamic content */ dots_req->in_utable = 0; /* not in URL control table */ dots_req->is_proxy = 0; /* not reverse proxy */ dots_req->qcache_bypass = 0; /* not bypass qcache */ ap_set_module_config(r->request_config, &dots_module, dots_req); } /* * Don't do HTTP redirect or reverse proxy for /dotslash-status */ if (strncmp(r->parsed_uri.path, "/dotslash-status", 16) == 0) { return DECLINED; } if (is_dynamic(r->uri)) { dots_req->is_dynamic = 1; /* is dynamic content */ } /* * Check HTTP header Cache-Control to decide whether bypass QCache */ cache_control = apr_table_get(r->headers_in, "Cache-Control"); if (cache_control && (strstr(cache_control, "max-age=0") || strstr(cache_control, "no-cache"))) { dots_req->qcache_bypass = 1; /* bypass qcache for this request */ } if (ptable->status == S_SOS && r->method_number == M_GET && dots_req->qcache_bypass == 0 && ptable->redi_prob > 0 && ptable->no_resc_cap == 0) { // may need HTTP redirect, for GET only prob = drand48(); if (prob > ptable->redi_prob) return DECLINED; rcode = get_rescserv(r, r->connection->remote_ip, &resc_serv, (int *)&resc_port, dots_req->is_dynamic); if (rcode == A_OK) { dots_req->is_redirect = 1; /* is an HTTP redirect */ sprintf(dots_req->redirect_uri, "http://%s:%d%s", resc_serv, resc_port, r->unparsed_uri); apr_table_setn(r->headers_out, "Location", dots_req->redirect_uri); return HTTP_MOVED_TEMPORARILY; } } else if (strcmp(r->hostname, my_fullname) != 0 && strcmp(r->hostname, my_hostname) != 0 && strcmp(r->hostname, shmptr->my_ip) != 0) { /* NOT for myself */ rcode = map_origserv(r->hostname, &dots_req->orig_serv, &dots_req->orig_ip, (int *)&dots_req->orig_port, (int *)&dots_req->qcache_ttl); if (rcode == A_Expired || // an Old_Map (rcode == A_OK && dots_req->qcache_bypass == 1)) { // bypass qcache dots_req->is_redirect = 1; /* is an HTTP redirect */ sprintf(dots_req->redirect_uri, "http://%s:%d%s", dots_req->orig_ip, dots_req->orig_port, r->unparsed_uri); apr_table_setn(r->headers_out, "Location", dots_req->redirect_uri); return HTTP_MOVED_TEMPORARILY; } else if (rcode == A_OK) { // Active_Map, ptable->status == S_Rescue dots_req->is_rescue = 1; // is a rescue if (dots_req->is_dynamic) { return DECLINED; } // Generate a key for this URL, same as mod_cache if (r->hostname) { sprintf(dots_req->key, "%s%s?%s", r->hostname, r->uri, r->args); } else { sprintf(dots_req->key, "%s?%s", r->uri, r->args); } // Cache control for proxy purpose, is it in "mem" cache cache = (cache_request_rec *) ap_get_module_config( r->request_config, &cache_module); if (!cache) { cache = apr_pcalloc(r->pool, sizeof(cache_request_rec)); ap_set_module_config(r->request_config, &cache_module, cache); } cache->types="mem"; rv = cache_select_url(r, cache->types, r->unparsed_uri); if (rv == DECLINED) { // NOT in "mem" cache pthread_mutex_lock(&utable_mutex); p = find_unode(dots_req->key); if (p == NULL) { // NOT in URL control table dots_req->proxy_node = alloc_unode(dots_req->key); pthread_mutex_unlock(&utable_mutex); dots_req->in_utable = 1; // mark it as in URL control table } else { // already in URL control table if (p->done == 0) { // Wait until it is done p->nwait++; pthread_mutex_unlock(&utable_mutex); pthread_mutex_lock(&p->mutex); pthread_cond_wait(&p->cond, &p->mutex); pthread_mutex_unlock(&p->mutex); pthread_mutex_lock(&utable_mutex); p->nwait--; if (p->nwait == 0) release_unode(p); pthread_mutex_unlock(&utable_mutex); } else { // Go ahead if it has been done pthread_mutex_unlock(&utable_mutex); } } } } } return DECLINED; } /* * If the request uses an active Dotslash virtual host name, * convert it to a reverse proxy request to the origin server */ static int translate_name(request_rec *r) { char orig_uri[MAXNAME]; dots_request_rec *dots_req; dots_req = (dots_request_rec *) ap_get_module_config(r->request_config, &dots_module); if (QCacheServerSet == 1) { if (dots_req->qcache_bypass) { apr_table_addn(r->subprocess_env, "DotsQCacheBypass", "1"); } if (dots_req->is_rescue) { apr_table_addn(r->subprocess_env, "DotsIsRescue", "1"); apr_table_addn(r->subprocess_env, "DotsQCacheTTL", apr_psprintf(r->pool, "%d", dots_req->qcache_ttl)); apr_table_addn(r->subprocess_env,"DotsDBWsite",dots_req->orig_serv); } else { apr_table_addn(r->subprocess_env,"DotsDBWsite",my_fullname); } } if (dots_req->is_rescue) { /* need reverse proxy or dynamic content */ if (dots_req->is_dynamic) { /* for hostname mapping in PHP script */ apr_table_addn(r->subprocess_env, "ORIG_SERV", dots_req->orig_serv); apr_table_addn(r->subprocess_env, "ORIG_IP", dots_req->orig_ip); apr_table_addn(r->subprocess_env, "ORIG_PORT", apr_psprintf(r->pool, "%d", dots_req->orig_port)); apr_table_addn(r->subprocess_env, "SCRIPT_ROOT", ScriptRoot); if (strncmp(r->uri, "/_dots_404.php", 14) == 0) return DECLINED; /* filename mapping for PHP script */ r->filename = apr_pstrcat(r->pool, ScriptRoot, "/", dots_req->orig_serv, r->uri, NULL); r->canonical_filename = r->filename; return OK; } else { if (dots_req->in_utable == 0) { /* NOT in URL control table yet */ pthread_mutex_lock(&utable_mutex); dots_req->proxy_node = alloc_unode(dots_req->key); pthread_mutex_unlock(&utable_mutex); dots_req->in_utable = 1; /* mark it as in URL control table */ } dots_req->is_proxy = 1; /* it is a reverse proxy */ sprintf(orig_uri, "http://%s:%d", dots_req->orig_ip, dots_req->orig_port); r->filename = apr_pstrcat(r->pool, "proxy:", orig_uri, r->uri, NULL); r->handler = "proxy-server"; r->proxyreq = PROXYREQ_REVERSE; return OK; } } return DECLINED; } /* * Log the number of bytes sent for each reply */ static int log_transaction(request_rec *r) { conn_rec *c = r->connection; dots_request_rec *dots_req; dots_conn_rec *dots_conn; dots_unode_t *p; dots_pnode_t *pnode; dots_req = (dots_request_rec *) ap_get_module_config(r->request_config, &dots_module); if (dots_req->in_utable) { /* it is in URL control table */ pthread_mutex_lock(&utable_mutex); p = dots_req->proxy_node; p->done = 1; if (p->nwait == 0) { release_unode(p); pthread_mutex_unlock(&utable_mutex); } else { pthread_mutex_unlock(&utable_mutex); pthread_mutex_lock(&p->mutex); pthread_cond_broadcast(&p->cond); pthread_mutex_unlock(&p->mutex); } } dots_conn = (dots_conn_rec *) ap_get_module_config(c->conn_config, &dots_module); if (dots_req->is_reset) { /* skip counting for reset request */ dots_conn->curr_reply_len = 0; return DECLINED; } apr_proc_mutex_lock(ometer_mutex); ometer->num_reply++; if (dots_req->is_redirect) { ometer->num_redirect++; if (ometer->redi_msg_size == 0) { ometer->redi_msg_size = dots_conn->curr_reply_len; ometer->redi_msg_body = r->bytes_sent; ometer->redi_msg_alen = (int) (AccountingScale * (ometer->redi_msg_size + RedirectOverhead)); if (ometer->redi_msg_alen < ometer->redi_msg_size) ometer->redi_msg_alen = ometer->redi_msg_size; ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "Size of HTTP redirect=%d, body=%d, accounting len=%d", ometer->redi_msg_size, ometer->redi_msg_body, ometer->redi_msg_alen); } dots_conn->curr_reply_len = ometer->redi_msg_alen; } if (dots_req->is_proxy) ometer->num_proxy++; if (dots_req->is_rescue) ometer->num_rescue++; ometer->reply_bytes += dots_conn->curr_reply_len; apr_proc_mutex_unlock(ometer_mutex); if (dots_req->is_rescue) { /* count rescue bytes for each orig_serv */ apr_proc_mutex_lock(ptable_mutex); pnode = ptable->pused; while (pnode != NULL) { if (pnode->type == C_Origin && pnode->orig_serv == dots_req->orig_serv) { pnode->resc_bytes += dots_conn->curr_reply_len; pnode->num_resc++; break; } pnode = pnode->next; } apr_proc_mutex_unlock(ptable_mutex); } dots_conn->curr_reply_len = 0; return DECLINED; } static int iterate_func(void *req, const char *key, const char *value) { int stat; char *line; request_rec *r = (request_rec *)req; if (key == NULL || value == NULL || value[0] == '\0') return 1; line = apr_psprintf(r->pool, "%s => %s
\n", key, value); stat = ap_rputs(line, r); return 1; } /* * Dump request header */ static int req_header_handler(request_rec *r) { if (strcmp(r->handler, "request-header") != 0) return DECLINED; if (r->method_number != M_GET) return DECLINED; ap_set_content_type(r, "text/html"); apr_table_do(iterate_func, r, r->headers_in, NULL); return OK; } /* * Handle query for dotslash output status */ static int dots_status_handler(request_rec *r) { dots_pnode_t *p; unsigned long long qc_get, db_access, db_query, qc_conf; char *s; int i, nused, timeout; dots_request_rec *dots_req; if (strcmp(r->handler, "dotslash-status") != 0) return DECLINED; if (r->method_number != M_GET) return DECLINED; if (r->parsed_uri.query != NULL && strcmp(r->parsed_uri.query, "auto") == 0) { ap_set_content_type(r, "text/plain"); ap_rprintf(r, "Total Bytes: %llu\n", ometer->reply_bytes); ap_rprintf(r, "Num Request: %llu\n", ometer->num_reply); ap_rprintf(r, "Num Redirect: %llu\n", ometer->num_redirect); ap_rprintf(r, "Num Rescue: %llu\n", ometer->num_rescue); ap_rprintf(r, "Num Proxy: %llu\n", ometer->num_proxy); ap_rprintf(r, "CPU Uticks: %llu\n", ometer->cpu_uticks); ap_rprintf(r, "CPU Aticks: %llu\n", ometer->cpu_aticks); return OK; } ap_set_content_type(r, "text/html"); ap_rputs(DOCTYPE_HTML_3_2 "\nDotslash Status\n\n\n", r); if (strcmp(r->parsed_uri.path, "/dotslash-status/reset") == 0) { dots_req = (dots_request_rec *) ap_get_module_config(r->request_config, &dots_module); dots_req->is_reset = 1; reset_ometer(); ap_rputs("

Dotslash status has been reset

\n\n", r); ap_rputs("\n", r); return OK; } ap_rputs("

DotSlash Status

\n\n", r); ap_rputs("
Notes: (1) 1 kB = 1000 bytes; (2) the unit for all TTLs is the length of ControlInterval
\n\n", r); ap_rputs("

Server Information

\n", r); ap_rputs("\n\n", r); ap_rprintf(r, "\n
DomainNameApachePortDotsdPortCommunityType
%s%d%d%d
\n\n", my_fullname, ApachePort, DotsdPort, CommunityType); ap_rputs("

Dynamic DNS Information

\n", r); ap_rputs("\n\n", r); ap_rprintf(r, "\n
AliasBaseAliasPoolSizeAliasAllocNumAliasRegNum
%s%d%d%d
\n\n", my_aliasbase, alias_pool_size, shmptr->alias_alloc_num, shmptr->alias_reg_num); ap_rputs("\n\n", r); ap_rprintf(r, "\n
DnsRoundRobinStaticIP
%d%s
\n\n", dns_rr_set, shmptr->my_ip); ap_rputs("

Cache Information

\n", r); ap_rputs("\n\n", r); ap_rprintf(r, "\n
ScriptRoot
%s
\n\n",ScriptRoot); ap_rputs("\n\n", r); ap_rprintf(r, "\n
QCacheServerQCacheTTL
%s%d
\n\n", QCacheServer, QCacheTTL); qc_conf = shmptr->qcstat[QC_CONF1] + shmptr->qcstat[QC_CONF2]; qc_get = shmptr->qcstat[QC_MISS] + shmptr->qcstat[QC_OK] + qc_conf; ap_rputs("\n\n", r); ap_rprintf(r, "\n", shmptr->qcstat[QC_MISS], qc_conf, shmptr->qcstat[QC_CONF1], shmptr->qcstat[QC_OK], qc_get); if (qc_get > 0) { ap_rprintf(r, "\n
QC_MISSQC_CONFQC_CONF1QC_OKQC_GET
%llu%llu%llu%llu%llu
%4.1f%%%4.1f%%%4.1f%%%4.1f%%100%%
\n\n", 100.0*shmptr->qcstat[QC_MISS]/qc_get, 100.0*qc_conf/qc_get, 100.0*shmptr->qcstat[QC_CONF1]/qc_get, 100.0*shmptr->qcstat[QC_OK]/qc_get); } else { ap_rprintf(r, "\n\n"); } db_access = shmptr->qcstat[DBR_NC] + shmptr->qcstat[DBW_NC] + shmptr->qcstat[DBR_QC] + shmptr->qcstat[DBW_QC]; db_query = db_access + shmptr->qcstat[QC_OK] + shmptr->qcstat[DBW_OFF]; ap_rputs("\n\n", r); ap_rprintf(r, "\n", shmptr->qcstat[DBR_QC], shmptr->qcstat[DBW_QC], shmptr->qcstat[DBR_NC], shmptr->qcstat[DBW_NC], db_access); if (db_access > 0) { ap_rprintf(r, "\n
DBR_QCDBW_QCDBR_NCDBW_NCDB_ACCESS
%llu%llu%llu%llu%llu
%4.1f%%%4.1f%%%4.1f%%%4.1f%%100%%
\n\n", 100.0*shmptr->qcstat[DBR_QC]/db_access, 100.0*shmptr->qcstat[DBW_QC]/db_access, 100.0*shmptr->qcstat[DBR_NC]/db_access, 100.0*shmptr->qcstat[DBW_NC]/db_access); } else { ap_rprintf(r, "\n\n"); } ap_rputs("\n\n", r); ap_rprintf(r, "\n", shmptr->qcstat[QC_OK], db_access, shmptr->qcstat[DBW_OFF], db_query); if (db_query > 0) { ap_rprintf(r, "\n
QC_OKDB_ACCESSDBW_OFFDB_QUERY
%llu%llu%llu%llu
%4.1f%%%4.1f%%%4.1f%%100%%
\n\n", 100.0*shmptr->qcstat[QC_OK]/db_query, 100.0*db_access/db_query, 100.0*shmptr->qcstat[DBW_OFF]/db_query); } else { ap_rprintf(r, "\n\n"); } ap_rputs("

Control Parameters

\n", r); ap_rputs("\n\n", r); ap_rprintf(r, "\n
MaxDataRateControlIntervalRescueIdleTTLOriginIdleTTLPastOriginTTL
%d kB/sec%d sec%d%d%d
\n\n", MaxDataRate, ControlInterval, RescueIdleTTL, OriginIdleTTL, PastOriginTTL); ap_rputs("\n\n", r); ap_rprintf(r, "\n
CpuMonitorCpuUpperThresholdCpuLowerThresholdLinkUpperThresholdLinkLowerThreshold
%d%4.2f%4.2f%4.2f%4.2f
\n\n", cpu_monitor, cpu_upper_thd, cpu_lower_thd, link_upper_thd, link_lower_thd); ap_rputs("

Response Statistics

\n", r); ap_rputs("\n\n", r); ap_rprintf(r, "\n
NumResponsesNumRedirectsNumRescuesNumProxysTotalBytes
%llu%llu%llu%llu%llu
\n\n", ometer->num_reply, ometer->num_redirect, ometer->num_rescue, ometer->num_proxy, ometer->reply_bytes); ap_rputs("

Service Discovery Information

\n", r); ap_rputs("\n\n", r); ap_rprintf(r, "\n
ServiceRegistryPortDiscoverTTLRegisterTTL
%s%d%d%d
\n\n", shmptr->srvloc_server, shmptr->srvloc_port, DiscoverTTL, RegisterTTL); ap_rprintf(r, "

Rescue Server Candidates: %d

\n", ctable->count); if (ctable->count > 0) { ap_rputs("\n\n", r); for (i=0; icount; i++) { ap_rprintf(r, "\n", ctable->entry[i].name, ctable->entry[i].ip, ctable->entry[i].port, ctable->entry[i].link_rate, ctable->entry[i].cpu_rate, ctable->entry[i].status); } ap_rputs("
RescueServerStaticIPDotsdPortLinkRateCpuRateStatus
%s%s%d%d kB/sec%4.2f%d
\n\n", r); } p = ptable->pused; nused = 0; while (p != NULL) { nused++; p = p->next; } ap_rprintf(r, "

Dotslash Peers: %d

\n\n", nused); if (nused > 0) { ap_rputs("\n\n", r); p = ptable->pused; while (p != NULL) { if (p->type == C_Rescue) { ap_rprintf(r, "\n", p->resc_serv, p->resc_port, p->redi_rate, p->redi_data_rate); } else if (p->type == C_Origin) { ap_rprintf(r, "\n", p->orig_serv, p->orig_port, p->resc_serv, p->redi_rate, p->redi_data_rate, p->orig_idle_ttl, p->qcache_ttl); } else if (p->type == C_PastOrig) { ap_rprintf(r, "\n", p->orig_serv, p->orig_port, p->resc_serv, p->pastorig_ttl); } p = p->next; } ap_rputs("
PeerTypePeerNameApachePortAliasRediRateRediDataRateOrigTTLPastOriginTTLQcacheTTL
Rescue%s%dN/A%d reqs/sec%d kB/secN/AN/AN/A
Origin%s%d%s%d reqs/sec%d kB/sec%dN/A%d
ExpiredOrigin%s%d%sN/AN/AN/A%dN/A
\n\n", r); } if (strncmp(r->parsed_uri.path, "/dotslash-status/auto-refresh", 29) == 0) { s = r->parsed_uri.path+29; if (s != NULL && strlen(s) > 1) { timeout = atoi(s+1); if (timeout < 1) timeout = 30; } else { timeout = 30; } ap_rprintf(r, "\n\n", 1000*timeout); } ap_rputs("\n", r); return OK; } /* * check whether the request is from a rescue server */ static int from_rescserv(request_rec *r) { dots_pnode_t *p = ptable->pused; while (p != NULL) { if (p->type == C_Rescue) { if (strcmp(p->resc_ip, r->connection->remote_ip) == 0) return 1; } p = p->next; } return 0; } /* * All requests for PHP scripts from rescue servers are regarded * as text/plain content type, so as to bypass PHP content handler. * This applies to other server-side scripts for dynamic content */ static int fixups(request_rec *r) { if (from_rescserv(r) == 1 && strcmp(r->content_type, "application/x-httpd-php") == 0) { /* ap_set_content_type(r, "text/plain"); */ r->content_type = "text/plain"; r->no_cache = 1; } return OK; } /* * Add output filter for each connection */ static int pre_conn(conn_rec *c, void *csd) { ap_add_output_filter(DOTS_OUT_FILTER, NULL, NULL, c); return OK; } /* * Calculate the length of current reply */ static apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb) { apr_off_t length; conn_rec *c = f->c; dots_conn_rec *dots_conn; dots_conn = (dots_conn_rec *) ap_get_module_config(c->conn_config, &dots_module); if (dots_conn == NULL) { dots_conn = apr_pcalloc(c->pool, sizeof(dots_conn_rec)); dots_conn->curr_reply_len = 0; ap_set_module_config(c->conn_config, &dots_module, dots_conn); } apr_brigade_length(bb, 0, &length); if (length > 0) dots_conn->curr_reply_len += length; return ap_pass_brigade(f->next, bb); } static void register_hooks(apr_pool_t *p) { ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_child_init(child_init, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_pre_connection(pre_conn, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_post_read_request(post_read_request, NULL, NULL, APR_HOOK_FIRST); ap_hook_translate_name(translate_name, NULL, NULL, APR_HOOK_FIRST); ap_hook_log_transaction(log_transaction, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_fixups(fixups,NULL,NULL,APR_HOOK_REALLY_LAST); ap_hook_handler(dots_status_handler, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_handler(req_header_handler, NULL, NULL, APR_HOOK_MIDDLE); ap_register_output_filter(DOTS_OUT_FILTER, output_filter, NULL, AP_FTYPE_NETWORK); } static const char *setCommunityType(cmd_parms *cmd, void *dummy, const char *parameter) { if (strcasecmp(parameter, "open") == 0) { CommunityType = Open_Community; } else if (strcasecmp(parameter, "closed") == 0) { CommunityType = Closed_Community; } else if (strcasecmp(parameter, "insurance") == 0) { CommunityType = Insurance_Community; } else { CommunityType = Open_Community; // default } return NULL; } static const char *setDotsdPidFile(cmd_parms *cmd, void *dummy, const char *parameter) { DotsdPidFileSet = 1; sprintf(DotsdPidFile, "%s", parameter); return NULL; } static const char *setControlInterval(cmd_parms *cmd, void *dummy, const char *parameter) { int tmp; sscanf(parameter, "%d", &tmp); if (tmp <= 0 || tmp > 1000) tmp = 1; /* default to 1 second */ ControlInterval = tmp; return NULL; } static const char *setMaxDataRate(cmd_parms *cmd, void *dummy, const char *parameter) { int tmp; sscanf(parameter, "%d", &tmp); if (tmp <= 0 || tmp > 1000000) tmp = 1000; /* default to 1000 kB/s */ MaxDataRate = tmp; return NULL; } static const char *setLinkUpperThreshold(cmd_parms *cmd, void *dummy, const char *parameter) { sscanf(parameter, "%f", &link_upper_thd); if (link_upper_thd < 0.0 || link_upper_thd > 1.0) link_upper_thd = 0.75; return NULL; } static const char *setLinkLowerThreshold(cmd_parms *cmd, void *dummy, const char *parameter) { sscanf(parameter, "%f", &link_lower_thd); if (link_lower_thd < 0.0 || link_lower_thd > 1.0) link_lower_thd = 0.50; return NULL; } static const char *setCpuMonitor(cmd_parms *cmd, void *dummy, const char *parameter) { // We regard "Off" and "0" as "Off", and anything else as "On" if (!strcasecmp(parameter, "off") || !strcmp(parameter, "0")) { cpu_monitor = 0; } else { cpu_monitor = 1; } return NULL; } static const char *setCpuUpperThreshold(cmd_parms *cmd, void *dummy, const char *parameter) { sscanf(parameter, "%f", &cpu_upper_thd); if (cpu_upper_thd < 0.0 || cpu_upper_thd > 1.0) cpu_upper_thd = 0.75; return NULL; } static const char *setCpuLowerThreshold(cmd_parms *cmd, void *dummy, const char *parameter) { sscanf(parameter, "%f", &cpu_lower_thd); if (cpu_lower_thd < 0.0 || cpu_lower_thd > 1.0) cpu_lower_thd = 0.50; return NULL; } static const char *setRescueIdleTTL(cmd_parms *cmd, void *dummy, const char *parameter) { int tmp; sscanf(parameter, "%d", &tmp); if (tmp < 1 || tmp > 600) tmp = 60; /* default to 60 second */ RescueIdleTTL = tmp; return NULL; } static const char *setOriginIdleTTL(cmd_parms *cmd, void *dummy, const char *parameter) { int tmp; sscanf(parameter, "%d", &tmp); if (tmp < 1 || tmp > 600) tmp = 60; /* default to 60 second */ OriginIdleTTL = tmp; return NULL; } static const char *setPastOriginTTL(cmd_parms *cmd, void *dummy, const char *parameter) { sscanf(parameter, "%d", &PastOriginTTL); return NULL; } static const char *setSrvloc(cmd_parms *cmd, void *dummy, const char *parameter) { // We regard "Off" and "0" as "Off", and anything else as "On" if (!strcasecmp(parameter, "off") || !strcmp(parameter, "0")) { srvloc_set = 0; } else { srvloc_set = 1; } return NULL; } static const char *setSrvlocServer(cmd_parms *cmd, void *dummy, const char *parameter) { SrvlocServerSet = 1; sprintf(SrvlocServer, "%s", parameter); return NULL; } static const char *setSrvlocPort(cmd_parms *cmd, void *dummy, const char *parameter) { sscanf(parameter, "%d", &SrvlocPort); return NULL; } static const char *setDiscoverTTL(cmd_parms *cmd, void *dummy, const char *parameter) { sscanf(parameter, "%d", &DiscoverTTL); return NULL; } static const char *setRegisterTTL(cmd_parms *cmd, void *dummy, const char *parameter) { sscanf(parameter, "%d", &RegisterTTL); return NULL; } static const char *setRedirectOverhead(cmd_parms *cmd, void *dummy, const char *parameter) { int tmp; sscanf(parameter, "%d", &tmp); if (tmp < 200 || tmp > 500) tmp = 358; /* default to 358 bytes */ RedirectOverhead = tmp; return NULL; } static const char *setAccountingScale(cmd_parms *cmd, void *dummy, const char *parameter) { float tmp; sscanf(parameter, "%f", &tmp); if (tmp < 0.5 || tmp > 0.9) tmp = 0.8; /* defalut to 0.8 */ AccountingScale = tmp; return NULL; } static const char *setDnsUpdateServer(cmd_parms *cmd, void *dummy, const char *parameter) { ddns_server_set = 1; sprintf(ddns_server, "%s", parameter); return NULL; } static const char *setAliasBase(cmd_parms *cmd, void *dummy, const char *parameter) { int i; my_aliasbase_set = 1; sprintf(my_aliasbase, "%s", parameter); i = strlen(my_aliasbase); while (i>0 && my_aliasbase[i-1] == '.') { my_aliasbase[i-1] = 0; // remove trailing dots i = strlen(my_aliasbase); } return NULL; } static const char *setAliasPoolSize(cmd_parms *cmd, void *dummy, const char *parameter) { sscanf(parameter, "%d", &alias_pool_size); if (alias_pool_size < 1 || alias_pool_size > 10) alias_pool_size = 3; return NULL; } static const char *setAliasRegNum(cmd_parms *cmd, void *dummy, const char *parameter) { sscanf(parameter, "%d", &alias_reg_num); return NULL; } static const char *setAliasAllocNum(cmd_parms *cmd, void *dummy, const char *parameter) { sscanf(parameter, "%d", &alias_alloc_num); return NULL; } static const char *setDnsRoundRobin(cmd_parms *cmd, void *dummy, const char *parameter) { // We regard "Off" and "0" as "Off", and anything else as "On" if (!strcasecmp(parameter, "off") || !strcmp(parameter, "0")) { dns_rr_set = 0; } else { dns_rr_set = 1; } return NULL; } static const char *setDnsStaticName(cmd_parms *cmd, void *dummy, const char *parameter) { my_staticname_set = 1; sprintf(my_staticname, "%s", parameter); return NULL; } static const char *setTsig(cmd_parms *cmd, void *dummy, const char *parameter) { // We regard "Off" and "0" as "Off", and anything else as "On" if (!strcasecmp(parameter, "off") || !strcmp(parameter, "0")) { dns_tsig_set = 0; } else { dns_tsig_set = 1; } return NULL; } static const char *setTsigKey(cmd_parms *cmd, void *dummy, const char *parameter) { my_tsigkey_set = 1; sprintf(my_tsigkey, "%s", parameter); return NULL; } static const char *setTsigSecret(cmd_parms *cmd, void *dummy, const char *parameter) { my_tsigsecret_set = 1; sprintf(my_tsigsecret, "%s", parameter); return NULL; } static const char *setTsigUpdateScript(cmd_parms *cmd, void *dummy, const char *parameter) { ddns_script_set = 1; sprintf(ddns_script, "%s", parameter); return NULL; } static const char *setScriptRoot(cmd_parms *cmd, void *dummy, const char *parameter) { ScriptRootSet = 1; sprintf(ScriptRoot, "%s", parameter); return NULL; } static const char *setQCacheServer(cmd_parms *cmd, void *dummy, const char *parameter) { QCacheServerSet = 1; sprintf(QCacheServer, "%s", parameter); return NULL; } static const char *setQCacheTTL(cmd_parms *cmd, void *dummy, const char *parameter) { sscanf(parameter, "%d", &QCacheTTL); return NULL; } static const command_rec cmds[] = { AP_INIT_TAKE1("DotsCommunityType", setCommunityType, NULL, RSRC_CONF, "Type of mutual-aid community"), AP_INIT_TAKE1("DotsDotsdPidFile", setDotsdPidFile, NULL, RSRC_CONF, "Dotsd pid file name"), AP_INIT_TAKE1("DotsControlInterval", setControlInterval, NULL, RSRC_CONF, "Interval for performing control adjustments (in seconds)"), AP_INIT_TAKE1("DotsMaxDataRate", setMaxDataRate, NULL, RSRC_CONF, "Max data rate (in kB/second) for outbond HTTP traffic"), AP_INIT_TAKE1("DotsLinkUpperThreshold", setLinkUpperThreshold, NULL, RSRC_CONF, "Link Upper Threshold"), AP_INIT_TAKE1("DotsLinkLowerThreshold", setLinkLowerThreshold, NULL, RSRC_CONF, "Link Lower Threshold"), AP_INIT_TAKE1("DotsCpuMonitor", setCpuMonitor, NULL, RSRC_CONF, "CPU Monitor: On/Off"), AP_INIT_TAKE1("DotsCpuUpperThreshold", setCpuUpperThreshold, NULL, RSRC_CONF, "CPU Upper Threshold"), AP_INIT_TAKE1("DotsCpuLowerThreshold", setCpuLowerThreshold, NULL, RSRC_CONF, "CPU Lower Threshold"), AP_INIT_TAKE1("DotsRescueIdleTTL", setRescueIdleTTL, NULL, RSRC_CONF, "Rescue server idle TTL default to 300"), AP_INIT_TAKE1("DotsOriginIdleTTL", setOriginIdleTTL, NULL, RSRC_CONF, "Origin server idle TTL default to 300"), AP_INIT_TAKE1("DotsPastOriginTTL", setPastOriginTTL, NULL, RSRC_CONF, "Past origin server TTL default to 3600"), AP_INIT_TAKE1("DotsSrvloc", setSrvloc, NULL, RSRC_CONF, "Whether to enable service discovery"), AP_INIT_TAKE1("DotsSrvlocServer", setSrvlocServer, NULL, RSRC_CONF, "FQDN or IP address of mSLP DA"), AP_INIT_TAKE1("DotsSrvlocPort", setSrvlocPort, NULL, RSRC_CONF, "Port number of mSLP DA"), AP_INIT_TAKE1("DotsDiscoverTTL", setDiscoverTTL, NULL, RSRC_CONF, "TTL for UA to discover, defalut to 120"), AP_INIT_TAKE1("DotsRegisterTTL", setRegisterTTL, NULL, RSRC_CONF, "TTL for SA to register, default to 120"), AP_INIT_TAKE1("DotsRedirectOverhead", setRedirectOverhead, NULL, RSRC_CONF, "TCP/IP/Ethernet header overhead for each HTTP redirect"), AP_INIT_TAKE1("DotsAccountingScale", setAccountingScale, NULL, RSRC_CONF, "Accounting scale for HTTP redirects"), AP_INIT_TAKE1("DotsDnsUpdateServer", setDnsUpdateServer, NULL, RSRC_CONF, "DNS server for sending updates"), AP_INIT_TAKE1("DotsAliasBase", setAliasBase, NULL, RSRC_CONF, "Base name used for constructing aliases"), AP_INIT_TAKE1("DotsAliasPoolSize", setAliasPoolSize, NULL, RSRC_CONF, "Max number of registered but unused aliases, default 0"), AP_INIT_TAKE1("DotsAliasRegNum", setAliasRegNum, NULL, RSRC_CONF, "Max registered alias sequence number, 0 for none"), AP_INIT_TAKE1("DotsAliasAllocNum", setAliasAllocNum, NULL, RSRC_CONF, "Max allocated alias sequence number, 0 for none"), AP_INIT_TAKE1("DotsDnsRoundRobin", setDnsRoundRobin, NULL, RSRC_CONF, "DNS round robin: On/Off"), AP_INIT_TAKE1("DotsDnsStaticName", setDnsStaticName, NULL, RSRC_CONF, "A static DNS name is needed if DNS round robin is on"), AP_INIT_TAKE1("DotsTsig", setTsig, NULL, RSRC_CONF, "Whether use TSIG-signed update"), AP_INIT_TAKE1("DotsTsigKey", setTsigKey, NULL, RSRC_CONF, "TSIG key name"), AP_INIT_TAKE1("DotsTsigSecret", setTsigSecret, NULL, RSRC_CONF, "TSIG secret"), AP_INIT_TAKE1("DotsTsigUpdateScript", setTsigUpdateScript, NULL, RSRC_CONF, "DNS tsig update script file"), AP_INIT_TAKE1("DotsScriptRoot", setScriptRoot, NULL, RSRC_CONF, "Root directory of PHP scripts"), AP_INIT_TAKE1("DotsQCacheServer", setQCacheServer, NULL, RSRC_CONF, "Server of query result cache"), AP_INIT_TAKE1("DotsQCacheTTL", setQCacheTTL, NULL, RSRC_CONF, "TTL for query result cache"), {NULL} }; module AP_MODULE_DECLARE_DATA dots_module = { STANDARD20_MODULE_STUFF, NULL, /* per-directory config creator */ NULL, /* dir config merger */ NULL, /* server config creator */ NULL, /* server config merger */ cmds, /* command table */ register_hooks /* set up other request processing hooks */ };