int send_data(const char *msg, Session *sn, Request *rq) { int len = msg != NULL?strlen(msg):0; int retVal = REQ_ABORTED; if(len > 0) { char buf[50]; buf[0] = '\0'; sprintf(buf, "%d", len); protocol_status(sn, rq, PROTOCOL_OK, NULL); param_free(pblock_remove("content-type", rq->srvhdrs)); pblock_nvinsert("content-type", "text/html", rq->srvhdrs); pblock_nvinsert("content-length", buf, rq->srvhdrs); /* Send the headers to the client*/ protocol_start_response(sn, rq); /* Write the output using net_write*/ if (IO_ERROR == net_write(sn->csd, (char *)msg, len)) { retVal = REQ_EXIT; } else { retVal = net_flush(sn->csd); } } return retVal; }
static int sendResponse(Session *sn, Request *rq, HTTPResponse *resp) { pb_param *pb_entry; /* * collect up the headers */ pb_entry = pblock_remove(CONTENT_TYPE,rq->srvhdrs); /* remove default */ param_free(pb_entry); /* aB. Need to free parameters we remove from pblocks !!! */ st_perform(resp->headers,gethdr,rq); /* * ensure a content length */ if (pblock_findval(CONTENT_LENGTH, rq->srvhdrs) == NULL) { char length[64]; util_itoa(resp->content_length,length); pblock_nvinsert(CONTENT_LENGTH,length, rq->srvhdrs); } protocol_status(sn, rq, resp->status, resp->statusMsg); if (protocol_start_response(sn, rq) == REQ_NOACTION) { WOLog(WO_ERR,"protocol_start_response() returned REQ_NOACTION (!?)"); return REQ_PROCEED; } if (resp->content_length) if (net_write(sn->csd, resp->content, resp->content_length) == IO_ERROR) { WOLog(WO_ERR,"Failed to send content to client"); return REQ_EXIT; } return REQ_PROCEED; }
static int process_new_notification(pblock *param, Session *sn, Request *rq, void* agent_config) { handle_notification(sn, rq, agent_config); /* Use the protocol_status function to set the status of the * response before calling protocol_start_response. */ protocol_status(sn, rq, PROTOCOL_OK, NULL); /* Although we would expect the ObjectType stage to * set the content-type, set it here just to be * completely sure that it gets set to text/html. */ param_free(pblock_remove("content-type", rq->srvhdrs)); pblock_nvinsert("content-type", "text/html", rq->srvhdrs); pblock_nvinsert("content-length", "2", rq->srvhdrs); /* Send the headers to the client*/ protocol_start_response(sn, rq); /* Write the output using net_write*/ if (IO_ERROR == net_write(sn->csd, "OK", 2)) { return REQ_EXIT; } return REQ_PROCEED; }
static int do_deny(Session *sn, Request *rq, am_status_t status) { int retVal = REQ_ABORTED; /* Set the return code 403 Forbidden */ protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL); am_web_log_info("do_redirect() Status code= %s.", am_status_to_string(status)); return retVal; }
int htaccess_evaluate(pblock *pb, Session *sn, Request *rq) { char *p = pblock_findval("path", rq->vars); struct stat *finfo = request_stat_path(p, rq); int isdir = finfo && S_ISDIR(finfo->st_mode); /* some java servlets that are set in rules.properties don't set this */ if(!pblock_findval("ntrans-base", rq->vars)) return REQ_NOACTION; char *methstr = pblock_findval("method", rq->reqpb); int methnum; HttpMethodRegistry& registry = HttpMethodRegistry::GetRegistry(); /* treat HEAD as a GET */ if (!strcasecmp(methstr, "HEAD")) methnum = registry.HttpMethodRegistry::GetMethodIndex("GET"); else methnum = registry.HttpMethodRegistry::GetMethodIndex(methstr); /* if it isn't a method we support, don't allow access */ if (methnum == -1) { protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL); return REQ_ABORTED; } switch(htaccess_evaluate_access(p, isdir, methnum, pb, sn, rq)) { case ACCESS_OK: return REQ_PROCEED; case ACCESS_FORBIDDEN: default: protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL); log_error(LOG_SECURITY, "htaccess-find", sn, rq, "access of %s denied by server configuration.", p); /* fallthrough */ case ACCESS_AUTHFAIL: /* the data structures will be set up by http_auth.c */ return REQ_ABORTED; } }
/** * This function is used for sending a 302 redirect in the browser. */ static void do_url_redirect(Session *sn, Request *rq, char* redirect_url) { /* Set the return code to 302 Redirect */ protocol_status(sn, rq, PROTOCOL_REDIRECT, NULL); /* set the new URL to redirect */ pblock_nvinsert("escape", "no", rq->vars); param_free(pblock_remove("Location", rq->srvhdrs)); pblock_nvinsert("Location", redirect_url, rq->srvhdrs); protocol_start_response(sn, rq); }
int service_reconfig(pblock *pb, Session *sn, Request *rq) { param_free(pblock_remove("content-type", rq->srvhdrs)); pblock_nvinsert("content-type", "text/html", rq->srvhdrs); protocol_status(sn, rq, PROTOCOL_OK, NULL); protocol_start_response(sn, rq); PR_fprintf(sn->csd, "<html><head><title>" "Dynamic Reconfiguration" "</title></head><body>\n" "<H2>Dynamic Reconfiguration</H2>\n" "<p>pid: %d</p>\n", getpid()); // Asynchronous reconfiguration WebServer::RequestReconfiguration(); PR_fprintf(sn->csd, "</body></html>"); return REQ_PROCEED; }
int service_dumpstats(pblock *pb, Session *sn, Request *rq) { char *refresh, *refresh_val = NULL; /* See if client asked for automatic refresh in query string */ if ((refresh = pblock_findval("query", rq->reqpb)) != NULL ) { if (!strncmp("refresh", refresh, 7)) { refresh_val = strchr(refresh, '='); if (refresh_val) refresh_val++; } } param_free(pblock_remove("content-type", rq->srvhdrs)); pblock_nvinsert("content-type", "text/plain", rq->srvhdrs); if (refresh_val) pblock_nvinsert("refresh", refresh_val, rq->srvhdrs); httpfilter_buffer_output(sn, rq, PR_TRUE); protocol_status(sn, rq, PROTOCOL_OK, NULL); protocol_start_response(sn,rq); PR_fprintf(sn->csd, PRODUCT_DAEMON_BIN" pid: %d\n", getpid()); StatsHeaderNode *hdr = StatsManager::getHeader(); if (!hdr) { PR_fprintf(sn->csd, "\nStatistics disabled\n"); return REQ_PROCEED; } int rv = REQ_PROCEED; #ifdef XP_WIN32 rv = write_stats_dump(sn->csd, hdr); #else if (hdr && (hdr->hdrStats.maxProcs == 1)) { rv = write_stats_dump(sn->csd, hdr); } else { StatsManager::serviceDumpStats(sn->csd, NULL); } #endif return rv; }
static int do_redirect(Session *sn, Request *rq, am_status_t status, am_policy_result_t *policy_result, const char *original_url, const char* method, void* agent_config) { int retVal = REQ_ABORTED; char *redirect_url = NULL; const am_map_t advice_map = policy_result->advice_map; am_status_t ret = AM_SUCCESS; ret = am_web_get_url_to_redirect(status, advice_map, original_url, method, AM_RESERVED, &redirect_url, agent_config); if (ret == AM_SUCCESS && redirect_url != NULL) { char *advice_txt = NULL; if (B_FALSE == am_web_use_redirect_for_advice(agent_config) && policy_result->advice_string != NULL) { // Composite advice is sent as a POST ret = am_web_build_advice_response(policy_result, redirect_url, &advice_txt); am_web_log_debug("do_redirect(): policy status=%s,", "response[%s]", am_status_to_string(status), advice_txt); if (ret == AM_SUCCESS) { retVal = send_data(advice_txt, sn, rq); } else { am_web_log_error("do_redirect(): Error while building " "advice response body:%s", am_status_to_string(ret)); retVal = REQ_EXIT; } } else { // No composite advice or composite advice is redirected am_web_log_debug("do_redirect() policy status = %s, ", "redirection URL is %s", am_status_to_string(status), redirect_url); // we need to modify the redirect_url with the policy advice if (B_TRUE == am_web_use_redirect_for_advice(agent_config) && policy_result->advice_string != NULL) { char *redirect_url_with_advice = NULL; ret = am_web_build_advice_redirect_url(policy_result, redirect_url, &redirect_url_with_advice); if (ret == AM_SUCCESS) { redirect_url = redirect_url_with_advice; am_web_log_debug("do_redirect(): policy status=%s, " "redirect url with advice [%s]", am_status_to_string(status), redirect_url); } else { am_web_log_error("do_redirect(): Error while building " "the redirect url with advice:%s", am_status_to_string(ret)); } } /* redirection is enabled by the PathCheck directive */ /* Set the return code to 302 Redirect */ protocol_status(sn, rq, PROTOCOL_REDIRECT, NULL); /* set the new URL to redirect */ //pblock_nvinsert("url", redirect_url, rq->vars); pblock_nvinsert("escape", "no", rq->vars); param_free(pblock_remove("Location", rq->srvhdrs)); pblock_nvinsert("Location", redirect_url, rq->srvhdrs); protocol_start_response(sn, rq); am_web_free_memory(redirect_url); } } else if (ret == AM_NO_MEMORY) { /* Set the return code 500 Internal Server Error. */ protocol_status(sn, rq, PROTOCOL_SERVER_ERROR, NULL); am_web_log_error("do_redirect() Status code= %s.", am_status_to_string(status)); } else { /* Set the return code 403 Forbidden */ protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL); am_web_log_info("do_redirect() Status code= %s.", am_status_to_string(status)); } return retVal; }
PRStatus FcgiParser::parseAuthHeaders(pblock *pb) { Request *rq = request->getOrigRequest(); authHeaderPrefixLen = strlen(AUTH_HEADER_PREFIX); char *s; if(pb) { if((s = pblock_findval("status", pb))) { if((strlen(s) < 3) || (!isdigit(s[0]) || (!isdigit(s[1])) || (!isdigit(s[2])))) { s = NULL; } else { char ch = s[3]; s[3] = '\0'; int statusNum = atoi(s); s[3] = ch; rq->status_num = statusNum; if(statusNum == 200) authorized = PR_TRUE; } } if(!authorized) { protocol_status(request->getOrigSession(), request->getOrigRequest(), PROTOCOL_UNAUTHORIZED, NULL); } else { //retain only the headers starting with "Varaiable-" for (int i = 0; i < pb->hsize; i++) { pb_entry *p = pb->ht[i]; while (p) { const char *name = PL_strdup(p->param->name); const char *value = PL_strdup(p->param->value); if (*name == 'v' && (!PL_strcmp(name, "variable-"))) { pb_param *pparam = pblock_fr((char *)name, pb, PR_TRUE); param_free(pparam); name += authHeaderPrefixLen; // remove the prefix from name if(*name == 'r' && (!PL_strcmp(name, "remote_user"))) { pblock_nvinsert("auth-user", value, rq->vars); } if(*name == 'a' && (!PL_strcmp(name, "auth_type"))) { pblock_nvinsert("auth-type", value, rq->vars); } pblock_nvinsert(name, value, rq->headers); } PL_strfree((char *)name); PL_strfree((char *)value); p = p->next; } //while } //for } //if } else { lastError = INVALID_HTTP_HEADER; request->log(LOG_FAILURE, GetString(DBT_invalid_response)); return PR_FAILURE; } return PR_SUCCESS; }
PRStatus FcgiParser::parseHttpHeader(CircularBuffer& to) { if(!waitingForDataParse) return PR_SUCCESS; const char *data = httpHeader.data(); int len = httpHeader.length(); PRUint8 flag = 0; while(len-- && flag < 2) { switch(*data) { case '\r': break; case '\n': flag++; break; default: flag = 0; break; } data++; } /* * Return (to be called later when we have more data) */ if(flag < 2) return PR_SUCCESS; waitingForDataParse = PR_FALSE; Request *rq = request->getOrigRequest(); pblock *authpb = NULL; if(fcgiRole == FCGI_AUTHORIZER) { authpb = pblock_create(rq->srvhdrs->hsize); } register int x ,y; register char c; int nh; char t[REQ_MAX_LINE]; PRBool headerEnd = PR_FALSE; char* statusHeader = pblock_findval("status", rq->srvhdrs); char *next = const_cast<char *>(httpHeader.data()); nh = 0; x = 0; y = -1; for(; !headerEnd;) { c = *(next++); switch(c) { case CR: // Silently ignore CRs break; case LF: if (x == 0) { headerEnd = PR_TRUE; break; } t[x] = '\0'; if(y == -1) { request->log(LOG_FAILURE, "name without value: got line \"%s\"", t); return PR_FAILURE; } while(t[y] && isspace(t[y])) ++y; // Do not change the status header to 200 if it was already set // This would happen only if it were a cgi error handler // and so the status had been already set on the request // originally if (!statusHeader || // If we don't already have a Status: header PL_strcmp(t, "status") || // or this isn't a Status: header PL_strncmp(&t[y], "200", 3)) // or this isn't "Status: 200" { if(!PL_strcmp(t, "content-type")) { pb_param* pParam = pblock_remove ( "content-type", rq->srvhdrs ); if ( pParam ) param_free ( pParam ); } if(fcgiRole == FCGI_AUTHORIZER) { pblock_nvinsert(t, &t[y], authpb); } else { pblock_nvinsert(t, &t[y], rq->srvhdrs); } // !FCGI_AUTHORIZER } x = 0; y = -1; ++nh; break; case ':': if(y == -1) { y = x+1; c = '\0'; } default: t[x++] = ((y == -1) && isupper(c) ? tolower(c) : c); } } // for if(fcgiRole == FCGI_AUTHORIZER) { if(parseAuthHeaders(authpb) != PR_SUCCESS) { pblock_free(authpb); return PR_FAILURE; } pblock_copy(authpb, rq->srvhdrs); pblock_free(authpb); } else { /* * We're done scanning the FCGI script's header output. Now * we have to write to the client: status, FCGI header, and * any over-read FCGI output. */ char *s; char *l = pblock_findval("location", rq->srvhdrs); if((s = pblock_findval("status", rq->srvhdrs))) { if((strlen(s) < 3) || (!isdigit(s[0]) || (!isdigit(s[1])) || (!isdigit(s[2])))) { s = NULL; } else { char ch = s[3]; s[3] = '\0'; int statusNum = atoi(s); s[3] = ch; rq->status_num = statusNum; } } if(!s) { if (l) pblock_nvinsert("url", l, rq->vars); protocol_status(request->getOrigSession(), request->getOrigRequest(), (l ? PROTOCOL_REDIRECT : PROTOCOL_OK), NULL); } } len = next - httpHeader.data(); len = httpHeader.length() - len; if(len < 0) return PR_FAILURE; /* * Only send the body for methods other than HEAD. */ if(!request->isHead()) { if(len > 0) { if(to.addData(next, len) != len) return PR_FAILURE; } } next = NULL; return PR_SUCCESS; }
End_request(uint32_t _app_status_, Protocol_status _proto_status_) { app_status() = _app_status_; protocol_status() = _proto_status_; }
/** ************************************************************************** * 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; }