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; }
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; }
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; }