NSAPI_PUBLIC cache_t * cache_create(unsigned int cache_max, unsigned int hash_size, public_cache_functions_t *fnlist) { cache_t *newentry; unsigned int index; SOLARIS_PROBE(cache_create_start, "cache"); NS_ASSERT(cache_max > 0); NS_ASSERT(hash_size > 0); NS_ASSERT(fnlist); if ( (newentry = (cache_t *)PERM_MALLOC(sizeof(cache_t))) == NULL) { ereport(LOG_FAILURE, XP_GetAdminStr(DBT_insufficientMemoryToCreateHashTa_)); SOLARIS_PROBE(cache_create_end, "cache"); return NULL; } #ifdef CACHE_DEBUG newentry->magic = CACHE_MAGIC; #endif newentry->cache_size = 0; newentry->hash_size = hash_size; newentry->max_size = cache_max; newentry->insert_ok = 0; newentry->insert_fail = 0; newentry->deletes = 0; newentry->cache_hits = 0; newentry->cache_lookups = 0; newentry->lock = crit_init(); newentry->virtual_fn = fnlist; if ( (newentry->table = (cache_entry_t **) PERM_MALLOC((sizeof(cache_entry_t *)) * (newentry->hash_size))) == NULL) { ereport(LOG_FAILURE, XP_GetAdminStr(DBT_insufficientMemoryToCreateHashTa_1)); SOLARIS_PROBE(cache_create_end, "cache"); return NULL; } for (index=0; index<newentry->hash_size; index++) newentry->table[index] = NULL; newentry->mru_head = newentry->lru_head = NULL; #ifdef IRIX newentry->fast_mode = 0; newentry->garbage_list_head = NULL; newentry->gc_time = 0; #endif if (!cache_crit) cache_crit = crit_init(); crit_enter(cache_crit); newentry->next = cache_list; cache_list = newentry; crit_exit(cache_crit); SOLARIS_PROBE(cache_create_end, "cache"); return newentry; }
Session *session_alloc(SYS_NETFD csd, struct sockaddr_in *sac) { NSAPISession *nsn = (NSAPISession *)PERM_MALLOC(sizeof(NSAPISession)); nsn->thread_data = NULL; nsn->httpfilter = NULL; nsn->input_done = PR_FALSE; nsn->input_os_pos = 0; nsn->exec_rq = NULL; nsn->filter_rq = NULL; nsn->session_clone = PR_FALSE; nsn->received = 0; nsn->transmitted = 0; Session *ns = &nsn->sn; ns->csd = csd; ns->iaddr = sac->sin_addr; ns->csd_open = 1; ns->fill = 0; ns->ssl = 0; // mark SSL off by default ns->clientauth = 0; // mark clientauth off by default ns->subject = NULL; ns->pr_local_addr = NULL; ns->pr_client_addr = NULL; return ns; }
NSAPI_PUBLIC int ACL_ExprAddArg( NSErr_t *errp, ACLExprHandle_t *expr, const char *arg ) { if ( expr == NULL ) return(ACLERRUNDEF); if (expr->expr_argv == NULL) expr->expr_argv = (char **) PERM_MALLOC( 2 * sizeof(char *) ); else expr->expr_argv = (char **) PERM_REALLOC( expr->expr_argv, (expr->expr_argc+2) * sizeof(char *) ); if (expr->expr_argv == NULL) return(ACLERRNOMEM); expr->expr_argv[expr->expr_argc] = PERM_STRDUP( arg ); if (expr->expr_argv[expr->expr_argc] == NULL) return(ACLERRNOMEM); expr->expr_argc++; expr->expr_argv[expr->expr_argc] = NULL; return(0); }
NSAPI_PUBLIC int INTnet_buffer_input(SYS_NETFD sd, int sz) { NetLayer *nl = _netlayer_find(sd); if (nl) { if (sz > nl->maxsize) { void *inbuf = PERM_REALLOC(nl->inbuf, sz); if (!inbuf) return -1; nl->inbuf = (char *) inbuf; } nl->maxsize = sz; return 0; } nl = (NetLayer *) PERM_MALLOC(sizeof(NetLayer)); if (!nl) return -1; nl->pos = 0; nl->cursize = 0; nl->maxsize = sz; nl->inbuf = (char *) PERM_MALLOC(sz); if (!nl->inbuf) { PERM_FREE(nl); return -1; } PRFileDesc *layer = PR_CreateIOLayerStub(_netlayer_identity, &_netlayer_methods); if (!layer) { PERM_FREE(nl->inbuf); PERM_FREE(nl); return -1; } layer->secret = (PRFilePrivate *) nl; PRStatus rv = PR_PushIOLayer(sd, PR_NSPR_IO_LAYER, layer); if (rv != PR_SUCCESS) { PR_Close(layer); return -1; } return 0; }
NSAPI_PUBLIC int ACL_ListPostParseForAuth(NSErr_t *errp, ACLListHandle_t *acl_list ) { ACLHandle_t *acl; ACLWrapper_t *wrap; ACLExprHandle_t *expr; char *method; char *database; int rv; ACLDbType_t *dbtype; ACLMethod_t *methodtype; if ( acl_list == NULL ) return(0); // for all ACLs for ( wrap = acl_list->acl_list_head; wrap; wrap = wrap->wrap_next ) { acl = wrap->acl; if ( acl == NULL ) continue; // for all expressions with the ACL for ( expr = acl->expr_list_head; expr; expr = expr->expr_next ) { if ( expr->expr_type != ACL_EXPR_TYPE_AUTH || expr->expr_auth == NULL) continue; // get method attribute - this is a name now rv = PListGetValue(expr->expr_auth, ACL_ATTR_METHOD_INDEX, (void **) &method, NULL); if ( rv >= 0 ) { methodtype = (ACLMethod_t *)PERM_MALLOC(sizeof(ACLMethod_t)); rv = ACL_MethodFind(errp, method, methodtype); if (rv < 0) { nserrGenerate(errp, ACLERRUNDEF, ACLERR3800, ACL_Program, 3, acl->tag, "method", method); PERM_FREE(methodtype); return(ACLERRUNDEF); } // replace it with a method type rv = PListSetValue(expr->expr_auth, ACL_ATTR_METHOD_INDEX, methodtype, NULL); if ( rv < 0 ) { nserrGenerate(errp, ACLERRNOMEM, ACLERR3810, ACL_Program, 0); return(ACLERRNOMEM); } PERM_FREE(method); } } } return(0); }
/* LASIpTreeAlloc * Malloc a node and set the actions to LAS_EVAL_FALSE */ static LASIpTree_t * LASIpTreeAllocNode(NSErr_t *errp) { LASIpTree_t *newnode; newnode = (LASIpTree_t *)PERM_MALLOC(sizeof(LASIpTree_t)); if (newnode == NULL) { nserrGenerate(errp, ACLERRNOMEM, ACLERR5000, ACL_Program, 1, XP_GetAdminStr(DBT_lasiptreeallocNoMemoryN_)); return NULL; } newnode->action[0] = (LASIpTree_t *)LAS_EVAL_FALSE; newnode->action[1] = (LASIpTree_t *)LAS_EVAL_FALSE; return newnode; }
// // One-time routine for poll manager initialization. // PRStatus PollManager::Initialize(PRUint32 maxKeepAlives, PRUint32 numThreads, PRIntervalTime pollInterval) { // Number of keepalive connections we will support this->maxKeepAlives_ = maxKeepAlives; // Number of poll threads this->numThreads_ = numThreads; log_ereport(LOG_VERBOSE, XP_GetAdminStr(DBT_PollManagerMaxKAConn), this->maxKeepAlives_); threads_ = (KAPollThread **)PERM_MALLOC(numThreads_ * sizeof(KAPollThread *)); // Figure out the number of keep-alive connections each keep-alive poll // thread should accomodate then add some margin for acceptors and newly // accepted connections PRInt32 numDescPerThread = (maxKeepAlives_ + numThreads_) / numThreads_ + 1; PRUint32 i; PRStatus status = PR_SUCCESS; if (threads_) { // Initialize the poll arrays and start those poll threads. for (i = 0; i < this->numThreads_; i++) { KAPollThread *thr = new KAPollThread(this, numDescPerThread, pollInterval); if (thr) status = thr->start(PR_GLOBAL_BOUND_THREAD, PR_UNJOINABLE_THREAD); if (!thr || (status == PR_FAILURE)) { if (thr) delete thr; break; } threads_[i] = thr; } } if (i != this->numThreads_) { Freeup(); return PR_FAILURE; } else return PR_SUCCESS; }
/* * Gets a name list of consisting of all ACL names for input list. * * Input: * acl_list an ACL List handle * name_list pointer to a list of string pointers * Returns: * 0 success * < 0 failure */ NSAPI_PUBLIC int ACL_ListGetNameList(NSErr_t *errp, ACLListHandle_t *acl_list, char ***name_list) { const int block_size = 50; ACLWrapper_t *wrapper; int list_index; int list_size; char **tmp_list; char **local_list; const char *name; if ( acl_list == NULL ) return(ACLERRUNDEF); list_size = block_size; local_list = (char **) PERM_MALLOC(sizeof(char *) * list_size); if ( local_list == NULL ) return(ACLERRNOMEM); list_index = 0; local_list[list_index] = NULL; for ( wrapper = acl_list->acl_list_head; wrapper != NULL; wrapper = wrapper->wrap_next ) { if ( wrapper->acl->tag ) name = wrapper->acl->tag; else name = "noname"; if ( list_index + 2 > list_size ) { list_size += block_size; tmp_list = (char **) PERM_REALLOC(local_list, sizeof(char *) * list_size); if ( tmp_list == NULL ) { ACL_NameListDestroy(errp, local_list); return(ACLERRNOMEM); } local_list = tmp_list; } local_list[list_index] = PERM_STRDUP(name); if ( local_list[list_index] == NULL ) { ACL_NameListDestroy(errp, local_list); return(ACLERRNOMEM); } list_index++; local_list[list_index] = NULL; } *name_list = local_list; return(0); }
NSAPI_PUBLIC FSMUTEX fsmutex_init(char *name, int number, int flags) { fsmutex_s *ret = (fsmutex_s *) PERM_MALLOC(sizeof(fsmutex_s)); ret->flags = flags; if(_fsmutex_create(ret, name, number) == -1) { PERM_FREE(ret); return NULL; } #ifdef THREAD_ANY if(flags & FSMUTEX_NEEDCRIT) ret->crit = crit_init(); #endif return (FSMUTEX) ret; }
NSAPI_PUBLIC dns_cache_entry_t * dns_cache_insert(char *host, unsigned int ip, unsigned int verified) { dns_cache_entry_t *newentry; if ( !dns_cache || !ip) { return NULL; } if ( (newentry = (dns_cache_entry_t *)PERM_MALLOC(sizeof(dns_cache_entry_t))) == NULL) { ereport(LOG_FAILURE, XP_GetAdminStr(DBT_dnsCacheInsertErrorAllocatingEnt_)); goto error; } if (host) { if ( (newentry->host = PERM_STRDUP(host)) == NULL) { ereport(LOG_FAILURE, XP_GetAdminStr(DBT_dnsCacheInsertMallocFailure_)); goto error; } } else newentry->host = NULL; #ifdef CACHE_DEBUG newentry->cache.magic = CACHE_ENTRY_MAGIC; #endif newentry->ip = ip; newentry->verified = verified; newentry->last_access = ft_time(); if ( cache_insert_p(dns_cache, &(newentry->cache), (void *)&(newentry->ip), (void *)newentry, &dns_cache_entry_functions) < 0) { /* Not a bad error; it just means the cache is full */ goto error; } return newentry; error: if (newentry) { if (newentry->host) PERM_FREE(newentry->host); PERM_FREE(newentry); } return NULL; }
cache_entry_t * cache_create_entry(void) { cache_entry_t *newentry; SOLARIS_PROBE(cache_create_entry_start, "cache"); if ( (newentry = (cache_entry_t *)PERM_MALLOC(sizeof(cache_entry_t))) == NULL) { ereport(LOG_FAILURE, XP_GetAdminStr(DBT_unableToAllocateHashEntry_)); SOLARIS_PROBE(cache_create_entry_end, "cache"); return NULL; } #ifdef CACHE_DEBUG newentry->magic = CACHE_ENTRY_MAGIC; #endif SOLARIS_PROBE(cache_create_entry_end, "cache"); return newentry; }
static Symbol_t * acl_sym_new(ACLHandle_t *acl) { Symbol_t *sym; /* It's not there, so add it */ sym = (Symbol_t *) PERM_MALLOC(sizeof(Symbol_t)); if ( sym == NULL ) return(NULL); sym->sym_name = PERM_STRDUP(acl->tag); if ( sym->sym_name == NULL ) { PERM_FREE(sym); return(NULL); } sym->sym_type = ACLSYMACL; sym->sym_data = (void *) acl; return(sym); }
/* * LASIpEval * INPUT * attr_name The string "ip" - in lower case. * comparator CMP_OP_EQ or CMP_OP_NE only * attr_pattern A comma-separated list of IP addresses and netmasks * in dotted-decimal form. Netmasks are optionally * prepended to the IP address using a plus sign. E.g. * 255.255.255.0+123.45.67.89. Any byte in the IP address * (but not the netmask) can be wildcarded using "*" * *cachable Always set to ACL_INDEF_CACHABLE * subject Subject property list * resource Resource property list * auth_info The authentication info if any * RETURNS * ret code The usual LAS return codes. */ int LASIpEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, char *attr_pattern, ACLCachable_t *cachable, void **LAS_cookie, PList_t subject, PList_t resource, PList_t auth_info, PList_t global_auth) { void *pip; int retcode; LASIpContext_t *context; int rv; #ifndef UTEST *cachable = ACL_INDEF_CACHABLE; #endif if (strcmp(attr_name, "ip") != 0) { nserrGenerate(errp, ACLERRINVAL, ACLERR5200, ACL_Program, 2, XP_GetAdminStr(DBT_lasIpBuildReceivedRequestForAttr_), attr_name); return LAS_EVAL_INVALID; } if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { nserrGenerate(errp, ACLERRINVAL, ACLERR5210, ACL_Program, 2, XP_GetAdminStr(DBT_lasipevalIllegalComparatorDN_), comparator_string(comparator)); return LAS_EVAL_INVALID; } /* GET THE IP ADDR FROM THE SESSION CONTEXT AND STORE IT IN THE * VARIABLE ip. */ #ifndef UTEST rv = ACL_GetAttribute(errp, ACL_ATTR_IP, &pip, subject, resource, auth_info, global_auth); PRNetAddr *ip_addr = (PRNetAddr *)pip; if (rv != LAS_EVAL_TRUE) { if (subject || resource) { /* Don't ereport if called from ACL_CachableAclList */ char rv_str[16]; sprintf(rv_str, "%d", rv); nserrGenerate(errp, ACLERRINVAL, ACLERR5220, ACL_Program, 2, XP_GetAdminStr(DBT_lasipevalUnableToGetSessionAddre_), rv_str); } ereport(LOG_VERBOSE, "ERROR Attribute Getter for ACL_ATTR_IP returned error %d.", rv); return LAS_EVAL_FAIL; } #else PRNetAddr *ip_addr = LASIpGetIpv6(); if (ip_addr == NULL) { ereport(LOG_VERBOSE, "ERROR IP Address returned from LASIpGetIpv6() is NULL."); return LAS_EVAL_FAIL; } #endif int ipVersion = PR_AF_INET; if (ip_addr->ipv6.family == PR_AF_INET6) { ipVersion = PR_AF_INET6; } else if (ip_addr->inet.family != PR_AF_INET) { PR_ASSERT(0); } /* If this is the first time through, build the pattern tree first. */ if (*LAS_cookie == NULL) { if (strcspn(attr_pattern, "0123456789.*ABCDEF:abcdef,+ \t")) { nserrGenerate(errp,ACLERRINVAL,ACLERR5120,ACL_Program,2, XP_GetAdminStr(DBT_lasIpIncorrentIPPattern),attr_pattern); return LAS_EVAL_INVALID; } ACL_CritEnter(); context = (LASIpContext *) *LAS_cookie; if (*LAS_cookie == NULL) { /* must check again */ *LAS_cookie = context = (LASIpContext_t *)PERM_MALLOC(sizeof(LASIpContext_t)); if (context == NULL) { nserrGenerate(errp, ACLERRNOMEM, ACLERR5230, ACL_Program, 1, XP_GetAdminStr(DBT_lasipevalUnableToAllocateContext_)); ACL_CritExit(); return LAS_EVAL_FAIL; } context->treetop = NULL; retcode = LASIpBuild(errp, attr_pattern, &context->treetop, ipVersion); if ((retcode == PR_AF_INET6) || (retcode == PR_AF_INET) || (retcode == (PR_AF_INET6+PR_AF_INET))) context->ipVersion = retcode; else { ACL_CritExit(); return (retcode); } } ACL_CritExit(); } else context = (LASIpContext *) *LAS_cookie; return traverseTreeAndCompareIPs(errp, ip_addr, context, attr_pattern, ipVersion, (comparator == CMP_OP_EQ)?1:0); }
/* * LASDnsEval * INPUT * attr_name The string "dns" - in lower case. * comparator CMP_OP_EQ or CMP_OP_NE only * attr_pattern A comma-separated list of DNS names * Any segment(s) in a DNS name can be wildcarded using * "*". Note that this is not a true Regular Expression * form. * *cachable Always set to ACL_INDEF_CACHE * subject Subject property list * resource Resource property list * auth_info Authentication info, if any * RETURNS * ret code The usual LAS return codes. */ int LASDnsEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, char *attr_pattern, ACLCachable_t *cachable, void **LAS_cookie, PList_t subject, PList_t resource, PList_t auth_info, PList_t global_auth) { int result; int aliasflg; char *my_dns; LASDnsContext_t *context = NULL; int rv; *cachable = ACL_INDEF_CACHABLE; if (strcmp(attr_name, "dns") == 0) { /* Enable aliasflg for "dns", which allows "dns" hostname to look up * DSN hash table using the primary hostname. */ aliasflg = 1; } else if (strcmp(attr_name, "dnsalias") == 0) { aliasflg = 1; } else { nserrGenerate(errp, ACLERRINVAL, ACLERR4800, ACL_Program, 2, XP_GetAdminStr(DBT_lasDnsBuildReceivedRequestForAtt_), attr_name); return LAS_EVAL_INVALID; } if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { nserrGenerate(errp, ACLERRINVAL, ACLERR4810, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsevalIllegalComparatorDN_), comparator_string(comparator)); return LAS_EVAL_INVALID; } /* If this is the first time through, build the pattern tree first. */ if (*LAS_cookie == NULL) { ACL_CritEnter(); if (*LAS_cookie == NULL) { /* Must check again */ *LAS_cookie = context = (LASDnsContext_t *)PERM_MALLOC(sizeof(LASDnsContext_t)); if (context == NULL) { nserrGenerate(errp, ACLERRNOMEM, ACLERR4820, ACL_Program, 1, XP_GetAdminStr(DBT_lasdnsevalUnableToAllocateContex_)); ACL_CritExit(); return LAS_EVAL_FAIL; } context->Table = NULL; if (LASDnsBuild(errp, attr_pattern, context, aliasflg) == LAS_EVAL_INVALID) { /* Error is already printed in LASDnsBuild */ ACL_CritExit(); return LAS_EVAL_FAIL; } /* After this line, it is assured context->Table is not NULL. */ } else { context = (LASDnsContext *) *LAS_cookie; } ACL_CritExit(); } else { ACL_CritEnter(); context = (LASDnsContext *) *LAS_cookie; ACL_CritExit(); } /* Call the DNS attribute getter */ #ifdef UTEST LASDnsGetDns(&my_dns); /* gets stuffed on return */ #else rv = ACL_GetAttribute(errp, ACL_ATTR_DNS, (void **)&my_dns, subject, resource, auth_info, global_auth); if (rv != LAS_EVAL_TRUE) { if (subject || resource) { char rv_str[16]; /* Don't ereport if called from ACL_CachableAclList */ sprintf(rv_str, "%d", rv); nserrGenerate(errp, ACLERRINVAL, ACLERR4830, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsevalUnableToGetDnsErrorDN_), rv_str); } return LAS_EVAL_FAIL; } #endif result = LASDnsMatch(my_dns, context); if (comparator == CMP_OP_NE) { if (result == LAS_EVAL_FALSE) return LAS_EVAL_TRUE; else if (result == LAS_EVAL_TRUE) return LAS_EVAL_FALSE; } return (result); }
NSAPI_PUBLIC int ACL_ListPostParseForAuth(NSErr_t *errp, ACLListHandle_t *acl_list ) { ACLHandle_t *acl; ACLWrapper_t *wrap; ACLExprHandle_t *expr; char *method; char *database; int rv; ACLDbType_t *dbtype; ACLMethod_t *methodtype; if ( acl_list == NULL ) return(0); for ( wrap = acl_list->acl_list_head; wrap; wrap = wrap->wrap_next ) { acl = wrap->acl; if ( acl == NULL ) continue; for ( expr = acl->expr_list_head; expr; expr = expr->expr_next ) { if ( expr->expr_type != ACL_EXPR_TYPE_AUTH || expr->expr_auth == NULL) continue; rv = PListGetValue(expr->expr_auth, ACL_ATTR_METHOD_INDEX, (void **) &method, NULL); if ( rv >= 0 ) { methodtype = (ACLMethod_t *)PERM_MALLOC(sizeof(ACLMethod_t)); rv = ACL_MethodFind(errp, method, methodtype); if (rv) { nserrGenerate(errp, ACLERRUNDEF, ACLERR3800, ACL_Program, 3, acl->tag, "method", method); PERM_FREE(methodtype); return(ACLERRUNDEF); } rv = PListSetValue(expr->expr_auth, ACL_ATTR_METHOD_INDEX, methodtype, NULL); if ( rv < 0 ) { nserrGenerate(errp, ACLERRNOMEM, ACLERR3810, ACL_Program, 0); return(ACLERRNOMEM); } PERM_FREE(method); } rv = PListGetValue(expr->expr_auth, ACL_ATTR_DATABASE_INDEX, (void **) &database, NULL); if (rv < 0) continue; /* The following function lets user use databases which are * not registered by their administrators. This also fixes * the backward compatibility. */ dbtype = (ACLDbType_t *)PERM_MALLOC(sizeof(ACLDbType_t)); rv = ACL_RegisterDbFromACL(errp, (const char *) database, dbtype); if (rv < 0) { nserrGenerate(errp, ACLERRUNDEF, ACLERR3800, ACL_Program, 3, acl->tag, "database", database); PERM_FREE(dbtype); return(ACLERRUNDEF); } rv = PListInitProp(expr->expr_auth, ACL_ATTR_DBTYPE_INDEX, ACL_ATTR_DBTYPE, dbtype, NULL); if ( rv < 0 ) { nserrGenerate(errp, ACLERRNOMEM, ACLERR3810, ACL_Program, 0); return(ACLERRNOMEM); } } } return(0); }
/** ************************************************************************** * check-request-limits SAF * * See top of this file for the pb parameters this SAF can consume and the * defaults for each. * * This SAF counts requests per interval for the given monitor/bucket. If * the request/sec in an interval exceeds the max-rps given then it returns * REQ_ABORTED for this and all subsequent matching requests in the current * interval. After the next interval request rate recomputation the request * limiting may be discontinued if the conditions of 'continue' are met. * * Separately, concurrent requests for the same bucket may be limited to * the given number if max-connections is given. * * On the next request after purge_timeout, a purge sweep of the buckets is * done, deleting any entries which have not seen any recomputes in the * purge interval (unless timeout disabled by setting it to zero). * * For more information, refer to the WS7.0 security functional spec at: * http://sac.eng/arc/WSARC/2004/076/ * * For params: http://docs.sun.com/source/817-1835-10/npgmysaf.html#wp14889 * For returns: http://docs.sun.com/source/817-1835-10/npgmysaf.html#wp14969 * * This returns: * REQ_NOACTION: If the request can go on. * REQ_ABORTED: If the request has hit limits and is not to be processed. * Return code is set to 'error' param (default 503). * */ int check_request_limits(pblock *pb, Session *sn, Request *rq) { const char * param; int response = REQ_NOACTION; if (rq->rq_attr.req_restarted) { // Do not count restarted requests as new requests for the // purpose of reqlimit accounting as it is just one client // request. (This is particularly important for // max-connections since processing restarts would cause the // conc counter to increase more than once for a given request // but only decrease once at the end). return response; } time_t time_now = rq->req_start; assert (time_now != NULL); // Get max-rps int max_rps = 0; param = pblock_findval(MAXRPS_PARAM, pb); if (param) { max_rps = atoi(param); } // Get max-connections int conc = 0; param = pblock_findval(CONC_PARAM, pb); if (param) { conc = atoi(param); } // We must have at least max-rps or max-connections, otherwise can't // do anything meaningful here if (!max_rps && !conc) { log_error(LOG_MISCONFIG, "check-request-limits", sn, rq, XP_GetAdminStr(DBT_reqlimitCantWork)); return response; } // Decide bucket name; if none, we use the anonymous bucket anon_bucket bucket_info * bucket = NULL; const char * bucket_name = pblock_findval(MONITOR_PARAM, pb); if (!bucket_name) { bucket = &anon_bucket; } // interval (in seconds), or use default int interval = DEFAULT_INTERVAL; param = pblock_findval(INTERVAL_PARAM, pb); if (param) { interval = atoi(param); } // Check continue, or use default int cont = DEFAULT_CONTINUE; param = pblock_findval(CONTINUE_PARAM, pb); if (param) { if (!strcmp(CONT_THRESHOLD_VAL, param)) { cont = CONT_THRESHOLD; } else if (!strcmp(CONT_SILENCE_VAL, param)) { cont = CONT_SILENCE; } else { // Log config error but continue since we have default log_error(LOG_MISCONFIG, "check-request-limits", sn, rq, XP_GetAdminStr(DBT_reqlimitBadContinue)); } } //----- START_CRIT ------------------------------ crit_enter(reqlimit_crit); if (purge_timeout && (time_now > next_timeout)) { // run purge if needed handle_purge_timeout(time_now); } // If using anon bucket we already have reference to it, otherwise need // to go find it in hashtable (and if not found, create one) if (!bucket) { bucket = (bucket_info *)PL_HashTableLookup(hashtable, bucket_name); if (!bucket) { // Need to create new entry for this one log_error(LOG_VERBOSE, "check-request-limits", sn, rq, "creating new entry for [%s]", bucket_name); bucket = (bucket_info *)PERM_MALLOC(sizeof(bucket_info)); bucket->count = 1; bucket->time = time_now + interval; bucket->state = REQ_NOACTION; bucket->conc = 0; // handle conc case on initial? PL_HashTableAdd(hashtable, (const void *)PERM_STRDUP(bucket_name), (void *)bucket); // Since it is the first request, no need to check more crit_exit(reqlimit_crit); return response; } } // If we are doing max-rps limiting then handle it otherwise don't bother if (max_rps) { bucket->count++; if (time_now > bucket->time) { // Interval or more has passed, time to recompute and recheck int time_interval = time_now - bucket->time + interval; int rps = bucket->count / time_interval; log_error(LOG_VERBOSE, "check-request-limits", sn, rq, "bucket [%s] %d req/s (%d req in %d sec)", bucket_name ? bucket_name: "", rps, bucket->count, time_interval); if (rps > max_rps) { // Start limiting bucket->state = REQ_ABORTED; log_error(LOG_WARN, "check-request-limits", sn, rq, XP_GetAdminStr(DBT_reqlimitAboveMaxRPS), rps, max_rps, bucket_name ? bucket_name: ""); } else { // Reset state if we're under threshhold or if this is first // hit (which means an interval with zero hits has already // passed) if ((cont == CONT_THRESHOLD) || (bucket->count == 1)) { bucket->state = REQ_NOACTION; } } // Prepare for next interval by resetting count and recompute time bucket->count = 0; bucket->time = time_now + interval; } response = bucket->state; } // If decision to reject already done, no need to check or increase conc // since this is not getting processed anyway. Otherwise, do it if needed. if (conc && response != REQ_ABORTED) { if (bucket->conc >= conc) { // Note that this reject is based on conditions at this instant // instead of over an interval, so is independent of bucket->state response = REQ_ABORTED; } else { bucket->conc++; // This queues up a call to fn associated with req_cleanup // (here, reqlimit_conc_done) to be called after request is done request_set_data(rq, req_cleanup, bucket); } } crit_exit(reqlimit_crit); //----- END_CRIT ------------------------------ if (response == REQ_NOACTION) { return REQ_NOACTION; } // abort this request int err = DEFAULT_ERROR; param = pblock_findval(ERROR_PARAM, pb); if (param) { err = atoi(param); } protocol_status(sn, rq, err, NULL); log_error(LOG_VERBOSE, "check-request-limits", sn, rq, "Rejecting request matching bucket [%s] with status %d", bucket_name ? bucket_name: "", err); return response; }