| 1 | #include <ctype.h> |
|---|
| 2 | #include <stdlib.h> |
|---|
| 3 | #include <string.h> |
|---|
| 4 | |
|---|
| 5 | #include "base.h" |
|---|
| 6 | #include "log.h" |
|---|
| 7 | #include "buffer.h" |
|---|
| 8 | |
|---|
| 9 | #include "plugin.h" |
|---|
| 10 | |
|---|
| 11 | #include "inet_ntop_cache.h" |
|---|
| 12 | #ifdef HAVE_CONFIG_H |
|---|
| 13 | #include "config.h" |
|---|
| 14 | #endif |
|---|
| 15 | |
|---|
| 16 | /** |
|---|
| 17 | * mod_extforward.c for lighttpd, by comman.kang <at> gmail <dot> com |
|---|
| 18 | * FIXME: only support ipv4 |
|---|
| 19 | * |
|---|
| 20 | * Config example: |
|---|
| 21 | * |
|---|
| 22 | * Trust proxy 10.0.0.232 and 10.0.0.232 |
|---|
| 23 | * extforward.forwarder = ( "10.0.0.232" => "trust", |
|---|
| 24 | * "10.0.0.233" => "trust" ) |
|---|
| 25 | * |
|---|
| 26 | * Trust all proxies (NOT RECOMMENDED!) |
|---|
| 27 | * extforward.forwarder = ( "all" => "trust") |
|---|
| 28 | * |
|---|
| 29 | * Note: In order to see the "real" ip address in access log , |
|---|
| 30 | * you'll have to load mod_extforward after mod_accesslog. |
|---|
| 31 | * like this: |
|---|
| 32 | * |
|---|
| 33 | * server.modules = ( |
|---|
| 34 | * ..... |
|---|
| 35 | * mod_accesslog, |
|---|
| 36 | * mod_extforward |
|---|
| 37 | * ) |
|---|
| 38 | * |
|---|
| 39 | * Known issues: |
|---|
| 40 | * seems causing segfault with mod_ssl and $HTTP{"socket"} directives |
|---|
| 41 | * |
|---|
| 42 | * ChangeLog: |
|---|
| 43 | * 2005.12.19 Initial Version |
|---|
| 44 | * 2005.12.19 fixed conflict with conditional directives |
|---|
| 45 | */ |
|---|
| 46 | |
|---|
| 47 | |
|---|
| 48 | /* plugin config for all request/connections */ |
|---|
| 49 | |
|---|
| 50 | typedef struct { |
|---|
| 51 | array *forwarder; |
|---|
| 52 | } plugin_config; |
|---|
| 53 | |
|---|
| 54 | typedef struct { |
|---|
| 55 | PLUGIN_DATA; |
|---|
| 56 | |
|---|
| 57 | plugin_config **config_storage; |
|---|
| 58 | |
|---|
| 59 | plugin_config conf; |
|---|
| 60 | } plugin_data; |
|---|
| 61 | |
|---|
| 62 | |
|---|
| 63 | /* context , used for restore remote ip */ |
|---|
| 64 | |
|---|
| 65 | typedef struct { |
|---|
| 66 | sock_addr saved_remote_addr; |
|---|
| 67 | } handler_ctx; |
|---|
| 68 | |
|---|
| 69 | |
|---|
| 70 | static handler_ctx * handler_ctx_init(sock_addr oldaddr) { |
|---|
| 71 | handler_ctx * hctx; |
|---|
| 72 | hctx = calloc(1, sizeof(*hctx)); |
|---|
| 73 | hctx->saved_remote_addr = oldaddr; |
|---|
| 74 | return hctx; |
|---|
| 75 | } |
|---|
| 76 | |
|---|
| 77 | static void handler_ctx_free(handler_ctx *hctx) { |
|---|
| 78 | free(hctx); |
|---|
| 79 | } |
|---|
| 80 | |
|---|
| 81 | /* init the plugin data */ |
|---|
| 82 | INIT_FUNC(mod_extforward_init) { |
|---|
| 83 | plugin_data *p; |
|---|
| 84 | p = calloc(1, sizeof(*p)); |
|---|
| 85 | return p; |
|---|
| 86 | } |
|---|
| 87 | |
|---|
| 88 | /* detroy the plugin data */ |
|---|
| 89 | FREE_FUNC(mod_extforward_free) { |
|---|
| 90 | plugin_data *p = p_d; |
|---|
| 91 | |
|---|
| 92 | UNUSED(srv); |
|---|
| 93 | |
|---|
| 94 | if (!p) return HANDLER_GO_ON; |
|---|
| 95 | |
|---|
| 96 | if (p->config_storage) { |
|---|
| 97 | size_t i; |
|---|
| 98 | |
|---|
| 99 | for (i = 0; i < srv->config_context->used; i++) { |
|---|
| 100 | plugin_config *s = p->config_storage[i]; |
|---|
| 101 | |
|---|
| 102 | if (!s) continue; |
|---|
| 103 | |
|---|
| 104 | array_free(s->forwarder); |
|---|
| 105 | |
|---|
| 106 | free(s); |
|---|
| 107 | } |
|---|
| 108 | free(p->config_storage); |
|---|
| 109 | } |
|---|
| 110 | |
|---|
| 111 | |
|---|
| 112 | free(p); |
|---|
| 113 | |
|---|
| 114 | return HANDLER_GO_ON; |
|---|
| 115 | } |
|---|
| 116 | |
|---|
| 117 | /* handle plugin config and check values */ |
|---|
| 118 | |
|---|
| 119 | SETDEFAULTS_FUNC(mod_extforward_set_defaults) { |
|---|
| 120 | plugin_data *p = p_d; |
|---|
| 121 | size_t i = 0; |
|---|
| 122 | |
|---|
| 123 | config_values_t cv[] = { |
|---|
| 124 | { "extforward.forwarder", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ |
|---|
| 125 | { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } |
|---|
| 126 | }; |
|---|
| 127 | |
|---|
| 128 | if (!p) return HANDLER_ERROR; |
|---|
| 129 | |
|---|
| 130 | p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); |
|---|
| 131 | |
|---|
| 132 | for (i = 0; i < srv->config_context->used; i++) { |
|---|
| 133 | plugin_config *s; |
|---|
| 134 | |
|---|
| 135 | s = calloc(1, sizeof(plugin_config)); |
|---|
| 136 | s->forwarder = array_init(); |
|---|
| 137 | |
|---|
| 138 | cv[0].destination = s->forwarder; |
|---|
| 139 | |
|---|
| 140 | p->config_storage[i] = s; |
|---|
| 141 | |
|---|
| 142 | if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { |
|---|
| 143 | return HANDLER_ERROR; |
|---|
| 144 | } |
|---|
| 145 | } |
|---|
| 146 | |
|---|
| 147 | return HANDLER_GO_ON; |
|---|
| 148 | } |
|---|
| 149 | |
|---|
| 150 | #define PATCH(x) \ |
|---|
| 151 | p->conf.x = s->x; |
|---|
| 152 | static int mod_extforward_patch_connection(server *srv, connection *con, plugin_data *p) { |
|---|
| 153 | size_t i, j; |
|---|
| 154 | plugin_config *s = p->config_storage[0]; |
|---|
| 155 | |
|---|
| 156 | PATCH(forwarder); |
|---|
| 157 | |
|---|
| 158 | /* skip the first, the global context */ |
|---|
| 159 | for (i = 1; i < srv->config_context->used; i++) { |
|---|
| 160 | data_config *dc = (data_config *)srv->config_context->data[i]; |
|---|
| 161 | s = p->config_storage[i]; |
|---|
| 162 | |
|---|
| 163 | /* condition didn't match */ |
|---|
| 164 | if (!config_check_cond(srv, con, dc)) continue; |
|---|
| 165 | |
|---|
| 166 | /* merge config */ |
|---|
| 167 | for (j = 0; j < dc->value->used; j++) { |
|---|
| 168 | data_unset *du = dc->value->data[j]; |
|---|
| 169 | |
|---|
| 170 | if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.forwarder"))) { |
|---|
| 171 | PATCH(forwarder); |
|---|
| 172 | } |
|---|
| 173 | } |
|---|
| 174 | } |
|---|
| 175 | |
|---|
| 176 | return 0; |
|---|
| 177 | } |
|---|
| 178 | #undef PATCH |
|---|
| 179 | |
|---|
| 180 | |
|---|
| 181 | static void put_string_into_array_len(array *ary, const char *str, int len) |
|---|
| 182 | { |
|---|
| 183 | data_string *tempdata; |
|---|
| 184 | if (len == 0) |
|---|
| 185 | return; |
|---|
| 186 | tempdata = data_string_init(); |
|---|
| 187 | buffer_copy_string_len(tempdata->value,str,len); |
|---|
| 188 | array_insert_unique(ary,(data_unset *)tempdata); |
|---|
| 189 | } |
|---|
| 190 | /* |
|---|
| 191 | extract an forward array from the environment |
|---|
| 192 | */ |
|---|
| 193 | static array *extract_forward_array(buffer *pbuffer) |
|---|
| 194 | { |
|---|
| 195 | array *result = array_init(); |
|---|
| 196 | if (pbuffer->used > 0) { |
|---|
| 197 | char *base, *curr; |
|---|
| 198 | /* state variable, 0 means not in string, 1 means in string */ |
|---|
| 199 | int in_str = 0; |
|---|
| 200 | for (base = pbuffer->ptr, curr = pbuffer->ptr; *curr; curr++) |
|---|
| 201 | { |
|---|
| 202 | if (in_str) { |
|---|
| 203 | if (*curr > '9' || ( *curr < '0' && *curr != '.' ) ) { |
|---|
| 204 | /* found an separator , insert value into result array */ |
|---|
| 205 | put_string_into_array_len(result, base, curr-base); |
|---|
| 206 | /* change state to not in string */ |
|---|
| 207 | in_str = 0; |
|---|
| 208 | } |
|---|
| 209 | } else { |
|---|
| 210 | if (*curr >= '0' && *curr <= '9') |
|---|
| 211 | { |
|---|
| 212 | /* found leading char of an IP address, move base pointer change states */ |
|---|
| 213 | base = curr; |
|---|
| 214 | in_str = 1; |
|---|
| 215 | } |
|---|
| 216 | } |
|---|
| 217 | } |
|---|
| 218 | /* if breaking out while in str, we get to the end of string, so add it */ |
|---|
| 219 | if (in_str) |
|---|
| 220 | { |
|---|
| 221 | put_string_into_array_len(result, base, curr-base); |
|---|
| 222 | } |
|---|
| 223 | } |
|---|
| 224 | return result; |
|---|
| 225 | } |
|---|
| 226 | |
|---|
| 227 | #define IP_TRUSTED 1 |
|---|
| 228 | #define IP_UNTRUSTED 0 |
|---|
| 229 | /* |
|---|
| 230 | check whether ip is trusted, return 1 for trusted , 0 for untrusted |
|---|
| 231 | */ |
|---|
| 232 | static int is_proxy_trusted(const char *ipstr, plugin_data *p) |
|---|
| 233 | { |
|---|
| 234 | data_string* allds = (data_string *) array_get_element(p->conf.forwarder,"all"); |
|---|
| 235 | if (allds) { |
|---|
| 236 | if (strcasecmp(allds->value->ptr,"trust") == 0) |
|---|
| 237 | return IP_TRUSTED; |
|---|
| 238 | else |
|---|
| 239 | return IP_UNTRUSTED; |
|---|
| 240 | } |
|---|
| 241 | return (data_string *)array_get_element(p->conf.forwarder,ipstr) ? IP_TRUSTED : IP_UNTRUSTED ; |
|---|
| 242 | } |
|---|
| 243 | |
|---|
| 244 | |
|---|
| 245 | URIHANDLER_FUNC(mod_extforward_uri_handler) { |
|---|
| 246 | plugin_data *p = p_d; |
|---|
| 247 | data_string *forwarded = NULL; |
|---|
| 248 | UNUSED(srv); |
|---|
| 249 | mod_extforward_patch_connection(srv, con, p); |
|---|
| 250 | |
|---|
| 251 | /* if the remote ip itself is not trusted , then do nothing */ |
|---|
| 252 | if (IP_UNTRUSTED == is_proxy_trusted (inet_ntoa(con->dst_addr.ipv4.sin_addr) ,p) ) |
|---|
| 253 | return HANDLER_GO_ON; |
|---|
| 254 | |
|---|
| 255 | /* log_error_write(srv, __FILE__, __LINE__,"s","remote address is trusted proxy, go on\n");*/ |
|---|
| 256 | if (con->request.headers && ( |
|---|
| 257 | (forwarded = (data_string *)array_get_element(con->request.headers,"X-Forwarded-For")) || |
|---|
| 258 | (forwarded = (data_string *) array_get_element(con->request.headers,"Forwarded-For")) ) |
|---|
| 259 | ) |
|---|
| 260 | { |
|---|
| 261 | /* log_error_write(srv, __FILE__, __LINE__,"s","found forwarded header\n");*/ |
|---|
| 262 | /* found forwarded for header */ |
|---|
| 263 | int i; |
|---|
| 264 | array *forward_array = extract_forward_array(forwarded->value); |
|---|
| 265 | unsigned int real_remote_addr = 0xFFFFFFFF; |
|---|
| 266 | for (i = forward_array->used -1 ;i>=0; i--) |
|---|
| 267 | { |
|---|
| 268 | data_string *ds = (data_string *) forward_array->data[i]; |
|---|
| 269 | if (ds) { |
|---|
| 270 | /* log_error_write(srv, __FILE__, __LINE__,"ss","forward",ds->value->ptr);*/ |
|---|
| 271 | real_remote_addr = inet_addr(ds->value->ptr); |
|---|
| 272 | /* check whether it is trusted */ |
|---|
| 273 | if (IP_UNTRUSTED == is_proxy_trusted(ds->value->ptr,p) ) |
|---|
| 274 | break; |
|---|
| 275 | /* log_error_write(srv, __FILE__, __LINE__,"ss",ds->value->ptr," is trusted"); */ |
|---|
| 276 | } |
|---|
| 277 | else { |
|---|
| 278 | /* bug ? bailing out here */ |
|---|
| 279 | break; |
|---|
| 280 | } |
|---|
| 281 | } |
|---|
| 282 | if (real_remote_addr != 0xFFFFFFFF) /* parsed and inet_addr not returning -1 */ |
|---|
| 283 | { |
|---|
| 284 | /* log_error_write(srv, __FILE__, __LINE__,"ss","use forward",inet_ntoa(*((struct in_addr *)&real_remote_addr)));*/ |
|---|
| 285 | /* we found the remote address, modify current connection and save the old address */ |
|---|
| 286 | if (con->plugin_ctx[p->id]) { |
|---|
| 287 | log_error_write(srv, __FILE__, __LINE__,"patching an already patched connection!"); |
|---|
| 288 | handler_ctx_free(con->plugin_ctx[p->id]); |
|---|
| 289 | con->plugin_ctx[p->id] = NULL; |
|---|
| 290 | } |
|---|
| 291 | /* save old address */ |
|---|
| 292 | con->plugin_ctx[p->id] = handler_ctx_init(con->dst_addr); |
|---|
| 293 | /* patch connection address */ |
|---|
| 294 | con->dst_addr.ipv4.sin_addr.s_addr = real_remote_addr; |
|---|
| 295 | } |
|---|
| 296 | array_free(forward_array); |
|---|
| 297 | } |
|---|
| 298 | |
|---|
| 299 | /* not found */ |
|---|
| 300 | return HANDLER_GO_ON; |
|---|
| 301 | } |
|---|
| 302 | |
|---|
| 303 | CONNECTION_FUNC(mod_extforward_restore) { |
|---|
| 304 | plugin_data *p = p_d; |
|---|
| 305 | UNUSED(srv); |
|---|
| 306 | mod_extforward_patch_connection(srv, con, p); |
|---|
| 307 | /* restore this connection 's remote ip */ |
|---|
| 308 | if (con->plugin_ctx[p->id]) { |
|---|
| 309 | handler_ctx *hctx = con->plugin_ctx[p->id]; |
|---|
| 310 | con->dst_addr = hctx->saved_remote_addr; |
|---|
| 311 | handler_ctx_free(hctx); |
|---|
| 312 | con->plugin_ctx[p->id] = NULL; |
|---|
| 313 | } |
|---|
| 314 | return HANDLER_GO_ON; |
|---|
| 315 | } |
|---|
| 316 | |
|---|
| 317 | |
|---|
| 318 | /* this function is called at dlopen() time and inits the callbacks */ |
|---|
| 319 | |
|---|
| 320 | int mod_extforward_plugin_init(plugin *p) { |
|---|
| 321 | p->version = LIGHTTPD_VERSION_ID; |
|---|
| 322 | p->name = buffer_init_string("extforward"); |
|---|
| 323 | |
|---|
| 324 | p->init = mod_extforward_init; |
|---|
| 325 | p->handle_uri_clean = mod_extforward_uri_handler; |
|---|
| 326 | p->handle_request_done = mod_extforward_restore; |
|---|
| 327 | p->connection_reset = mod_extforward_restore; |
|---|
| 328 | p->set_defaults = mod_extforward_set_defaults; |
|---|
| 329 | p->cleanup = mod_extforward_free; |
|---|
| 330 | |
|---|
| 331 | p->data = NULL; |
|---|
| 332 | |
|---|
| 333 | return 0; |
|---|
| 334 | } |
|---|