blob: 7f3199fdf0641bbb0c8f2e036fc1bbac775b8122 [file] [log] [blame]
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* Originally developed by Aaron Bannert and Justin Erenkrantz, eBuilt.
*/
#include <flood_profile.h>
#include <stdlib.h>
#include <string.h>
#include <apr_file_io.h>
#include <apr_network_io.h>
#include <apr_strings.h>
#include <apr_uri.h>
#include <apr_lib.h>
#include "config.h"
#include "flood_net.h"
extern apr_file_t *local_stdout;
extern apr_file_t *local_stderr;
/* Allowable mechanisms for payload template generation. */
enum param_e {
RANDOM_DATA,
SEQUENTIAL_DATA,
FILE_DATA
};
typedef enum param_e param_e;
typedef struct {
char *url;
method_e method;
char *payload;
apr_int64_t predelay;
apr_int64_t predelayprecision;
apr_int64_t postdelay;
apr_int64_t postdelayprecision;
param_e payloadtype;
int payloadparamcount;
char *payloadtemplate;
param_e requestparamtype;
int requestparamcount;
char *requesttemplate;
char *responsetemplate;
} url_t;
typedef struct cookie_t {
char *name;
char *value;
char *path;
char *expires;
char *raw;
struct cookie_t *next;
} cookie_t;
typedef struct {
apr_pool_t *pool;
int execute_rounds;
int urls;
url_t *url;
cookie_t *cookie;
int states;
/* There's a max to dynamicism. */
char *state[10];
int current_round;
int current_url;
} round_robin_profile_t;
/* Construct a request */
apr_status_t round_robin_create_req(profile_t *profile, request_t *r)
{
round_robin_profile_t *p;
char *cookies;
cookie_t *cook;
p = (round_robin_profile_t*)profile;
/* Do we want to save the entire response? */
r->wantresponse = p->url[p->current_url].responsetemplate ? 1 : 0;
/* FIXME: This algorithm sucks. */
if (p->cookie)
{
cookies = apr_pstrdup(p->pool, "Cookie: ");
cook = p->cookie;
while (cook)
{
if (cook != p->cookie)
cookies = apr_pstrcat(p->pool, cookies, ";");
cookies = apr_pstrcat(p->pool, cookies, cook->name, "=",
cook->value, NULL);
cook = cook->next;
}
cookies = apr_pstrcat(p->pool, cookies, CRLF, NULL);
}
else
cookies = "";
switch (r->method)
{
case GET:
r->rbuf = apr_psprintf(r->pool,
"GET %s%s%s HTTP/1.1" CRLF
"User-Agent: Flood/" FLOOD_VERSION CRLF
"Connection: %s" CRLF
"Host: %s" CRLF
"%s" CRLF,
r->parsed_uri->path,
r->parsed_uri->query ? "?" : "",
r->parsed_uri->query ? r->parsed_uri->query : "",
r->keepalive ? "Keep-Alive" : "Close",
r->parsed_uri->hostinfo,
cookies);
r->rbuftype = POOL;
r->rbufsize = strlen(r->rbuf);
break;
case HEAD:
r->rbuf = apr_psprintf(r->pool,
"HEAD %s%s%s HTTP/1.1" CRLF
"User-Agent: Flood/" FLOOD_VERSION CRLF
"Connection: %s" CRLF
"Host: %s" CRLF
"%s" CRLF,
r->parsed_uri->path,
r->parsed_uri->query ? "?" : "",
r->parsed_uri->query ? r->parsed_uri->query : "",
r->keepalive ? "Keep-Alive" : "Close",
r->parsed_uri->hostinfo,
cookies);
r->rbuftype = POOL;
r->rbufsize = strlen(r->rbuf);
break;
case POST:
/* FIXME */
if (r->payload) {
r->rbuf = apr_psprintf(r->pool,
"POST %s%s%s HTTP/1.1" CRLF
"User-Agent: Flood/" FLOOD_VERSION CRLF
"Connection: %s" CRLF
"Host: %s" CRLF
"Content-Length: %d" CRLF
"Content-type: application/x-www-form-urlencoded" CRLF
"%s" CRLF
"%s",
r->parsed_uri->path,
r->parsed_uri->query ? "?" : "",
r->parsed_uri->query ? r->parsed_uri->query : "",
r->keepalive ? "Keep-Alive" : "Close",
r->parsed_uri->hostinfo,
r->payloadsize,
cookies,
(char*)r->payload);
} else { /* There is no payload, but it's still a POST */
r->rbuf = apr_psprintf(r->pool,
"POST %s%s%s HTTP/1.1" CRLF
"User-Agent: Flood/" FLOOD_VERSION CRLF
"Connection: %s" CRLF
"Host: %s" CRLF
"%s" CRLF "",
r->parsed_uri->path,
r->parsed_uri->query ? "?" : "",
r->parsed_uri->query ? r->parsed_uri->query : "",
r->keepalive ? "Keep-Alive" : "Close",
r->parsed_uri->hostinfo,
cookies);
}
r->rbuftype = POOL;
r->rbufsize = strlen(r->rbuf);
break;
}
return APR_SUCCESS;
}
apr_status_t round_robin_profile_init(profile_t **profile, config_t *config, const char *profile_name, apr_pool_t *pool)
{
apr_status_t stat;
int i;
struct apr_xml_elem *root_elem, *profile_elem,
*urllist_elem, *count_elem, *useurllist_elem, *e;
round_robin_profile_t *p;
char *xml_profile, *xml_urllist, *urllist_name;
p = apr_pcalloc(pool, sizeof(round_robin_profile_t));
p->pool = pool;
/* yeah, yeah; calloc(), whatever...this is readability baby! */
p->current_url = 0; /* start on the first URL */
p->current_round = 0; /* start counting rounds at 0 */
/* get the XML pathes to the profile and the urllist */
xml_profile = apr_pstrdup(pool, XML_PROFILE);
xml_urllist = apr_pstrdup(pool, XML_URLLIST);
if ((stat = retrieve_root_xml_elem(&root_elem, config)) != APR_SUCCESS) {
return stat;
}
/* retrieve our profile xml element */
if ((stat = retrieve_xml_elem_with_childmatch(
&profile_elem, root_elem,
xml_profile, "name", profile_name)) != APR_SUCCESS)
return stat;
/* find the count */
if ((stat = retrieve_xml_elem_child(
&count_elem, profile_elem, XML_PROFILE_COUNT)) != APR_SUCCESS) {
/* if it's missing, just default to 1 */
p->execute_rounds = 1;
} else {
if (count_elem->first_cdata.first && count_elem->first_cdata.first->text) {
p->execute_rounds = strtol(count_elem->first_cdata.first->text, NULL, 10);
if (p->execute_rounds == LONG_MAX || p->execute_rounds == LONG_MIN)
/* error, over/under-flow */
return errno;
} else {
apr_file_printf(local_stderr,
"Profile '%s' has element <%s> with no value, assuming 1.\n",
profile_name, XML_PROFILE_COUNT);
p->execute_rounds = 1;
}
}
#ifdef PROFILE_DEBUG
apr_file_printf(local_stdout,
"Profile '%s' will be run %d times.\n", profile_name, p->execute_rounds);
#endif /* PROFILE_DEBUG */
/* find out what the name of our urllist is */
if ((stat = retrieve_xml_elem_child(
&useurllist_elem, profile_elem, XML_PROFILE_USEURLLIST)) != APR_SUCCESS) {
/* useurllist is a required parameter, error */
apr_file_printf(local_stderr,
"Profile '%s' has no <%s> parameter.\n",
profile_name, XML_PROFILE_USEURLLIST);
return APR_EGENERAL;
} else {
urllist_name = apr_pstrdup(pool, useurllist_elem->first_cdata.first->text);
}
/* retrieve our urllist xml element */
if ((stat = retrieve_xml_elem_with_childmatch(
&urllist_elem, root_elem,
xml_urllist, XML_URLLIST_NAME, urllist_name)) != APR_SUCCESS)
return stat;
/* find the urllist for this profile, put 'em in this list */
if ((p->urls = count_xml_elem_child(urllist_elem, XML_URLLIST_URL)) <= 0) {
apr_file_printf(local_stderr, "Urllist '%s' doesn't have any urls!\n", urllist_name);
return APR_EGENERAL;
}
p->url = apr_pcalloc(p->pool, sizeof(url_t) * (p->urls + 1));
i = 0;
for (e = urllist_elem->first_child; e; e = e->next) {
if (strncasecmp(e->name, XML_URLLIST_URL, FLOOD_STRLEN_MAX) == 0) {
/* Do we need strdup? */
if (e->first_cdata.first && e->first_cdata.first->text)
p->url[i].url = apr_pstrdup(pool, e->first_cdata.first->text);
if (e->attr)
{
apr_xml_attr *attr = e->attr;
while (attr)
{
if (strncasecmp(attr->name, XML_URLLIST_METHOD,
FLOOD_STRLEN_MAX) == 0) {
if (strncasecmp(attr->value, XML_URLLIST_METHOD_POST, 4) == 0)
p->url[i].method = POST;
else if (strncasecmp(attr->value, XML_URLLIST_METHOD_HEAD, 4) == 0)
p->url[i].method = HEAD;
else if (strncasecmp(attr->value, XML_URLLIST_METHOD_GET, 3) == 0)
p->url[i].method = GET;
else {
apr_file_printf(local_stderr, "Attribute %s has invalid value %s.\n",
XML_URLLIST_METHOD, attr->value);
return APR_EGENERAL;
}
}
else if (strncasecmp(attr->name, XML_URLLIST_PAYLOAD,
FLOOD_STRLEN_MAX) == 0) {
p->url[i].payload = (char*)attr->value;
}
else if (strncasecmp(attr->name, XML_URLLIST_PREDELAY,
FLOOD_STRLEN_MAX) == 0) {
char *endptr;
p->url[i].predelay = strtoll(attr->value, &endptr, 10);
if (*endptr != '\0')
{
apr_file_printf(local_stderr,
"Attribute %s has invalid value %s.\n",
XML_URLLIST_PREDELAY, attr->value);
return APR_EGENERAL;
}
p->url[i].predelay *= APR_USEC_PER_SEC;
}
else if (strncasecmp(attr->name, XML_URLLIST_PREDELAYPRECISION,
FLOOD_STRLEN_MAX) == 0) {
char *endptr;
p->url[i].predelayprecision = strtoll(attr->value, &endptr, 10);
if (*endptr != '\0')
{
apr_file_printf(local_stderr,
"Attribute %s has invalid value %s.\n",
XML_URLLIST_PREDELAYPRECISION, attr->value);
return APR_EGENERAL;
}
p->url[i].predelayprecision *= APR_USEC_PER_SEC;
}
else if (strncasecmp(attr->name, XML_URLLIST_POSTDELAY,
FLOOD_STRLEN_MAX) == 0) {
char *endptr;
p->url[i].postdelay = strtoll(attr->value, &endptr, 10);
if (*endptr != '\0')
{
apr_file_printf(local_stderr,
"Attribute %s has invalid value %s.\n",
XML_URLLIST_POSTDELAY, attr->value);
return APR_EGENERAL;
}
p->url[i].postdelay *= APR_USEC_PER_SEC;
}
else if (strncasecmp(attr->name, XML_URLLIST_POSTDELAYPRECISION,
FLOOD_STRLEN_MAX) == 0) {
char *endptr;
p->url[i].postdelayprecision = strtoll(attr->value, &endptr, 10);
if (*endptr != '\0')
{
apr_file_printf(local_stderr,
"Attribute %s has invalid value %s.\n",
XML_URLLIST_POSTDELAYPRECISION, attr->value);
return APR_EGENERAL;
}
p->url[i].postdelayprecision *= APR_USEC_PER_SEC;
}
else if (strncasecmp(attr->name, XML_URLLIST_PAYLOAD_PARAM,
FLOOD_STRLEN_MAX) == 0) {
if (strncasecmp(attr->value, "random", 6) == 0)
p->url[i].payloadtype = RANDOM_DATA;
else if (strncasecmp(attr->value, "seq", 3) == 0)
p->url[i].payloadtype = SEQUENTIAL_DATA;
else if (strncasecmp(attr->value, "file", 3) == 0)
p->url[i].payloadtype = FILE_DATA;
else {
apr_file_printf(local_stderr,
"Attribute %s has invalid value %s.\n",
XML_URLLIST_PAYLOAD_PARAM,
attr->value);
return APR_EGENERAL;
}
}
else if (strncasecmp(attr->name,
XML_URLLIST_PAYLOAD_PARAM_COUNT,
FLOOD_STRLEN_MAX) == 0) {
char *endptr;
p->url[i].payloadparamcount = strtoll(attr->value,
&endptr, 10);
if (*endptr != '\0')
{
apr_file_printf(local_stderr,
"Attribute %s has invalid value %s.\n",
XML_URLLIST_PAYLOAD_PARAM_COUNT, attr->value);
return APR_EGENERAL;
}
}
else if (strncasecmp(attr->name,
XML_URLLIST_PAYLOAD_TEMPLATE,
FLOOD_STRLEN_MAX) == 0) {
p->url[i].payloadtemplate = (char*)attr->value;
}
else if (strncasecmp(attr->name, XML_URLLIST_REQUEST_PARAM,
FLOOD_STRLEN_MAX) == 0) {
if (strncasecmp(attr->value, "random", 6) == 0)
p->url[i].requestparamtype = RANDOM_DATA;
else if (strncasecmp(attr->value, "seq", 3) == 0)
p->url[i].requestparamtype = SEQUENTIAL_DATA;
else if (strncasecmp(attr->value, "file", 3) == 0)
p->url[i].requestparamtype = FILE_DATA;
else {
apr_file_printf(local_stderr,
"Attribute %s has invalid value %s.\n",
XML_URLLIST_REQUEST_PARAM,
attr->value);
return APR_EGENERAL;
}
}
else if (strncasecmp(attr->name,
XML_URLLIST_REQUEST_PARAM_COUNT,
FLOOD_STRLEN_MAX) == 0) {
char *endptr;
p->url[i].requestparamcount = strtoll(attr->value,
&endptr, 10);
if (*endptr != '\0')
{
apr_file_printf(local_stderr,
"Attribute %s has invalid value %s.\n",
XML_URLLIST_REQUEST_PARAM_COUNT,
attr->value);
return APR_EGENERAL;
}
}
else if (strncasecmp(attr->name,
XML_URLLIST_REQUEST_TEMPLATE,
FLOOD_STRLEN_MAX) == 0) {
p->url[i].requesttemplate = (char*)attr->value;
}
else if (strncasecmp(attr->name,
XML_URLLIST_RESPONSE_TEMPLATE,
FLOOD_STRLEN_MAX) == 0) {
p->url[i].responsetemplate = (char*)attr->value;
}
attr = attr->next;
}
}
else
{
p->url[i].method = GET;
p->url[i].payload = NULL;
}
i++;
}
}
*profile = p;
return APR_SUCCESS;
}
char *expand_param_string(round_robin_profile_t *rp, char *template)
{
char *cpy, *cur, *prev, *data, *returnValue;
prev = template;
returnValue = NULL;
cur = strstr(prev, "$");
while (cur)
{
/* What do we want to fill in? */
if (apr_isdigit(*(cur+1)))
data = rp->state[*(cur+1) - '0'];
/* May be 0, but that's okay. */
if (cur-prev)
{
/* Copy the $ character, but we'll set it to NULL soon. */
cpy = apr_pmemdup(rp->pool, prev, cur - prev + 1);
cpy[cur-prev] = '\0';
if (!returnValue)
returnValue = apr_pstrcat(rp->pool, cpy, data, NULL);
else
returnValue = apr_pstrcat(rp->pool, returnValue, cpy, data, NULL);
}
else
{
if (!returnValue)
returnValue = apr_pstrdup(rp->pool, data);
else
returnValue = apr_pstrcat(rp->pool, returnValue, data, NULL);
}
prev = cur + 2;
cur = strstr(prev, "$");
}
if (*prev)
{
if (!returnValue)
returnValue = apr_pstrdup(rp->pool, prev);
else
returnValue = apr_pstrcat(rp->pool, returnValue, prev, NULL);
}
return returnValue;
}
char *parse_param_string(round_robin_profile_t *rp, char *template,
int paramcount, param_e payload)
{
int i;
char *cpy, *cur, *prev, *data, *returnValue;
prev = template;
returnValue = NULL;
for (i = 0; i < paramcount; i++)
{
cur = strstr(prev, "$");
if (!cur)
return NULL;
/* What do we want to fill in? */
if (*(cur+1) == '$')
{
switch (payload) {
case RANDOM_DATA:
data = apr_psprintf(rp->pool, "%d", rand());
break;
case SEQUENTIAL_DATA:
break;
case FILE_DATA:
break;
}
rp->state[rp->states++] = apr_pstrdup(rp->pool, data);
}
else if (apr_isdigit(*(cur+1)))
{
data = rp->state[*(cur+1) - '0'];
}
else
return NULL;
/* May be 0, but that's okay. */
if (cur-prev)
{
/* Copy the $ character, but we'll set it to NULL soon. */
cpy = apr_pmemdup(rp->pool, prev, cur - prev + 1);
cpy[cur-prev] = '\0';
if (!returnValue)
returnValue = apr_pstrcat(rp->pool, cpy, data, NULL);
else
returnValue = apr_pstrcat(rp->pool, returnValue, cpy, data, NULL);
}
else
{
if (!returnValue)
returnValue = apr_pstrdup(rp->pool, data);
else
returnValue = apr_pstrcat(rp->pool, returnValue, data, NULL);
}
prev = cur + 2;
}
if (*prev)
{
if (!returnValue)
returnValue = apr_pstrdup(rp->pool, prev);
else
returnValue = apr_pstrcat(rp->pool, returnValue, prev, NULL);
}
return returnValue;
}
apr_status_t round_robin_get_next_url(request_t **request, profile_t *profile)
{
round_robin_profile_t *rp;
request_t *r;
rp = (round_robin_profile_t*)profile;
/* FIXME: precompute request_t in profile_init */
r = apr_pcalloc(rp->pool, sizeof(request_t));
r->pool = rp->pool;
if (rp->url[rp->current_url].requesttemplate)
{
r->uri = parse_param_string(rp,
rp->url[rp->current_url].requesttemplate,
rp->url[rp->current_url].requestparamcount,
rp->url[rp->current_url].requestparamtype);
}
else
r->uri = rp->url[rp->current_url].url;
r->method = rp->url[rp->current_url].method;
/* We're created by calloc, so no need to set payload to be null or
* payloadsize to be 0.
*/
if (rp->url[rp->current_url].payload)
{
r->payload = rp->url[rp->current_url].payload;
r->payloadsize = strlen(rp->url[rp->current_url].payload);
}
else if (rp->url[rp->current_url].payloadtemplate)
{
r->payload = parse_param_string(rp,
rp->url[rp->current_url].payloadtemplate,
rp->url[rp->current_url].payloadparamcount,
rp->url[rp->current_url].payloadtype);
r->payloadsize = strlen(r->payload);
}
/* If they want a sleep, do it now. */
if (rp->url[rp->current_url].predelay) {
apr_int64_t real_predelay = rp->url[rp->current_url].predelay;
/* If the delay has a precision, adjust the
* delay by some random fraction of the precision here */
if (rp->url[rp->current_url].predelayprecision) {
/* FIXME: this should be more portable, like apr_generate_random_bytes() */
float factor = -1.0 + (2.0*rand()/(RAND_MAX+1.0));
apr_file_printf(local_stderr,
"Generating random predelay factor of %fus\n",
factor);
real_predelay += rp->url[rp->current_url].predelayprecision * factor;
}
/* we can only delay positive times, can't go back in time :( */
if (real_predelay < 0)
real_predelay = 0;
apr_file_printf(local_stderr,
"Generating random predelay of %" APR_INT64_T_FMT "us\n",
real_predelay);
/* only bother going to sleep if we generated a delay */
if (real_predelay > 0)
apr_sleep(real_predelay);
}
r->parsed_uri = apr_pcalloc(rp->pool, sizeof(apr_uri_components));
apr_uri_parse_components(rp->pool, r->uri, r->parsed_uri);
if (!r->parsed_uri->port)
{
r->parsed_uri->port =
apr_uri_default_port_for_scheme(r->parsed_uri->scheme);
}
if (!r->parsed_uri->path) /* If / is not there, be nice. */
r->parsed_uri->path = "/";
#ifdef PROFILE_DEBUG
apr_file_printf(local_stdout, "Generating request to: %s\n", r->uri);
#endif /* PROFILE_DEBUG */
*request = r;
return APR_SUCCESS;
}
apr_status_t round_robin_postprocess(profile_t *profile,
request_t *req,
response_t *resp)
{
round_robin_profile_t *rp;
char *cookieheader, *cookievalue, *cookieend;
rp = (round_robin_profile_t*)profile;
/* FIXME: This algorithm sucks. I need to be shot for writing such
* atrocious code. Grr. */
cookieheader = strstr(resp->rbuf, "Set-Cookie: ");
if (cookieheader)
{
/* Point to the value */
cookieheader += 12;
cookievalue = (char*) memchr(cookieheader, '=',
resp->rbufsize - (int)(cookieheader - (int)(resp->rbuf)));
if (cookievalue)
{
cookie_t * cookie = apr_pcalloc(rp->pool, sizeof(cookie_t));
++cookievalue;
cookie->name = apr_palloc(rp->pool, cookievalue - cookieheader);
apr_cpystrn(cookie->name, cookieheader, cookievalue - cookieheader);
cookieheader = cookievalue;
cookieend = (char*) memchr(cookieheader, '\r',
resp->rbufsize - (int)(cookieheader - (int)(resp->rbuf)));
cookievalue = (char*) memchr(cookieheader, ';',
cookieend - cookieheader);
if (!cookievalue)
cookievalue = cookieend;
++cookievalue;
cookie->value = apr_palloc(rp->pool, cookievalue - cookieheader);
apr_cpystrn(cookie->value, cookieheader,
cookievalue - cookieheader);
cookie->next = rp->cookie;
rp->cookie = cookie;
}
}
if (rp->url[rp->current_url].responsetemplate)
{
char *c, *endfirsthalf, *ec, *bc;
char *firsthalf, *secondhalf;
int firsthalflen;
c = rp->url[rp->current_url].responsetemplate;
endfirsthalf = strstr(c,"$$");
if (!endfirsthalf)
return APR_EGENERAL;
firsthalf = apr_palloc(rp->pool, endfirsthalf - c + 1);
apr_cpystrn(firsthalf, c, endfirsthalf - c + 1);
firsthalf = expand_param_string(rp, firsthalf);
firsthalflen = strlen(firsthalf);
secondhalf = expand_param_string(rp, endfirsthalf+2);
bc = resp->rbuf;
/* The response pattern must be on the same line. */
do
{
bc = strstr(bc, firsthalf);
if (!bc)
return APR_EGENERAL;
/* Skip the part that was given. */
bc += firsthalflen;
/* Now search for the second half. */
ec = strstr(bc, secondhalf);
if (!ec)
return APR_EGENERAL;
}
while (memchr(bc, '\n', ec - bc));
/* Increment it by one, so that we copy everything. */
ec++;
rp->state[rp->states] = apr_palloc(rp->pool, ec - bc);
apr_cpystrn(rp->state[rp->states++], bc, ec - bc);
}
return APR_SUCCESS;
}
apr_status_t verify_200(int *verified, profile_t *profile, request_t *req, response_t *resp)
{
round_robin_profile_t *rp;
int res;
rp = (round_robin_profile_t*) profile;
res = memcmp(resp->rbuf, "HTTP/1.1 2", 10);
if (!res)
*verified = FLOOD_VALID;
else if (memcmp(resp->rbuf + 9, "3", 1) == 0) /* Accept 3xx as okay. */
*verified = FLOOD_VALID;
else
*verified = FLOOD_INVALID;
return APR_SUCCESS;
}
int round_robin_loop_condition(profile_t *profile)
{
round_robin_profile_t *rp;
int real_current_url;
rp = (round_robin_profile_t*)profile;
real_current_url = rp->current_url; /* save the real one before we try to increment */
rp->current_url++;
/* Adjust counters for profile */
if (rp->current_url >= rp->urls) {
rp->current_url = 0;
/* Loop cond tells us when to stop. */
rp->current_round++;
}
#ifdef PROFILE_DEBUG
apr_file_printf(local_stdout, "Round %d of %d, %s.\n",
rp->current_round, rp->execute_rounds,
(rp->current_round < rp->execute_rounds ? "Continuing" : "Finished"));
#endif /* PROFILE_DEBUG */
if (rp->current_round >= rp->execute_rounds)
return 0;
else { /* we'll continue, so do delay stuff now if necessary */
/* If they want a sleep, do it now. */
if (rp->url[real_current_url].postdelay) {
apr_int64_t real_postdelay = rp->url[real_current_url].postdelay;
/* If the delay has a precision, adjust the
* delay by some random fraction of the precision here */
if (rp->url[real_current_url].postdelayprecision) {
/* FIXME: this should be more portable, like apr_generate_random_bytes() */
float factor = -1.0 + (2.0*rand()/(RAND_MAX+1.0));
apr_file_printf(local_stderr,
"Generating random postdelay factor of %fus\n",
factor);
real_postdelay += rp->url[real_current_url].postdelayprecision * factor;
}
/* we can only delay positive times, can't go back in time :( */
if (real_postdelay < 0)
real_postdelay = 0;
apr_file_printf(local_stderr,
"Generating random postdelay of %" APR_INT64_T_FMT "us\n",
real_postdelay);
/* only bother going to sleep if we generated a delay */
if (real_postdelay > 0)
apr_sleep(real_postdelay);
}
return 1;
}
}
apr_status_t round_robin_profile_destroy(profile_t *profile)
{
/* FIXME: free() the memory used by this profile, or reset() the pool
* (or whatever semantics apr uses, I dunno...) -aaron */
return APR_SUCCESS;
}