Example #1
0
static cond_result_t config_check_cond_nocache(server *srv, connection *con, data_config *dc) {
	buffer *l;
	server_socket *srv_sock = con->srv_socket;

	/* check parent first */
	if (dc->parent && dc->parent->context_ndx) {
		/**
		 * a nested conditional 
		 *
		 * if the parent is not decided yet or false, we can't be true either 
		 */
		if (con->conf.log_condition_handling) {
			log_error_write(srv, __FILE__, __LINE__,  "sb", "go parent", dc->parent->key);
		}

		switch (config_check_cond_cached(srv, con, dc->parent)) {
		case COND_RESULT_FALSE:
			return COND_RESULT_FALSE;
		case COND_RESULT_UNSET:
			return COND_RESULT_UNSET;
		default:
			break;
		}
	}

	if (dc->prev) {
		/**
		 * a else branch
		 *
		 * we can only be executed, if all of our previous brothers 
		 * are false
		 */
		if (con->conf.log_condition_handling) {
			log_error_write(srv, __FILE__, __LINE__,  "sb", "go prev", dc->prev->key);
		}

		/* make sure prev is checked first */
		config_check_cond_cached(srv, con, dc->prev);

		/* one of prev set me to FALSE */
		switch (con->cond_cache[dc->context_ndx].result) {
		case COND_RESULT_FALSE:
			return con->cond_cache[dc->context_ndx].result;
		default:
			break;
		}
	}

	if (!con->conditional_is_valid[dc->comp]) {
		if (con->conf.log_condition_handling) {
			log_error_write(srv, __FILE__, __LINE__,  "dss", 
				dc->comp,
				dc->key->ptr,
				con->conditional_is_valid[dc->comp] ? "yeah" : "nej");
		}

		return COND_RESULT_UNSET;
	}

	/* pass the rules */

	switch (dc->comp) {
	case COMP_HTTP_HOST: {
		char *ck_colon = NULL, *val_colon = NULL;

		if (!buffer_string_is_empty(con->uri.authority)) {

			/*
			 * append server-port to the HTTP_POST if necessary
			 */

			l = con->uri.authority;

			switch(dc->cond) {
			case CONFIG_COND_NE:
			case CONFIG_COND_EQ:
				ck_colon = strchr(dc->string->ptr, ':');
				val_colon = strchr(l->ptr, ':');

				if (NULL != ck_colon && NULL == val_colon) {
					/* condition "host:port" but client send "host" */
					buffer_copy_buffer(srv->cond_check_buf, l);
					buffer_append_string_len(srv->cond_check_buf, CONST_STR_LEN(":"));
					buffer_append_int(srv->cond_check_buf, sock_addr_get_port(&(srv_sock->addr)));
					l = srv->cond_check_buf;
				} else if (NULL != val_colon && NULL == ck_colon) {
					/* condition "host" but client send "host:port" */
					buffer_copy_string_len(srv->cond_check_buf, l->ptr, val_colon - l->ptr);
					l = srv->cond_check_buf;
				}
				break;
			default:
				break;
			}
#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT
		} else if (!buffer_string_is_empty(con->tlsext_server_name)) {
			l = con->tlsext_server_name;
#endif
		} else {
			l = srv->empty_string;
		}
		break;
	}
	case COMP_HTTP_REMOTE_IP: {
		char *nm_slash;
		/* handle remoteip limitations
		 *
		 * "10.0.0.1" is provided for all comparisions
		 *
		 * only for == and != we support
		 *
		 * "10.0.0.1/24"
		 */

		if ((dc->cond == CONFIG_COND_EQ ||
		     dc->cond == CONFIG_COND_NE) &&
		    (con->dst_addr.plain.sa_family == AF_INET) &&
		    (NULL != (nm_slash = strchr(dc->string->ptr, '/')))) {
			int nm_bits;
			long nm;
			char *err;
			struct in_addr val_inp;

			if (*(nm_slash+1) == '\0') {
				log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: no number after / ", dc->string);

				return COND_RESULT_FALSE;
			}

			nm_bits = strtol(nm_slash + 1, &err, 10);

			if (*err) {
				log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: non-digit found in netmask:", dc->string, err);

				return COND_RESULT_FALSE;
			}

			if (nm_bits > 32 || nm_bits < 0) {
				log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: invalid netmask:", dc->string, err);

				return COND_RESULT_FALSE;
			}

			/* take IP convert to the native */
			buffer_copy_string_len(srv->cond_check_buf, dc->string->ptr, nm_slash - dc->string->ptr);
#ifdef __WIN32
			if (INADDR_NONE == (val_inp.s_addr = inet_addr(srv->cond_check_buf->ptr))) {
				log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf);

				return COND_RESULT_FALSE;
			}

#else
			if (0 == inet_aton(srv->cond_check_buf->ptr, &val_inp)) {
				log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf);

				return COND_RESULT_FALSE;
			}
#endif

			/* build netmask */
			nm = nm_bits ? htonl(~((1 << (32 - nm_bits)) - 1)) : 0;

			if ((val_inp.s_addr & nm) == (con->dst_addr.ipv4.sin_addr.s_addr & nm)) {
				return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
			} else {
				return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
			}
		} else {
			l = con->dst_addr_buf;
		}
		break;
	}
	case COMP_HTTP_SCHEME:
		l = con->uri.scheme;
		break;

	case COMP_HTTP_URL:
		l = con->uri.path;
		break;

	case COMP_HTTP_QUERY_STRING:
		l = con->uri.query;
		break;

	case COMP_SERVER_SOCKET:
		l = srv_sock->srv_token;
		break;

	case COMP_HTTP_REFERER: {
		data_string *ds;

		if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Referer"))) {
			l = ds->value;
		} else {
			l = srv->empty_string;
		}
		break;
	}
	case COMP_HTTP_COOKIE: {
		data_string *ds;
		if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Cookie"))) {
			l = ds->value;
		} else {
			l = srv->empty_string;
		}
		break;
	}
	case COMP_HTTP_USER_AGENT: {
		data_string *ds;
		if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "User-Agent"))) {
			l = ds->value;
		} else {
			l = srv->empty_string;
		}
		break;
	}
	case COMP_HTTP_REQUEST_METHOD: {
		const char *method = get_http_method_name(con->request.http_method);

		/* we only have the request method as const char but we need a buffer for comparing */

		buffer_copy_string(srv->tmp_buf, method);

		l = srv->tmp_buf;

		break;
	}
	case COMP_HTTP_LANGUAGE: {
		data_string *ds;
		if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Accept-Language"))) {
			l = ds->value;
		} else {
			l = srv->empty_string;
		}
		break;
	}
	default:
		return COND_RESULT_FALSE;
	}

	if (NULL == l) {
		if (con->conf.log_condition_handling) {
			log_error_write(srv, __FILE__, __LINE__,  "bsbs", dc->comp_key,
					"(", l, ") compare to NULL");
		}
		return COND_RESULT_FALSE;
	}

	if (con->conf.log_condition_handling) {
		log_error_write(srv, __FILE__, __LINE__,  "bsbsb", dc->comp_key,
				"(", l, ") compare to ", dc->string);
	}
	switch(dc->cond) {
	case CONFIG_COND_NE:
	case CONFIG_COND_EQ:
		if (buffer_is_equal(l, dc->string)) {
			return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
		} else {
			return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
		}
		break;
#ifdef HAVE_PCRE_H
	case CONFIG_COND_NOMATCH:
	case CONFIG_COND_MATCH: {
		cond_cache_t *cache = &con->cond_cache[dc->context_ndx];
		int n;

#ifndef elementsof
#define elementsof(x) (sizeof(x) / sizeof(x[0]))
#endif
		n = pcre_exec(dc->regex, dc->regex_study, CONST_BUF_LEN(l), 0, 0,
				cache->matches, elementsof(cache->matches));

		cache->patterncount = n;
		if (n > 0) {
			cache->comp_value = l;
			cache->comp_type  = dc->comp;
			return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
		} else {
			/* cache is already cleared */
			return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
		}
		break;
	}
#endif
	default:
		/* no way */
		break;
	}

	return COND_RESULT_FALSE;
}
Example #2
0
static handler_t process_rewrite_rules(server *srv, connection *con, plugin_data *p, pcre_keyvalue_buffer *kvb, int repeat_idx) {
	handler_ctx *hctx;
	struct burl_parts_t burl;
	pcre_keyvalue_ctx ctx;
	handler_t rc;

	if (con->plugin_ctx[p->id]) {
		hctx = con->plugin_ctx[p->id];

		if (hctx->loops++ > 100) {
			data_config *dc = p->conf.context;
			if (NULL == dc) {
				log_error_write(srv, __FILE__, __LINE__,  "s",
						"ENDLESS LOOP IN rewrite-rule DETECTED ... aborting request");
				return HANDLER_ERROR;
			}
			log_error_write(srv, __FILE__, __LINE__,  "SbbSBS",
					"ENDLESS LOOP IN rewrite-rule DETECTED ... aborting request, perhaps you want to use url.rewrite-once instead of url.rewrite-repeat ($", dc->comp_key, dc->op, "\"", dc->string, "\")");

			return HANDLER_ERROR;
		}

		if (hctx->state == REWRITE_STATE_FINISHED) return HANDLER_GO_ON;
	}

	ctx.cache = p->conf.context ? &con->cond_cache[p->conf.context->context_ndx] : NULL;
	ctx.burl = &burl;
	burl.scheme    = con->uri.scheme;
	burl.authority = con->uri.authority;
	burl.port      = sock_addr_get_port(&con->srv_socket->addr);
	burl.path      = con->uri.path_raw;
	burl.query     = con->uri.query;
	if (buffer_string_is_empty(burl.authority))
		burl.authority = con->server_name;

	rc = pcre_keyvalue_buffer_process(kvb, &ctx, con->request.uri, srv->tmp_buf);
	if (HANDLER_FINISHED == rc && !buffer_is_empty(srv->tmp_buf) && srv->tmp_buf->ptr[0] == '/') {
		buffer_copy_buffer(con->request.uri, srv->tmp_buf);
		if (con->plugin_ctx[p->id] == NULL) {
			hctx = handler_ctx_init();
			con->plugin_ctx[p->id] = hctx;
		} else {
			hctx = con->plugin_ctx[p->id];
		}
		if (ctx.m < repeat_idx) hctx->state = REWRITE_STATE_FINISHED;
		buffer_reset(con->physical.path);
		rc = HANDLER_COMEBACK;
	}
	else if (HANDLER_FINISHED == rc) {
		rc = HANDLER_ERROR;
		log_error_write(srv, __FILE__, __LINE__, "sb",
				"mod_rewrite invalid result (not beginning with '/') while processing uri:",
				con->request.uri);
	}
	else if (HANDLER_ERROR == rc) {
		log_error_write(srv, __FILE__, __LINE__, "sb",
				"pcre_exec() error while processing uri:",
				con->request.uri);
	}
	return rc;
}
Example #3
0
static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *cgi_handler) {
	pid_t pid;

	int to_cgi_fds[2];
	int from_cgi_fds[2];
	int from_cgi_err_fds[2];
	struct stat st;

#ifndef _WIN32

	if (cgi_handler && cgi_handler->used > 1) {
		/* stat the exec file */
		if (-1 == (stat(cgi_handler->ptr, &st))) {
			log_error_write(srv, __FILE__, __LINE__, "sbss",
					"stat for cgi-handler", cgi_handler,
					"failed:", strerror(errno));
			return -1;
		}
	}

	if (pipe(to_cgi_fds)) {
		log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
		return -1;
	}

	if (pipe(from_cgi_fds)) {
		close(to_cgi_fds[0]); close(to_cgi_fds[1]);
		log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
		return -1;
	}

	if (pipe(from_cgi_err_fds)) {
		close(to_cgi_fds[0]); close(to_cgi_fds[1]);
		close(from_cgi_fds[0]); close(from_cgi_fds[1]);
		log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
		return -1;
	}

	/* fork, execve */
	switch (pid = fork()) {
	case 0: {
		/* child */
		char **args;
		int argc;
		int i = 0;
		char buf[32];
		size_t n;
		char_array env;
		char *c;
		const char *s;
		server_socket *srv_sock = con->srv_socket;

		/* move stdout to from_cgi_fd[1] */
		close(STDOUT_FILENO);
		dup2(from_cgi_fds[1], STDOUT_FILENO);
		close(from_cgi_fds[1]);
		/* not needed */
		close(from_cgi_fds[0]);

		/* move stderr to from_cgi_err_fd[1] */
		close(STDERR_FILENO);
		dup2(from_cgi_err_fds[1], STDERR_FILENO);
		close(from_cgi_err_fds[1]);
		/* not needed */
		close(from_cgi_err_fds[0]);

		/* move the stdin to to_cgi_fd[0] */
		close(STDIN_FILENO);
		dup2(to_cgi_fds[0], STDIN_FILENO);
		close(to_cgi_fds[0]);
		/* not needed */
		close(to_cgi_fds[1]);

		/* create environment */
		env.ptr = NULL;
		env.size = 0;
		env.used = 0;

		cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_NAME"/"PACKAGE_VERSION));

		s = sock_addr_to_p(srv, &srv_sock->addr);
		cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s));
		/* !!! careful: s maybe reused for SERVER_NAME !!! */

		if (!buffer_is_empty(con->server_name)) {
			size_t len = con->server_name->used - 1;
			char *colon = strchr(con->server_name->ptr, ':');
			if (colon) len = colon - con->server_name->ptr;

			cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len);
		} else {
			/* use SERVER_ADDR */
			cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s));
		}
		cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1"));

		s = get_http_version_name(con->request.http_version);

		cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s));

		LI_ltostr(buf, sock_addr_get_port(&srv_sock->addr));
		cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf));

		s = get_http_method_name(con->request.http_method);
		cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s));

		if (!buffer_is_empty(con->request.pathinfo)) {
			cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
		}
		cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200"));
		if (!buffer_is_empty(con->uri.query)) {
			cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query));
		} else {
			/* set a empty QUERY_STRING */
			cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN(""));
		}
		if (!buffer_is_empty(con->request.orig_uri)) {
			cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
		}

		s = sock_addr_to_p(srv, &con->dst_addr);
		cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s));

		LI_ltostr(buf, sock_addr_get_port(&con->dst_addr));
		cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf));

		if (!buffer_is_empty(con->authed_user)) {
			cgi_env_add(&env, CONST_STR_LEN("REMOTE_USER"),
				    CONST_BUF_LEN(con->authed_user));
		}

#ifdef USE_OPENSSL
		if (srv_sock->is_ssl) {
			cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
		}
#endif

		/* request.content_length < SSIZE_MAX, see request.c */
		if (con->request.content_length > 0) {
			LI_ltostr(buf, con->request.content_length);
			cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf));
		}
		cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
		cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
		cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root));

		/* for valgrind */
		if (NULL != (s = getenv("LD_PRELOAD"))) {
			cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s));
		}

		if (NULL != (s = getenv("LD_LIBRARY_PATH"))) {
			cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s));
		}
#ifdef __CYGWIN__
		/* CYGWIN needs SYSTEMROOT */
		if (NULL != (s = getenv("SYSTEMROOT"))) {
			cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s));
		}
#endif

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

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

			if (ds->value->used && ds->key->used) {
				size_t j;

				buffer_reset(p->tmp_buf);

				if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) {
					buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("HTTP_"));
					p->tmp_buf->used--; /* strip \0 after HTTP_ */
				}

				buffer_prepare_append(p->tmp_buf, ds->key->used + 2);

				for (j = 0; j < ds->key->used - 1; j++) {
					char cr = '_';
					if (light_isalpha(ds->key->ptr[j])) {
						/* upper-case */
						cr = ds->key->ptr[j] & ~32;
					} else if (light_isdigit(ds->key->ptr[j])) {
						/* copy */
						cr = ds->key->ptr[j];
					}
					p->tmp_buf->ptr[p->tmp_buf->used++] = cr;
				}
				p->tmp_buf->ptr[p->tmp_buf->used++] = '\0';

				cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
			}
		}

		for (n = 0; n < con->environment->used; n++) {
			data_string *ds;

			ds = (data_string *)con->environment->data[n];

			if (ds->value->used && ds->key->used) {
				size_t j;

				buffer_reset(p->tmp_buf);

				buffer_prepare_append(p->tmp_buf, ds->key->used + 2);

				for (j = 0; j < ds->key->used - 1; j++) {
					char cr = '_';
					if (light_isalpha(ds->key->ptr[j])) {
						/* upper-case */
						cr = ds->key->ptr[j] & ~32;
					} else if (light_isdigit(ds->key->ptr[j])) {
						/* copy */
						cr = ds->key->ptr[j];
					}
					p->tmp_buf->ptr[p->tmp_buf->used++] = cr;
				}
				p->tmp_buf->ptr[p->tmp_buf->used++] = '\0';

				cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
			}
		}

		if (env.size == env.used) {
			env.size += 16;
			env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr));
		}

		env.ptr[env.used] = NULL;

		/* set up args */
		argc = 3;
		args = malloc(sizeof(*args) * argc);
		i = 0;

		if (cgi_handler && cgi_handler->used > 1) {
			args[i++] = cgi_handler->ptr;
		}
		args[i++] = con->physical.path->ptr;
		args[i++] = NULL;

		/* search for the last / */
		if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) {
			*c = '\0';

			/* change to the physical directory */
			if (-1 == chdir(con->physical.path->ptr)) {
				log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path);
			}
			*c = '/';
		}

		/* we don't need the client socket */
		for (i = 3; i < 256; i++) {
			close(i);
		}

		/* exec the cgi */
		execve(args[0], args, env.ptr);

		/* */
		SEGFAULT("execve(%s) failed: %s", args[0], strerror(errno));
		break;
	}
	case -1:
		/* error */
		ERROR("fork() failed: %s", strerror(errno));
		close(to_cgi_fds[0]); close(to_cgi_fds[1]);
		close(from_cgi_fds[0]); close(from_cgi_fds[1]);
		close(from_cgi_err_fds[0]); close(from_cgi_err_fds[1]);
		return -1;
		break;
	default: {
		cgi_session *sess;
		/* father */

		close(from_cgi_fds[1]);
		close(from_cgi_err_fds[1]);
		close(to_cgi_fds[0]);

		/* register PID and wait for them asyncronously */
		con->mode = p->id;
		buffer_reset(con->physical.path);

		sess = cgi_session_init();

		sess->remote_con = con;
		sess->pid = pid;

		assert(sess->sock);

		sess->sock->fd = from_cgi_fds[0];
		sess->sock->type = IOSOCKET_TYPE_PIPE;
		sess->sock_err->fd = from_cgi_err_fds[0];
		sess->sock_err->type = IOSOCKET_TYPE_PIPE;
		sess->wb_sock->fd = to_cgi_fds[1];
		sess->wb_sock->type = IOSOCKET_TYPE_PIPE;

		if (-1 == fdevent_fcntl_set(srv->ev, sess->sock)) {
			log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));

			cgi_session_free(sess);

			return -1;
		}

		if (-1 == fdevent_fcntl_set(srv->ev, sess->sock_err)) {
			log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));

			cgi_session_free(sess);

			return -1;
		}

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

		fdevent_register(srv->ev, sess->sock, cgi_handle_fdevent, sess);
		fdevent_event_add(srv->ev, sess->sock, FDEVENT_IN);

		fdevent_register(srv->ev, sess->sock_err, cgi_handle_err_fdevent, sess);
		fdevent_event_add(srv->ev, sess->sock_err, FDEVENT_IN);

		sess->state = CGI_STATE_READ_RESPONSE_HEADER;

		break;
	}
	}

	return 0;
#else
	return -1;
#endif
}
static cond_result_t config_check_cond_nocache(server *srv, connection *con, data_config *dc) {
	buffer *l; 
	server_socket *srv_sock = con->srv_socket; //socket 插座、接口
	
	/* check parent first */
	if (dc->parent && dc->parent->context_ndx) { //如果父节点存在,但父节点未被判断或父节点是错误的,那么子节点也不能进行判断或子节点是错误的
		/**
		 * a nested conditional 
		 *
		 * if the parent is not decided yet or false, we can't be true either 
		 */
		if (con->conf.log_condition_handling) {
			log_error_write(srv, __FILE__, __LINE__,  "sb", "go parent", dc->parent->key);
		}

		switch (config_check_cond_cached(srv, con, dc->parent)) {
		case COND_RESULT_FALSE:
			return COND_RESULT_FALSE;
		case COND_RESULT_UNSET:
			return COND_RESULT_UNSET;
		default:
			break;
		}
	}

	if (dc->prev) {
		/**
		 * a else branch
		 *
		 * we can only be executed, if all of our previous brothers 
		 * are false
		 */
		 //存在前驱块,那么需要先判断前驱块状态
		if (con->conf.log_condition_handling) {
			log_error_write(srv, __FILE__, __LINE__,  "sb", "go prev", dc->prev->key);
		}

		/* make sure prev is checked first */
		config_check_cond_cached(srv, con, dc->prev);

		/* one of prev set me to FALSE */
		//在判断前驱块状态时候有可能就已经设置了本快的状态(config_check_cond_cached函数调用如前驱块为真,该前驱块以下的块将全都设置为假),如果为假则直接返回。
		switch (con->cond_cache[dc->context_ndx].result) {
		case COND_RESULT_FALSE:
			return con->cond_cache[dc->context_ndx].result;
		default:
			break;
		}
	}

	if (!con->conditional_is_valid[dc->comp]) {
		if (con->conf.log_condition_handling) {
			log_error_write(srv, __FILE__, __LINE__,  "dss", 
				dc->comp,
				dc->key->ptr,
				con->conditional_is_valid[dc->comp] ? "yeah" : "nej");
		}

		return COND_RESULT_UNSET;
	}

	/* pass the rules */
	//开始实际的连接状态判断,Lighttpd1.4.20提供的条件配置有10个,分别为server_socket HTTP_URL HTTP_HOST HTTP_REFERER HTTP_USER_AGENT HTTP_COOKIE
	//HTTP_REMOTE_IP HTTP_QUERY_STRING HTTP_SCHEME HTTP_REQUEST_METHOD

	switch (dc->comp) {
	case COMP_HTTP_HOST: {
		char *ck_colon = NULL, *val_colon = NULL;

		if (!buffer_is_empty(con->uri.authority)) { //authority内保存是请求连接的Host信息(可能是域名也可能是IP地址)

			/*
			 * append server-port to the HTTP_POST if necessary
			 */

			l = con->uri.authority;

			switch(dc->cond) {
			case CONFIG_COND_NE:
			case CONFIG_COND_EQ:
				ck_colon = strchr(dc->string->ptr, ':');
				val_colon = strchr(l->ptr, ':');

				if (ck_colon == val_colon) { //请求连接的Host信息与条件配置块的Host条件设置格式一致(即两者都包含有端口号或都没有包含端口号),则什么都不做。
					/* nothing to do with it */
					break;
				}
				if (ck_colon) { //请求连接的Host信息没有半酣端口号而条件配置块的Host包含端口号,因此给请求连接的Host加上端口号
					/* condition "host:port" but client send "host" */
					buffer_copy_string_buffer(srv->cond_check_buf, l);
					buffer_append_string_len(srv->cond_check_buf, CONST_STR_LEN(":"));
					buffer_append_long(srv->cond_check_buf, sock_addr_get_port(&(srv_sock->addr)));
					l = srv->cond_check_buf;
				} else if (!ck_colon) { //请求连接的Host信息包含端口号而条件配置信息块的Host没有包含端口号,因此将请求连接Host的端口号去掉。
					/* condition "host" but client send "host:port" */
					buffer_copy_string_len(srv->cond_check_buf, l->ptr, val_colon - l->ptr);
					l = srv->cond_check_buf;
				}
				break;
			default:
				break;
			}
		} else {
			l = srv->empty_string;
		}
		break;
	}
	case COMP_HTTP_REMOTE_IP: { //REMOTE adj 遥远的
		char *nm_slash;
		/* handle remoteip limitations
		 *
		 * "10.0.0.1" is provided for all comparisions
		 *
		 * only for == and != we support
		 *
		 * "10.0.0.1/24"
		 */

		if ((dc->cond == CONFIG_COND_EQ ||
		     dc->cond == CONFIG_COND_NE) &&
		    (con->dst_addr.plain.sa_family == AF_INET) &&
		    (NULL != (nm_slash = strchr(dc->string->ptr, '/')))) {
			int nm_bits;
			long nm;
			char *err;
			struct in_addr val_inp;

			if (*(nm_slash+1) == '\0') { //无分类域间路由选择CIDR(CIDR记法,斜线记法),这里对CIDR格式字符串进行检验
				log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: no number after / ", dc->string); //CIDR格式不对,缺少表示网络前缀位数的数字

				return COND_RESULT_FALSE;
			}
			/*
				函数strtol()声明在头文件stdlib.h内,原型为long int strtol(const char *nptr,char **endptr,int base);用于将参数nptr字符串根据
				base指定的进制转换成对应的长整型数。参数base范围从2至36,或0(即默认采用十进制做转换,但遇到如'0x'前置字符则会使用十六进制做转换)。
				strtol()会扫描参数nptr字符串,跳过前面的空格字符,知道遇上数字或正负号才开始做转换,在遇到非数字或字符串结束时('\0')结束转换,并将结果返回。
				若参数endptr不为NULL,则会将不符合调节而终止的nptr中的字符指针由endptr返回。该函数执行成功返回转换后的长整型数,否则返回ERANGE(表示指定的专函字符串超出合法范围)
				并将错误代码存入errno中,此处用于获取端口十进制的整型数。
			*/
			nm_bits = strtol(nm_slash + 1, &err, 10);

			if (*err) {
				log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: non-digit found in netmask:", dc->string, err);

				return COND_RESULT_FALSE;
			}

			/* take IP convert to the native */
			buffer_copy_string_len(srv->cond_check_buf, dc->string->ptr, nm_slash - dc->string->ptr);
#ifdef __WIN32
			if (INADDR_NONE == (val_inp.s_addr = inet_addr(srv->cond_check_buf->ptr))) {
				log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf);

				return COND_RESULT_FALSE;
			}

#else
			/*
				函数inet_ston()声明在头文件sys/scoket.h内,原型为int inet_aton(const char *cp,struct in_addr *inp); 用于将参数cp所指的字符串形式的网络地址
				转换成网络地址成网络使用的二进制数形式,然后存于参数inp所指的in_addr结构中。
			*/
			if (0 == inet_aton(srv->cond_check_buf->ptr, &val_inp)) {
				log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf);

				return COND_RESULT_FALSE;
			}
#endif

			/* build netmask */
			/*
				函数htonl()声明在头文件srpa/inet.h内,原型为unint32_t htonl(uint32_t hostlong); 用来将参数hostlong指定的32位无符号长整型由主机字节顺序转换成网络字符顺序。
			*/
			nm = htonl(~((1 << (32 - nm_bits)) - 1));

			if ((val_inp.s_addr & nm) == (con->dst_addr.ipv4.sin_addr.s_addr & nm)) { //当前连接的客户端IP地址与条件配置信息块的条件设置匹配,按需返回结果
				return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
			} else { //不匹配
				return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
			}
		} else {
			l = con->dst_addr_buf;
		}
		break;
	}
	case COMP_HTTP_SCHEME:
		l = con->uri.scheme;
		break;

	case COMP_HTTP_URL:
		l = con->uri.path;
		break;

	case COMP_HTTP_QUERY_STRING:
		l = con->uri.query;
		break;

	case COMP_SERVER_SOCKET:
		l = srv_sock->srv_token;
		break;

	case COMP_HTTP_REFERER: {
		data_string *ds;

		if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Referer"))) {
			l = ds->value;
		} else {
			l = srv->empty_string;
		}
		break;
	}
	case COMP_HTTP_COOKIE: {
		data_string *ds;
		if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Cookie"))) {
			l = ds->value;
		} else {
			l = srv->empty_string;
		}
		break;
	}
	case COMP_HTTP_USER_AGENT: {
		data_string *ds;
		if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "User-Agent"))) {
			l = ds->value;
		} else {
			l = srv->empty_string;
		}
		break;
	}
	case COMP_HTTP_REQUEST_METHOD: {
		/*
			get_http_method_name()函数根据当前连接的请求方法(通过分析请求行得知)返回对应的字符串,比如"GET"、"POST"等
		*/
		const char *method = get_http_method_name(con->request.http_method);

		/* we only have the request method as const char but we need a buffer for comparing */
		//为了后面的统一匹配比较,利用该字符串初始化buffer结构体。
		buffer_copy_string(srv->tmp_buf, method);

		l = srv->tmp_buf;

		break;
	}
	default:
		return COND_RESULT_FALSE;
	}

	if (NULL == l) { //当前连接匹配字段为空,则返回假
		if (con->conf.log_condition_handling) {
			log_error_write(srv, __FILE__, __LINE__,  "bsbs", dc->comp_key,
					"(", l, ") compare to NULL");
		}
		return COND_RESULT_FALSE;
	}

	if (con->conf.log_condition_handling) {
		log_error_write(srv, __FILE__, __LINE__,  "bsbsb", dc->comp_key,
				"(", l, ") compare to ", dc->string);
	}
	switch(dc->cond) {
	case CONFIG_COND_NE:
	case CONFIG_COND_EQ:
		if (buffer_is_equal(l, dc->string)) { //相等或不等匹配
			return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
		} else {
			return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
		}
		break;
#ifdef HAVE_PCRE_H
	/*
		正则式匹配需要相应库的支持,GNU/Linux下有两套正则式编程支持库:POSIX库和PCRE库,POSIX库不需要单独安装,能满足一般需求,但是速度稍慢些,
		读者查看MAN手册。PCRE库久负盛名,功能强大,匹配速度快,但是可能需要单独安装。关于PCRE库的更多介绍,读者可以查阅站点:http://www.pcre.org/。
		此处用的是PCRE库。
	*/
	case CONFIG_COND_NOMATCH:
	case CONFIG_COND_MATCH: {
		cond_cache_t *cache = &con->cond_cache[dc->context_ndx];
		int n;

#ifndef elementsof
#define elementsof(x) (sizeof(x) / sizeof(x[0]))
#endif
		n = pcre_exec(dc->regex, dc->regex_study, l->ptr, l->used - 1, 0, 0,
				cache->matches, elementsof(cache->matches)); //利用PCRE库函数pcre_exec()执行匹配操作,如果不匹配或执行出错则返回一个负值(其中,不匹配则返回PCRE_ERROR_NOMATCH(该宏值为-1)),
				                                             //如果匹配成功将返回一个正数。关于函数pcre_exec()的详细说明可以参考说明文档:http://www.pcre.org/prce.txt.

		cache->patterncount = n;
		if (n > 0) { //匹配成功
			cache->comp_value = l;
			cache->comp_type  = dc->comp;
			return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
		} else {
			/* cache is already cleared */
			return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
		}
		break;
	}
#endif
	default:
		/* no way */
		break;
	}

	return COND_RESULT_FALSE;
}