예제 #1
0
static int cache_call_lua(server *srv, connection *con, plugin_data *p, buffer *cml_file) {
	buffer *b;
	char *c;

	/* cleanup basedir */
	b = p->baseurl;
	buffer_copy_buffer(b, con->uri.path);
	for (c = b->ptr + buffer_string_length(b); c > b->ptr && *c != '/'; c--);

	if (*c == '/') {
		buffer_string_set_length(b, c - b->ptr + 1);
	}

	b = p->basedir;
	buffer_copy_buffer(b, con->physical.path);
	for (c = b->ptr + buffer_string_length(b); c > b->ptr && *c != '/'; c--);

	if (*c == '/') {
		buffer_string_set_length(b, c - b->ptr + 1);
	}


	/* prepare variables
	 *   - cookie-based
	 *   - get-param-based
	 */
	return cache_parse_lua(srv, con, p, cml_file);
}
예제 #2
0
static void accesslog_append_escaped(buffer *dest, buffer *str) {
	char *ptr, *start, *end;

	/* replaces non-printable chars with \xHH where HH is the hex representation of the byte */
	/* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */
	if (buffer_string_is_empty(str)) return;
	buffer_string_prepare_append(dest, buffer_string_length(str));

	for (ptr = start = str->ptr, end = str->ptr + buffer_string_length(str); ptr < end; ptr++) {
		unsigned char const c = (unsigned char) *ptr;
		if (c >= ' ' && c <= '~' && c != '"' && c != '\\') {
			/* nothing to change, add later as one block */
		} else {
			/* copy previous part */
			if (start < ptr) {
				buffer_append_string_len(dest, start, ptr - start);
			}
			start = ptr + 1;

			switch (c) {
			case '"':
				BUFFER_APPEND_STRING_CONST(dest, "\\\"");
				break;
			case '\\':
				BUFFER_APPEND_STRING_CONST(dest, "\\\\");
				break;
			case '\b':
				BUFFER_APPEND_STRING_CONST(dest, "\\b");
				break;
			case '\n':
				BUFFER_APPEND_STRING_CONST(dest, "\\n");
				break;
			case '\r':
				BUFFER_APPEND_STRING_CONST(dest, "\\r");
				break;
			case '\t':
				BUFFER_APPEND_STRING_CONST(dest, "\\t");
				break;
			case '\v':
				BUFFER_APPEND_STRING_CONST(dest, "\\v");
				break;
			default: {
					/* non printable char => \xHH */
					char hh[5] = {'\\','x',0,0,0};
					char h = c / 16;
					hh[2] = (h > 9) ? (h - 10 + 'A') : (h + '0');
					h = c % 16;
					hh[3] = (h > 9) ? (h - 10 + 'A') : (h + '0');
					buffer_append_string_len(dest, &hh[0], 4);
				}
				break;
			}
		}
	}

	if (start < end) {
		buffer_append_string_len(dest, start, end - start);
	}
}
예제 #3
0
int network_writev_mem_chunks(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
	struct iovec chunks[MAX_CHUNKS];
	size_t num_chunks;
	off_t max_bytes = *p_max_bytes;
	off_t toSend;
	ssize_t r;
	UNUSED(con);

	force_assert(NULL != cq->first);
	force_assert(MEM_CHUNK == cq->first->type);

	{
		chunk const *c;

		toSend = 0;
		num_chunks = 0;
		for (c = cq->first; NULL != c && MEM_CHUNK == c->type && num_chunks < MAX_CHUNKS && toSend < max_bytes; c = c->next) {
			size_t c_len;

			force_assert(c->offset >= 0 && c->offset <= (off_t)buffer_string_length(c->mem));
			c_len = buffer_string_length(c->mem) - c->offset;
			if (c_len > 0) {
				toSend += c_len;

				chunks[num_chunks].iov_base = c->mem->ptr + c->offset;
				chunks[num_chunks].iov_len = c_len;

				++num_chunks;
			}
		}
	}

	if (0 == num_chunks) {
		chunkqueue_remove_finished_chunks(cq);
		return 0;
	}

	r = writev(fd, chunks, num_chunks);

	if (r < 0) switch (errno) {
	case EAGAIN:
	case EINTR:
		break;
	case EPIPE:
	case ECONNRESET:
		return -2;
	default:
		log_error_write(srv, __FILE__, __LINE__, "ssd",
				"writev failed:", strerror(errno), fd);
		return -1;
	}

	if (r >= 0) {
		*p_max_bytes -= r;
		chunkqueue_mark_written(cq, r);
	}

	return (r > 0 && r == toSend) ? 0 : -3;
}
예제 #4
0
void chunkqueue_get_memory(chunkqueue *cq, char **mem, size_t *len, size_t min_size, size_t alloc_size) {
    static const size_t REALLOC_MAX_SIZE = 256;
    chunk *c;
    buffer *b;
    char *dummy_mem;
    size_t dummy_len;

    force_assert(NULL != cq);
    if (NULL == mem) mem = &dummy_mem;
    if (NULL == len) len = &dummy_len;

    /* default values: */
    if (0 == min_size) min_size = 1024;
    if (0 == alloc_size) alloc_size = 4096;
    if (alloc_size < min_size) alloc_size = min_size;

    if (NULL != cq->last && MEM_CHUNK == cq->last->type) {
        size_t have;

        b = cq->last->mem;
        have = buffer_string_space(b);

        /* unused buffer: allocate space */
        if (buffer_string_is_empty(b)) {
            buffer_string_prepare_copy(b, alloc_size);
            have = buffer_string_space(b);
        }
        /* if buffer is really small just make it bigger */
        else if (have < min_size && b->size <= REALLOC_MAX_SIZE) {
            size_t cur_len = buffer_string_length(b);
            size_t new_size = cur_len + min_size, append;
            if (new_size < alloc_size) new_size = alloc_size;

            append = new_size - cur_len;
            if (append >= min_size) {
                buffer_string_prepare_append(b, append);
                have = buffer_string_space(b);
            }
        }

        /* return pointer into existing buffer if large enough */
        if (have >= min_size) {
            *mem = b->ptr + buffer_string_length(b);
            *len = have;
            return;
        }
    }

    /* allocate new chunk */
    c = chunkqueue_get_unused_chunk(cq);
    c->type = MEM_CHUNK;
    chunkqueue_append_chunk(cq, c);

    b = c->mem;
    buffer_string_prepare_append(b, alloc_size);

    *mem = b->ptr + buffer_string_length(b);
    *len = buffer_string_space(b);
}
예제 #5
0
int network_write_mem_chunk(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
	chunk* const c = cq->first;
	off_t c_len;
	ssize_t r;
	UNUSED(con);
	
	force_assert(NULL != c);
	force_assert(MEM_CHUNK == c->type);
	force_assert(c->offset >= 0 && c->offset <= (off_t)buffer_string_length(c->mem));

	c_len = buffer_string_length(c->mem) - c->offset;
	if (c_len > *p_max_bytes) c_len = *p_max_bytes;

	if (0 == c_len) {
		chunkqueue_remove_finished_chunks(cq);
		return 0;
	}

#if defined(__WIN32)
	if ((r = send(fd, c->mem->ptr + c->offset, c_len, 0)) < 0) {
		int lastError = WSAGetLastError();
		switch (lastError) {
		case WSAEINTR:
		case WSAEWOULDBLOCK:
			break;
		case WSAECONNRESET:
		case WSAETIMEDOUT:
		case WSAECONNABORTED:
			return -2;
		default:
			log_error_write(srv, __FILE__, __LINE__, "sdd",
				"send failed: ", lastError, fd);
			return -1;
		}
	}
#else /* __WIN32 */
	if ((r = write(fd, c->mem->ptr + c->offset, c_len)) < 0) {
		switch (errno) {
		case EAGAIN:
		case EINTR:
			break;
		case EPIPE:
		case ECONNRESET:
			return -2;
		default:
			log_error_write(srv, __FILE__, __LINE__, "ssd",
				"write failed:", strerror(errno), fd);
			return -1;
		}
	}
#endif /* __WIN32 */

	if (r >= 0) {
		*p_max_bytes -= r;
		chunkqueue_mark_written(cq, r);
	}

	return (r > 0 && r == c_len) ? 0 : -3;
}
예제 #6
0
파일: request.c 프로젝트: glensc/lighttpd
int http_request_header_finished(server *srv, connection *con) {
	UNUSED(srv);

	if (buffer_string_length(con->request.request) < 4) return 0;

	if (0 == memcmp(con->request.request->ptr + buffer_string_length(con->request.request) - 4, CONST_STR_LEN("\r\n\r\n"))) return 1;
	if (NULL != strstr(con->request.request->ptr, "\r\n\r\n")) return 1;

	return 0;
}
예제 #7
0
void http_response_xsendfile (server *srv, connection *con, buffer *path, const array *xdocroot) {
    const int status = con->http_status;
    int valid = 1;

    /* reset Content-Length, if set by backend
     * Content-Length might later be set to size of X-Sendfile static file,
     * determined by open(), fstat() to reduces race conditions if the file
     * is modified between stat() (stat_cache_get_entry()) and open(). */
    if (con->parsed_response & HTTP_CONTENT_LENGTH) {
        data_string *ds = (data_string *) array_get_element(con->response.headers, "Content-Length");
        if (ds) buffer_reset(ds->value);
        con->parsed_response &= ~HTTP_CONTENT_LENGTH;
        con->response.content_length = -1;
    }

    buffer_urldecode_path(path);
    buffer_path_simplify(path, path);
    if (con->conf.force_lowercase_filenames) {
        buffer_to_lower(path);
    }

    /* check that path is under xdocroot(s)
     * - xdocroot should have trailing slash appended at config time
     * - con->conf.force_lowercase_filenames is not a server-wide setting,
     *   and so can not be definitively applied to xdocroot at config time*/
    if (xdocroot->used) {
        size_t i, xlen = buffer_string_length(path);
        for (i = 0; i < xdocroot->used; ++i) {
            data_string *ds = (data_string *)xdocroot->data[i];
            size_t dlen = buffer_string_length(ds->value);
            if (dlen <= xlen
                    && (!con->conf.force_lowercase_filenames
                        ? 0 == memcmp(path->ptr, ds->value->ptr, dlen)
                        : 0 == strncasecmp(path->ptr, ds->value->ptr, dlen))) {
                break;
            }
        }
        if (i == xdocroot->used) {
            log_error_write(srv, __FILE__, __LINE__, "SBs",
                            "X-Sendfile (", path,
                            ") not under configured x-sendfile-docroot(s)");
            con->http_status = 403;
            valid = 0;
        }
    }

    if (valid) http_response_send_file(srv, con, path);

    if (con->http_status >= 400 && status < 300) {
        con->mode = DIRECT;
    } else if (0 != status && 200 != status) {
        con->http_status = status;
    }
}
예제 #8
0
void buffer_append_string_buffer(buffer *b, const buffer *src) {
	if (NULL == src) {
		buffer_append_string_len(b, NULL, 0);
	} else {
		buffer_append_string_len(b, src->ptr, buffer_string_length(src));
	}
}
예제 #9
0
static int split_get_params(array *get_params, buffer *qrystr) {
	size_t is_key = 1, klen = 0;
	char *key = qrystr->ptr, *val = NULL;

	if (buffer_string_is_empty(qrystr)) return 0;
	for (size_t i = 0, len = buffer_string_length(qrystr); i <= len; ++i) {
		switch(qrystr->ptr[i]) {
		case '=':
			if (is_key) {
				val = qrystr->ptr + i + 1;
				klen = (size_t)(qrystr->ptr + i - key);
				is_key = 0;
			}

			break;
		case '&':
		case '\0': /* fin symbol */
			if (!is_key) {
				/* we need at least a = since the last & */
				array_insert_key_value(get_params, key, klen, val, qrystr->ptr + i - val);
			}

			key = qrystr->ptr + i + 1;
			val = NULL;
			is_key = 1;
			break;
		}
	}

	return 0;
}
예제 #10
0
static buffer * cgi_get_handler(array *a, buffer *fn) {
	size_t k, s_len = buffer_string_length(fn);
	for (k = 0; k < a->used; ++k) {
		data_string *ds = (data_string *)a->data[k];
		size_t ct_len = buffer_string_length(ds->key);

		if (buffer_is_empty(ds->key)) continue;
		if (s_len < ct_len) continue;

		if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) {
			return ds->value;
		}
	}

	return NULL;
}
예제 #11
0
void buffer_copy_buffer(buffer *b, const buffer *src) {
	if (NULL == src || 0 == src->used) {
		buffer_string_prepare_copy(b, 0);
		b->used = 0; /* keep special empty state for now */
	} else {
		buffer_copy_string_len(b, src->ptr, buffer_string_length(src));
	}
}
예제 #12
0
static int proxy_create_env(server *srv, handler_ctx *hctx) {
	size_t i;

	connection *con   = hctx->remote_conn;
	buffer *b;

	/* build header */

	b = buffer_init();

	/* request line */
	buffer_copy_string(b, get_http_method_name(con->request.http_method));
	buffer_append_string_len(b, CONST_STR_LEN(" "));

	buffer_append_string_buffer(b, con->request.uri);
	buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.0\r\n"));

	proxy_append_header(con, "X-Forwarded-For", (char *)inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
	/* http_host is NOT is just a pointer to a buffer
	 * which is NULL if it is not set */
	if (!buffer_string_is_empty(con->request.http_host)) {
		proxy_set_header(con, "X-Host", con->request.http_host->ptr);
	}
	proxy_set_header(con, "X-Forwarded-Proto", con->uri.scheme->ptr);

	/* request header */
	for (i = 0; i < con->request.headers->used; i++) {
		data_string *ds;

		ds = (data_string *)con->request.headers->data[i];

		if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
			if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Connection"))) continue;
			if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Proxy-Connection"))) continue;

			buffer_append_string_buffer(b, ds->key);
			buffer_append_string_len(b, CONST_STR_LEN(": "));
			buffer_append_string_buffer(b, ds->value);
			buffer_append_string_len(b, CONST_STR_LEN("\r\n"));
		}
	}

	buffer_append_string_len(b, CONST_STR_LEN("\r\n"));

	hctx->wb->bytes_in += buffer_string_length(b);
	chunkqueue_append_buffer(hctx->wb, b);
	buffer_free(b);

	/* body */

	if (con->request.content_length) {
		chunkqueue *req_cq = con->request_content_queue;

		chunkqueue_steal(hctx->wb, req_cq, req_cq->bytes_in);
	}

	return 0;
}
예제 #13
0
static int mod_evhost_parse_host(connection *con,array *host) {
    register char *ptr = con->uri.authority->ptr + buffer_string_length(con->uri.authority);
    char *colon = ptr; /* needed to filter out the colon (if exists) */
    int first = 1;
    data_string *ds;
    int i;

    /* first, find the domain + tld */
    for(; ptr > con->uri.authority->ptr; ptr--) {
        if(*ptr == '.') {
            if(first) first = 0;
            else      break;
        } else if(*ptr == ':') {
            colon = ptr;
            first = 1;
        }
    }

    ds = data_string_init();
    buffer_copy_string_len(ds->key,CONST_STR_LEN("%0"));

    /* if we stopped at a dot, skip the dot */
    if (*ptr == '.') ptr++;
    buffer_copy_string_len(ds->value, ptr, colon-ptr);

    array_insert_unique(host,(data_unset *)ds);

    /* if the : is not the start of the authority, go on parsing the hostname */

    if (colon != con->uri.authority->ptr) {
        for(ptr = colon - 1, i = 1; ptr > con->uri.authority->ptr; ptr--) {
            if(*ptr == '.') {
                if (ptr != colon - 1) {
                    /* is something between the dots */
                    ds = data_string_init();
                    buffer_copy_string_len(ds->key,CONST_STR_LEN("%"));
                    buffer_append_int(ds->key, i++);
                    buffer_copy_string_len(ds->value,ptr+1,colon-ptr-1);

                    array_insert_unique(host,(data_unset *)ds);
                }
                colon = ptr;
            }
        }

        /* if the . is not the first charactor of the hostname */
        if (colon != ptr) {
            ds = data_string_init();
            buffer_copy_string_len(ds->key,CONST_STR_LEN("%"));
            buffer_append_int(ds->key, i /* ++ */);
            buffer_copy_string_len(ds->value,ptr,colon-ptr);

            array_insert_unique(host,(data_unset *)ds);
        }
    }

    return 0;
}
예제 #14
0
void chunkqueue_steal(chunkqueue *dest, chunkqueue *src, off_t len) {
	while (len > 0) {
		chunk *c = src->first;
		off_t clen = 0, use;

		if (NULL == c) break;

		switch (c->type) {
		case MEM_CHUNK:
			clen = buffer_string_length(c->mem);
			break;
		case FILE_CHUNK:
			clen = c->file.length;
			break;
		}
		force_assert(clen >= c->offset);
		clen -= c->offset;
		use = len >= clen ? clen : len;

		src->bytes_out += use;
		dest->bytes_in += use;
		len -= use;

		if (0 == clen) {
			/* drop empty chunk */
			src->first = c->next;
			if (c == src->last) src->last = NULL;
			chunkqueue_push_unused_chunk(src, c);
			continue;
		}

		if (use == clen) {
			/* move complete chunk */
			src->first = c->next;
			if (c == src->last) src->last = NULL;

			chunkqueue_append_chunk(dest, c);
			continue;
		}

		/* partial chunk with length "use" */

		switch (c->type) {
		case MEM_CHUNK:
			chunkqueue_append_mem(dest, c->mem->ptr + c->offset, use);
			break;
		case FILE_CHUNK:
			/* tempfile flag is in "last" chunk after the split */
			chunkqueue_append_file(dest, c->file.name, c->file.start + c->offset, use);
			break;
		}

		c->offset += use;
		force_assert(0 == len);
	}
}
예제 #15
0
static int build_doc_root(server *srv, connection *con, plugin_data *p, buffer *out, buffer *host) {
	stat_cache_entry *sce = NULL;
	force_assert(!buffer_string_is_empty(p->conf.server_root));

	buffer_string_prepare_copy(out, 127);
	buffer_copy_buffer(out, p->conf.server_root);

	if (!buffer_string_is_empty(host)) {
		/* a hostname has to start with a alpha-numerical character
		 * and must not contain a slash "/"
		 */
		char *dp;

		buffer_append_slash(out);

		if (NULL == (dp = strchr(host->ptr, ':'))) {
			buffer_append_string_buffer(out, host);
		} else {
			buffer_append_string_len(out, host->ptr, dp - host->ptr);
		}
	}
	buffer_append_slash(out);

	if (buffer_string_length(p->conf.document_root) > 1 && p->conf.document_root->ptr[0] == '/') {
		buffer_append_string_len(out, p->conf.document_root->ptr + 1, buffer_string_length(p->conf.document_root) - 1);
	} else {
		buffer_append_string_buffer(out, p->conf.document_root);
		buffer_append_slash(out);
	}

	if (HANDLER_ERROR == stat_cache_get_entry(srv, con, out, &sce)) {
		if (p->conf.debug) {
			log_error_write(srv, __FILE__, __LINE__, "sb",
					strerror(errno), out);
		}
		return -1;
	} else if (!S_ISDIR(sce->st.st_mode)) {
		return -1;
	}

	return 0;
}
예제 #16
0
파일: log.c 프로젝트: glensc/lighttpd
int log_error_write_multiline_buffer(server *srv, const char *filename, unsigned int line, buffer *multiline, const char *fmt, ...) {
	va_list ap;
	size_t prefix_len;
	buffer *b = srv->errorlog_buf;
	char *pos, *end, *current_line;

	if (buffer_string_is_empty(multiline)) return 0;

	if (-1 == log_buffer_prepare(b, srv, filename, line)) return 0;

	va_start(ap, fmt);
	log_buffer_append_printf(b, fmt, ap);
	va_end(ap);

	prefix_len = buffer_string_length(b);

	current_line = pos = multiline->ptr;
	end = multiline->ptr + buffer_string_length(multiline);

	for ( ; pos <= end ; ++pos) {
		switch (*pos) {
		case '\n':
		case '\r':
		case '\0': /* handles end of string */
			if (current_line < pos) {
				/* truncate to prefix */
				buffer_string_set_length(b, prefix_len);

				buffer_append_string_len(b, current_line, pos - current_line);
				log_write(srv, b);
			}
			current_line = pos + 1;
			break;
		default:
			break;
		}
	}

	return 0;
}
예제 #17
0
static int buffer_copy_dirname(buffer *dst, buffer *file) {
	size_t i;

	if (buffer_string_is_empty(file)) return -1;

	for (i = buffer_string_length(file); i > 0; i--) {
		if (file->ptr[i] == '/') {
			buffer_copy_string_len(dst, file->ptr, i);
			return 0;
		}
	}

	return -1;
}
예제 #18
0
static int split_get_params(array *get_params, buffer *qrystr) {
	size_t is_key = 1;
	size_t i, len;
	char *key = NULL, *val = NULL;

	key = qrystr->ptr;

	/* we need the \0 */
	len = buffer_string_length(qrystr);
	for (i = 0; i <= len; i++) {
		switch(qrystr->ptr[i]) {
		case '=':
			if (is_key) {
				val = qrystr->ptr + i + 1;

				qrystr->ptr[i] = '\0';

				is_key = 0;
			}

			break;
		case '&':
		case '\0': /* fin symbol */
			if (!is_key) {
				data_string *ds;
				/* we need at least a = since the last & */

				/* terminate the value */
				qrystr->ptr[i] = '\0';

				if (NULL == (ds = (data_string *)array_get_unused_element(get_params, TYPE_STRING))) {
					ds = data_string_init();
				}
				buffer_copy_string_len(ds->key, key, strlen(key));
				buffer_copy_string_len(ds->value, val, strlen(val));

				array_insert_unique(get_params, (data_unset *)ds);
			}

			key = qrystr->ptr + i + 1;
			val = NULL;
			is_key = 1;
			break;
		}
	}

	return 0;
}
예제 #19
0
static int mod_authn_gssapi_create_krb5_ccache(server *srv, connection *con, plugin_data *p, krb5_context kcontext, krb5_principal princ, krb5_ccache *ccache)
{
    buffer * const kccname = buffer_init_string("FILE:/tmp/krb5cc_gssapi_XXXXXX");
    char * const ccname    = kccname->ptr + sizeof("FILE:")-1;
    const size_t ccnamelen = buffer_string_length(kccname)-(sizeof("FILE:")-1);
    /*(future: might consider using server.upload-dirs instead of /tmp)*/
    /* coverity[secure_temp : FALSE] */
    int fd = mkstemp(ccname);
    if (fd < 0) {
        log_error_write(srv, __FILE__, __LINE__, "ss", "mkstemp():", strerror(errno));
        buffer_free(kccname);
        return -1;
    }
    close(fd);

    do {
        krb5_error_code problem;

        problem = krb5_cc_resolve(kcontext, kccname->ptr, ccache);
        if (problem) {
            mod_authn_gssapi_log_krb5_error(srv, __FILE__, __LINE__, "krb5_cc_resolve", NULL, kcontext, problem);
            break;
        }

        problem = krb5_cc_initialize(kcontext, *ccache, princ);
        if (problem) {
            mod_authn_gssapi_log_krb5_error(srv, __FILE__, __LINE__, "krb5_cc_initialize", kccname->ptr, kcontext, problem);
            break;
        }

        con->plugin_ctx[p->id] = kccname;

        array_set_key_value(con->environment, CONST_STR_LEN("KRB5CCNAME"), ccname, ccnamelen);
        array_set_key_value(con->request.headers, CONST_STR_LEN("X-Forwarded-Keytab"), ccname, ccnamelen);

        return 0;

    } while (0);

    if (*ccache) {
        krb5_cc_destroy(kcontext, *ccache);
        *ccache = NULL;
    }
    unlink(ccname);
    buffer_free(kccname);

    return -1;
}
예제 #20
0
static off_t chunk_remaining_length(const chunk *c) {
    off_t len = 0;
    switch (c->type) {
    case MEM_CHUNK:
        len = buffer_string_length(c->mem);
        break;
    case FILE_CHUNK:
        len = c->file.length;
        break;
    default:
        force_assert(c->type == MEM_CHUNK || c->type == FILE_CHUNK);
        break;
    }
    force_assert(c->offset <= len);
    return len - c->offset;
}
예제 #21
0
static void test_buffer_string_space(void) {
	buffer *b = buffer_init();
	size_t space;

	space = buffer_string_space(b);
	assert(0 == space);
	buffer_copy_string_len(b, CONST_STR_LEN(""));
	space = buffer_string_space(b);
	assert(space > 0);
	assert(space + buffer_string_length(b) == b->size - 1);
	buffer_commit(b, b->size - 1);
	assert(b->used == b->size);
	space = buffer_string_space(b);
	assert(0 == space);

	buffer_free(b);
}
예제 #22
0
static int cache_export_get_params(lua_State *L, int tbl, buffer *qrystr) {
	size_t is_key = 1;
	size_t i, len;
	char *key = NULL, *val = NULL;

	key = qrystr->ptr;

	/* we need the \0 */
	len = buffer_string_length(qrystr);
	for (i = 0; i <= len; i++) {
		switch(qrystr->ptr[i]) {
		case '=':
			if (is_key) {
				val = qrystr->ptr + i + 1;

				qrystr->ptr[i] = '\0';

				is_key = 0;
			}

			break;
		case '&':
		case '\0': /* fin symbol */
			if (!is_key) {
				/* we need at least a = since the last & */

				/* terminate the value */
				qrystr->ptr[i] = '\0';

				c_to_lua_push(L, tbl,
					key, strlen(key),
					val, strlen(val));
			}

			key = qrystr->ptr + i + 1;
			val = NULL;
			is_key = 1;
			break;
		}
	}

	return 0;
}
예제 #23
0
static void data_string_print(const data_unset *d, int depth) {
	data_string *ds = (data_string *)d;
	size_t i, len;
	UNUSED(depth);

	/* empty and uninitialized strings */
	if (buffer_string_is_empty(ds->value)) {
		fputs("\"\"", stdout);
		return;
	}

	/* print out the string as is, except prepend " with backslash */
	putc('"', stdout);
	len = buffer_string_length(ds->value);
	for (i = 0; i < len; i++) {
		unsigned char c = ds->value->ptr[i];
		if (c == '"') {
			fputs("\\\"", stdout);
		} else {
			putc(c, stdout);
		}
	}
	putc('"', stdout);
}
예제 #24
0
static int cgi_demux_response(server *srv, handler_ctx *hctx) {
	plugin_data *p    = hctx->plugin_data;
	connection  *con  = hctx->remote_conn;

	while(1) {
		int n;
		int toread;

#if defined(__WIN32)
		buffer_string_prepare_copy(hctx->response, 4 * 1024);
#else
		if (ioctl(con->fd, FIONREAD, &toread) || toread <= 4*1024) {
			buffer_string_prepare_copy(hctx->response, 4 * 1024);
		} else {
			if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
			buffer_string_prepare_copy(hctx->response, toread);
		}
#endif

		if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
			if (errno == EAGAIN || errno == EINTR) {
				/* would block, wait for signal */
				fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
				return FDEVENT_HANDLED_NOT_FINISHED;
			}
			/* error */
			log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
			return FDEVENT_HANDLED_ERROR;
		}

		if (n == 0) {
			/* read finished */
			return FDEVENT_HANDLED_FINISHED;
		}

		buffer_commit(hctx->response, n);

		/* split header from body */

		if (con->file_started == 0) {
			int is_header = 0;
			int is_header_end = 0;
			size_t last_eol = 0;
			size_t i, header_len;

			buffer_append_string_buffer(hctx->response_header, hctx->response);

			/**
			 * we have to handle a few cases:
			 *
			 * nph:
			 * 
			 *   HTTP/1.0 200 Ok\n
			 *   Header: Value\n
			 *   \n
			 *
			 * CGI:
			 *   Header: Value\n
			 *   Status: 200\n
			 *   \n
			 *
			 * and different mixes of \n and \r\n combinations
			 * 
			 * Some users also forget about CGI and just send a response and hope 
			 * we handle it. No headers, no header-content seperator
			 * 
			 */
			
			/* nph (non-parsed headers) */
			if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1;

			header_len = buffer_string_length(hctx->response_header);
			for (i = 0; !is_header_end && i < header_len; i++) {
				char c = hctx->response_header->ptr[i];

				switch (c) {
				case ':':
					/* we found a colon
					 *
					 * looks like we have a normal header 
					 */
					is_header = 1;
					break;
				case '\n':
					/* EOL */
					if (is_header == 0) {
						/* we got a EOL but we don't seem to got a HTTP header */

						is_header_end = 1;

						break;
					}

					/**
					 * check if we saw a \n(\r)?\n sequence 
					 */
					if (last_eol > 0 && 
					    ((i - last_eol == 1) || 
					     (i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) {
						is_header_end = 1;
						break;
					}

					last_eol = i;

					break;
				}
			}

			if (is_header_end) {
				if (!is_header) {
					/* no header, but a body */
					if (0 != http_chunk_append_buffer(srv, con, hctx->response_header)) {
						return FDEVENT_HANDLED_ERROR;
					}
				} else {
					const char *bstart;
					size_t blen;

					/* the body starts after the EOL */
					bstart = hctx->response_header->ptr + i;
					blen = header_len - i;

					/**
					 * i still points to the char after the terminating EOL EOL
					 *
					 * put it on the last \n again
					 */
					i--;

					/* string the last \r?\n */
					if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) {
						i--;
					}

					buffer_string_set_length(hctx->response_header, i);

					/* parse the response header */
					cgi_response_parse(srv, con, p, hctx->response_header);

					if (con->http_status >= 300 && con->http_status < 400) {
						/*(con->parsed_response & HTTP_LOCATION)*/
						data_string *ds;
						if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "Location"))
						    && ds->value->ptr[0] == '/') {
							if (++con->loops_per_request > 5) {
								log_error_write(srv, __FILE__, __LINE__, "sb", "too many internal loops while processing request:", con->request.orig_uri);
								con->http_status = 500; /* Internal Server Error */
								con->mode = DIRECT;
								return FDEVENT_HANDLED_FINISHED;
							}

							buffer_copy_buffer(con->request.uri, ds->value);

							if (con->request.content_length) {
								if (con->request.content_length != con->request_content_queue->bytes_in) {
									con->keep_alive = 0;
								}
								con->request.content_length = 0;
								chunkqueue_reset(con->request_content_queue);
							}

							if (con->http_status != 307 && con->http_status != 308) {
								/* Note: request body (if any) sent to initial dynamic handler
								 * and is not available to the internal redirect */
								con->request.http_method = HTTP_METHOD_GET;
							}

							connection_response_reset(srv, con); /*(includes con->http_status = 0)*/

							con->mode = DIRECT;
							return FDEVENT_HANDLED_COMEBACK;
						}
					}

					if (hctx->conf.xsendfile_allow) {
						data_string *ds;
						if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-Sendfile"))) {
							http_response_xsendfile(srv, con, ds->value, hctx->conf.xsendfile_docroot);
							return FDEVENT_HANDLED_FINISHED;
						}
					}

					if (blen > 0) {
						if (0 != http_chunk_append_mem(srv, con, bstart, blen)) {
							return FDEVENT_HANDLED_ERROR;
						}
					}
				}

				con->file_started = 1;
			} else {
				/*(reuse MAX_HTTP_REQUEST_HEADER as max size for response headers from backends)*/
				if (header_len > MAX_HTTP_REQUEST_HEADER) {
					log_error_write(srv, __FILE__, __LINE__, "sb", "response headers too large for", con->uri.path);
					con->http_status = 502; /* Bad Gateway */
					con->mode = DIRECT;
					return FDEVENT_HANDLED_FINISHED;
				}
			}
		} else {
			if (0 != http_chunk_append_buffer(srv, con, hctx->response)) {
				return FDEVENT_HANDLED_ERROR;
			}
			if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
			    && chunkqueue_length(con->write_queue) > 65536 - 4096) {
				if (!con->is_writable) {
					/*(defer removal of FDEVENT_IN interest since
					 * connection_state_machine() might be able to send data
					 * immediately, unless !con->is_writable, where
					 * connection_state_machine() might not loop back to call
					 * mod_cgi_handle_subrequest())*/
					fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
				}
				break;
			}
		}

#if 0
		log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
#endif
	}

	return FDEVENT_HANDLED_NOT_FINISHED;
}
예제 #25
0
static int cgi_write_request(server *srv, handler_ctx *hctx, int fd) {
	connection *con = hctx->remote_conn;
	chunkqueue *cq = con->request_content_queue;
	chunk *c;

	/* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms.
	 * solution: if this is still a problem on windows, then substitute
	 * socketpair() for pipe() and closesocket() for close() on windows.
	 */

	for (c = cq->first; c; c = cq->first) {
		ssize_t r = -1;

		switch(c->type) {
		case FILE_CHUNK:
			r = cgi_write_file_chunk_mmap(srv, con, fd, cq);
			break;

		case MEM_CHUNK:
			if ((r = write(fd, c->mem->ptr + c->offset, buffer_string_length(c->mem) - c->offset)) < 0) {
				switch(errno) {
				case EAGAIN:
				case EINTR:
					/* ignore and try again */
					r = 0;
					break;
				case EPIPE:
				case ECONNRESET:
					/* connection closed */
					r = -2;
					break;
				default:
					/* fatal error */
					log_error_write(srv, __FILE__, __LINE__, "ss", "write failed due to: ", strerror(errno));
					r = -1;
					break;
				}
			} else if (r > 0) {
				chunkqueue_mark_written(cq, r);
			}
			break;
		}

		if (0 == r) break; /*(might block)*/

		switch (r) {
		case -1:
			/* fatal error */
			return -1;
		case -2:
			/* connection reset */
			log_error_write(srv, __FILE__, __LINE__, "s", "failed to send post data to cgi, connection closed by CGI");
			/* skip all remaining data */
			chunkqueue_mark_written(cq, chunkqueue_length(cq));
			break;
		default:
			break;
		}
	}

	if (cq->bytes_out == (off_t)con->request.content_length) {
		/* sent all request body input */
		/* close connection to the cgi-script */
		if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/
			--srv->cur_fds;
			if (close(fd)) {
				log_error_write(srv, __FILE__, __LINE__, "sds", "cgi stdin close failed ", fd, strerror(errno));
			}
		} else {
			cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/
		}
	} else {
		off_t cqlen = cq->bytes_in - cq->bytes_out;
		if (cq->bytes_in != con->request.content_length && cqlen < 65536 - 16384) {
			/*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
			if (!(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) {
				con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN;
				con->is_readable = 1; /* trigger optimistic read from client */
			}
		}
		if (-1 == hctx->fdtocgi) { /*(not registered yet)*/
			hctx->fdtocgi = fd;
			hctx->fde_ndx_tocgi = -1;
			fdevent_register(srv->ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx);
		}
		if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/
			if ((fdevent_event_get_interest(srv->ev, hctx->fdtocgi) & FDEVENT_OUT)) {
				fdevent_event_set(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi, 0);
			}
		} else {
			/* more request body remains to be sent to CGI so register for fdevents */
			fdevent_event_set(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi, FDEVENT_OUT);
		}
	}

	return 0;
}
예제 #26
0
static handler_t mod_redirect_uri_handler(server *srv, connection *con, void *p_data) {
#ifdef HAVE_PCRE_H
	plugin_data *p = p_data;
	size_t i;

	/*
	 * REWRITE URL
	 *
	 * e.g. redirect /base/ to /index.php?section=base
	 *
	 */

	mod_redirect_patch_connection(srv, con, p);

	buffer_copy_buffer(p->match_buf, con->request.uri);

	for (i = 0; i < p->conf.redirect->used; i++) {
		pcre *match;
		pcre_extra *extra;
		const char *pattern;
		size_t pattern_len;
		int n;
		pcre_keyvalue *kv = p->conf.redirect->kv[i];
# define N 10
		int ovec[N * 3];

		match       = kv->key;
		extra       = kv->key_extra;
		pattern     = kv->value->ptr;
		pattern_len = buffer_string_length(kv->value);

		if ((n = pcre_exec(match, extra, CONST_BUF_LEN(p->match_buf), 0, 0, ovec, 3 * N)) < 0) {
			if (n != PCRE_ERROR_NOMATCH) {
				log_error_write(srv, __FILE__, __LINE__, "sd",
						"execution error while matching: ", n);
				return HANDLER_ERROR;
			}
		} else {
			const char **list;
			size_t start;
			size_t k;

			/* it matched */
			pcre_get_substring_list(p->match_buf->ptr, ovec, n, &list);

			/* search for $[0-9] */

			buffer_reset(p->location);

			start = 0;
			for (k = 0; k + 1 < pattern_len; k++) {
				if (pattern[k] == '$' || pattern[k] == '%') {
					/* got one */

					size_t num = pattern[k + 1] - '0';

					buffer_append_string_len(p->location, pattern + start, k - start);

					if (!isdigit((unsigned char)pattern[k + 1])) {
						/* enable escape: "%%" => "%", "%a" => "%a", "$$" => "$" */
						buffer_append_string_len(p->location, pattern+k, pattern[k] == pattern[k+1] ? 1 : 2);
					} else if (pattern[k] == '$') {
						/* n is always > 0 */
						if (num < (size_t)n) {
							buffer_append_string(p->location, list[num]);
						}
					} else if (p->conf.context == NULL) {
						/* we have no context, we are global */
						log_error_write(srv, __FILE__, __LINE__, "sb",
								"used a rewrite containing a %[0-9]+ in the global scope, ignored:",
								kv->value);
					} else {
						config_append_cond_match_buffer(con, p->conf.context, p->location, num);
					}

					k++;
					start = k + 1;
				}
			}

			buffer_append_string_len(p->location, pattern + start, pattern_len - start);

			pcre_free(list);

			response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->location));

			con->http_status = p->conf.redirect_code > 99 && p->conf.redirect_code < 1000 ? p->conf.redirect_code : 301;
			con->mode = DIRECT;
			con->file_finished = 1;

			return HANDLER_FINISHED;
		}
	}
#undef N

#else
	UNUSED(srv);
	UNUSED(con);
	UNUSED(p_data);
#endif

	return HANDLER_GO_ON;
}
예제 #27
0
handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) {
#ifdef HAVE_FAM_H
	fam_dir_entry *fam_dir = NULL;
	int dir_ndx = -1;
	splay_tree *dir_node = NULL;
#endif
	stat_cache_entry *sce = NULL;
	stat_cache *sc;
	struct stat st;
	size_t k;
	int fd;
	struct stat lst;
#ifdef DEBUG_STAT_CACHE
	size_t i;
#endif

	int file_ndx;
	splay_tree *file_node = NULL;

	*ret_sce = NULL;

	/*
	 * check if the directory for this file has changed
	 */

	sc = srv->stat_cache;

	buffer_copy_buffer(sc->hash_key, name);
	buffer_append_int(sc->hash_key, con->conf.follow_symlink);

	file_ndx = hashme(sc->hash_key);
	sc->files = splaytree_splay(sc->files, file_ndx);

#ifdef DEBUG_STAT_CACHE
	for (i = 0; i < ctrl.used; i++) {
		if (ctrl.ptr[i] == file_ndx) break;
	}
#endif

	if (sc->files && (sc->files->key == file_ndx)) {
#ifdef DEBUG_STAT_CACHE
		/* it was in the cache */
		force_assert(i < ctrl.used);
#endif

		/* we have seen this file already and
		 * don't stat() it again in the same second */

		file_node = sc->files;

		sce = file_node->data;

		/* check if the name is the same, we might have a collision */

		if (buffer_is_equal(name, sce->name)) {
			if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) {
				if (sce->stat_ts == srv->cur_ts) {
					*ret_sce = sce;
					return HANDLER_GO_ON;
				}
			}
		} else {
			/* oops, a collision,
			 *
			 * file_node is used by the FAM check below to see if we know this file
			 * and if we can save a stat().
			 *
			 * BUT, the sce is not reset here as the entry into the cache is ok, we
			 * it is just not pointing to our requested file.
			 *
			 *  */

			file_node = NULL;
		}
	} else {
#ifdef DEBUG_STAT_CACHE
		if (i != ctrl.used) {
			log_error_write(srv, __FILE__, __LINE__, "xSB",
				file_ndx, "was already inserted but not found in cache, ", name);
		}
		force_assert(i == ctrl.used);
#endif
	}

#ifdef HAVE_FAM_H
	/* dir-check */
	if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
		if (0 != buffer_copy_dirname(sc->dir_name, name)) {
			log_error_write(srv, __FILE__, __LINE__, "sb",
				"no '/' found in filename:", name);
			return HANDLER_ERROR;
		}

		buffer_copy_buffer(sc->hash_key, sc->dir_name);
		buffer_append_int(sc->hash_key, con->conf.follow_symlink);

		dir_ndx = hashme(sc->hash_key);

		sc->dirs = splaytree_splay(sc->dirs, dir_ndx);

		if (sc->dirs && (sc->dirs->key == dir_ndx)) {
			dir_node = sc->dirs;
		}

		if (dir_node && file_node) {
			/* we found a file */

			sce = file_node->data;
			fam_dir = dir_node->data;

			if (fam_dir->version == sce->dir_version) {
				/* the stat()-cache entry is still ok */

				*ret_sce = sce;
				return HANDLER_GO_ON;
			}
		}
	}
#endif

	/*
	 * *lol*
	 * - open() + fstat() on a named-pipe results in a (intended) hang.
	 * - stat() if regular file + open() to see if we can read from it is better
	 *
	 * */
	if (-1 == stat(name->ptr, &st)) {
		return HANDLER_ERROR;
	}


	if (S_ISREG(st.st_mode)) {
		/* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */
		if (name->ptr[buffer_string_length(name) - 1] == '/') {
			errno = ENOTDIR;
			return HANDLER_ERROR;
		}

		/* try to open the file to check if we can read it */
		if (-1 == (fd = open(name->ptr, O_RDONLY))) {
			return HANDLER_ERROR;
		}
		close(fd);
	}

	if (NULL == sce) {
#ifdef DEBUG_STAT_CACHE
		int osize = splaytree_size(sc->files);
#endif

		sce = stat_cache_entry_init();
		buffer_copy_buffer(sce->name, name);

		sc->files = splaytree_insert(sc->files, file_ndx, sce);
#ifdef DEBUG_STAT_CACHE
		if (ctrl.size == 0) {
			ctrl.size = 16;
			ctrl.used = 0;
			ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr));
		} else if (ctrl.size == ctrl.used) {
			ctrl.size += 16;
			ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr));
		}

		ctrl.ptr[ctrl.used++] = file_ndx;

		force_assert(sc->files);
		force_assert(sc->files->data == sce);
		force_assert(osize + 1 == splaytree_size(sc->files));
#endif
	}

	sce->st = st;
	sce->stat_ts = srv->cur_ts;

	/* catch the obvious symlinks
	 *
	 * this is not a secure check as we still have a race-condition between
	 * the stat() and the open. We can only solve this by
	 * 1. open() the file
	 * 2. fstat() the fd
	 *
	 * and keeping the file open for the rest of the time. But this can
	 * only be done at network level.
	 *
	 * per default it is not a symlink
	 * */
#ifdef HAVE_LSTAT
	sce->is_symlink = 0;

	/* we want to only check for symlinks if we should block symlinks.
	 */
	if (!con->conf.follow_symlink) {
		if (stat_cache_lstat(srv, name, &lst)  == 0) {
#ifdef DEBUG_STAT_CACHE
				log_error_write(srv, __FILE__, __LINE__, "sb",
						"found symlink", name);
#endif
				sce->is_symlink = 1;
		}

		/*
		 * we assume "/" can not be symlink, so
		 * skip the symlink stuff if our path is /
		 **/
		else if (buffer_string_length(name) > 1) {
			buffer *dname;
			char *s_cur;

			dname = buffer_init();
			buffer_copy_buffer(dname, name);

			while ((s_cur = strrchr(dname->ptr, '/'))) {
				buffer_string_set_length(dname, s_cur - dname->ptr);
				if (dname->ptr == s_cur) {
#ifdef DEBUG_STAT_CACHE
					log_error_write(srv, __FILE__, __LINE__, "s", "reached /");
#endif
					break;
				}
#ifdef DEBUG_STAT_CACHE
				log_error_write(srv, __FILE__, __LINE__, "sbs",
						"checking if", dname, "is a symlink");
#endif
				if (stat_cache_lstat(srv, dname, &lst)  == 0) {
					sce->is_symlink = 1;
#ifdef DEBUG_STAT_CACHE
					log_error_write(srv, __FILE__, __LINE__, "sb",
							"found symlink", dname);
#endif
					break;
				};
			};
			buffer_free(dname);
		};
	};
#endif

	if (S_ISREG(st.st_mode)) {
		/* determine mimetype */
		buffer_reset(sce->content_type);
#if defined(HAVE_XATTR) || defined(HAVE_EXTATTR)
		if (con->conf.use_xattr) {
			stat_cache_attr_get(sce->content_type, name->ptr);
		}
#endif
		/* xattr did not set a content-type. ask the config */
		if (buffer_string_is_empty(sce->content_type)) {
			size_t namelen = buffer_string_length(name);

			for (k = 0; k < con->conf.mimetypes->used; k++) {
				data_string *ds = (data_string *)con->conf.mimetypes->data[k];
				buffer *type = ds->key;
				size_t typelen = buffer_string_length(type);

				if (buffer_is_empty(type)) continue;

				/* check if the right side is the same */
				if (typelen > namelen) continue;

				if (0 == strncasecmp(name->ptr + namelen - typelen, type->ptr, typelen)) {
					buffer_copy_buffer(sce->content_type, ds->value);
					break;
				}
			}
		}
		etag_create(sce->etag, &(sce->st), con->etag_flags);
	} else if (S_ISDIR(st.st_mode)) {
		etag_create(sce->etag, &(sce->st), con->etag_flags);
	}

#ifdef HAVE_FAM_H
	if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
		/* is this directory already registered ? */
		if (!dir_node) {
			fam_dir = fam_dir_entry_init();

			buffer_copy_buffer(fam_dir->name, sc->dir_name);

			fam_dir->version = 1;

			fam_dir->req = calloc(1, sizeof(FAMRequest));

			if (0 != FAMMonitorDirectory(&sc->fam, fam_dir->name->ptr,
						     fam_dir->req, fam_dir)) {

				log_error_write(srv, __FILE__, __LINE__, "sbsbs",
						"monitoring dir failed:",
						fam_dir->name, 
						"file:", name,
						FamErrlist[FAMErrno]);

				fam_dir_entry_free(&sc->fam, fam_dir);
				fam_dir = NULL;
			} else {
				int osize = 0;

				if (sc->dirs) {
					osize = sc->dirs->size;
				}

				sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir);
				force_assert(sc->dirs);
				force_assert(sc->dirs->data == fam_dir);
				force_assert(osize == (sc->dirs->size - 1));
			}
		} else {
			fam_dir = dir_node->data;
		}

		/* bind the fam_fc to the stat() cache entry */

		if (fam_dir) {
			sce->dir_version = fam_dir->version;
		}
	}
#endif

	*ret_sce = sce;

	return HANDLER_GO_ON;
}
예제 #28
0
static handler_t deflate_compress_response(server *srv, connection *con, handler_ctx *hctx) {
	off_t len, max;
	int close_stream;

	/* move all chunk from write_queue into our in_queue, then adjust
	 * counters since con->write_queue is reused for compressed output */
	len = chunkqueue_length(con->write_queue);
	chunkqueue_remove_finished_chunks(con->write_queue);
	chunkqueue_append_chunkqueue(hctx->in_queue, con->write_queue);
	con->write_queue->bytes_in  -= len;
	con->write_queue->bytes_out -= len;

	max = chunkqueue_length(hctx->in_queue);
      #if 0
	/* calculate max bytes to compress for this call */
	if (p->conf.sync_flush && max > (len = p->conf.work_block_size << 10)) {
		max = len;
	}
      #endif

	/* Compress chunks from in_queue into chunks for write_queue */
	while (max) {
		chunk *c = hctx->in_queue->first;

		switch(c->type) {
		case MEM_CHUNK:
			len = buffer_string_length(c->mem) - c->offset;
			if (len > max) len = max;
			if (mod_deflate_compress(srv, con, hctx, (unsigned char *)c->mem->ptr+c->offset, len) < 0) {
				log_error_write(srv, __FILE__, __LINE__, "s",
						"compress failed.");
				return HANDLER_ERROR;
			}
			break;
		case FILE_CHUNK:
			len = c->file.length - c->offset;
			if (len > max) len = max;
			if ((len = mod_deflate_file_chunk(srv, con, hctx, c, len)) < 0) {
				log_error_write(srv, __FILE__, __LINE__, "s",
						"compress file chunk failed.");
				return HANDLER_ERROR;
			}
			break;
		default:
			log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known");
			return HANDLER_ERROR;
		}

		max -= len;
		chunkqueue_mark_written(hctx->in_queue, len);
	}

	/*(currently should always be true)*/
	/*(current implementation requires response be complete)*/
	close_stream = (con->file_finished && chunkqueue_is_empty(hctx->in_queue));
	if (mod_deflate_stream_flush(srv, con, hctx, close_stream) < 0) {
		log_error_write(srv, __FILE__, __LINE__, "s", "flush error");
		return HANDLER_ERROR;
	}

	return close_stream ? HANDLER_FINISHED : HANDLER_GO_ON;
}
예제 #29
0
static int load_next_chunk(server *srv, connection *con, chunkqueue *cq, off_t max_bytes, const char **data, size_t *data_len) {
	chunk * const c = cq->first;

#define LOCAL_SEND_BUFSIZE (64 * 1024)
	/* this is a 64k sendbuffer
	 *
	 * it has to stay at the same location all the time to satisfy the needs
	 * of SSL_write to pass the SAME parameter in case of a _WANT_WRITE
	 *
	 * the buffer is allocated once, is NOT realloced and is NOT freed at shutdown
	 * -> we expect a 64k block to 'leak' in valgrind
	 * */
	static char *local_send_buffer = NULL;

	force_assert(NULL != c);

	switch (c->type) {
	case MEM_CHUNK:
		{
			size_t have;

			force_assert(c->offset >= 0 && c->offset <= (off_t)buffer_string_length(c->mem));

			have = buffer_string_length(c->mem) - c->offset;
			if ((off_t) have > max_bytes) have = max_bytes;

			*data = c->mem->ptr + c->offset;
			*data_len = have;
		}
		return 0;

	case FILE_CHUNK:
		if (NULL == local_send_buffer) {
			local_send_buffer = malloc(LOCAL_SEND_BUFSIZE);
			force_assert(NULL != local_send_buffer);
		}

		if (0 != network_open_file_chunk(srv, con, cq)) return -1;

		{
			off_t offset, toSend;

			force_assert(c->offset >= 0 && c->offset <= c->file.length);
			offset = c->file.start + c->offset;
			toSend = c->file.length - c->offset;

			if (toSend > LOCAL_SEND_BUFSIZE) toSend = LOCAL_SEND_BUFSIZE;
			if (toSend > max_bytes) toSend = max_bytes;

			if (-1 == lseek(c->file.fd, offset, SEEK_SET)) {
				log_error_write(srv, __FILE__, __LINE__, "ss", "lseek: ", strerror(errno));
				return -1;
			}
			if (-1 == (toSend = read(c->file.fd, local_send_buffer, toSend))) {
				log_error_write(srv, __FILE__, __LINE__, "ss", "read: ", strerror(errno));
				return -1;
			}

			*data = local_send_buffer;
			*data_len = toSend;
		}
		return 0;
	}

	return -1;
}
예제 #30
0
static int http_list_directory(server *srv, connection *con, plugin_data *p, buffer *dir) {
	DIR *dp;
	buffer *out;
	struct dirent *dent;
	struct stat st;
	char *path, *path_file;
	size_t i;
	int hide_dotfiles = p->conf.hide_dot_files;
	dirls_list_t dirs, files, *list;
	dirls_entry_t *tmp;
	char sizebuf[sizeof("999.9K")];
	char datebuf[sizeof("2005-Jan-01 22:23:24")];
	size_t k;
	const char *content_type;
	long name_max;
#if defined(HAVE_XATTR) || defined(HAVE_EXTATTR)
	char attrval[128];
	int attrlen;
#endif
#ifdef HAVE_LOCALTIME_R
	struct tm tm;
#endif

	if (buffer_string_is_empty(dir)) return -1;

	i = buffer_string_length(dir);

#ifdef HAVE_PATHCONF
	if (0 >= (name_max = pathconf(dir->ptr, _PC_NAME_MAX))) {
		/* some broken fs (fuse) return 0 instead of -1 */
#ifdef NAME_MAX
		name_max = NAME_MAX;
#else
		name_max = 255; /* stupid default */
#endif
	}
#elif defined __WIN32
	name_max = FILENAME_MAX;
#else
	name_max = NAME_MAX;
#endif

	path = malloc(i + name_max + 1);
	force_assert(NULL != path);
	memcpy(path, dir->ptr, i+1);
	path_file = path + i;

	if (NULL == (dp = opendir(path))) {
		log_error_write(srv, __FILE__, __LINE__, "sbs",
			"opendir failed:", dir, strerror(errno));

		free(path);
		return -1;
	}

	dirs.ent   = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE);
	force_assert(dirs.ent);
	dirs.size  = DIRLIST_BLOB_SIZE;
	dirs.used  = 0;
	files.ent  = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE);
	force_assert(files.ent);
	files.size = DIRLIST_BLOB_SIZE;
	files.used = 0;

	while ((dent = readdir(dp)) != NULL) {
		unsigned short exclude_match = 0;

		if (dent->d_name[0] == '.') {
			if (hide_dotfiles)
				continue;
			if (dent->d_name[1] == '\0')
				continue;
			if (dent->d_name[1] == '.' && dent->d_name[2] == '\0')
				continue;
		}

		if (p->conf.hide_readme_file && !buffer_string_is_empty(p->conf.show_readme)) {
			if (strcmp(dent->d_name, p->conf.show_readme->ptr) == 0)
				continue;
		}
		if (p->conf.hide_header_file && !buffer_string_is_empty(p->conf.show_header)) {
			if (strcmp(dent->d_name, p->conf.show_header->ptr) == 0)
				continue;
		}

		/* compare d_name against excludes array
		 * elements, skipping any that match.
		 */
#ifdef HAVE_PCRE_H
		for(i = 0; i < p->conf.excludes->used; i++) {
			int n;
#define N 10
			int ovec[N * 3];
			pcre *regex = p->conf.excludes->ptr[i]->regex;

			if ((n = pcre_exec(regex, NULL, dent->d_name,
				    strlen(dent->d_name), 0, 0, ovec, 3 * N)) < 0) {
				if (n != PCRE_ERROR_NOMATCH) {
					log_error_write(srv, __FILE__, __LINE__, "sd",
						"execution error while matching:", n);

					/* aborting would require a lot of manual cleanup here.
					 * skip instead (to not leak names that break pcre matching)
					 */
					exclude_match = 1;
					break;
				}
			}
			else {
				exclude_match = 1;
				break;
			}
		}

		if (exclude_match) {
			continue;
		}
#endif

		i = strlen(dent->d_name);

		/* NOTE: the manual says, d_name is never more than NAME_MAX
		 *       so this should actually not be a buffer-overflow-risk
		 */
		if (i > (size_t)name_max) continue;

		memcpy(path_file, dent->d_name, i + 1);
		if (stat(path, &st) != 0)
			continue;

		list = &files;
		if (S_ISDIR(st.st_mode))
			list = &dirs;

		if (list->used == list->size) {
			list->size += DIRLIST_BLOB_SIZE;
			list->ent   = (dirls_entry_t**) realloc(list->ent, sizeof(dirls_entry_t*) * list->size);
			force_assert(list->ent);
		}

		tmp = (dirls_entry_t*) malloc(sizeof(dirls_entry_t) + 1 + i);
		tmp->mtime = st.st_mtime;
		tmp->size  = st.st_size;
		tmp->namelen = i;
		memcpy(DIRLIST_ENT_NAME(tmp), dent->d_name, i + 1);

		list->ent[list->used++] = tmp;
	}
	closedir(dp);

	if (dirs.used) http_dirls_sort(dirs.ent, dirs.used);

	if (files.used) http_dirls_sort(files.ent, files.used);

	out = buffer_init();
	http_list_directory_header(srv, con, p, out);

	/* directories */
	for (i = 0; i < dirs.used; i++) {
		tmp = dirs.ent[i];

#ifdef HAVE_LOCALTIME_R
		localtime_r(&(tmp->mtime), &tm);
		strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm);
#else
		strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime)));
#endif

		buffer_append_string_len(out, CONST_STR_LEN("<tr class=\"d\"><td class=\"n\"><a href=\""));
		buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART);
		buffer_append_string_len(out, CONST_STR_LEN("/\">"));
		buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML);
		buffer_append_string_len(out, CONST_STR_LEN("</a>/</td><td class=\"m\">"));
		buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1);
		buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">- &nbsp;</td><td class=\"t\">Directory</td></tr>\n"));

		free(tmp);
	}

	/* files */
	for (i = 0; i < files.used; i++) {
		tmp = files.ent[i];

		content_type = NULL;
#if defined(HAVE_XATTR)
		if (con->conf.use_xattr) {
			memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1);
			attrlen = sizeof(attrval) - 1;
			if (attr_get(path, srv->srvconf.xattr_name->ptr, attrval, &attrlen, 0) == 0) {
				attrval[attrlen] = '\0';
				content_type = attrval;
			}
		}
#elif defined(HAVE_EXTATTR)
		if (con->conf.use_xattr) {
			memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1);
			if(-1 != (attrlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, srv->srvconf.xattr_name->ptr, attrval, sizeof(attrval)-1))) {
				attrval[attrlen] = '\0';
				content_type = attrval;
			}
		}
#endif

		if (content_type == NULL) {
			content_type = "application/octet-stream";
			for (k = 0; k < con->conf.mimetypes->used; k++) {
				data_string *ds = (data_string *)con->conf.mimetypes->data[k];
				size_t ct_len;

				if (buffer_is_empty(ds->key))
					continue;

				ct_len = buffer_string_length(ds->key);
				if (tmp->namelen < ct_len)
					continue;

				if (0 == strncasecmp(DIRLIST_ENT_NAME(tmp) + tmp->namelen - ct_len, ds->key->ptr, ct_len)) {
					content_type = ds->value->ptr;
					break;
				}
			}
		}

#ifdef HAVE_LOCALTIME_R
		localtime_r(&(tmp->mtime), &tm);
		strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm);
#else
		strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime)));
#endif
		http_list_directory_sizefmt(sizebuf, sizeof(sizebuf), tmp->size);

		buffer_append_string_len(out, CONST_STR_LEN("<tr><td class=\"n\"><a href=\""));
		buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART);
		buffer_append_string_len(out, CONST_STR_LEN("\">"));
		buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML);
		buffer_append_string_len(out, CONST_STR_LEN("</a></td><td class=\"m\">"));
		buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1);
		buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">"));
		buffer_append_string(out, sizebuf);
		buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"t\">"));
		buffer_append_string(out, content_type);
		buffer_append_string_len(out, CONST_STR_LEN("</td></tr>\n"));

		free(tmp);
	}

	free(files.ent);
	free(dirs.ent);
	free(path);

	http_list_directory_footer(srv, con, p, out);

	/* Insert possible charset to Content-Type */
	if (buffer_string_is_empty(p->conf.encoding)) {
		response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
	} else {
		buffer_copy_string_len(p->content_charset, CONST_STR_LEN("text/html; charset="));
		buffer_append_string_buffer(p->content_charset, p->conf.encoding);
		response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->content_charset));
	}

	con->file_finished = 1;
	chunkqueue_append_buffer(con->write_queue, out);
	buffer_free(out);

	return 0;
}