static apr_status_t set_resource_limits(request_rec *r, apr_procattr_t *procattr) { #if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \ defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS) core_dir_config *conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); apr_status_t rv; #ifdef RLIMIT_CPU rv = apr_procattr_limit_set(procattr, APR_LIMIT_CPU, conf->limit_cpu); ap_assert(rv == APR_SUCCESS); /* otherwise, we're out of sync with APR */ #endif #if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) rv = apr_procattr_limit_set(procattr, APR_LIMIT_MEM, conf->limit_mem); ap_assert(rv == APR_SUCCESS); /* otherwise, we're out of sync with APR */ #endif #ifdef RLIMIT_NPROC rv = apr_procattr_limit_set(procattr, APR_LIMIT_NPROC, conf->limit_nproc); ap_assert(rv == APR_SUCCESS); /* otherwise, we're out of sync with APR */ #endif #endif /* if at least one limit defined */ return APR_SUCCESS; }
h2_task *h2_task_create(conn_rec *slave, int stream_id, const h2_request *req, h2_mplx *m, h2_bucket_beam *input, apr_interval_time_t timeout, apr_size_t output_max_mem) { apr_pool_t *pool; h2_task *task; ap_assert(slave); ap_assert(req); apr_pool_create(&pool, slave->pool); task = apr_pcalloc(pool, sizeof(h2_task)); if (task == NULL) { return NULL; } task->id = "000"; task->stream_id = stream_id; task->c = slave; task->mplx = m; task->pool = pool; task->request = req; task->timeout = timeout; task->input.beam = input; task->output.max_buffer = output_max_mem; return task; }
static void watch_callback(AvahiWatch *w, int fd, AvahiWatchEvent event, void *userdata) { char c; ssize_t l; struct runtime_data *r = userdata; ap_assert(w); ap_assert(fd == sigterm_pipe_fds[0]); ap_assert(event == AVAHI_WATCH_IN); ap_assert(r); l = read(fd, &c, sizeof(c)); ap_assert(l == sizeof(c)); avahi_simple_poll_quit(r->simple_poll); }
static void service_callback(AVAHI_GCC_UNUSED AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { struct service_data *j = userdata; switch (state) { case AVAHI_ENTRY_GROUP_UNCOMMITED: case AVAHI_ENTRY_GROUP_REGISTERING: case AVAHI_ENTRY_GROUP_ESTABLISHED: break; case AVAHI_ENTRY_GROUP_COLLISION: { char *n; ap_assert(j->chosen_name); n = avahi_alternative_service_name(j->chosen_name); ap_log_error(APLOG_MARK, APLOG_WARNING, 0, j->runtime->main_server, "Name collision on '%s', changing to '%s'", j->chosen_name, n); apr_pool_clear(j->pool); j->chosen_name = apr_pstrdup(j->pool, n); create_service(j); break; } case AVAHI_ENTRY_GROUP_FAILURE: ap_log_error(APLOG_MARK, APLOG_ERR, 0, j->runtime->main_server, "Failed to register service: %s", avahi_strerror(avahi_client_errno(j->runtime->client))); break; } }
static int SSL_recvwithtimeout(BUFF *fb, char *buf, int len) { int iostate = 1; fd_set fdset; struct timeval tv; int err = WSAEWOULDBLOCK; int rv; int sock = fb->fd_in; SSL *ssl; int retry; ssl = ap_ctx_get(fb->ctx, "ssl"); if (!(tv.tv_sec = ap_check_alarm())) return (SSL_read(ssl, buf, len)); rv = ioctlsocket(sock, FIONBIO, &iostate); iostate = 0; ap_assert(!rv); rv = SSL_read(ssl, buf, len); if (rv <= 0) { if (BIO_sock_should_retry(rv)) { do { retry = 0; FD_ZERO(&fdset); FD_SET((unsigned int)sock, &fdset); tv.tv_usec = 0; rv = select(FD_SETSIZE, &fdset, NULL, NULL, &tv); if (rv == SOCKET_ERROR) err = WSAGetLastError(); else if (rv == 0) { ioctlsocket(sock, FIONBIO, &iostate); ap_check_alarm(); WSASetLastError(WSAEWOULDBLOCK); return (SOCKET_ERROR); } else { rv = SSL_read(ssl, buf, len); if (rv == SOCKET_ERROR) { if (BIO_sock_should_retry(rv)) { ap_log_error(APLOG_MARK,APLOG_DEBUG, NULL, "select claimed we could read, " "but in fact we couldn't. " "This is a bug in Windows."); retry = 1; Sleep(100); } else { err = WSAGetLastError(); } } } } while(retry); } } ioctlsocket(sock, FIONBIO, &iostate); if (rv == SOCKET_ERROR) WSASetLastError(err); return (rv); }
static int start_child_process(apr_pool_t *p, server_rec *server, struct global_config_data *d) { apr_proc_t* proc; apr_status_t status; /* ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, server, "Spawning child pid=%lu", (unsigned long) getpid()); */ proc = apr_palloc(p, sizeof(apr_proc_t)); ap_assert(proc); switch (status = apr_proc_fork(proc, p)) { case APR_INCHILD: child_process(p, server, d); exit(1); /* never reached */ break; case APR_INPARENT: apr_pool_note_subprocess(p, proc, APR_KILL_ONLY_ONCE); /* ap_log_error(APLOG_MARK, APLOG_NOTICE, status, server, "Child process %lu", (unsigned long) proc->pid); */ break; default: ap_log_error(APLOG_MARK, APLOG_ERR, status, server, "apr_proc_fork() failed"); return HTTP_INTERNAL_SERVER_ERROR; } return OK; }
void ap_init_scoreboard(void *shared_score) { char *more_storage; int i; ap_calc_scoreboard_size(); ap_scoreboard_image = calloc(1, sizeof(scoreboard) + server_limit * sizeof(worker_score *)); more_storage = shared_score; ap_scoreboard_image->global = (global_score *)more_storage; more_storage += sizeof(global_score); ap_scoreboard_image->parent = (process_score *)more_storage; more_storage += sizeof(process_score) * server_limit; ap_scoreboard_image->servers = (worker_score **)((char*)ap_scoreboard_image + sizeof(scoreboard)); for (i = 0; i < server_limit; i++) { ap_scoreboard_image->servers[i] = (worker_score *)more_storage; more_storage += thread_limit * sizeof(worker_score); } if (lb_limit) { ap_scoreboard_image->balancers = (lb_score *)more_storage; more_storage += lb_limit * sizeof(lb_score); } ap_assert(more_storage == (char*)shared_score + scoreboard_size); ap_scoreboard_image->global->server_limit = server_limit; ap_scoreboard_image->global->thread_limit = thread_limit; ap_scoreboard_image->global->lb_limit = lb_limit; }
const h2_config *h2_config_sget(server_rec *s) { h2_config *cfg = (h2_config *)ap_get_module_config(s->module_config, &http2_module); ap_assert(cfg); return cfg; }
static void create_all_services(struct runtime_data *r) { struct service_data *j; ap_assert(r); for (j = r->services; j; j = j->next) create_service(j); }
AP_DECLARE(void) ap_init_scoreboard(void *shared_score) { char *more_storage; int i; ap_calc_scoreboard_size(); ap_scoreboard_image = ap_calloc(1, sizeof(scoreboard) + server_limit * sizeof(worker_score *)); more_storage = shared_score; ap_scoreboard_image->global = (global_score *)more_storage; more_storage += sizeof(global_score); ap_scoreboard_image->parent = (process_score *)more_storage; more_storage += sizeof(process_score) * server_limit; ap_scoreboard_image->servers = (worker_score **)((char*)ap_scoreboard_image + sizeof(scoreboard)); for (i = 0; i < server_limit; i++) { ap_scoreboard_image->servers[i] = (worker_score *)more_storage; more_storage += thread_limit * sizeof(worker_score); } ap_assert(more_storage == (char*)shared_score + scoreboard_size); ap_scoreboard_image->global->server_limit = server_limit; ap_scoreboard_image->global->thread_limit = thread_limit; /* * ******* begin amiya 20140221 ******* */ //we initialize these in worker since ap_daemons_limit //is not defined until mpm_hook is run ap_scoreboard_image->global->running_maxclients = 0; ap_scoreboard_image->global->running_keepalivetimeout = 0; /* * ******* end amiya ******* */ }
static int google_analytics_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { regex_tag_exists = ap_pregcomp(p, tag_exists, (AP_REG_EXTENDED | AP_REG_ICASE | AP_REG_NOSUB)); ap_assert(regex_tag_exists != NULL); pattern_body_end_tag = apr_strmatch_precompile(p, body_end_tag, 0); return OK; }
// helper function for sending dovecot authentication protocol handshake request int send_handshake(apr_pool_t * p, request_rec * r, int sock) { char * const handshake_data = apr_psprintf(p, "VERSION\t%u\t%u\n" "CPID\t%u\n", AUTH_PROTOCOL_MAJOR_VERSION, AUTH_PROTOCOL_MINOR_VERSION, (unsigned int)getpid()); ap_assert(handshake_data != NULL); if (send(sock, handshake_data, strlen(handshake_data), 0) > 0) { return 1; } else { return 0; } }
static void *create_authn_dovecot_dir_config(apr_pool_t * p, char *d) { authn_dovecot_config_rec * const conf = apr_pcalloc(p, sizeof(*conf)); ap_assert(conf != NULL); conf->dovecotauthsocket = "/var/run/dovecot/auth-client"; /* just to illustrate the default really */ conf->dovecotauthtimeout = 5; conf->authoritative = 1; // by default we are authoritative return conf; }
void mpm_nt_eventlog_stderr_open(char *argv0, apr_pool_t *p) { SECURITY_ATTRIBUTES sa; HANDLE hProc = GetCurrentProcess(); HANDLE hPipeRead = NULL; HANDLE hPipeWrite = NULL; HANDLE hDup = NULL; DWORD threadid; apr_file_t *eventlog_file; apr_file_t *stderr_file; display_name = argv0; /* Create a pipe to send stderr messages to the system error log. * * _dup2() duplicates the write handle inheritable for us. */ sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = FALSE; CreatePipe(&hPipeRead, &hPipeWrite, NULL, 0); ap_assert(hPipeRead && hPipeWrite); stderr_ready = CreateEvent(NULL, FALSE, FALSE, NULL); stderr_thread = CreateThread(NULL, 0, service_stderr_thread, (LPVOID) hPipeRead, 0, &threadid); ap_assert(stderr_ready && stderr_thread); WaitForSingleObject(stderr_ready, INFINITE); if ((apr_file_open_stderr(&stderr_file, p) == APR_SUCCESS) && (apr_os_file_put(&eventlog_file, &hPipeWrite, APR_WRITE, p) == APR_SUCCESS)) apr_file_dup2(stderr_file, eventlog_file, p); /* The code above _will_ corrupt the StdHandle... * and we must do so anyways. We set this up only * after we initialized the posix stderr API. */ ap_open_stderr_log(p); }
apr_int32_t util_timestring_to_seconds(char *string) { char *character; apr_int32_t number = 0; if (string == NULL) return 0; character = string; /* calculate number */ while (apr_isdigit(*character) || apr_isspace(*character)) { if (apr_isdigit(*character)) { /* translate to number */ unsigned digit = (unsigned) *character - (unsigned) '0'; ap_assert(digit < 10); number = (number * (apr_int32_t) 10) + (apr_int32_t) digit; } character += 1; } if (*character != '\0') { switch(*character) { case 'w': case 'W': number = number * SECONDS_IN_WEEK; break; case 'd': case 'D': number = number * SECONDS_IN_DAY; break; case 'h': case 'H': number = number * SECONDS_IN_HOUR; break; case 'm': case 'M': number = number * SECONDS_IN_MINUTE; break; case 's': case 'S': default: /* this is only here for clarity */ number = number; break; } } if (number > MAX_CACHE_TIMEOUT) number = MAX_CACHE_TIMEOUT; return number; }
static void *create_server_config(apr_pool_t *p, AVAHI_GCC_UNUSED server_rec *s) { struct global_config_data *d; d = apr_palloc(p, sizeof(struct global_config_data)); ap_assert(d); d->enabled = 0; d->user_dir = 1; d->vhost = 1; d->user_dir_path = "public_html"; return d; }
static int set_nonblock(int fd) { int n; ap_assert(fd >= 0); if ((n = fcntl(fd, F_GETFL)) < 0) return -1; if (n & O_NONBLOCK) return 0; return fcntl(fd, F_SETFL, n|O_NONBLOCK); }
// helper function used for sending prepared data to socket for authorization against dovecot auth int send_auth_request(apr_pool_t * p, request_rec * r, int sock, const char *user, const char *pass, char *remotehost) { struct iovec concat[4]; size_t const up_size = strlen(user) + strlen(pass) + 2; if (up_size > BUFFMAX - 1024) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Dovecot Authentication: User and pass length is over (or close) BUFFMAX=%i which is NOT allowed size=%u\n", BUFFMAX, (unsigned int)up_size); return 0; } size_t const eup_size = apr_base64_encode_len(up_size); char * const encoded_user_pass = (char *)apr_palloc(p, sizeof(char) * eup_size); ap_assert(encoded_user_pass != NULL); // this beautifull code snippet from bellow (concat blah blah) is needed for use of apr_pstrcatv // as without using apr_pstrcatv apr_pstrcat will remove \0 which we need for creating base64 encoded user_pass combination... concat[0].iov_base = (void *)"\0"; concat[0].iov_len = 1; concat[1].iov_base = (void *)user; concat[1].iov_len = strlen(user); concat[2].iov_base = (void *)"\0"; concat[2].iov_len = 1; concat[3].iov_base = (void *)pass; concat[3].iov_len = strlen(pass); char * const user_pass = apr_pstrcatv(p, concat, 4, NULL); ap_assert(user_pass != NULL); apr_base64_encode(encoded_user_pass, user_pass, up_size); char * const data = apr_psprintf(p, "AUTH\t1\tPLAIN\tservice=apache\tnologin" // local ip (lip) is hardcoded as we are using local unix socket anyway... "\tlip=127.0.0.1\trip=%s\tsecured\tresp=%s\n", remotehost, encoded_user_pass); ap_assert(data != NULL); size_t const d_size = strlen(data); if (send(sock, data, d_size, 0) > 0) { // scrub user credentials memset(user_pass, '\0', up_size); memset(encoded_user_pass, '\0', eup_size); memset(data, '\0', d_size); return 1; } else { return 0; } }
static apr_status_t h2_filter_slave_output(ap_filter_t* filter, apr_bucket_brigade* brigade) { h2_task *task = h2_ctx_cget_task(filter->c); apr_status_t status; ap_assert(task); status = slave_out(task, filter, brigade); if (status != APR_SUCCESS) { h2_task_rst(task, H2_ERR_INTERNAL_ERROR); } return status; }
static void add_service(struct runtime_data *r, const char *host_name, uint16_t port, const char *location, const char *name, const char *types, int append_host_name, const char *txt_record) { struct service_data *d; char *w; ap_assert(r); /* ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "add_service: %s %s %s %s", host_name, location, name, txt_record); */ d = apr_palloc(r->pool, sizeof(struct service_data)); ap_assert(d); d->pool = NULL; d->runtime = r; d->host_name = apr_pstrdup(r->pool, host_name); d->port = port; d->location = apr_pstrdup(r->pool, location); d->name = apr_pstrdup(r->pool, name); d->append_host_name = append_host_name; d->chosen_name = NULL; d->types = apr_array_make(r->pool, 4, sizeof(char*)); if (types) while (*(w = ap_getword_conf(r->pool, &types)) != 0) *(char**) apr_array_push(d->types) = w; d->txt_record = apr_array_make(r->pool, 4, sizeof(char*)); if (txt_record) while (*(w = ap_getword_conf(r->pool, &txt_record)) != 0) *(char**) apr_array_push(d->txt_record) = w; d->group = NULL; d->next = r->services; r->services = d; /* ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "done"); */ }
apr_status_t h2_conn_run(conn_rec *c) { apr_status_t status; int mpm_state = 0; h2_session *session = h2_ctx_get_session(c); ap_assert(session); do { if (c->cs) { c->cs->sense = CONN_SENSE_DEFAULT; c->cs->state = CONN_STATE_HANDLER; } status = h2_session_process(session, async_mpm); if (APR_STATUS_IS_EOF(status)) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, H2_SSSN_LOG(APLOGNO(03045), session, "process, closing conn")); c->keepalive = AP_CONN_CLOSE; } else { c->keepalive = AP_CONN_KEEPALIVE; } if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) { break; } } while (!async_mpm && c->keepalive == AP_CONN_KEEPALIVE && mpm_state != AP_MPMQ_STOPPING); if (c->cs) { switch (session->state) { case H2_SESSION_ST_INIT: case H2_SESSION_ST_IDLE: case H2_SESSION_ST_BUSY: case H2_SESSION_ST_WAIT: c->cs->state = CONN_STATE_WRITE_COMPLETION; break; case H2_SESSION_ST_CLEANUP: case H2_SESSION_ST_DONE: default: c->cs->state = CONN_STATE_LINGER; break; } } return APR_SUCCESS; }
static char *extract_ip(apr_array_header_t *arr, apr_array_header_t *proxy_ips, int recursive) { int i; char **ips = (char **)arr->elts; int len = arr->nelts; ap_assert(len >= 0); if (!recursive) return ips[len-1]; for (i = len-1; i >= 0; i--) { if (!is_in_array(ips[i], proxy_ips)) { return ips[i]; } } return ips[0]; }
static void reset_services(struct runtime_data *r) { struct service_data *j; ap_assert(r); for (j = r->services; j; j = j->next) { if (j->group) avahi_entry_group_reset(j->group); if (j->pool) apr_pool_clear(j->pool); j->chosen_name = NULL; } }
static void set_and_comp_regexp(cookie_dir_rec *dcfg, apr_pool_t *p, const char *cookie_name) { int danger_chars = 0; const char *sp = cookie_name; /* The goal is to end up with this regexp, * ^cookie_name=([^;,]+)|[;,][ \t]+cookie_name=([^;,]+) * with cookie_name obviously substituted either * with the real cookie name set by the user in httpd.conf, or with the * default COOKIE_NAME. */ /* Anyway, we need to escape the cookie_name before pasting it * into the regex */ while (*sp) { if (!apr_isalnum(*sp)) { ++danger_chars; } ++sp; } if (danger_chars) { char *cp; cp = apr_palloc(p, sp - cookie_name + danger_chars + 1); /* 1 == \0 */ sp = cookie_name; cookie_name = cp; while (*sp) { if (!apr_isalnum(*sp)) { *cp++ = '\\'; } *cp++ = *sp++; } *cp = '\0'; } dcfg->regexp_string = apr_pstrcat(p, "^", cookie_name, "=([^;,]+)|[;,][ \t]*", cookie_name, "=([^;,]+)", NULL); dcfg->regexp = ap_pregcomp(p, dcfg->regexp_string, AP_REG_EXTENDED); ap_assert(dcfg->regexp != NULL); }
/* Called whenever the client or server state changes */ static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) { struct runtime_data *r = userdata; ap_assert(r); r->client = c; /* ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->main_server, "client_callback(%u)", state); */ switch (state) { case AVAHI_CLIENT_S_RUNNING: create_all_services(r); break; case AVAHI_CLIENT_S_COLLISION: reset_services(r); break; case AVAHI_CLIENT_FAILURE: if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) { int error; free_services(r); avahi_client_free(r->client); if ((r->client = avahi_client_new(avahi_simple_poll_get(r->simple_poll), AVAHI_CLIENT_NO_FAIL, client_callback, r, &error))) break; ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "avahi_client_new() failed: %s", avahi_strerror(error)); } else ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->main_server, "Client failure: %s", avahi_strerror(avahi_client_errno(c))); avahi_simple_poll_quit(r->simple_poll); break; case AVAHI_CLIENT_S_REGISTERING: case AVAHI_CLIENT_CONNECTING: /* do nothing */ break; } }
static apr_status_t h2_filter_parse_h1(ap_filter_t* f, apr_bucket_brigade* bb) { h2_task *task = h2_ctx_cget_task(f->c); apr_status_t status; ap_assert(task); /* There are cases where we need to parse a serialized http/1.1 * response. One example is a 100-continue answer in serialized mode * or via a mod_proxy setup */ while (bb && !task->output.sent_response) { status = h2_from_h1_parse_response(task, f, bb); ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, f->c, "h2_task(%s): parsed response", task->id); if (APR_BRIGADE_EMPTY(bb) || status != APR_SUCCESS) { return status; } } return ap_pass_brigade(f->next, bb); }
apr_status_t h2_slave_run_pre_connection(conn_rec *slave, apr_socket_t *csd) { if (slave->keepalives == 0) { /* Simulate that we had already a request on this connection. Some * hooks trigger special behaviour when keepalives is 0. * (Not necessarily in pre_connection, but later. Set it here, so it * is in place.) */ slave->keepalives = 1; /* We signal that this connection will be closed after the request. * Which is true in that sense that we throw away all traffic data * on this slave connection after each requests. Although we might * reuse internal structures like memory pools. * The wanted effect of this is that httpd does not try to clean up * any dangling data on this connection when a request is done. Which * is unneccessary on a h2 stream. */ slave->keepalive = AP_CONN_CLOSE; return ap_run_pre_connection(slave, csd); } ap_assert(slave->output_filters); return APR_SUCCESS; }
static SOCKET remove_job(void) { joblist *job; SOCKET sock; WaitForSingleObject(allowed_globals.jobsemaphore, INFINITE); apr_thread_mutex_lock(allowed_globals.jobmutex); if (shutdown_in_progress && !allowed_globals.jobhead) { apr_thread_mutex_unlock(allowed_globals.jobmutex); return (INVALID_SOCKET); } job = allowed_globals.jobhead; ap_assert(job); allowed_globals.jobhead = job->next; if (allowed_globals.jobhead == NULL) allowed_globals.jobtail = NULL; apr_thread_mutex_unlock(allowed_globals.jobmutex); sock = job->sock; free(job); return (sock); }
static void save_req_info(request_rec *r) { /* to save for the request: * r->the_request + * foreach header: * '|' + header field */ int len = strlen(r->the_request); char *ch; ap_table_do(count_headers, &len, r->headers_in, NULL); request_plus_headers = ap_palloc(r->pool, len + 2 /* 2 for the '\n' + '\0' at end */); ch = request_plus_headers; strcpy(ch, r->the_request); ch += strlen(ch); ap_table_do(copy_headers, &ch, r->headers_in, NULL); *ch = '\n'; *(ch + 1) = '\0'; ap_assert(ch == request_plus_headers + len); ap_register_cleanup(r->pool, NULL, clear_req_info, ap_null_cleanup); }
static apr_status_t do_pattmatch(ap_filter_t *f, apr_bucket *inb, apr_bucket_brigade *mybb, apr_pool_t *pool) { int i; int force_quick = 0; ap_regmatch_t regm[AP_MAX_REG_MATCH]; apr_size_t bytes; apr_size_t len; const char *buff; struct ap_varbuf vb; apr_bucket *b; apr_bucket *tmp_b; subst_dir_conf *cfg = (subst_dir_conf *) ap_get_module_config(f->r->per_dir_config, &substitute_module); subst_pattern_t *script; APR_BRIGADE_INSERT_TAIL(mybb, inb); ap_varbuf_init(pool, &vb, 0); script = (subst_pattern_t *) cfg->patterns->elts; /* * Simple optimization. If we only have one pattern, then * we can safely avoid the overhead of flattening */ if (cfg->patterns->nelts == 1) { force_quick = 1; } for (i = 0; i < cfg->patterns->nelts; i++) { for (b = APR_BRIGADE_FIRST(mybb); b != APR_BRIGADE_SENTINEL(mybb); b = APR_BUCKET_NEXT(b)) { if (APR_BUCKET_IS_METADATA(b)) { /* * we should NEVER see this, because we should never * be passed any, but "handle" it just in case. */ continue; } if (apr_bucket_read(b, &buff, &bytes, APR_BLOCK_READ) == APR_SUCCESS) { int have_match = 0; vb.strlen = 0; if (script->pattern) { const char *repl; /* * space_left counts how many bytes we have left until the * line length reaches max_line_length. */ apr_size_t space_left = cfg->max_line_length; apr_size_t repl_len = strlen(script->replacement); while ((repl = apr_strmatch(script->pattern, buff, bytes))) { have_match = 1; /* get offset into buff for pattern */ len = (apr_size_t) (repl - buff); if (script->flatten && !force_quick) { /* * We are flattening the buckets here, meaning * that we don't do the fast bucket splits. * Instead we copy over what the buckets would * contain and use them. This is slow, since we * are constanting allocing space and copying * strings. */ if (vb.strlen + len + repl_len > cfg->max_line_length) return APR_ENOMEM; ap_varbuf_strmemcat(&vb, buff, len); ap_varbuf_strmemcat(&vb, script->replacement, repl_len); } else { /* * The string before the match but after the * previous match (if any) has length 'len'. * Check if we still have space for this string and * the replacement string. */ if (space_left < len + repl_len) return APR_ENOMEM; space_left -= len + repl_len; /* * We now split off the string before the match * as its own bucket, then isolate the matched * string and delete it. */ SEDRMPATBCKT(b, len, tmp_b, script->patlen); /* * Finally, we create a bucket that contains the * replacement... */ tmp_b = apr_bucket_transient_create(script->replacement, script->replen, f->r->connection->bucket_alloc); /* ... and insert it */ APR_BUCKET_INSERT_BEFORE(b, tmp_b); } /* now we need to adjust buff for all these changes */ len += script->patlen; bytes -= len; buff += len; } if (have_match) { if (script->flatten && !force_quick) { /* XXX: we should check for AP_MAX_BUCKETS here and * XXX: call ap_pass_brigade accordingly */ char *copy = ap_varbuf_pdup(pool, &vb, NULL, 0, buff, bytes, &len); tmp_b = apr_bucket_pool_create(copy, len, pool, f->r->connection->bucket_alloc); APR_BUCKET_INSERT_BEFORE(b, tmp_b); apr_bucket_delete(b); b = tmp_b; } else { /* * We want the behaviour to be predictable. * Therefore we try to always error out if the * line length is larger than the limit, * regardless of the content of the line. So, * let's check if the remaining non-matching * string does not exceed the limit. */ if (space_left < b->length) return APR_ENOMEM; } } } else if (script->regexp) { int left = bytes; const char *pos = buff; char *repl; apr_size_t space_left = cfg->max_line_length; while (!ap_regexec_len(script->regexp, pos, left, AP_MAX_REG_MATCH, regm, 0)) { apr_status_t rv; have_match = 1; if (script->flatten && !force_quick) { /* check remaining buffer size */ /* Note that the last param in ap_varbuf_regsub below * must stay positive. If it gets 0, it would mean * unlimited space available. */ if (vb.strlen + regm[0].rm_so >= cfg->max_line_length) return APR_ENOMEM; /* copy bytes before the match */ if (regm[0].rm_so > 0) ap_varbuf_strmemcat(&vb, pos, regm[0].rm_so); /* add replacement string, last argument is unsigned! */ rv = ap_varbuf_regsub(&vb, script->replacement, pos, AP_MAX_REG_MATCH, regm, cfg->max_line_length - vb.strlen); if (rv != APR_SUCCESS) return rv; } else { apr_size_t repl_len; /* acount for string before the match */ if (space_left <= regm[0].rm_so) return APR_ENOMEM; space_left -= regm[0].rm_so; rv = ap_pregsub_ex(pool, &repl, script->replacement, pos, AP_MAX_REG_MATCH, regm, space_left); if (rv != APR_SUCCESS) return rv; repl_len = strlen(repl); space_left -= repl_len; len = (apr_size_t) (regm[0].rm_eo - regm[0].rm_so); SEDRMPATBCKT(b, regm[0].rm_so, tmp_b, len); tmp_b = apr_bucket_transient_create(repl, repl_len, f->r->connection->bucket_alloc); APR_BUCKET_INSERT_BEFORE(b, tmp_b); } /* * reset to past what we just did. pos now maps to b * again */ pos += regm[0].rm_eo; left -= regm[0].rm_eo; } if (have_match && script->flatten && !force_quick) { char *copy; /* Copy result plus the part after the last match into * a bucket. */ copy = ap_varbuf_pdup(pool, &vb, NULL, 0, pos, left, &len); tmp_b = apr_bucket_pool_create(copy, len, pool, f->r->connection->bucket_alloc); APR_BUCKET_INSERT_BEFORE(b, tmp_b); apr_bucket_delete(b); b = tmp_b; } } else { ap_assert(0); continue; } } } script++; } ap_varbuf_free(&vb); return APR_SUCCESS; }