├── README.md ├── config └── src ├── ngx_http_socks_module.c ├── ngx_http_socks_module.h └── ngx_http_socks_upstream.c /README.md: -------------------------------------------------------------------------------- 1 | # socks-nginx-module 2 | 3 | An `nginx_http_proxy_module` fork with SOCKS5 support 4 | 5 | ## Building 6 | 7 | nginx >= **1.9.1** is supported. 8 | 9 | ```bash 10 | # apt-get install git build-essential zlib1g-dev libpcre3 libpcre3-dev unzip 11 | 12 | $ git clone https://github.com/dannote/socks-nginx-module 13 | $ wget http://nginx.org/download/nginx-1.9.15.tar.gz 14 | 15 | $ tar -xzvf nginx-1.9.15.tar.gz 16 | 17 | $ cd nginx-1.9.15 18 | 19 | # See http://nginx.org/en/docs/configure.html for more configuration options 20 | $ ./configure --add-module=../socks-nginx-module 21 | 22 | $ make 23 | # make install 24 | ``` 25 | 26 | ## Configuring 27 | 28 | Sample HTTP to SOCKS5 proxy configuration: 29 | 30 | ``` 31 | location / { 32 | socks_set_header Host $http_host; 33 | socks_set_header Proxy-Connection ''; 34 | socks_pass_header Server; 35 | socks_redirect off; 36 | socks_http_version 1.1; 37 | socks_tunnel_header X-Connect; 38 | socks_buffers 16 16k; 39 | socks_buffer_size 32k; 40 | socks_cache proxy; 41 | socks_cache_valid 30s; 42 | socks_cache_use_stale error timeout invalid_header updating 43 | http_500 http_502 http_503 http_504; 44 | socks_pass socks5://127.0.0.1:1080; 45 | } 46 | ``` 47 | 48 | All [ngx_http_proxy_module](http://nginx.org/en/docs/http/ngx_http_proxy_module.html) directives are supported. 49 | 50 | ### socks_tunnel_header 51 | 52 | Context: `http`, `server`, `location` 53 | 54 | As nginx HTTP parser doesn't support HTTP CONNECT method, a special header can be set to indicate tunnel connection. 55 | 56 | This directive can be exploited with the following HAProxy configuration: 57 | 58 | ``` 59 | frontend local 60 | bind *:8080 61 | mode http 62 | http-request set-method GET if METH_CONNECT 63 | http-request set-uri https://%[req.hdr(Host)]/ if METH_CONNECT 64 | http-request add-header X-Connect true if METH_CONNECT 65 | default_backend nginx 66 | 67 | backend nginx 68 | mode http 69 | server proxy 127.0.0.1:8080 maxconn 100000 70 | ``` 71 | 72 | ### socks_set_host 73 | 74 | Context: `http`, `server`, `location` 75 | 76 | Default: `socks_set_host $http_host;` 77 | 78 | Overrides the endpoint server. 79 | 80 | This example will proxy requests to `ipinfo.io` via local Tor daemon: 81 | 82 | ``` 83 | location /ip { 84 | socks_pass socks5://127.0.0.1:9050; 85 | socks_set_host ipinfo.io; 86 | socks_set_header Host ipinfo.io; 87 | socks_redirect off; 88 | socks_http_version 1.1; 89 | } 90 | ``` -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_socks_module 2 | 3 | HTTP_MODULES="$HTTP_MODULES ngx_http_socks_module" 4 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_socks_module.c $ngx_addon_dir/src/ngx_http_socks_upstream.c" 5 | NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ngx_http_socks_module.h" 6 | 7 | have=NGX_HTTP_SOCKS_MODULE . auto/have -------------------------------------------------------------------------------- /src/ngx_http_socks_module.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Danila Poyarkov 4 | * 5 | * Based on ngx_http_proxy_module.c 6 | * Copyright (C) Igor Sysoev 7 | * Copyright (C) Nginx, Inc. 8 | */ 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | #include "ngx_http_socks_module.h" 15 | 16 | 17 | static ngx_int_t ngx_http_socks_eval(ngx_http_request_t *r, 18 | ngx_http_socks_ctx_t *ctx, ngx_http_socks_loc_conf_t *slcf); 19 | #if (NGX_HTTP_CACHE) 20 | static ngx_int_t ngx_http_socks_create_key(ngx_http_request_t *r); 21 | #endif 22 | static ngx_int_t ngx_http_socks_create_request(ngx_http_request_t *r); 23 | static ngx_int_t ngx_http_socks_reinit_request(ngx_http_request_t *r); 24 | static ngx_int_t ngx_http_socks_body_output_filter(void *data, ngx_chain_t *in); 25 | static ngx_int_t ngx_http_socks_process_status_line(ngx_http_request_t *r); 26 | static ngx_int_t ngx_http_socks_process_header(ngx_http_request_t *r); 27 | static ngx_int_t ngx_http_socks_input_filter_init(void *data); 28 | static ngx_int_t ngx_http_socks_copy_filter(ngx_event_pipe_t *p, 29 | ngx_buf_t *buf); 30 | static ngx_int_t ngx_http_socks_chunked_filter(ngx_event_pipe_t *p, 31 | ngx_buf_t *buf); 32 | static ngx_int_t ngx_http_socks_non_buffered_copy_filter(void *data, 33 | ssize_t bytes); 34 | static ngx_int_t ngx_http_socks_non_buffered_chunked_filter(void *data, 35 | ssize_t bytes); 36 | static void ngx_http_socks_abort_request(ngx_http_request_t *r); 37 | static void ngx_http_socks_finalize_request(ngx_http_request_t *r, 38 | ngx_int_t rc); 39 | 40 | static ngx_int_t ngx_http_socks_host_variable(ngx_http_request_t *r, 41 | ngx_http_variable_value_t *v, uintptr_t data); 42 | static ngx_int_t ngx_http_socks_port_variable(ngx_http_request_t *r, 43 | ngx_http_variable_value_t *v, uintptr_t data); 44 | static ngx_int_t 45 | ngx_http_socks_add_x_forwarded_for_variable(ngx_http_request_t *r, 46 | ngx_http_variable_value_t *v, uintptr_t data); 47 | static ngx_int_t 48 | ngx_http_socks_internal_body_length_variable(ngx_http_request_t *r, 49 | ngx_http_variable_value_t *v, uintptr_t data); 50 | static ngx_int_t ngx_http_socks_internal_chunked_variable(ngx_http_request_t *r, 51 | ngx_http_variable_value_t *v, uintptr_t data); 52 | static ngx_int_t ngx_http_socks_rewrite_redirect(ngx_http_request_t *r, 53 | ngx_table_elt_t *h, size_t prefix); 54 | static ngx_int_t ngx_http_socks_rewrite_cookie(ngx_http_request_t *r, 55 | ngx_table_elt_t *h); 56 | static ngx_int_t ngx_http_socks_rewrite_cookie_value(ngx_http_request_t *r, 57 | ngx_table_elt_t *h, u_char *value, ngx_array_t *rewrites); 58 | static ngx_int_t ngx_http_socks_rewrite(ngx_http_request_t *r, 59 | ngx_table_elt_t *h, size_t prefix, size_t len, ngx_str_t *replacement); 60 | 61 | static ngx_int_t ngx_http_socks_add_variables(ngx_conf_t *cf); 62 | static void *ngx_http_socks_create_main_conf(ngx_conf_t *cf); 63 | static void *ngx_http_socks_create_loc_conf(ngx_conf_t *cf); 64 | static char *ngx_http_socks_merge_loc_conf(ngx_conf_t *cf, 65 | void *parent, void *child); 66 | static ngx_int_t ngx_http_socks_init_headers(ngx_conf_t *cf, 67 | ngx_http_socks_loc_conf_t *conf, ngx_http_socks_headers_t *headers, 68 | ngx_keyval_t *default_headers); 69 | 70 | static char *ngx_http_socks_pass(ngx_conf_t *cf, ngx_command_t *cmd, 71 | void *conf); 72 | static char *ngx_http_socks_redirect(ngx_conf_t *cf, ngx_command_t *cmd, 73 | void *conf); 74 | static char *ngx_http_socks_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd, 75 | void *conf); 76 | static char *ngx_http_socks_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, 77 | void *conf); 78 | static char *ngx_http_socks_store(ngx_conf_t *cf, ngx_command_t *cmd, 79 | void *conf); 80 | #if (NGX_HTTP_CACHE) 81 | static char *ngx_http_socks_cache(ngx_conf_t *cf, ngx_command_t *cmd, 82 | void *conf); 83 | static char *ngx_http_socks_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, 84 | void *conf); 85 | #endif 86 | 87 | static char *ngx_http_socks_lowat_check(ngx_conf_t *cf, void *post, void *data); 88 | 89 | static ngx_int_t ngx_http_socks_rewrite_regex(ngx_conf_t *cf, 90 | ngx_http_socks_rewrite_t *pr, ngx_str_t *regex, ngx_uint_t caseless); 91 | 92 | static void ngx_http_socks_set_vars(ngx_url_t *u, ngx_http_socks_vars_t *v); 93 | static char *ngx_http_socks_tunnel_header(ngx_conf_t *cf, ngx_command_t *cmd, 94 | void *conf); 95 | static char *ngx_http_socks_set_host(ngx_conf_t *cf, ngx_command_t *cmd, 96 | void *conf); 97 | 98 | 99 | static ngx_conf_post_t ngx_http_socks_lowat_post = 100 | { ngx_http_socks_lowat_check }; 101 | 102 | 103 | static ngx_conf_bitmask_t ngx_http_socks_next_upstream_masks[] = { 104 | { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR }, 105 | { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT }, 106 | { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER }, 107 | { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 }, 108 | { ngx_string("http_502"), NGX_HTTP_UPSTREAM_FT_HTTP_502 }, 109 | { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 }, 110 | { ngx_string("http_504"), NGX_HTTP_UPSTREAM_FT_HTTP_504 }, 111 | { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 }, 112 | { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 }, 113 | { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING }, 114 | { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF }, 115 | { ngx_null_string, 0 } 116 | }; 117 | 118 | 119 | static ngx_conf_enum_t ngx_http_socks_http_version[] = { 120 | { ngx_string("1.0"), NGX_HTTP_VERSION_10 }, 121 | { ngx_string("1.1"), NGX_HTTP_VERSION_11 }, 122 | { ngx_null_string, 0 } 123 | }; 124 | 125 | 126 | static ngx_command_t ngx_http_socks_commands[] = { 127 | 128 | { ngx_string("socks_pass"), 129 | NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1, 130 | ngx_http_socks_pass, 131 | NGX_HTTP_LOC_CONF_OFFSET, 132 | 0, 133 | NULL }, 134 | 135 | { ngx_string("socks_redirect"), 136 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, 137 | ngx_http_socks_redirect, 138 | NGX_HTTP_LOC_CONF_OFFSET, 139 | 0, 140 | NULL }, 141 | 142 | { ngx_string("socks_cookie_domain"), 143 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, 144 | ngx_http_socks_cookie_domain, 145 | NGX_HTTP_LOC_CONF_OFFSET, 146 | 0, 147 | NULL }, 148 | 149 | { ngx_string("socks_cookie_path"), 150 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, 151 | ngx_http_socks_cookie_path, 152 | NGX_HTTP_LOC_CONF_OFFSET, 153 | 0, 154 | NULL }, 155 | 156 | { ngx_string("socks_store"), 157 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 158 | ngx_http_socks_store, 159 | NGX_HTTP_LOC_CONF_OFFSET, 160 | 0, 161 | NULL }, 162 | 163 | { ngx_string("socks_store_access"), 164 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123, 165 | ngx_conf_set_access_slot, 166 | NGX_HTTP_LOC_CONF_OFFSET, 167 | offsetof(ngx_http_socks_loc_conf_t, upstream.store_access), 168 | NULL }, 169 | 170 | { ngx_string("socks_buffering"), 171 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 172 | ngx_conf_set_flag_slot, 173 | NGX_HTTP_LOC_CONF_OFFSET, 174 | offsetof(ngx_http_socks_loc_conf_t, upstream.buffering), 175 | NULL }, 176 | 177 | { ngx_string("socks_request_buffering"), 178 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 179 | ngx_conf_set_flag_slot, 180 | NGX_HTTP_LOC_CONF_OFFSET, 181 | offsetof(ngx_http_socks_loc_conf_t, upstream.request_buffering), 182 | NULL }, 183 | 184 | { ngx_string("socks_ignore_client_abort"), 185 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 186 | ngx_conf_set_flag_slot, 187 | NGX_HTTP_LOC_CONF_OFFSET, 188 | offsetof(ngx_http_socks_loc_conf_t, upstream.ignore_client_abort), 189 | NULL }, 190 | 191 | { ngx_string("socks_bind"), 192 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 193 | ngx_http_socks_upstream_bind_set_slot, 194 | NGX_HTTP_LOC_CONF_OFFSET, 195 | offsetof(ngx_http_socks_loc_conf_t, upstream.local), 196 | NULL }, 197 | 198 | { ngx_string("socks_connect_timeout"), 199 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 200 | ngx_conf_set_msec_slot, 201 | NGX_HTTP_LOC_CONF_OFFSET, 202 | offsetof(ngx_http_socks_loc_conf_t, upstream.connect_timeout), 203 | NULL }, 204 | 205 | { ngx_string("socks_send_timeout"), 206 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 207 | ngx_conf_set_msec_slot, 208 | NGX_HTTP_LOC_CONF_OFFSET, 209 | offsetof(ngx_http_socks_loc_conf_t, upstream.send_timeout), 210 | NULL }, 211 | 212 | { ngx_string("socks_send_lowat"), 213 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 214 | ngx_conf_set_size_slot, 215 | NGX_HTTP_LOC_CONF_OFFSET, 216 | offsetof(ngx_http_socks_loc_conf_t, upstream.send_lowat), 217 | &ngx_http_socks_lowat_post }, 218 | 219 | { ngx_string("socks_intercept_errors"), 220 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 221 | ngx_conf_set_flag_slot, 222 | NGX_HTTP_LOC_CONF_OFFSET, 223 | offsetof(ngx_http_socks_loc_conf_t, upstream.intercept_errors), 224 | NULL }, 225 | 226 | { ngx_string("socks_set_header"), 227 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, 228 | ngx_conf_set_keyval_slot, 229 | NGX_HTTP_LOC_CONF_OFFSET, 230 | offsetof(ngx_http_socks_loc_conf_t, headers_source), 231 | NULL }, 232 | 233 | { ngx_string("socks_headers_hash_max_size"), 234 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 235 | ngx_conf_set_num_slot, 236 | NGX_HTTP_LOC_CONF_OFFSET, 237 | offsetof(ngx_http_socks_loc_conf_t, headers_hash_max_size), 238 | NULL }, 239 | 240 | { ngx_string("socks_headers_hash_bucket_size"), 241 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 242 | ngx_conf_set_num_slot, 243 | NGX_HTTP_LOC_CONF_OFFSET, 244 | offsetof(ngx_http_socks_loc_conf_t, headers_hash_bucket_size), 245 | NULL }, 246 | 247 | { ngx_string("socks_set_body"), 248 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 249 | ngx_conf_set_str_slot, 250 | NGX_HTTP_LOC_CONF_OFFSET, 251 | offsetof(ngx_http_socks_loc_conf_t, body_source), 252 | NULL }, 253 | 254 | { ngx_string("socks_method"), 255 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 256 | ngx_conf_set_str_slot, 257 | NGX_HTTP_LOC_CONF_OFFSET, 258 | offsetof(ngx_http_socks_loc_conf_t, method), 259 | NULL }, 260 | 261 | { ngx_string("socks_pass_request_headers"), 262 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 263 | ngx_conf_set_flag_slot, 264 | NGX_HTTP_LOC_CONF_OFFSET, 265 | offsetof(ngx_http_socks_loc_conf_t, upstream.pass_request_headers), 266 | NULL }, 267 | 268 | { ngx_string("socks_pass_request_body"), 269 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 270 | ngx_conf_set_flag_slot, 271 | NGX_HTTP_LOC_CONF_OFFSET, 272 | offsetof(ngx_http_socks_loc_conf_t, upstream.pass_request_body), 273 | NULL }, 274 | 275 | { ngx_string("socks_buffer_size"), 276 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 277 | ngx_conf_set_size_slot, 278 | NGX_HTTP_LOC_CONF_OFFSET, 279 | offsetof(ngx_http_socks_loc_conf_t, upstream.buffer_size), 280 | NULL }, 281 | 282 | { ngx_string("socks_read_timeout"), 283 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 284 | ngx_conf_set_msec_slot, 285 | NGX_HTTP_LOC_CONF_OFFSET, 286 | offsetof(ngx_http_socks_loc_conf_t, upstream.read_timeout), 287 | NULL }, 288 | 289 | { ngx_string("socks_buffers"), 290 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, 291 | ngx_conf_set_bufs_slot, 292 | NGX_HTTP_LOC_CONF_OFFSET, 293 | offsetof(ngx_http_socks_loc_conf_t, upstream.bufs), 294 | NULL }, 295 | 296 | { ngx_string("socks_busy_buffers_size"), 297 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 298 | ngx_conf_set_size_slot, 299 | NGX_HTTP_LOC_CONF_OFFSET, 300 | offsetof(ngx_http_socks_loc_conf_t, upstream.busy_buffers_size_conf), 301 | NULL }, 302 | 303 | { ngx_string("socks_force_ranges"), 304 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 305 | ngx_conf_set_flag_slot, 306 | NGX_HTTP_LOC_CONF_OFFSET, 307 | offsetof(ngx_http_socks_loc_conf_t, upstream.force_ranges), 308 | NULL }, 309 | 310 | { ngx_string("socks_limit_rate"), 311 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 312 | ngx_conf_set_size_slot, 313 | NGX_HTTP_LOC_CONF_OFFSET, 314 | offsetof(ngx_http_socks_loc_conf_t, upstream.limit_rate), 315 | NULL }, 316 | 317 | #if (NGX_HTTP_CACHE) 318 | 319 | { ngx_string("socks_cache"), 320 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 321 | ngx_http_socks_cache, 322 | NGX_HTTP_LOC_CONF_OFFSET, 323 | 0, 324 | NULL }, 325 | 326 | { ngx_string("socks_cache_key"), 327 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 328 | ngx_http_socks_cache_key, 329 | NGX_HTTP_LOC_CONF_OFFSET, 330 | 0, 331 | NULL }, 332 | 333 | { ngx_string("socks_cache_path"), 334 | NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE, 335 | ngx_http_file_cache_set_slot, 336 | NGX_HTTP_MAIN_CONF_OFFSET, 337 | offsetof(ngx_http_socks_main_conf_t, caches), 338 | &ngx_http_socks_module }, 339 | 340 | { ngx_string("socks_cache_bypass"), 341 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 342 | ngx_http_set_predicate_slot, 343 | NGX_HTTP_LOC_CONF_OFFSET, 344 | offsetof(ngx_http_socks_loc_conf_t, upstream.cache_bypass), 345 | NULL }, 346 | 347 | { ngx_string("socks_no_cache"), 348 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 349 | ngx_http_set_predicate_slot, 350 | NGX_HTTP_LOC_CONF_OFFSET, 351 | offsetof(ngx_http_socks_loc_conf_t, upstream.no_cache), 352 | NULL }, 353 | 354 | { ngx_string("socks_cache_valid"), 355 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 356 | ngx_http_file_cache_valid_set_slot, 357 | NGX_HTTP_LOC_CONF_OFFSET, 358 | offsetof(ngx_http_socks_loc_conf_t, upstream.cache_valid), 359 | NULL }, 360 | 361 | { ngx_string("socks_cache_min_uses"), 362 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 363 | ngx_conf_set_num_slot, 364 | NGX_HTTP_LOC_CONF_OFFSET, 365 | offsetof(ngx_http_socks_loc_conf_t, upstream.cache_min_uses), 366 | NULL }, 367 | 368 | { ngx_string("socks_cache_use_stale"), 369 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 370 | ngx_conf_set_bitmask_slot, 371 | NGX_HTTP_LOC_CONF_OFFSET, 372 | offsetof(ngx_http_socks_loc_conf_t, upstream.cache_use_stale), 373 | &ngx_http_socks_next_upstream_masks }, 374 | 375 | { ngx_string("socks_cache_methods"), 376 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 377 | ngx_conf_set_bitmask_slot, 378 | NGX_HTTP_LOC_CONF_OFFSET, 379 | offsetof(ngx_http_socks_loc_conf_t, upstream.cache_methods), 380 | &ngx_http_upstream_cache_method_mask }, 381 | 382 | { ngx_string("socks_cache_lock"), 383 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 384 | ngx_conf_set_flag_slot, 385 | NGX_HTTP_LOC_CONF_OFFSET, 386 | offsetof(ngx_http_socks_loc_conf_t, upstream.cache_lock), 387 | NULL }, 388 | 389 | { ngx_string("socks_cache_lock_timeout"), 390 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 391 | ngx_conf_set_msec_slot, 392 | NGX_HTTP_LOC_CONF_OFFSET, 393 | offsetof(ngx_http_socks_loc_conf_t, upstream.cache_lock_timeout), 394 | NULL }, 395 | 396 | { ngx_string("socks_cache_lock_age"), 397 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 398 | ngx_conf_set_msec_slot, 399 | NGX_HTTP_LOC_CONF_OFFSET, 400 | offsetof(ngx_http_socks_loc_conf_t, upstream.cache_lock_age), 401 | NULL }, 402 | 403 | { ngx_string("socks_cache_revalidate"), 404 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 405 | ngx_conf_set_flag_slot, 406 | NGX_HTTP_LOC_CONF_OFFSET, 407 | offsetof(ngx_http_socks_loc_conf_t, upstream.cache_revalidate), 408 | NULL }, 409 | 410 | #if nginx_version >= 1009007 411 | { ngx_string("socks_cache_convert_head"), 412 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 413 | ngx_conf_set_flag_slot, 414 | NGX_HTTP_LOC_CONF_OFFSET, 415 | offsetof(ngx_http_socks_loc_conf_t, upstream.cache_convert_head), 416 | NULL }, 417 | #endif 418 | 419 | #endif 420 | 421 | { ngx_string("socks_temp_path"), 422 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234, 423 | ngx_conf_set_path_slot, 424 | NGX_HTTP_LOC_CONF_OFFSET, 425 | offsetof(ngx_http_socks_loc_conf_t, upstream.temp_path), 426 | NULL }, 427 | 428 | { ngx_string("socks_max_temp_file_size"), 429 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 430 | ngx_conf_set_size_slot, 431 | NGX_HTTP_LOC_CONF_OFFSET, 432 | offsetof(ngx_http_socks_loc_conf_t, upstream.max_temp_file_size_conf), 433 | NULL }, 434 | 435 | { ngx_string("socks_temp_file_write_size"), 436 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 437 | ngx_conf_set_size_slot, 438 | NGX_HTTP_LOC_CONF_OFFSET, 439 | offsetof(ngx_http_socks_loc_conf_t, upstream.temp_file_write_size_conf), 440 | NULL }, 441 | 442 | { ngx_string("socks_next_upstream"), 443 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 444 | ngx_conf_set_bitmask_slot, 445 | NGX_HTTP_LOC_CONF_OFFSET, 446 | offsetof(ngx_http_socks_loc_conf_t, upstream.next_upstream), 447 | &ngx_http_socks_next_upstream_masks }, 448 | 449 | { ngx_string("socks_next_upstream_tries"), 450 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 451 | ngx_conf_set_num_slot, 452 | NGX_HTTP_LOC_CONF_OFFSET, 453 | offsetof(ngx_http_socks_loc_conf_t, upstream.next_upstream_tries), 454 | NULL }, 455 | 456 | { ngx_string("socks_next_upstream_timeout"), 457 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 458 | ngx_conf_set_msec_slot, 459 | NGX_HTTP_LOC_CONF_OFFSET, 460 | offsetof(ngx_http_socks_loc_conf_t, upstream.next_upstream_timeout), 461 | NULL }, 462 | 463 | { ngx_string("socks_pass_header"), 464 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 465 | ngx_conf_set_str_array_slot, 466 | NGX_HTTP_LOC_CONF_OFFSET, 467 | offsetof(ngx_http_socks_loc_conf_t, upstream.pass_headers), 468 | NULL }, 469 | 470 | { ngx_string("socks_hide_header"), 471 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 472 | ngx_conf_set_str_array_slot, 473 | NGX_HTTP_LOC_CONF_OFFSET, 474 | offsetof(ngx_http_socks_loc_conf_t, upstream.hide_headers), 475 | NULL }, 476 | 477 | { ngx_string("socks_ignore_headers"), 478 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 479 | ngx_conf_set_bitmask_slot, 480 | NGX_HTTP_LOC_CONF_OFFSET, 481 | offsetof(ngx_http_socks_loc_conf_t, upstream.ignore_headers), 482 | &ngx_http_upstream_ignore_headers_masks }, 483 | 484 | { ngx_string("socks_http_version"), 485 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 486 | ngx_conf_set_enum_slot, 487 | NGX_HTTP_LOC_CONF_OFFSET, 488 | offsetof(ngx_http_socks_loc_conf_t, http_version), 489 | &ngx_http_socks_http_version }, 490 | 491 | { ngx_string("socks_tunnel_header"), 492 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 493 | ngx_http_socks_tunnel_header, 494 | NGX_HTTP_LOC_CONF_OFFSET, 495 | offsetof(ngx_http_socks_loc_conf_t, tunnel_header), 496 | NULL }, 497 | 498 | { ngx_string("socks_set_host"), 499 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 500 | ngx_http_socks_set_host, 501 | NGX_HTTP_LOC_CONF_OFFSET, 502 | offsetof(ngx_http_socks_loc_conf_t, host), 503 | NULL }, 504 | 505 | ngx_null_command 506 | }; 507 | 508 | 509 | static ngx_http_module_t ngx_http_socks_module_ctx = { 510 | ngx_http_socks_add_variables, /* preconfiguration */ 511 | NULL, /* postconfiguration */ 512 | 513 | ngx_http_socks_create_main_conf, /* create main configuration */ 514 | NULL, /* init main configuration */ 515 | 516 | NULL, /* create server configuration */ 517 | NULL, /* merge server configuration */ 518 | 519 | ngx_http_socks_create_loc_conf, /* create location configuration */ 520 | ngx_http_socks_merge_loc_conf /* merge location configuration */ 521 | }; 522 | 523 | 524 | ngx_module_t ngx_http_socks_module = { 525 | NGX_MODULE_V1, 526 | &ngx_http_socks_module_ctx, /* module context */ 527 | ngx_http_socks_commands, /* module directives */ 528 | NGX_HTTP_MODULE, /* module type */ 529 | NULL, /* init master */ 530 | NULL, /* init module */ 531 | NULL, /* init process */ 532 | NULL, /* init thread */ 533 | NULL, /* exit thread */ 534 | NULL, /* exit process */ 535 | NULL, /* exit master */ 536 | NGX_MODULE_V1_PADDING 537 | }; 538 | 539 | 540 | static char ngx_http_socks_version[] = " HTTP/1.0" CRLF; 541 | static char ngx_http_socks_version_11[] = " HTTP/1.1" CRLF; 542 | 543 | 544 | static ngx_keyval_t ngx_http_socks_headers[] = { 545 | { ngx_string("Host"), ngx_string("$socks_host") }, 546 | { ngx_string("Connection"), ngx_string("close") }, 547 | { ngx_string("Content-Length"), ngx_string("$socks_internal_body_length") }, 548 | { ngx_string("Transfer-Encoding"), ngx_string("$socks_internal_chunked") }, 549 | { ngx_string("TE"), ngx_string("") }, 550 | { ngx_string("Keep-Alive"), ngx_string("") }, 551 | { ngx_string("Expect"), ngx_string("") }, 552 | { ngx_string("Upgrade"), ngx_string("") }, 553 | { ngx_null_string, ngx_null_string } 554 | }; 555 | 556 | 557 | static ngx_str_t ngx_http_socks_hide_headers[] = { 558 | ngx_string("Date"), 559 | ngx_string("Server"), 560 | ngx_string("X-Pad"), 561 | ngx_string("X-Accel-Expires"), 562 | ngx_string("X-Accel-Redirect"), 563 | ngx_string("X-Accel-Limit-Rate"), 564 | ngx_string("X-Accel-Buffering"), 565 | ngx_string("X-Accel-Charset"), 566 | ngx_null_string 567 | }; 568 | 569 | 570 | #if (NGX_HTTP_CACHE) 571 | 572 | static ngx_keyval_t ngx_http_socks_cache_headers[] = { 573 | { ngx_string("Host"), ngx_string("$socks_host") }, 574 | { ngx_string("Connection"), ngx_string("close") }, 575 | { ngx_string("Content-Length"), ngx_string("$socks_internal_body_length") }, 576 | { ngx_string("Transfer-Encoding"), ngx_string("$socks_internal_chunked") }, 577 | { ngx_string("TE"), ngx_string("") }, 578 | { ngx_string("Keep-Alive"), ngx_string("") }, 579 | { ngx_string("Expect"), ngx_string("") }, 580 | { ngx_string("Upgrade"), ngx_string("") }, 581 | { ngx_string("If-Modified-Since"), 582 | ngx_string("$upstream_cache_last_modified") }, 583 | { ngx_string("If-Unmodified-Since"), ngx_string("") }, 584 | { ngx_string("If-None-Match"), ngx_string("$upstream_cache_etag") }, 585 | { ngx_string("If-Match"), ngx_string("") }, 586 | { ngx_string("Range"), ngx_string("") }, 587 | { ngx_string("If-Range"), ngx_string("") }, 588 | { ngx_null_string, ngx_null_string } 589 | }; 590 | 591 | #endif 592 | 593 | 594 | static ngx_http_variable_t ngx_http_socks_vars[] = { 595 | 596 | { ngx_string("socks_host"), NULL, ngx_http_socks_host_variable, 0, 597 | NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 }, 598 | 599 | { ngx_string("socks_port"), NULL, ngx_http_socks_port_variable, 0, 600 | NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 }, 601 | 602 | { ngx_string("socks_add_x_forwarded_for"), NULL, 603 | ngx_http_socks_add_x_forwarded_for_variable, 0, NGX_HTTP_VAR_NOHASH, 0 }, 604 | 605 | #if 0 606 | { ngx_string("socks_add_via"), NULL, NULL, 0, NGX_HTTP_VAR_NOHASH, 0 }, 607 | #endif 608 | 609 | { ngx_string("socks_internal_body_length"), NULL, 610 | ngx_http_socks_internal_body_length_variable, 0, 611 | NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, 612 | 613 | { ngx_string("socks_internal_chunked"), NULL, 614 | ngx_http_socks_internal_chunked_variable, 0, 615 | NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, 616 | 617 | { ngx_null_string, NULL, NULL, 0, 0, 0 } 618 | }; 619 | 620 | 621 | static ngx_path_init_t ngx_http_socks_temp_path = { 622 | ngx_string(NGX_HTTP_PROXY_TEMP_PATH), { 1, 2, 0 } 623 | }; 624 | 625 | 626 | static ngx_int_t 627 | ngx_http_socks_handler(ngx_http_request_t *r) 628 | { 629 | ngx_int_t rc; 630 | ngx_http_upstream_t *u; 631 | ngx_http_socks_ctx_t *ctx; 632 | ngx_http_socks_loc_conf_t *slcf; 633 | #if (NGX_HTTP_CACHE) 634 | ngx_http_socks_main_conf_t *pmcf; 635 | #endif 636 | 637 | if (r->http_version < NGX_HTTP_VERSION_11) { 638 | return NGX_HTTP_BAD_REQUEST; 639 | } 640 | 641 | if (ngx_http_socks_upstream_create(r) != NGX_OK) { 642 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 643 | } 644 | 645 | ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_socks_ctx_t)); 646 | if (ctx == NULL) { 647 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 648 | } 649 | 650 | ngx_http_set_ctx(r, ctx, ngx_http_socks_module); 651 | 652 | slcf = ngx_http_get_module_loc_conf(r, ngx_http_socks_module); 653 | 654 | u = r->upstream; 655 | 656 | if (slcf->proxy_lengths == NULL) { 657 | ctx->vars = slcf->vars; 658 | u->schema = slcf->vars.schema; 659 | } else { 660 | if (ngx_http_socks_eval(r, ctx, slcf) != NGX_OK) { 661 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 662 | } 663 | } 664 | 665 | u->output.tag = (ngx_buf_tag_t) &ngx_http_socks_module; 666 | 667 | u->conf = &slcf->upstream; 668 | 669 | #if (NGX_HTTP_CACHE) 670 | pmcf = ngx_http_get_module_main_conf(r, ngx_http_socks_module); 671 | 672 | u->caches = &pmcf->caches; 673 | u->create_key = ngx_http_socks_create_key; 674 | #endif 675 | 676 | u->create_request = ngx_http_socks_create_request; 677 | u->reinit_request = ngx_http_socks_reinit_request; 678 | u->process_header = ngx_http_socks_process_status_line; 679 | u->abort_request = ngx_http_socks_abort_request; 680 | u->finalize_request = ngx_http_socks_finalize_request; 681 | r->state = 0; 682 | 683 | if (slcf->redirects) { 684 | u->rewrite_redirect = ngx_http_socks_rewrite_redirect; 685 | } 686 | 687 | if (slcf->cookie_domains || slcf->cookie_paths) { 688 | u->rewrite_cookie = ngx_http_socks_rewrite_cookie; 689 | } 690 | 691 | u->buffering = slcf->upstream.buffering; 692 | 693 | u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t)); 694 | if (u->pipe == NULL) { 695 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 696 | } 697 | 698 | u->pipe->input_filter = ngx_http_socks_copy_filter; 699 | u->pipe->input_ctx = r; 700 | 701 | u->input_filter_init = ngx_http_socks_input_filter_init; 702 | u->input_filter = ngx_http_socks_non_buffered_copy_filter; 703 | u->input_filter_ctx = r; 704 | 705 | u->accel = 1; 706 | 707 | if (!slcf->upstream.request_buffering 708 | && slcf->body_values == NULL && slcf->upstream.pass_request_body 709 | && (!r->headers_in.chunked 710 | || slcf->http_version == NGX_HTTP_VERSION_11)) 711 | { 712 | r->request_body_no_buffering = 1; 713 | } 714 | 715 | rc = ngx_http_read_client_request_body(r, ngx_http_socks_upstream_init); 716 | 717 | if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { 718 | return rc; 719 | } 720 | 721 | return NGX_DONE; 722 | } 723 | 724 | 725 | static ngx_int_t 726 | ngx_http_socks_eval(ngx_http_request_t *r, ngx_http_socks_ctx_t *ctx, 727 | ngx_http_socks_loc_conf_t *slcf) 728 | { 729 | u_char *p; 730 | u_short port; 731 | ngx_str_t proxy; 732 | ngx_url_t url; 733 | ngx_http_upstream_t *u; 734 | 735 | if (ngx_http_script_run(r, &proxy, slcf->proxy_lengths->elts, 0, 736 | slcf->proxy_values->elts) 737 | == NULL) 738 | { 739 | return NGX_ERROR; 740 | } 741 | 742 | if (proxy.len > 9 743 | && ngx_strncasecmp(proxy.data, (u_char *) "socks5://", 9) == 0) 744 | { 745 | port = 1080; 746 | } else { 747 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 748 | "invalid URL prefix in \"%V\"", &proxy); 749 | return NGX_ERROR; 750 | } 751 | 752 | u = r->upstream; 753 | 754 | u->schema.len = 9; 755 | u->schema.data = proxy.data; 756 | 757 | ngx_memzero(&url, sizeof(ngx_url_t)); 758 | 759 | url.url.len = proxy.len - 9; 760 | url.url.data = proxy.data + 9; 761 | url.default_port = port; 762 | url.uri_part = 1; 763 | url.no_resolve = 1; 764 | 765 | if (ngx_parse_url(r->pool, &url) != NGX_OK) { 766 | if (url.err) { 767 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 768 | "%s in upstream \"%V\"", url.err, &url.url); 769 | } 770 | 771 | return NGX_ERROR; 772 | } 773 | 774 | if (url.uri.len) { 775 | if (url.uri.data[0] == '?') { 776 | p = ngx_pnalloc(r->pool, url.uri.len + 1); 777 | if (p == NULL) { 778 | return NGX_ERROR; 779 | } 780 | 781 | *p++ = '/'; 782 | ngx_memcpy(p, url.uri.data, url.uri.len); 783 | 784 | url.uri.len++; 785 | url.uri.data = p - 1; 786 | } 787 | } 788 | 789 | ngx_http_socks_set_vars(&url, &ctx->vars); 790 | 791 | u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t)); 792 | if (u->resolved == NULL) { 793 | return NGX_ERROR; 794 | } 795 | 796 | if (url.addrs && url.addrs[0].sockaddr) { 797 | u->resolved->sockaddr = url.addrs[0].sockaddr; 798 | u->resolved->socklen = url.addrs[0].socklen; 799 | u->resolved->naddrs = 1; 800 | u->resolved->host = url.addrs[0].name; 801 | 802 | } else { 803 | u->resolved->host = url.host; 804 | u->resolved->port = (in_port_t) (url.no_port ? port : url.port); 805 | u->resolved->no_port = url.no_port; 806 | } 807 | 808 | return NGX_OK; 809 | } 810 | 811 | 812 | #if (NGX_HTTP_CACHE) 813 | 814 | static ngx_int_t 815 | ngx_http_socks_create_key(ngx_http_request_t *r) 816 | { 817 | size_t len, loc_len; 818 | u_char *p; 819 | uintptr_t escape; 820 | ngx_str_t host; 821 | ngx_str_t *key; 822 | ngx_http_upstream_t *u; 823 | ngx_http_socks_ctx_t *ctx; 824 | ngx_http_socks_loc_conf_t *slcf; 825 | 826 | u = r->upstream; 827 | 828 | slcf = ngx_http_get_module_loc_conf(r, ngx_http_socks_module); 829 | 830 | ctx = ngx_http_get_module_ctx(r, ngx_http_socks_module); 831 | 832 | host = r->headers_in.host->value; 833 | 834 | key = ngx_array_push(&r->cache->keys); 835 | if (key == NULL) { 836 | return NGX_ERROR; 837 | } 838 | 839 | if (slcf->cache_key.value.data) { 840 | 841 | if (ngx_http_complex_value(r, &slcf->cache_key, key) != NGX_OK) { 842 | return NGX_ERROR; 843 | } 844 | 845 | return NGX_OK; 846 | } 847 | 848 | p = ngx_pnalloc(r->pool, host.len + 7); 849 | if (p == NULL) { 850 | return NGX_ERROR; 851 | } 852 | 853 | key->data = p; 854 | p = ngx_copy(p, "http://", 7); 855 | p = ngx_copy(p, host.data, host.len); 856 | key->len = p - key->data; 857 | 858 | key = ngx_array_push(&r->cache->keys); 859 | if (key == NULL) { 860 | return NGX_ERROR; 861 | } 862 | 863 | if (slcf->proxy_lengths && ctx->vars.uri.len) { 864 | 865 | *key = ctx->vars.uri; 866 | u->uri = ctx->vars.uri; 867 | 868 | return NGX_OK; 869 | 870 | } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main) 871 | { 872 | *key = r->unparsed_uri; 873 | u->uri = r->unparsed_uri; 874 | 875 | return NGX_OK; 876 | } 877 | 878 | loc_len = (r->valid_location && ctx->vars.uri.len) ? slcf->location.len : 0; 879 | 880 | if (r->quoted_uri || r->internal) { 881 | escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len, 882 | r->uri.len - loc_len, NGX_ESCAPE_URI); 883 | } else { 884 | escape = 0; 885 | } 886 | 887 | len = ctx->vars.uri.len + r->uri.len - loc_len + escape 888 | + sizeof("?") - 1 + r->args.len; 889 | 890 | p = ngx_pnalloc(r->pool, len); 891 | if (p == NULL) { 892 | return NGX_ERROR; 893 | } 894 | 895 | key->data = p; 896 | 897 | if (r->valid_location) { 898 | p = ngx_copy(p, ctx->vars.uri.data, ctx->vars.uri.len); 899 | } 900 | 901 | if (escape) { 902 | ngx_escape_uri(p, r->uri.data + loc_len, 903 | r->uri.len - loc_len, NGX_ESCAPE_URI); 904 | p += r->uri.len - loc_len + escape; 905 | 906 | } else { 907 | p = ngx_copy(p, r->uri.data + loc_len, r->uri.len - loc_len); 908 | } 909 | 910 | if (r->args.len > 0) { 911 | *p++ = '?'; 912 | p = ngx_copy(p, r->args.data, r->args.len); 913 | } 914 | 915 | key->len = p - key->data; 916 | u->uri = *key; 917 | 918 | return NGX_OK; 919 | } 920 | 921 | #endif 922 | 923 | static ngx_int_t 924 | ngx_http_socks_create_request(ngx_http_request_t *r) 925 | { 926 | size_t len, uri_len, loc_len, body_len; 927 | uintptr_t escape; 928 | ngx_buf_t *b; 929 | ngx_str_t method; 930 | ngx_uint_t i, unparsed_uri; 931 | ngx_chain_t *cl, *body; 932 | ngx_list_part_t *part; 933 | ngx_table_elt_t *header; 934 | ngx_http_upstream_t *u; 935 | ngx_http_socks_ctx_t *ctx; 936 | ngx_http_script_code_pt code; 937 | ngx_http_socks_headers_t *headers; 938 | ngx_http_script_engine_t e, le; 939 | ngx_http_socks_loc_conf_t *slcf; 940 | ngx_http_script_len_code_pt lcode; 941 | 942 | u = r->upstream; 943 | 944 | slcf = ngx_http_get_module_loc_conf(r, ngx_http_socks_module); 945 | 946 | #if (NGX_HTTP_CACHE) 947 | headers = u->cacheable ? &slcf->headers_cache : &slcf->headers; 948 | #else 949 | headers = &slcf->headers; 950 | #endif 951 | 952 | if (u->method.len) { 953 | /* HEAD was changed to GET to cache response */ 954 | method = u->method; 955 | method.len++; 956 | 957 | } else if (slcf->method.len) { 958 | method = slcf->method; 959 | 960 | } else { 961 | method = r->method_name; 962 | method.len++; 963 | } 964 | 965 | ctx = ngx_http_get_module_ctx(r, ngx_http_socks_module); 966 | 967 | if (method.len == 5 968 | && ngx_strncasecmp(method.data, (u_char *) "HEAD ", 5) == 0) 969 | { 970 | ctx->head = 1; 971 | } 972 | 973 | len = method.len + sizeof(ngx_http_socks_version) - 1 + sizeof(CRLF) - 1; 974 | 975 | escape = 0; 976 | loc_len = 0; 977 | unparsed_uri = 0; 978 | 979 | if (slcf->proxy_lengths && ctx->vars.uri.len) { 980 | uri_len = ctx->vars.uri.len; 981 | 982 | } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main) 983 | { 984 | unparsed_uri = 1; 985 | uri_len = r->unparsed_uri.len; 986 | 987 | } else { 988 | loc_len = (r->valid_location && ctx->vars.uri.len) ? 989 | slcf->location.len : 0; 990 | 991 | if (r->quoted_uri || r->space_in_uri || r->internal) { 992 | escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len, 993 | r->uri.len - loc_len, NGX_ESCAPE_URI); 994 | } 995 | 996 | uri_len = ctx->vars.uri.len + r->uri.len - loc_len + escape 997 | + sizeof("?") - 1 + r->args.len; 998 | } 999 | 1000 | if (uri_len == 0) { 1001 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 1002 | "zero length URI to proxy"); 1003 | return NGX_ERROR; 1004 | } 1005 | 1006 | len += uri_len; 1007 | 1008 | ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); 1009 | 1010 | ngx_http_script_flush_no_cacheable_variables(r, slcf->body_flushes); 1011 | ngx_http_script_flush_no_cacheable_variables(r, headers->flushes); 1012 | 1013 | if (slcf->body_lengths) { 1014 | le.ip = slcf->body_lengths->elts; 1015 | le.request = r; 1016 | le.flushed = 1; 1017 | body_len = 0; 1018 | 1019 | while (*(uintptr_t *) le.ip) { 1020 | lcode = *(ngx_http_script_len_code_pt *) le.ip; 1021 | body_len += lcode(&le); 1022 | } 1023 | 1024 | ctx->internal_body_length = body_len; 1025 | len += body_len; 1026 | 1027 | } else if (r->headers_in.chunked && r->reading_body) { 1028 | ctx->internal_body_length = -1; 1029 | ctx->internal_chunked = 1; 1030 | 1031 | } else { 1032 | ctx->internal_body_length = r->headers_in.content_length_n; 1033 | } 1034 | 1035 | le.ip = headers->lengths->elts; 1036 | le.request = r; 1037 | le.flushed = 1; 1038 | 1039 | while (*(uintptr_t *) le.ip) { 1040 | while (*(uintptr_t *) le.ip) { 1041 | lcode = *(ngx_http_script_len_code_pt *) le.ip; 1042 | len += lcode(&le); 1043 | } 1044 | le.ip += sizeof(uintptr_t); 1045 | } 1046 | 1047 | if (slcf->tunnel_header.len) { 1048 | part = &r->headers_in.headers.part; 1049 | header = part->elts; 1050 | 1051 | for (i = 0; /* void */; i++) { 1052 | 1053 | if (i >= part->nelts) { 1054 | if (part->next == NULL) { 1055 | break; 1056 | } 1057 | 1058 | part = part->next; 1059 | header = part->elts; 1060 | i = 0; 1061 | } 1062 | 1063 | if (ngx_strncmp(header[i].lowcase_key, 1064 | slcf->tunnel_header.data, slcf->tunnel_header.len) == 0) 1065 | { 1066 | ctx->tunnel = 1; 1067 | continue; 1068 | } 1069 | } 1070 | } 1071 | 1072 | if (slcf->host.value.data) { 1073 | if (ngx_http_complex_value(r, &slcf->host, &ctx->host) != NGX_OK) { 1074 | return NGX_ERROR; 1075 | } 1076 | } else { 1077 | ctx->host.len = r->headers_in.host->value.len; 1078 | ctx->host.data = r->headers_in.host->value.data; 1079 | } 1080 | 1081 | if (slcf->upstream.pass_request_headers) { 1082 | part = &r->headers_in.headers.part; 1083 | header = part->elts; 1084 | 1085 | for (i = 0; /* void */; i++) { 1086 | 1087 | if (i >= part->nelts) { 1088 | if (part->next == NULL) { 1089 | break; 1090 | } 1091 | 1092 | part = part->next; 1093 | header = part->elts; 1094 | i = 0; 1095 | } 1096 | 1097 | if (ngx_hash_find(&headers->hash, header[i].hash, 1098 | header[i].lowcase_key, header[i].key.len)) 1099 | { 1100 | continue; 1101 | } 1102 | 1103 | len += header[i].key.len + sizeof(": ") - 1 1104 | + header[i].value.len + sizeof(CRLF) - 1; 1105 | } 1106 | } 1107 | 1108 | 1109 | b = ngx_create_temp_buf(r->pool, len); 1110 | if (b == NULL) { 1111 | return NGX_ERROR; 1112 | } 1113 | 1114 | cl = ngx_alloc_chain_link(r->pool); 1115 | if (cl == NULL) { 1116 | return NGX_ERROR; 1117 | } 1118 | 1119 | cl->buf = b; 1120 | 1121 | 1122 | /* the request line */ 1123 | 1124 | b->last = ngx_copy(b->last, method.data, method.len); 1125 | 1126 | u->uri.data = b->last; 1127 | 1128 | if (slcf->proxy_lengths && ctx->vars.uri.len) { 1129 | b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len); 1130 | 1131 | } else if (unparsed_uri) { 1132 | b->last = ngx_copy(b->last, r->unparsed_uri.data, r->unparsed_uri.len); 1133 | 1134 | } else { 1135 | if (r->valid_location) { 1136 | b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len); 1137 | } 1138 | 1139 | if (escape) { 1140 | ngx_escape_uri(b->last, r->uri.data + loc_len, 1141 | r->uri.len - loc_len, NGX_ESCAPE_URI); 1142 | b->last += r->uri.len - loc_len + escape; 1143 | 1144 | } else { 1145 | b->last = ngx_copy(b->last, r->uri.data + loc_len, 1146 | r->uri.len - loc_len); 1147 | } 1148 | 1149 | if (r->args.len > 0) { 1150 | *b->last++ = '?'; 1151 | b->last = ngx_copy(b->last, r->args.data, r->args.len); 1152 | } 1153 | } 1154 | 1155 | u->uri.len = b->last - u->uri.data; 1156 | 1157 | if (slcf->http_version == NGX_HTTP_VERSION_11) { 1158 | b->last = ngx_cpymem(b->last, ngx_http_socks_version_11, 1159 | sizeof(ngx_http_socks_version_11) - 1); 1160 | 1161 | } else { 1162 | b->last = ngx_cpymem(b->last, ngx_http_socks_version, 1163 | sizeof(ngx_http_socks_version) - 1); 1164 | } 1165 | 1166 | ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); 1167 | 1168 | e.ip = headers->values->elts; 1169 | e.pos = b->last; 1170 | e.request = r; 1171 | e.flushed = 1; 1172 | 1173 | le.ip = headers->lengths->elts; 1174 | 1175 | while (*(uintptr_t *) le.ip) { 1176 | lcode = *(ngx_http_script_len_code_pt *) le.ip; 1177 | 1178 | /* skip the header line name length */ 1179 | (void) lcode(&le); 1180 | 1181 | if (*(ngx_http_script_len_code_pt *) le.ip) { 1182 | 1183 | for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) { 1184 | lcode = *(ngx_http_script_len_code_pt *) le.ip; 1185 | } 1186 | 1187 | e.skip = (len == sizeof(CRLF) - 1) ? 1 : 0; 1188 | 1189 | } else { 1190 | e.skip = 0; 1191 | } 1192 | 1193 | le.ip += sizeof(uintptr_t); 1194 | 1195 | while (*(uintptr_t *) e.ip) { 1196 | code = *(ngx_http_script_code_pt *) e.ip; 1197 | code((ngx_http_script_engine_t *) &e); 1198 | } 1199 | e.ip += sizeof(uintptr_t); 1200 | } 1201 | 1202 | b->last = e.pos; 1203 | 1204 | 1205 | if (slcf->upstream.pass_request_headers) { 1206 | part = &r->headers_in.headers.part; 1207 | header = part->elts; 1208 | 1209 | for (i = 0; /* void */; i++) { 1210 | 1211 | if (i >= part->nelts) { 1212 | if (part->next == NULL) { 1213 | break; 1214 | } 1215 | 1216 | part = part->next; 1217 | header = part->elts; 1218 | i = 0; 1219 | } 1220 | 1221 | if (ngx_hash_find(&headers->hash, header[i].hash, 1222 | header[i].lowcase_key, header[i].key.len)) 1223 | { 1224 | continue; 1225 | } 1226 | 1227 | b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len); 1228 | 1229 | *b->last++ = ':'; *b->last++ = ' '; 1230 | 1231 | b->last = ngx_copy(b->last, header[i].value.data, 1232 | header[i].value.len); 1233 | 1234 | *b->last++ = CR; *b->last++ = LF; 1235 | 1236 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1237 | "http socks header: \"%V: %V\"", 1238 | &header[i].key, &header[i].value); 1239 | } 1240 | } 1241 | 1242 | 1243 | /* add "\r\n" at the header end */ 1244 | *b->last++ = CR; *b->last++ = LF; 1245 | 1246 | if (slcf->body_values) { 1247 | e.ip = slcf->body_values->elts; 1248 | e.pos = b->last; 1249 | e.skip = 0; 1250 | 1251 | while (*(uintptr_t *) e.ip) { 1252 | code = *(ngx_http_script_code_pt *) e.ip; 1253 | code((ngx_http_script_engine_t *) &e); 1254 | } 1255 | 1256 | b->last = e.pos; 1257 | } 1258 | 1259 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1260 | "http socks header:%N\"%*s\"", 1261 | (size_t) (b->last - b->pos), b->pos); 1262 | 1263 | if (r->request_body_no_buffering) { 1264 | 1265 | u->request_bufs = cl; 1266 | 1267 | if (ctx->internal_chunked) { 1268 | u->output.output_filter = ngx_http_socks_body_output_filter; 1269 | u->output.filter_ctx = r; 1270 | } 1271 | 1272 | } else if (slcf->body_values == NULL && slcf->upstream.pass_request_body) { 1273 | 1274 | body = u->request_bufs; 1275 | u->request_bufs = cl; 1276 | 1277 | while (body) { 1278 | b = ngx_alloc_buf(r->pool); 1279 | if (b == NULL) { 1280 | return NGX_ERROR; 1281 | } 1282 | 1283 | ngx_memcpy(b, body->buf, sizeof(ngx_buf_t)); 1284 | 1285 | cl->next = ngx_alloc_chain_link(r->pool); 1286 | if (cl->next == NULL) { 1287 | return NGX_ERROR; 1288 | } 1289 | 1290 | cl = cl->next; 1291 | cl->buf = b; 1292 | 1293 | body = body->next; 1294 | } 1295 | 1296 | } else { 1297 | u->request_bufs = cl; 1298 | } 1299 | 1300 | b->flush = 1; 1301 | cl->next = NULL; 1302 | 1303 | return NGX_OK; 1304 | } 1305 | 1306 | 1307 | static ngx_int_t 1308 | ngx_http_socks_reinit_request(ngx_http_request_t *r) 1309 | { 1310 | ngx_http_socks_ctx_t *ctx; 1311 | 1312 | ctx = ngx_http_get_module_ctx(r, ngx_http_socks_module); 1313 | 1314 | if (ctx == NULL) { 1315 | return NGX_OK; 1316 | } 1317 | 1318 | ctx->status.code = 0; 1319 | ctx->status.count = 0; 1320 | ctx->status.start = NULL; 1321 | ctx->status.end = NULL; 1322 | ctx->chunked.state = 0; 1323 | 1324 | r->upstream->process_header = ngx_http_socks_process_status_line; 1325 | r->upstream->pipe->input_filter = ngx_http_socks_copy_filter; 1326 | r->upstream->input_filter = ngx_http_socks_non_buffered_copy_filter; 1327 | r->state = 0; 1328 | 1329 | return NGX_OK; 1330 | } 1331 | 1332 | 1333 | static ngx_int_t 1334 | ngx_http_socks_body_output_filter(void *data, ngx_chain_t *in) 1335 | { 1336 | ngx_http_request_t *r = data; 1337 | 1338 | off_t size; 1339 | u_char *chunk; 1340 | ngx_int_t rc; 1341 | ngx_buf_t *b; 1342 | ngx_chain_t *out, *cl, *tl, **ll, **fl; 1343 | ngx_http_socks_ctx_t *ctx; 1344 | 1345 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1346 | "socks output filter"); 1347 | 1348 | ctx = ngx_http_get_module_ctx(r, ngx_http_socks_module); 1349 | 1350 | if (in == NULL) { 1351 | out = in; 1352 | goto out; 1353 | } 1354 | 1355 | out = NULL; 1356 | ll = &out; 1357 | 1358 | if (!ctx->header_sent) { 1359 | /* first buffer contains headers, pass it unmodified */ 1360 | 1361 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1362 | "socks output header"); 1363 | 1364 | ctx->header_sent = 1; 1365 | 1366 | tl = ngx_alloc_chain_link(r->pool); 1367 | if (tl == NULL) { 1368 | return NGX_ERROR; 1369 | } 1370 | 1371 | tl->buf = in->buf; 1372 | *ll = tl; 1373 | ll = &tl->next; 1374 | 1375 | in = in->next; 1376 | 1377 | if (in == NULL) { 1378 | tl->next = NULL; 1379 | goto out; 1380 | } 1381 | } 1382 | 1383 | size = 0; 1384 | cl = in; 1385 | fl = ll; 1386 | 1387 | for ( ;; ) { 1388 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1389 | "socks output chunk: %d", ngx_buf_size(cl->buf)); 1390 | 1391 | size += ngx_buf_size(cl->buf); 1392 | 1393 | if (cl->buf->flush 1394 | || cl->buf->sync 1395 | || ngx_buf_in_memory(cl->buf) 1396 | || cl->buf->in_file) 1397 | { 1398 | tl = ngx_alloc_chain_link(r->pool); 1399 | if (tl == NULL) { 1400 | return NGX_ERROR; 1401 | } 1402 | 1403 | tl->buf = cl->buf; 1404 | *ll = tl; 1405 | ll = &tl->next; 1406 | } 1407 | 1408 | if (cl->next == NULL) { 1409 | break; 1410 | } 1411 | 1412 | cl = cl->next; 1413 | } 1414 | 1415 | if (size) { 1416 | tl = ngx_chain_get_free_buf(r->pool, &ctx->free); 1417 | if (tl == NULL) { 1418 | return NGX_ERROR; 1419 | } 1420 | 1421 | b = tl->buf; 1422 | chunk = b->start; 1423 | 1424 | if (chunk == NULL) { 1425 | /* the "0000000000000000" is 64-bit hexadecimal string */ 1426 | 1427 | chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1); 1428 | if (chunk == NULL) { 1429 | return NGX_ERROR; 1430 | } 1431 | 1432 | b->start = chunk; 1433 | b->end = chunk + sizeof("0000000000000000" CRLF) - 1; 1434 | } 1435 | 1436 | b->tag = (ngx_buf_tag_t) &ngx_http_socks_body_output_filter; 1437 | b->memory = 0; 1438 | b->temporary = 1; 1439 | b->pos = chunk; 1440 | b->last = ngx_sprintf(chunk, "%xO" CRLF, size); 1441 | 1442 | tl->next = *fl; 1443 | *fl = tl; 1444 | } 1445 | 1446 | if (cl->buf->last_buf) { 1447 | tl = ngx_chain_get_free_buf(r->pool, &ctx->free); 1448 | if (tl == NULL) { 1449 | return NGX_ERROR; 1450 | } 1451 | 1452 | b = tl->buf; 1453 | 1454 | b->tag = (ngx_buf_tag_t) &ngx_http_socks_body_output_filter; 1455 | b->temporary = 0; 1456 | b->memory = 1; 1457 | b->last_buf = 1; 1458 | b->pos = (u_char *) CRLF "0" CRLF CRLF; 1459 | b->last = b->pos + 7; 1460 | 1461 | cl->buf->last_buf = 0; 1462 | 1463 | *ll = tl; 1464 | 1465 | if (size == 0) { 1466 | b->pos += 2; 1467 | } 1468 | 1469 | } else if (size > 0) { 1470 | tl = ngx_chain_get_free_buf(r->pool, &ctx->free); 1471 | if (tl == NULL) { 1472 | return NGX_ERROR; 1473 | } 1474 | 1475 | b = tl->buf; 1476 | 1477 | b->tag = (ngx_buf_tag_t) &ngx_http_socks_body_output_filter; 1478 | b->temporary = 0; 1479 | b->memory = 1; 1480 | b->pos = (u_char *) CRLF; 1481 | b->last = b->pos + 2; 1482 | 1483 | *ll = tl; 1484 | 1485 | } else { 1486 | *ll = NULL; 1487 | } 1488 | 1489 | out: 1490 | 1491 | rc = ngx_chain_writer(&r->upstream->writer, out); 1492 | 1493 | ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out, 1494 | (ngx_buf_tag_t) &ngx_http_socks_body_output_filter); 1495 | 1496 | return rc; 1497 | } 1498 | 1499 | 1500 | static ngx_int_t 1501 | ngx_http_socks_process_status_line(ngx_http_request_t *r) 1502 | { 1503 | size_t len; 1504 | ngx_int_t rc; 1505 | ngx_http_upstream_t *u; 1506 | ngx_http_socks_ctx_t *ctx; 1507 | 1508 | ctx = ngx_http_get_module_ctx(r, ngx_http_socks_module); 1509 | 1510 | if (ctx == NULL) { 1511 | return NGX_ERROR; 1512 | } 1513 | 1514 | u = r->upstream; 1515 | 1516 | rc = ngx_http_parse_status_line(r, &u->buffer, &ctx->status); 1517 | 1518 | if (rc == NGX_AGAIN) { 1519 | return rc; 1520 | } 1521 | 1522 | if (rc == NGX_ERROR) { 1523 | 1524 | #if (NGX_HTTP_CACHE) 1525 | 1526 | if (r->cache) { 1527 | r->http_version = NGX_HTTP_VERSION_9; 1528 | return NGX_OK; 1529 | } 1530 | 1531 | #endif 1532 | 1533 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 1534 | "upstream sent no valid HTTP/1.0 header"); 1535 | 1536 | #if 0 1537 | if (u->accel) { 1538 | return NGX_HTTP_UPSTREAM_INVALID_HEADER; 1539 | } 1540 | #endif 1541 | 1542 | r->http_version = NGX_HTTP_VERSION_9; 1543 | u->state->status = NGX_HTTP_OK; 1544 | u->headers_in.connection_close = 1; 1545 | 1546 | return NGX_OK; 1547 | } 1548 | 1549 | if (u->state && u->state->status == 0) { 1550 | u->state->status = ctx->status.code; 1551 | } 1552 | 1553 | u->headers_in.status_n = ctx->status.code; 1554 | 1555 | len = ctx->status.end - ctx->status.start; 1556 | u->headers_in.status_line.len = len; 1557 | 1558 | u->headers_in.status_line.data = ngx_pnalloc(r->pool, len); 1559 | if (u->headers_in.status_line.data == NULL) { 1560 | return NGX_ERROR; 1561 | } 1562 | 1563 | ngx_memcpy(u->headers_in.status_line.data, ctx->status.start, len); 1564 | 1565 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1566 | "http socks status %ui \"%V\"", 1567 | u->headers_in.status_n, &u->headers_in.status_line); 1568 | 1569 | if (ctx->status.http_version < NGX_HTTP_VERSION_11) { 1570 | u->headers_in.connection_close = 1; 1571 | } 1572 | 1573 | u->process_header = ngx_http_socks_process_header; 1574 | 1575 | return ngx_http_socks_process_header(r); 1576 | } 1577 | 1578 | 1579 | static ngx_int_t 1580 | ngx_http_socks_process_header(ngx_http_request_t *r) 1581 | { 1582 | ngx_int_t rc; 1583 | ngx_table_elt_t *h; 1584 | ngx_http_upstream_t *u; 1585 | ngx_http_socks_ctx_t *ctx; 1586 | ngx_http_upstream_header_t *hh; 1587 | ngx_http_upstream_main_conf_t *umcf; 1588 | 1589 | umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); 1590 | 1591 | for ( ;; ) { 1592 | 1593 | rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1); 1594 | 1595 | if (rc == NGX_OK) { 1596 | 1597 | /* a header line has been parsed successfully */ 1598 | 1599 | h = ngx_list_push(&r->upstream->headers_in.headers); 1600 | if (h == NULL) { 1601 | return NGX_ERROR; 1602 | } 1603 | 1604 | h->hash = r->header_hash; 1605 | 1606 | h->key.len = r->header_name_end - r->header_name_start; 1607 | h->value.len = r->header_end - r->header_start; 1608 | 1609 | h->key.data = ngx_pnalloc(r->pool, 1610 | h->key.len + 1 + h->value.len + 1 + h->key.len); 1611 | if (h->key.data == NULL) { 1612 | return NGX_ERROR; 1613 | } 1614 | 1615 | h->value.data = h->key.data + h->key.len + 1; 1616 | h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1; 1617 | 1618 | ngx_memcpy(h->key.data, r->header_name_start, h->key.len); 1619 | h->key.data[h->key.len] = '\0'; 1620 | ngx_memcpy(h->value.data, r->header_start, h->value.len); 1621 | h->value.data[h->value.len] = '\0'; 1622 | 1623 | if (h->key.len == r->lowcase_index) { 1624 | ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); 1625 | 1626 | } else { 1627 | ngx_strlow(h->lowcase_key, h->key.data, h->key.len); 1628 | } 1629 | 1630 | hh = ngx_hash_find(&umcf->headers_in_hash, h->hash, 1631 | h->lowcase_key, h->key.len); 1632 | 1633 | if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { 1634 | return NGX_ERROR; 1635 | } 1636 | 1637 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1638 | "http socks header: \"%V: %V\"", 1639 | &h->key, &h->value); 1640 | 1641 | continue; 1642 | } 1643 | 1644 | if (rc == NGX_HTTP_PARSE_HEADER_DONE) { 1645 | 1646 | /* a whole header has been parsed successfully */ 1647 | 1648 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1649 | "http socks header done"); 1650 | 1651 | /* 1652 | * if no "Server" and "Date" in header line, 1653 | * then add the special empty headers 1654 | */ 1655 | 1656 | if (r->upstream->headers_in.server == NULL) { 1657 | h = ngx_list_push(&r->upstream->headers_in.headers); 1658 | if (h == NULL) { 1659 | return NGX_ERROR; 1660 | } 1661 | 1662 | h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash( 1663 | ngx_hash('s', 'e'), 'r'), 'v'), 'e'), 'r'); 1664 | 1665 | ngx_str_set(&h->key, "Server"); 1666 | ngx_str_null(&h->value); 1667 | h->lowcase_key = (u_char *) "server"; 1668 | } 1669 | 1670 | if (r->upstream->headers_in.date == NULL) { 1671 | h = ngx_list_push(&r->upstream->headers_in.headers); 1672 | if (h == NULL) { 1673 | return NGX_ERROR; 1674 | } 1675 | 1676 | h->hash = ngx_hash(ngx_hash(ngx_hash('d', 'a'), 't'), 'e'); 1677 | 1678 | ngx_str_set(&h->key, "Date"); 1679 | ngx_str_null(&h->value); 1680 | h->lowcase_key = (u_char *) "date"; 1681 | } 1682 | 1683 | /* clear content length if response is chunked */ 1684 | 1685 | u = r->upstream; 1686 | 1687 | if (u->headers_in.chunked) { 1688 | u->headers_in.content_length_n = -1; 1689 | } 1690 | 1691 | /* 1692 | * set u->keepalive if response has no body; this allows to keep 1693 | * connections alive in case of r->header_only or X-Accel-Redirect 1694 | */ 1695 | 1696 | ctx = ngx_http_get_module_ctx(r, ngx_http_socks_module); 1697 | 1698 | if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT 1699 | || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED 1700 | || ctx->head 1701 | || (!u->headers_in.chunked 1702 | && u->headers_in.content_length_n == 0)) 1703 | { 1704 | u->keepalive = !u->headers_in.connection_close; 1705 | } 1706 | 1707 | if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS) { 1708 | u->keepalive = 0; 1709 | 1710 | if (r->headers_in.upgrade) { 1711 | u->upgrade = 1; 1712 | } 1713 | } 1714 | 1715 | return NGX_OK; 1716 | } 1717 | 1718 | if (rc == NGX_AGAIN) { 1719 | return NGX_AGAIN; 1720 | } 1721 | 1722 | /* there was error while a header line parsing */ 1723 | 1724 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 1725 | "upstream sent invalid header"); 1726 | 1727 | return NGX_HTTP_UPSTREAM_INVALID_HEADER; 1728 | } 1729 | } 1730 | 1731 | 1732 | static ngx_int_t 1733 | ngx_http_socks_input_filter_init(void *data) 1734 | { 1735 | ngx_http_request_t *r = data; 1736 | ngx_http_upstream_t *u; 1737 | ngx_http_socks_ctx_t *ctx; 1738 | 1739 | u = r->upstream; 1740 | ctx = ngx_http_get_module_ctx(r, ngx_http_socks_module); 1741 | 1742 | if (ctx == NULL) { 1743 | return NGX_ERROR; 1744 | } 1745 | 1746 | ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1747 | "http socks filter init s:%d h:%d c:%d l:%O", 1748 | u->headers_in.status_n, ctx->head, u->headers_in.chunked, 1749 | u->headers_in.content_length_n); 1750 | 1751 | /* as per RFC2616, 4.4 Message Length */ 1752 | 1753 | if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT 1754 | || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED 1755 | || ctx->head) 1756 | { 1757 | /* 1xx, 204, and 304 and replies to HEAD requests */ 1758 | /* no 1xx since we don't send Expect and Upgrade */ 1759 | 1760 | u->pipe->length = 0; 1761 | u->length = 0; 1762 | u->keepalive = !u->headers_in.connection_close; 1763 | 1764 | } else if (u->headers_in.chunked) { 1765 | /* chunked */ 1766 | 1767 | u->pipe->input_filter = ngx_http_socks_chunked_filter; 1768 | u->pipe->length = 3; /* "0" LF LF */ 1769 | 1770 | u->input_filter = ngx_http_socks_non_buffered_chunked_filter; 1771 | u->length = 1; 1772 | 1773 | } else if (u->headers_in.content_length_n == 0) { 1774 | /* empty body: special case as filter won't be called */ 1775 | 1776 | u->pipe->length = 0; 1777 | u->length = 0; 1778 | u->keepalive = !u->headers_in.connection_close; 1779 | 1780 | } else { 1781 | /* content length or connection close */ 1782 | 1783 | u->pipe->length = u->headers_in.content_length_n; 1784 | u->length = u->headers_in.content_length_n; 1785 | } 1786 | 1787 | return NGX_OK; 1788 | } 1789 | 1790 | 1791 | static ngx_int_t 1792 | ngx_http_socks_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) 1793 | { 1794 | ngx_buf_t *b; 1795 | ngx_chain_t *cl; 1796 | ngx_http_request_t *r; 1797 | 1798 | if (buf->pos == buf->last) { 1799 | return NGX_OK; 1800 | } 1801 | 1802 | cl = ngx_chain_get_free_buf(p->pool, &p->free); 1803 | if (cl == NULL) { 1804 | return NGX_ERROR; 1805 | } 1806 | 1807 | b = cl->buf; 1808 | 1809 | ngx_memcpy(b, buf, sizeof(ngx_buf_t)); 1810 | b->shadow = buf; 1811 | b->tag = p->tag; 1812 | b->last_shadow = 1; 1813 | b->recycled = 1; 1814 | buf->shadow = b; 1815 | 1816 | ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "input buf #%d", b->num); 1817 | 1818 | if (p->in) { 1819 | *p->last_in = cl; 1820 | } else { 1821 | p->in = cl; 1822 | } 1823 | p->last_in = &cl->next; 1824 | 1825 | if (p->length == -1) { 1826 | return NGX_OK; 1827 | } 1828 | 1829 | p->length -= b->last - b->pos; 1830 | 1831 | if (p->length == 0) { 1832 | r = p->input_ctx; 1833 | p->upstream_done = 1; 1834 | r->upstream->keepalive = !r->upstream->headers_in.connection_close; 1835 | 1836 | } else if (p->length < 0) { 1837 | r = p->input_ctx; 1838 | p->upstream_done = 1; 1839 | 1840 | ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, 1841 | "upstream sent more data than specified in " 1842 | "\"Content-Length\" header"); 1843 | } 1844 | 1845 | return NGX_OK; 1846 | } 1847 | 1848 | 1849 | static ngx_int_t 1850 | ngx_http_socks_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf) 1851 | { 1852 | ngx_int_t rc; 1853 | ngx_buf_t *b, **prev; 1854 | ngx_chain_t *cl; 1855 | ngx_http_request_t *r; 1856 | ngx_http_socks_ctx_t *ctx; 1857 | 1858 | if (buf->pos == buf->last) { 1859 | return NGX_OK; 1860 | } 1861 | 1862 | r = p->input_ctx; 1863 | ctx = ngx_http_get_module_ctx(r, ngx_http_socks_module); 1864 | 1865 | if (ctx == NULL) { 1866 | return NGX_ERROR; 1867 | } 1868 | 1869 | b = NULL; 1870 | prev = &buf->shadow; 1871 | 1872 | for ( ;; ) { 1873 | 1874 | rc = ngx_http_parse_chunked(r, buf, &ctx->chunked); 1875 | 1876 | if (rc == NGX_OK) { 1877 | 1878 | /* a chunk has been parsed successfully */ 1879 | 1880 | cl = ngx_chain_get_free_buf(p->pool, &p->free); 1881 | if (cl == NULL) { 1882 | return NGX_ERROR; 1883 | } 1884 | 1885 | b = cl->buf; 1886 | 1887 | ngx_memzero(b, sizeof(ngx_buf_t)); 1888 | 1889 | b->pos = buf->pos; 1890 | b->start = buf->start; 1891 | b->end = buf->end; 1892 | b->tag = p->tag; 1893 | b->temporary = 1; 1894 | b->recycled = 1; 1895 | 1896 | *prev = b; 1897 | prev = &b->shadow; 1898 | 1899 | if (p->in) { 1900 | *p->last_in = cl; 1901 | } else { 1902 | p->in = cl; 1903 | } 1904 | p->last_in = &cl->next; 1905 | 1906 | /* STUB */ b->num = buf->num; 1907 | 1908 | ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0, 1909 | "input buf #%d %p", b->num, b->pos); 1910 | 1911 | if (buf->last - buf->pos >= ctx->chunked.size) { 1912 | 1913 | buf->pos += (size_t) ctx->chunked.size; 1914 | b->last = buf->pos; 1915 | ctx->chunked.size = 0; 1916 | 1917 | continue; 1918 | } 1919 | 1920 | ctx->chunked.size -= buf->last - buf->pos; 1921 | buf->pos = buf->last; 1922 | b->last = buf->last; 1923 | 1924 | continue; 1925 | } 1926 | 1927 | if (rc == NGX_DONE) { 1928 | 1929 | /* a whole response has been parsed successfully */ 1930 | 1931 | p->upstream_done = 1; 1932 | r->upstream->keepalive = !r->upstream->headers_in.connection_close; 1933 | 1934 | break; 1935 | } 1936 | 1937 | if (rc == NGX_AGAIN) { 1938 | 1939 | /* set p->length, minimal amount of data we want to see */ 1940 | 1941 | p->length = ctx->chunked.length; 1942 | 1943 | break; 1944 | } 1945 | 1946 | /* invalid response */ 1947 | 1948 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 1949 | "upstream sent invalid chunked response"); 1950 | 1951 | return NGX_ERROR; 1952 | } 1953 | 1954 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 1955 | "http socks chunked state %d, length %d", 1956 | ctx->chunked.state, p->length); 1957 | 1958 | if (b) { 1959 | b->shadow = buf; 1960 | b->last_shadow = 1; 1961 | 1962 | ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0, 1963 | "input buf %p %z", b->pos, b->last - b->pos); 1964 | 1965 | return NGX_OK; 1966 | } 1967 | 1968 | /* there is no data record in the buf, add it to free chain */ 1969 | 1970 | if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) { 1971 | return NGX_ERROR; 1972 | } 1973 | 1974 | return NGX_OK; 1975 | } 1976 | 1977 | 1978 | static ngx_int_t 1979 | ngx_http_socks_non_buffered_copy_filter(void *data, ssize_t bytes) 1980 | { 1981 | ngx_http_request_t *r = data; 1982 | 1983 | ngx_buf_t *b; 1984 | ngx_chain_t *cl, **ll; 1985 | ngx_http_upstream_t *u; 1986 | 1987 | u = r->upstream; 1988 | 1989 | for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) { 1990 | ll = &cl->next; 1991 | } 1992 | 1993 | cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs); 1994 | if (cl == NULL) { 1995 | return NGX_ERROR; 1996 | } 1997 | 1998 | *ll = cl; 1999 | 2000 | cl->buf->flush = 1; 2001 | cl->buf->memory = 1; 2002 | 2003 | b = &u->buffer; 2004 | 2005 | cl->buf->pos = b->last; 2006 | b->last += bytes; 2007 | cl->buf->last = b->last; 2008 | cl->buf->tag = u->output.tag; 2009 | 2010 | if (u->length == -1) { 2011 | return NGX_OK; 2012 | } 2013 | 2014 | u->length -= bytes; 2015 | 2016 | if (u->length == 0) { 2017 | u->keepalive = !u->headers_in.connection_close; 2018 | } 2019 | 2020 | return NGX_OK; 2021 | } 2022 | 2023 | 2024 | static ngx_int_t 2025 | ngx_http_socks_non_buffered_chunked_filter(void *data, ssize_t bytes) 2026 | { 2027 | ngx_http_request_t *r = data; 2028 | 2029 | ngx_int_t rc; 2030 | ngx_buf_t *b, *buf; 2031 | ngx_chain_t *cl, **ll; 2032 | ngx_http_upstream_t *u; 2033 | ngx_http_socks_ctx_t *ctx; 2034 | 2035 | ctx = ngx_http_get_module_ctx(r, ngx_http_socks_module); 2036 | 2037 | if (ctx == NULL) { 2038 | return NGX_ERROR; 2039 | } 2040 | 2041 | u = r->upstream; 2042 | buf = &u->buffer; 2043 | 2044 | buf->pos = buf->last; 2045 | buf->last += bytes; 2046 | 2047 | for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) { 2048 | ll = &cl->next; 2049 | } 2050 | 2051 | for ( ;; ) { 2052 | 2053 | rc = ngx_http_parse_chunked(r, buf, &ctx->chunked); 2054 | 2055 | if (rc == NGX_OK) { 2056 | 2057 | /* a chunk has been parsed successfully */ 2058 | 2059 | cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs); 2060 | if (cl == NULL) { 2061 | return NGX_ERROR; 2062 | } 2063 | 2064 | *ll = cl; 2065 | ll = &cl->next; 2066 | 2067 | b = cl->buf; 2068 | 2069 | b->flush = 1; 2070 | b->memory = 1; 2071 | 2072 | b->pos = buf->pos; 2073 | b->tag = u->output.tag; 2074 | 2075 | if (buf->last - buf->pos >= ctx->chunked.size) { 2076 | buf->pos += (size_t) ctx->chunked.size; 2077 | b->last = buf->pos; 2078 | ctx->chunked.size = 0; 2079 | 2080 | } else { 2081 | ctx->chunked.size -= buf->last - buf->pos; 2082 | buf->pos = buf->last; 2083 | b->last = buf->last; 2084 | } 2085 | 2086 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 2087 | "http socks out buf %p %z", 2088 | b->pos, b->last - b->pos); 2089 | 2090 | continue; 2091 | } 2092 | 2093 | if (rc == NGX_DONE) { 2094 | 2095 | /* a whole response has been parsed successfully */ 2096 | 2097 | u->keepalive = !u->headers_in.connection_close; 2098 | u->length = 0; 2099 | 2100 | break; 2101 | } 2102 | 2103 | if (rc == NGX_AGAIN) { 2104 | break; 2105 | } 2106 | 2107 | /* invalid response */ 2108 | 2109 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 2110 | "upstream sent invalid chunked response"); 2111 | 2112 | return NGX_ERROR; 2113 | } 2114 | 2115 | /* provide continuous buffer for subrequests in memory */ 2116 | 2117 | if (r->subrequest_in_memory) { 2118 | 2119 | cl = u->out_bufs; 2120 | 2121 | if (cl) { 2122 | buf->pos = cl->buf->pos; 2123 | } 2124 | 2125 | buf->last = buf->pos; 2126 | 2127 | for (cl = u->out_bufs; cl; cl = cl->next) { 2128 | ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 2129 | "http socks in memory %p-%p %uz", 2130 | cl->buf->pos, cl->buf->last, ngx_buf_size(cl->buf)); 2131 | 2132 | if (buf->last == cl->buf->pos) { 2133 | buf->last = cl->buf->last; 2134 | continue; 2135 | } 2136 | 2137 | buf->last = ngx_movemem(buf->last, cl->buf->pos, 2138 | cl->buf->last - cl->buf->pos); 2139 | 2140 | cl->buf->pos = buf->last - (cl->buf->last - cl->buf->pos); 2141 | cl->buf->last = buf->last; 2142 | } 2143 | } 2144 | 2145 | return NGX_OK; 2146 | } 2147 | 2148 | 2149 | static void 2150 | ngx_http_socks_abort_request(ngx_http_request_t *r) 2151 | { 2152 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 2153 | "abort http socks request"); 2154 | 2155 | return; 2156 | } 2157 | 2158 | 2159 | static void 2160 | ngx_http_socks_finalize_request(ngx_http_request_t *r, ngx_int_t rc) 2161 | { 2162 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 2163 | "finalize http socks request"); 2164 | 2165 | return; 2166 | } 2167 | 2168 | 2169 | static ngx_int_t 2170 | ngx_http_socks_host_variable(ngx_http_request_t *r, 2171 | ngx_http_variable_value_t *v, uintptr_t data) 2172 | { 2173 | ngx_http_socks_ctx_t *ctx; 2174 | 2175 | ctx = ngx_http_get_module_ctx(r, ngx_http_socks_module); 2176 | 2177 | if (ctx == NULL) { 2178 | v->not_found = 1; 2179 | return NGX_OK; 2180 | } 2181 | 2182 | v->len = ctx->vars.host_header.len; 2183 | v->valid = 1; 2184 | v->no_cacheable = 0; 2185 | v->not_found = 0; 2186 | v->data = ctx->vars.host_header.data; 2187 | 2188 | return NGX_OK; 2189 | } 2190 | 2191 | 2192 | static ngx_int_t 2193 | ngx_http_socks_port_variable(ngx_http_request_t *r, 2194 | ngx_http_variable_value_t *v, uintptr_t data) 2195 | { 2196 | ngx_http_socks_ctx_t *ctx; 2197 | 2198 | ctx = ngx_http_get_module_ctx(r, ngx_http_socks_module); 2199 | 2200 | if (ctx == NULL) { 2201 | v->not_found = 1; 2202 | return NGX_OK; 2203 | } 2204 | 2205 | v->len = ctx->vars.port.len; 2206 | v->valid = 1; 2207 | v->no_cacheable = 0; 2208 | v->not_found = 0; 2209 | v->data = ctx->vars.port.data; 2210 | 2211 | return NGX_OK; 2212 | } 2213 | 2214 | 2215 | static ngx_int_t 2216 | ngx_http_socks_add_x_forwarded_for_variable(ngx_http_request_t *r, 2217 | ngx_http_variable_value_t *v, uintptr_t data) 2218 | { 2219 | size_t len; 2220 | u_char *p; 2221 | ngx_uint_t i, n; 2222 | ngx_table_elt_t **h; 2223 | 2224 | v->valid = 1; 2225 | v->no_cacheable = 0; 2226 | v->not_found = 0; 2227 | 2228 | n = r->headers_in.x_forwarded_for.nelts; 2229 | h = r->headers_in.x_forwarded_for.elts; 2230 | 2231 | len = 0; 2232 | 2233 | for (i = 0; i < n; i++) { 2234 | len += h[i]->value.len + sizeof(", ") - 1; 2235 | } 2236 | 2237 | if (len == 0) { 2238 | v->len = r->connection->addr_text.len; 2239 | v->data = r->connection->addr_text.data; 2240 | return NGX_OK; 2241 | } 2242 | 2243 | len += r->connection->addr_text.len; 2244 | 2245 | p = ngx_pnalloc(r->pool, len); 2246 | if (p == NULL) { 2247 | return NGX_ERROR; 2248 | } 2249 | 2250 | v->len = len; 2251 | v->data = p; 2252 | 2253 | for (i = 0; i < n; i++) { 2254 | p = ngx_copy(p, h[i]->value.data, h[i]->value.len); 2255 | *p++ = ','; *p++ = ' '; 2256 | } 2257 | 2258 | ngx_memcpy(p, r->connection->addr_text.data, r->connection->addr_text.len); 2259 | 2260 | return NGX_OK; 2261 | } 2262 | 2263 | 2264 | static ngx_int_t 2265 | ngx_http_socks_internal_body_length_variable(ngx_http_request_t *r, 2266 | ngx_http_variable_value_t *v, uintptr_t data) 2267 | { 2268 | ngx_http_socks_ctx_t *ctx; 2269 | 2270 | ctx = ngx_http_get_module_ctx(r, ngx_http_socks_module); 2271 | 2272 | if (ctx == NULL || ctx->internal_body_length < 0) { 2273 | v->not_found = 1; 2274 | return NGX_OK; 2275 | } 2276 | 2277 | v->valid = 1; 2278 | v->no_cacheable = 0; 2279 | v->not_found = 0; 2280 | 2281 | v->data = ngx_pnalloc(r->pool, NGX_OFF_T_LEN); 2282 | 2283 | if (v->data == NULL) { 2284 | return NGX_ERROR; 2285 | } 2286 | 2287 | v->len = ngx_sprintf(v->data, "%O", ctx->internal_body_length) - v->data; 2288 | 2289 | return NGX_OK; 2290 | } 2291 | 2292 | 2293 | static ngx_int_t 2294 | ngx_http_socks_internal_chunked_variable(ngx_http_request_t *r, 2295 | ngx_http_variable_value_t *v, uintptr_t data) 2296 | { 2297 | ngx_http_socks_ctx_t *ctx; 2298 | 2299 | ctx = ngx_http_get_module_ctx(r, ngx_http_socks_module); 2300 | 2301 | if (ctx == NULL || !ctx->internal_chunked) { 2302 | v->not_found = 1; 2303 | return NGX_OK; 2304 | } 2305 | 2306 | v->valid = 1; 2307 | v->no_cacheable = 0; 2308 | v->not_found = 0; 2309 | 2310 | v->data = (u_char *) "chunked"; 2311 | v->len = sizeof("chunked") - 1; 2312 | 2313 | return NGX_OK; 2314 | } 2315 | 2316 | 2317 | static ngx_int_t 2318 | ngx_http_socks_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h, 2319 | size_t prefix) 2320 | { 2321 | size_t len; 2322 | ngx_int_t rc; 2323 | ngx_uint_t i; 2324 | ngx_http_socks_rewrite_t *pr; 2325 | ngx_http_socks_loc_conf_t *slcf; 2326 | 2327 | slcf = ngx_http_get_module_loc_conf(r, ngx_http_socks_module); 2328 | 2329 | pr = slcf->redirects->elts; 2330 | 2331 | if (pr == NULL) { 2332 | return NGX_DECLINED; 2333 | } 2334 | 2335 | len = h->value.len - prefix; 2336 | 2337 | for (i = 0; i < slcf->redirects->nelts; i++) { 2338 | rc = pr[i].handler(r, h, prefix, len, &pr[i]); 2339 | 2340 | if (rc != NGX_DECLINED) { 2341 | return rc; 2342 | } 2343 | } 2344 | 2345 | return NGX_DECLINED; 2346 | } 2347 | 2348 | 2349 | static ngx_int_t 2350 | ngx_http_socks_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h) 2351 | { 2352 | size_t prefix; 2353 | u_char *p; 2354 | ngx_int_t rc, rv; 2355 | ngx_http_socks_loc_conf_t *slcf; 2356 | 2357 | p = (u_char *) ngx_strchr(h->value.data, ';'); 2358 | if (p == NULL) { 2359 | return NGX_DECLINED; 2360 | } 2361 | 2362 | prefix = p + 1 - h->value.data; 2363 | 2364 | rv = NGX_DECLINED; 2365 | 2366 | slcf = ngx_http_get_module_loc_conf(r, ngx_http_socks_module); 2367 | 2368 | if (slcf->cookie_domains) { 2369 | p = ngx_strcasestrn(h->value.data + prefix, "domain=", 7 - 1); 2370 | 2371 | if (p) { 2372 | rc = ngx_http_socks_rewrite_cookie_value(r, h, p + 7, 2373 | slcf->cookie_domains); 2374 | if (rc == NGX_ERROR) { 2375 | return NGX_ERROR; 2376 | } 2377 | 2378 | if (rc != NGX_DECLINED) { 2379 | rv = rc; 2380 | } 2381 | } 2382 | } 2383 | 2384 | if (slcf->cookie_paths) { 2385 | p = ngx_strcasestrn(h->value.data + prefix, "path=", 5 - 1); 2386 | 2387 | if (p) { 2388 | rc = ngx_http_socks_rewrite_cookie_value(r, h, p + 5, 2389 | slcf->cookie_paths); 2390 | if (rc == NGX_ERROR) { 2391 | return NGX_ERROR; 2392 | } 2393 | 2394 | if (rc != NGX_DECLINED) { 2395 | rv = rc; 2396 | } 2397 | } 2398 | } 2399 | 2400 | return rv; 2401 | } 2402 | 2403 | 2404 | static ngx_int_t 2405 | ngx_http_socks_rewrite_cookie_value(ngx_http_request_t *r, ngx_table_elt_t *h, 2406 | u_char *value, ngx_array_t *rewrites) 2407 | { 2408 | size_t len, prefix; 2409 | u_char *p; 2410 | ngx_int_t rc; 2411 | ngx_uint_t i; 2412 | ngx_http_socks_rewrite_t *pr; 2413 | 2414 | prefix = value - h->value.data; 2415 | 2416 | p = (u_char *) ngx_strchr(value, ';'); 2417 | 2418 | len = p ? (size_t) (p - value) : (h->value.len - prefix); 2419 | 2420 | pr = rewrites->elts; 2421 | 2422 | for (i = 0; i < rewrites->nelts; i++) { 2423 | rc = pr[i].handler(r, h, prefix, len, &pr[i]); 2424 | 2425 | if (rc != NGX_DECLINED) { 2426 | return rc; 2427 | } 2428 | } 2429 | 2430 | return NGX_DECLINED; 2431 | } 2432 | 2433 | 2434 | static ngx_int_t 2435 | ngx_http_socks_rewrite_complex_handler(ngx_http_request_t *r, 2436 | ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_socks_rewrite_t *pr) 2437 | { 2438 | ngx_str_t pattern, replacement; 2439 | 2440 | if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) { 2441 | return NGX_ERROR; 2442 | } 2443 | 2444 | if (pattern.len > len 2445 | || ngx_rstrncmp(h->value.data + prefix, pattern.data, 2446 | pattern.len) != 0) 2447 | { 2448 | return NGX_DECLINED; 2449 | } 2450 | 2451 | if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) { 2452 | return NGX_ERROR; 2453 | } 2454 | 2455 | return ngx_http_socks_rewrite(r, h, prefix, pattern.len, &replacement); 2456 | } 2457 | 2458 | 2459 | #if (NGX_PCRE) 2460 | 2461 | static ngx_int_t 2462 | ngx_http_socks_rewrite_regex_handler(ngx_http_request_t *r, ngx_table_elt_t *h, 2463 | size_t prefix, size_t len, ngx_http_socks_rewrite_t *pr) 2464 | { 2465 | ngx_str_t pattern, replacement; 2466 | 2467 | pattern.len = len; 2468 | pattern.data = h->value.data + prefix; 2469 | 2470 | if (ngx_http_regex_exec(r, pr->pattern.regex, &pattern) != NGX_OK) { 2471 | return NGX_DECLINED; 2472 | } 2473 | 2474 | if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) { 2475 | return NGX_ERROR; 2476 | } 2477 | 2478 | if (prefix == 0 && h->value.len == len) { 2479 | h->value = replacement; 2480 | return NGX_OK; 2481 | } 2482 | 2483 | return ngx_http_socks_rewrite(r, h, prefix, len, &replacement); 2484 | } 2485 | 2486 | #endif 2487 | 2488 | 2489 | static ngx_int_t 2490 | ngx_http_socks_rewrite_domain_handler(ngx_http_request_t *r, 2491 | ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_socks_rewrite_t *pr) 2492 | { 2493 | u_char *p; 2494 | ngx_str_t pattern, replacement; 2495 | 2496 | if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) { 2497 | return NGX_ERROR; 2498 | } 2499 | 2500 | p = h->value.data + prefix; 2501 | 2502 | if (p[0] == '.') { 2503 | p++; 2504 | prefix++; 2505 | len--; 2506 | } 2507 | 2508 | if (pattern.len != len || ngx_rstrncasecmp(pattern.data, p, len) != 0) { 2509 | return NGX_DECLINED; 2510 | } 2511 | 2512 | if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) { 2513 | return NGX_ERROR; 2514 | } 2515 | 2516 | return ngx_http_socks_rewrite(r, h, prefix, len, &replacement); 2517 | } 2518 | 2519 | 2520 | static ngx_int_t 2521 | ngx_http_socks_rewrite(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix, 2522 | size_t len, ngx_str_t *replacement) 2523 | { 2524 | u_char *p, *data; 2525 | size_t new_len; 2526 | 2527 | new_len = replacement->len + h->value.len - len; 2528 | 2529 | if (replacement->len > len) { 2530 | 2531 | data = ngx_pnalloc(r->pool, new_len + 1); 2532 | if (data == NULL) { 2533 | return NGX_ERROR; 2534 | } 2535 | 2536 | p = ngx_copy(data, h->value.data, prefix); 2537 | p = ngx_copy(p, replacement->data, replacement->len); 2538 | 2539 | ngx_memcpy(p, h->value.data + prefix + len, 2540 | h->value.len - len - prefix + 1); 2541 | 2542 | h->value.data = data; 2543 | 2544 | } else { 2545 | p = ngx_copy(h->value.data + prefix, replacement->data, 2546 | replacement->len); 2547 | 2548 | ngx_memmove(p, h->value.data + prefix + len, 2549 | h->value.len - len - prefix + 1); 2550 | } 2551 | 2552 | h->value.len = new_len; 2553 | 2554 | return NGX_OK; 2555 | } 2556 | 2557 | 2558 | static ngx_int_t 2559 | ngx_http_socks_add_variables(ngx_conf_t *cf) 2560 | { 2561 | ngx_http_variable_t *var, *v; 2562 | 2563 | for (v = ngx_http_socks_vars; v->name.len; v++) { 2564 | var = ngx_http_add_variable(cf, &v->name, v->flags); 2565 | if (var == NULL) { 2566 | return NGX_ERROR; 2567 | } 2568 | 2569 | var->get_handler = v->get_handler; 2570 | var->data = v->data; 2571 | } 2572 | 2573 | return NGX_OK; 2574 | } 2575 | 2576 | 2577 | static void * 2578 | ngx_http_socks_create_main_conf(ngx_conf_t *cf) 2579 | { 2580 | ngx_http_socks_main_conf_t *conf; 2581 | 2582 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_socks_main_conf_t)); 2583 | if (conf == NULL) { 2584 | return NULL; 2585 | } 2586 | 2587 | #if (NGX_HTTP_CACHE) 2588 | if (ngx_array_init(&conf->caches, cf->pool, 4, 2589 | sizeof(ngx_http_file_cache_t *)) 2590 | != NGX_OK) 2591 | { 2592 | return NULL; 2593 | } 2594 | #endif 2595 | 2596 | return conf; 2597 | } 2598 | 2599 | 2600 | static void * 2601 | ngx_http_socks_create_loc_conf(ngx_conf_t *cf) 2602 | { 2603 | ngx_http_socks_loc_conf_t *conf; 2604 | 2605 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_socks_loc_conf_t)); 2606 | if (conf == NULL) { 2607 | return NULL; 2608 | } 2609 | 2610 | /* 2611 | * set by ngx_pcalloc(): 2612 | * 2613 | * conf->upstream.bufs.num = 0; 2614 | * conf->upstream.ignore_headers = 0; 2615 | * conf->upstream.next_upstream = 0; 2616 | * conf->upstream.cache_zone = NULL; 2617 | * conf->upstream.cache_use_stale = 0; 2618 | * conf->upstream.cache_methods = 0; 2619 | * conf->upstream.temp_path = NULL; 2620 | * conf->upstream.hide_headers_hash = { NULL, 0 }; 2621 | * conf->upstream.uri = { 0, NULL }; 2622 | * conf->upstream.location = NULL; 2623 | * conf->upstream.store_lengths = NULL; 2624 | * conf->upstream.store_values = NULL; 2625 | * 2626 | * conf->method = { 0, NULL }; 2627 | * conf->headers_source = NULL; 2628 | * conf->headers.lengths = NULL; 2629 | * conf->headers.values = NULL; 2630 | * conf->headers.hash = { NULL, 0 }; 2631 | * conf->headers_cache.lengths = NULL; 2632 | * conf->headers_cache.values = NULL; 2633 | * conf->headers_cache.hash = { NULL, 0 }; 2634 | * conf->body_lengths = NULL; 2635 | * conf->body_values = NULL; 2636 | * conf->body_source = { 0, NULL }; 2637 | * conf->redirects = NULL; 2638 | */ 2639 | 2640 | conf->upstream.store = NGX_CONF_UNSET; 2641 | conf->upstream.store_access = NGX_CONF_UNSET_UINT; 2642 | conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT; 2643 | conf->upstream.buffering = NGX_CONF_UNSET; 2644 | conf->upstream.request_buffering = NGX_CONF_UNSET; 2645 | conf->upstream.ignore_client_abort = NGX_CONF_UNSET; 2646 | conf->upstream.force_ranges = NGX_CONF_UNSET; 2647 | 2648 | conf->upstream.local = NGX_CONF_UNSET_PTR; 2649 | 2650 | conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC; 2651 | conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC; 2652 | conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC; 2653 | conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC; 2654 | 2655 | conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE; 2656 | conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE; 2657 | conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE; 2658 | 2659 | conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE; 2660 | conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE; 2661 | conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE; 2662 | 2663 | conf->upstream.pass_request_headers = NGX_CONF_UNSET; 2664 | conf->upstream.pass_request_body = NGX_CONF_UNSET; 2665 | 2666 | #if (NGX_HTTP_CACHE) 2667 | conf->upstream.cache = NGX_CONF_UNSET; 2668 | conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT; 2669 | conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR; 2670 | conf->upstream.no_cache = NGX_CONF_UNSET_PTR; 2671 | conf->upstream.cache_valid = NGX_CONF_UNSET_PTR; 2672 | conf->upstream.cache_lock = NGX_CONF_UNSET; 2673 | conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC; 2674 | conf->upstream.cache_lock_age = NGX_CONF_UNSET_MSEC; 2675 | conf->upstream.cache_revalidate = NGX_CONF_UNSET; 2676 | 2677 | #if nginx_version >= 1009007 2678 | conf->upstream.cache_convert_head = NGX_CONF_UNSET; 2679 | #endif 2680 | 2681 | #endif 2682 | 2683 | conf->upstream.hide_headers = NGX_CONF_UNSET_PTR; 2684 | conf->upstream.pass_headers = NGX_CONF_UNSET_PTR; 2685 | 2686 | conf->upstream.intercept_errors = NGX_CONF_UNSET; 2687 | 2688 | /* "proxy_cyclic_temp_file" is disabled */ 2689 | conf->upstream.cyclic_temp_file = 0; 2690 | 2691 | conf->redirect = NGX_CONF_UNSET; 2692 | conf->upstream.change_buffering = 1; 2693 | 2694 | conf->cookie_domains = NGX_CONF_UNSET_PTR; 2695 | conf->cookie_paths = NGX_CONF_UNSET_PTR; 2696 | 2697 | conf->http_version = NGX_CONF_UNSET_UINT; 2698 | 2699 | conf->headers_hash_max_size = NGX_CONF_UNSET_UINT; 2700 | conf->headers_hash_bucket_size = NGX_CONF_UNSET_UINT; 2701 | 2702 | ngx_str_set(&conf->upstream.module, "socks"); 2703 | 2704 | return conf; 2705 | } 2706 | 2707 | 2708 | static char * 2709 | ngx_http_socks_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 2710 | { 2711 | ngx_http_socks_loc_conf_t *prev = parent; 2712 | ngx_http_socks_loc_conf_t *conf = child; 2713 | 2714 | u_char *p; 2715 | size_t size; 2716 | ngx_int_t rc; 2717 | ngx_hash_init_t hash; 2718 | ngx_http_core_loc_conf_t *clcf; 2719 | ngx_http_socks_rewrite_t *pr; 2720 | ngx_http_script_compile_t sc; 2721 | 2722 | #if (NGX_HTTP_CACHE) 2723 | 2724 | if (conf->upstream.store > 0) { 2725 | conf->upstream.cache = 0; 2726 | } 2727 | 2728 | if (conf->upstream.cache > 0) { 2729 | conf->upstream.store = 0; 2730 | } 2731 | 2732 | #endif 2733 | 2734 | if (conf->upstream.store == NGX_CONF_UNSET) { 2735 | ngx_conf_merge_value(conf->upstream.store, 2736 | prev->upstream.store, 0); 2737 | 2738 | conf->upstream.store_lengths = prev->upstream.store_lengths; 2739 | conf->upstream.store_values = prev->upstream.store_values; 2740 | } 2741 | 2742 | ngx_conf_merge_uint_value(conf->upstream.store_access, 2743 | prev->upstream.store_access, 0600); 2744 | 2745 | ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries, 2746 | prev->upstream.next_upstream_tries, 0); 2747 | 2748 | ngx_conf_merge_value(conf->upstream.buffering, 2749 | prev->upstream.buffering, 1); 2750 | 2751 | ngx_conf_merge_value(conf->upstream.request_buffering, 2752 | prev->upstream.request_buffering, 1); 2753 | 2754 | ngx_conf_merge_value(conf->upstream.ignore_client_abort, 2755 | prev->upstream.ignore_client_abort, 0); 2756 | 2757 | ngx_conf_merge_value(conf->upstream.force_ranges, 2758 | prev->upstream.force_ranges, 0); 2759 | 2760 | ngx_conf_merge_ptr_value(conf->upstream.local, 2761 | prev->upstream.local, NULL); 2762 | 2763 | ngx_conf_merge_msec_value(conf->upstream.connect_timeout, 2764 | prev->upstream.connect_timeout, 60000); 2765 | 2766 | ngx_conf_merge_msec_value(conf->upstream.send_timeout, 2767 | prev->upstream.send_timeout, 60000); 2768 | 2769 | ngx_conf_merge_msec_value(conf->upstream.read_timeout, 2770 | prev->upstream.read_timeout, 60000); 2771 | 2772 | ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout, 2773 | prev->upstream.next_upstream_timeout, 0); 2774 | 2775 | ngx_conf_merge_size_value(conf->upstream.send_lowat, 2776 | prev->upstream.send_lowat, 0); 2777 | 2778 | ngx_conf_merge_size_value(conf->upstream.buffer_size, 2779 | prev->upstream.buffer_size, 2780 | (size_t) ngx_pagesize); 2781 | 2782 | ngx_conf_merge_size_value(conf->upstream.limit_rate, 2783 | prev->upstream.limit_rate, 0); 2784 | 2785 | ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs, 2786 | 8, ngx_pagesize); 2787 | 2788 | if (conf->upstream.bufs.num < 2) { 2789 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2790 | "there must be at least 2 \"socks_buffers\""); 2791 | return NGX_CONF_ERROR; 2792 | } 2793 | 2794 | 2795 | size = conf->upstream.buffer_size; 2796 | if (size < conf->upstream.bufs.size) { 2797 | size = conf->upstream.bufs.size; 2798 | } 2799 | 2800 | 2801 | ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf, 2802 | prev->upstream.busy_buffers_size_conf, 2803 | NGX_CONF_UNSET_SIZE); 2804 | 2805 | if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) { 2806 | conf->upstream.busy_buffers_size = 2 * size; 2807 | } else { 2808 | conf->upstream.busy_buffers_size = 2809 | conf->upstream.busy_buffers_size_conf; 2810 | } 2811 | 2812 | if (conf->upstream.busy_buffers_size < size) { 2813 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2814 | "\"socks_busy_buffers_size\" must be equal to or greater than " 2815 | "the maximum of the value of \"socks_buffer_size\" and " 2816 | "one of the \"socks_buffers\""); 2817 | 2818 | return NGX_CONF_ERROR; 2819 | } 2820 | 2821 | if (conf->upstream.busy_buffers_size 2822 | > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size) 2823 | { 2824 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2825 | "\"socks_busy_buffers_size\" must be less than " 2826 | "the size of all \"socks_buffers\" minus one buffer"); 2827 | 2828 | return NGX_CONF_ERROR; 2829 | } 2830 | 2831 | 2832 | ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf, 2833 | prev->upstream.temp_file_write_size_conf, 2834 | NGX_CONF_UNSET_SIZE); 2835 | 2836 | if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) { 2837 | conf->upstream.temp_file_write_size = 2 * size; 2838 | } else { 2839 | conf->upstream.temp_file_write_size = 2840 | conf->upstream.temp_file_write_size_conf; 2841 | } 2842 | 2843 | if (conf->upstream.temp_file_write_size < size) { 2844 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2845 | "\"socks_temp_file_write_size\" must be equal to or greater " 2846 | "than the maximum of the value of \"socks_buffer_size\" and " 2847 | "one of the \"socks_buffers\""); 2848 | 2849 | return NGX_CONF_ERROR; 2850 | } 2851 | 2852 | ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf, 2853 | prev->upstream.max_temp_file_size_conf, 2854 | NGX_CONF_UNSET_SIZE); 2855 | 2856 | if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) { 2857 | conf->upstream.max_temp_file_size = 1024 * 1024 * 1024; 2858 | } else { 2859 | conf->upstream.max_temp_file_size = 2860 | conf->upstream.max_temp_file_size_conf; 2861 | } 2862 | 2863 | if (conf->upstream.max_temp_file_size != 0 2864 | && conf->upstream.max_temp_file_size < size) 2865 | { 2866 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2867 | "\"socks_max_temp_file_size\" must be equal to zero to disable " 2868 | "temporary files usage or must be equal to or greater than " 2869 | "the maximum of the value of \"socks_buffer_size\" and " 2870 | "one of the \"socks_buffers\""); 2871 | 2872 | return NGX_CONF_ERROR; 2873 | } 2874 | 2875 | 2876 | ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers, 2877 | prev->upstream.ignore_headers, 2878 | NGX_CONF_BITMASK_SET); 2879 | 2880 | 2881 | ngx_conf_merge_bitmask_value(conf->upstream.next_upstream, 2882 | prev->upstream.next_upstream, 2883 | (NGX_CONF_BITMASK_SET 2884 | |NGX_HTTP_UPSTREAM_FT_ERROR 2885 | |NGX_HTTP_UPSTREAM_FT_TIMEOUT)); 2886 | 2887 | if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) { 2888 | conf->upstream.next_upstream = NGX_CONF_BITMASK_SET 2889 | |NGX_HTTP_UPSTREAM_FT_OFF; 2890 | } 2891 | 2892 | if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path, 2893 | prev->upstream.temp_path, 2894 | &ngx_http_socks_temp_path) 2895 | != NGX_OK) 2896 | { 2897 | return NGX_CONF_ERROR; 2898 | } 2899 | 2900 | 2901 | #if (NGX_HTTP_CACHE) 2902 | 2903 | if (conf->upstream.cache == NGX_CONF_UNSET) { 2904 | ngx_conf_merge_value(conf->upstream.cache, 2905 | prev->upstream.cache, 0); 2906 | 2907 | conf->upstream.cache_zone = prev->upstream.cache_zone; 2908 | conf->upstream.cache_value = prev->upstream.cache_value; 2909 | } 2910 | 2911 | if (conf->upstream.cache_zone && conf->upstream.cache_zone->data == NULL) { 2912 | ngx_shm_zone_t *shm_zone; 2913 | 2914 | shm_zone = conf->upstream.cache_zone; 2915 | 2916 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2917 | "\"socks_cache\" zone \"%V\" is unknown", 2918 | &shm_zone->shm.name); 2919 | 2920 | return NGX_CONF_ERROR; 2921 | } 2922 | 2923 | ngx_conf_merge_uint_value(conf->upstream.cache_min_uses, 2924 | prev->upstream.cache_min_uses, 1); 2925 | 2926 | ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale, 2927 | prev->upstream.cache_use_stale, 2928 | (NGX_CONF_BITMASK_SET 2929 | |NGX_HTTP_UPSTREAM_FT_OFF)); 2930 | 2931 | if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) { 2932 | conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET 2933 | |NGX_HTTP_UPSTREAM_FT_OFF; 2934 | } 2935 | 2936 | if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) { 2937 | conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE; 2938 | } 2939 | 2940 | if (conf->upstream.cache_methods == 0) { 2941 | conf->upstream.cache_methods = prev->upstream.cache_methods; 2942 | } 2943 | 2944 | conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD; 2945 | 2946 | ngx_conf_merge_ptr_value(conf->upstream.cache_bypass, 2947 | prev->upstream.cache_bypass, NULL); 2948 | 2949 | ngx_conf_merge_ptr_value(conf->upstream.no_cache, 2950 | prev->upstream.no_cache, NULL); 2951 | 2952 | ngx_conf_merge_ptr_value(conf->upstream.cache_valid, 2953 | prev->upstream.cache_valid, NULL); 2954 | 2955 | if (conf->cache_key.value.data == NULL) { 2956 | conf->cache_key = prev->cache_key; 2957 | } 2958 | 2959 | ngx_conf_merge_value(conf->upstream.cache_lock, 2960 | prev->upstream.cache_lock, 0); 2961 | 2962 | ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout, 2963 | prev->upstream.cache_lock_timeout, 5000); 2964 | 2965 | ngx_conf_merge_msec_value(conf->upstream.cache_lock_age, 2966 | prev->upstream.cache_lock_age, 5000); 2967 | 2968 | ngx_conf_merge_value(conf->upstream.cache_revalidate, 2969 | prev->upstream.cache_revalidate, 0); 2970 | 2971 | #if nginx_version >= 1009007 2972 | ngx_conf_merge_value(conf->upstream.cache_convert_head, 2973 | prev->upstream.cache_convert_head, 1); 2974 | #endif 2975 | 2976 | #endif 2977 | 2978 | ngx_conf_merge_str_value(conf->method, prev->method, ""); 2979 | 2980 | if (conf->method.len 2981 | && conf->method.data[conf->method.len - 1] != ' ') 2982 | { 2983 | conf->method.data[conf->method.len] = ' '; 2984 | conf->method.len++; 2985 | } 2986 | 2987 | ngx_conf_merge_value(conf->upstream.pass_request_headers, 2988 | prev->upstream.pass_request_headers, 1); 2989 | ngx_conf_merge_value(conf->upstream.pass_request_body, 2990 | prev->upstream.pass_request_body, 1); 2991 | 2992 | ngx_conf_merge_value(conf->upstream.intercept_errors, 2993 | prev->upstream.intercept_errors, 0); 2994 | 2995 | ngx_conf_merge_str_value(conf->tunnel_header, 2996 | prev->tunnel_header, ""); 2997 | 2998 | if (conf->host.value.data == NULL) { 2999 | conf->host = prev->host; 3000 | } 3001 | 3002 | ngx_conf_merge_value(conf->redirect, prev->redirect, 1); 3003 | 3004 | if (conf->redirect) { 3005 | 3006 | if (conf->redirects == NULL) { 3007 | conf->redirects = prev->redirects; 3008 | } 3009 | 3010 | if (conf->redirects == NULL && conf->url.data) { 3011 | 3012 | conf->redirects = ngx_array_create(cf->pool, 1, 3013 | sizeof(ngx_http_socks_rewrite_t)); 3014 | if (conf->redirects == NULL) { 3015 | return NGX_CONF_ERROR; 3016 | } 3017 | 3018 | pr = ngx_array_push(conf->redirects); 3019 | if (pr == NULL) { 3020 | return NGX_CONF_ERROR; 3021 | } 3022 | 3023 | ngx_memzero(&pr->pattern.complex, 3024 | sizeof(ngx_http_complex_value_t)); 3025 | 3026 | ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t)); 3027 | 3028 | pr->handler = ngx_http_socks_rewrite_complex_handler; 3029 | 3030 | if (conf->vars.uri.len) { 3031 | pr->pattern.complex.value = conf->url; 3032 | pr->replacement.value = conf->location; 3033 | 3034 | } else { 3035 | pr->pattern.complex.value.len = conf->url.len 3036 | + sizeof("/") - 1; 3037 | 3038 | p = ngx_pnalloc(cf->pool, pr->pattern.complex.value.len); 3039 | if (p == NULL) { 3040 | return NGX_CONF_ERROR; 3041 | } 3042 | 3043 | pr->pattern.complex.value.data = p; 3044 | 3045 | p = ngx_cpymem(p, conf->url.data, conf->url.len); 3046 | *p = '/'; 3047 | 3048 | ngx_str_set(&pr->replacement.value, "/"); 3049 | } 3050 | } 3051 | } 3052 | 3053 | ngx_conf_merge_ptr_value(conf->cookie_domains, prev->cookie_domains, NULL); 3054 | 3055 | ngx_conf_merge_ptr_value(conf->cookie_paths, prev->cookie_paths, NULL); 3056 | 3057 | ngx_conf_merge_uint_value(conf->http_version, prev->http_version, 3058 | NGX_HTTP_VERSION_10); 3059 | 3060 | ngx_conf_merge_uint_value(conf->headers_hash_max_size, 3061 | prev->headers_hash_max_size, 512); 3062 | 3063 | ngx_conf_merge_uint_value(conf->headers_hash_bucket_size, 3064 | prev->headers_hash_bucket_size, 64); 3065 | 3066 | conf->headers_hash_bucket_size = ngx_align(conf->headers_hash_bucket_size, 3067 | ngx_cacheline_size); 3068 | 3069 | hash.max_size = conf->headers_hash_max_size; 3070 | hash.bucket_size = conf->headers_hash_bucket_size; 3071 | hash.name = "socks_headers_hash"; 3072 | 3073 | if (ngx_http_socks_upstream_hide_headers_hash(cf, &conf->upstream, 3074 | &prev->upstream, ngx_http_socks_hide_headers, &hash) 3075 | != NGX_OK) 3076 | { 3077 | return NGX_CONF_ERROR; 3078 | } 3079 | 3080 | clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); 3081 | 3082 | if (clcf->noname 3083 | && conf->upstream.upstream == NULL && conf->proxy_lengths == NULL) 3084 | { 3085 | conf->upstream.upstream = prev->upstream.upstream; 3086 | conf->location = prev->location; 3087 | conf->vars = prev->vars; 3088 | 3089 | conf->proxy_lengths = prev->proxy_lengths; 3090 | conf->proxy_values = prev->proxy_values; 3091 | } 3092 | 3093 | if (clcf->lmt_excpt && clcf->handler == NULL 3094 | && (conf->upstream.upstream || conf->proxy_lengths)) 3095 | { 3096 | clcf->handler = ngx_http_socks_handler; 3097 | } 3098 | 3099 | if (conf->body_source.data == NULL) { 3100 | conf->body_flushes = prev->body_flushes; 3101 | conf->body_source = prev->body_source; 3102 | conf->body_lengths = prev->body_lengths; 3103 | conf->body_values = prev->body_values; 3104 | } 3105 | 3106 | if (conf->body_source.data && conf->body_lengths == NULL) { 3107 | 3108 | ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); 3109 | 3110 | sc.cf = cf; 3111 | sc.source = &conf->body_source; 3112 | sc.flushes = &conf->body_flushes; 3113 | sc.lengths = &conf->body_lengths; 3114 | sc.values = &conf->body_values; 3115 | sc.complete_lengths = 1; 3116 | sc.complete_values = 1; 3117 | 3118 | if (ngx_http_script_compile(&sc) != NGX_OK) { 3119 | return NGX_CONF_ERROR; 3120 | } 3121 | } 3122 | 3123 | if (conf->headers_source == NULL) { 3124 | conf->headers = prev->headers; 3125 | #if (NGX_HTTP_CACHE) 3126 | conf->headers_cache = prev->headers_cache; 3127 | #endif 3128 | conf->headers_source = prev->headers_source; 3129 | } 3130 | 3131 | rc = ngx_http_socks_init_headers(cf, conf, &conf->headers, 3132 | ngx_http_socks_headers); 3133 | if (rc != NGX_OK) { 3134 | return NGX_CONF_ERROR; 3135 | } 3136 | 3137 | #if (NGX_HTTP_CACHE) 3138 | 3139 | if (conf->upstream.cache) { 3140 | rc = ngx_http_socks_init_headers(cf, conf, &conf->headers_cache, 3141 | ngx_http_socks_cache_headers); 3142 | if (rc != NGX_OK) { 3143 | return NGX_CONF_ERROR; 3144 | } 3145 | } 3146 | 3147 | #endif 3148 | 3149 | return NGX_CONF_OK; 3150 | } 3151 | 3152 | 3153 | static ngx_int_t 3154 | ngx_http_socks_init_headers(ngx_conf_t *cf, ngx_http_socks_loc_conf_t *conf, 3155 | ngx_http_socks_headers_t *headers, ngx_keyval_t *default_headers) 3156 | { 3157 | u_char *p; 3158 | size_t size; 3159 | uintptr_t *code; 3160 | ngx_uint_t i; 3161 | ngx_array_t headers_names, headers_merged; 3162 | ngx_keyval_t *src, *s, *h; 3163 | ngx_hash_key_t *hk; 3164 | ngx_hash_init_t hash; 3165 | ngx_http_script_compile_t sc; 3166 | ngx_http_script_copy_code_t *copy; 3167 | 3168 | if (headers->hash.buckets) { 3169 | return NGX_OK; 3170 | } 3171 | 3172 | if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t)) 3173 | != NGX_OK) 3174 | { 3175 | return NGX_ERROR; 3176 | } 3177 | 3178 | if (ngx_array_init(&headers_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t)) 3179 | != NGX_OK) 3180 | { 3181 | return NGX_ERROR; 3182 | } 3183 | 3184 | if (conf->headers_source == NULL) { 3185 | conf->headers_source = ngx_array_create(cf->pool, 4, 3186 | sizeof(ngx_keyval_t)); 3187 | if (conf->headers_source == NULL) { 3188 | return NGX_ERROR; 3189 | } 3190 | } 3191 | 3192 | headers->lengths = ngx_array_create(cf->pool, 64, 1); 3193 | if (headers->lengths == NULL) { 3194 | return NGX_ERROR; 3195 | } 3196 | 3197 | headers->values = ngx_array_create(cf->pool, 512, 1); 3198 | if (headers->values == NULL) { 3199 | return NGX_ERROR; 3200 | } 3201 | 3202 | src = conf->headers_source->elts; 3203 | for (i = 0; i < conf->headers_source->nelts; i++) { 3204 | 3205 | s = ngx_array_push(&headers_merged); 3206 | if (s == NULL) { 3207 | return NGX_ERROR; 3208 | } 3209 | 3210 | *s = src[i]; 3211 | } 3212 | 3213 | h = default_headers; 3214 | 3215 | while (h->key.len) { 3216 | 3217 | src = headers_merged.elts; 3218 | for (i = 0; i < headers_merged.nelts; i++) { 3219 | if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) { 3220 | goto next; 3221 | } 3222 | } 3223 | 3224 | s = ngx_array_push(&headers_merged); 3225 | if (s == NULL) { 3226 | return NGX_ERROR; 3227 | } 3228 | 3229 | *s = *h; 3230 | 3231 | next: 3232 | 3233 | h++; 3234 | } 3235 | 3236 | 3237 | src = headers_merged.elts; 3238 | for (i = 0; i < headers_merged.nelts; i++) { 3239 | 3240 | hk = ngx_array_push(&headers_names); 3241 | if (hk == NULL) { 3242 | return NGX_ERROR; 3243 | } 3244 | 3245 | hk->key = src[i].key; 3246 | hk->key_hash = ngx_hash_key_lc(src[i].key.data, src[i].key.len); 3247 | hk->value = (void *) 1; 3248 | 3249 | if (src[i].value.len == 0) { 3250 | continue; 3251 | } 3252 | 3253 | if (ngx_http_script_variables_count(&src[i].value) == 0) { 3254 | copy = ngx_array_push_n(headers->lengths, 3255 | sizeof(ngx_http_script_copy_code_t)); 3256 | if (copy == NULL) { 3257 | return NGX_ERROR; 3258 | } 3259 | 3260 | copy->code = (ngx_http_script_code_pt) 3261 | ngx_http_script_copy_len_code; 3262 | copy->len = src[i].key.len + sizeof(": ") - 1 3263 | + src[i].value.len + sizeof(CRLF) - 1; 3264 | 3265 | 3266 | size = (sizeof(ngx_http_script_copy_code_t) 3267 | + src[i].key.len + sizeof(": ") - 1 3268 | + src[i].value.len + sizeof(CRLF) - 1 3269 | + sizeof(uintptr_t) - 1) 3270 | & ~(sizeof(uintptr_t) - 1); 3271 | 3272 | copy = ngx_array_push_n(headers->values, size); 3273 | if (copy == NULL) { 3274 | return NGX_ERROR; 3275 | } 3276 | 3277 | copy->code = ngx_http_script_copy_code; 3278 | copy->len = src[i].key.len + sizeof(": ") - 1 3279 | + src[i].value.len + sizeof(CRLF) - 1; 3280 | 3281 | p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); 3282 | 3283 | p = ngx_cpymem(p, src[i].key.data, src[i].key.len); 3284 | *p++ = ':'; *p++ = ' '; 3285 | p = ngx_cpymem(p, src[i].value.data, src[i].value.len); 3286 | *p++ = CR; *p = LF; 3287 | 3288 | } else { 3289 | copy = ngx_array_push_n(headers->lengths, 3290 | sizeof(ngx_http_script_copy_code_t)); 3291 | if (copy == NULL) { 3292 | return NGX_ERROR; 3293 | } 3294 | 3295 | copy->code = (ngx_http_script_code_pt) 3296 | ngx_http_script_copy_len_code; 3297 | copy->len = src[i].key.len + sizeof(": ") - 1; 3298 | 3299 | 3300 | size = (sizeof(ngx_http_script_copy_code_t) 3301 | + src[i].key.len + sizeof(": ") - 1 + sizeof(uintptr_t) - 1) 3302 | & ~(sizeof(uintptr_t) - 1); 3303 | 3304 | copy = ngx_array_push_n(headers->values, size); 3305 | if (copy == NULL) { 3306 | return NGX_ERROR; 3307 | } 3308 | 3309 | copy->code = ngx_http_script_copy_code; 3310 | copy->len = src[i].key.len + sizeof(": ") - 1; 3311 | 3312 | p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); 3313 | p = ngx_cpymem(p, src[i].key.data, src[i].key.len); 3314 | *p++ = ':'; *p = ' '; 3315 | 3316 | 3317 | ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); 3318 | 3319 | sc.cf = cf; 3320 | sc.source = &src[i].value; 3321 | sc.flushes = &headers->flushes; 3322 | sc.lengths = &headers->lengths; 3323 | sc.values = &headers->values; 3324 | 3325 | if (ngx_http_script_compile(&sc) != NGX_OK) { 3326 | return NGX_ERROR; 3327 | } 3328 | 3329 | 3330 | copy = ngx_array_push_n(headers->lengths, 3331 | sizeof(ngx_http_script_copy_code_t)); 3332 | if (copy == NULL) { 3333 | return NGX_ERROR; 3334 | } 3335 | 3336 | copy->code = (ngx_http_script_code_pt) 3337 | ngx_http_script_copy_len_code; 3338 | copy->len = sizeof(CRLF) - 1; 3339 | 3340 | 3341 | size = (sizeof(ngx_http_script_copy_code_t) 3342 | + sizeof(CRLF) - 1 + sizeof(uintptr_t) - 1) 3343 | & ~(sizeof(uintptr_t) - 1); 3344 | 3345 | copy = ngx_array_push_n(headers->values, size); 3346 | if (copy == NULL) { 3347 | return NGX_ERROR; 3348 | } 3349 | 3350 | copy->code = ngx_http_script_copy_code; 3351 | copy->len = sizeof(CRLF) - 1; 3352 | 3353 | p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t); 3354 | *p++ = CR; *p = LF; 3355 | } 3356 | 3357 | code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t)); 3358 | if (code == NULL) { 3359 | return NGX_ERROR; 3360 | } 3361 | 3362 | *code = (uintptr_t) NULL; 3363 | 3364 | code = ngx_array_push_n(headers->values, sizeof(uintptr_t)); 3365 | if (code == NULL) { 3366 | return NGX_ERROR; 3367 | } 3368 | 3369 | *code = (uintptr_t) NULL; 3370 | } 3371 | 3372 | code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t)); 3373 | if (code == NULL) { 3374 | return NGX_ERROR; 3375 | } 3376 | 3377 | *code = (uintptr_t) NULL; 3378 | 3379 | 3380 | hash.hash = &headers->hash; 3381 | hash.key = ngx_hash_key_lc; 3382 | hash.max_size = conf->headers_hash_max_size; 3383 | hash.bucket_size = conf->headers_hash_bucket_size; 3384 | hash.name = "socks_headers_hash"; 3385 | hash.pool = cf->pool; 3386 | hash.temp_pool = NULL; 3387 | 3388 | return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts); 3389 | } 3390 | 3391 | 3392 | static char * 3393 | ngx_http_socks_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 3394 | { 3395 | ngx_http_socks_loc_conf_t *slcf = conf; 3396 | 3397 | u_short port; 3398 | ngx_str_t *value, *url; 3399 | ngx_url_t u; 3400 | ngx_uint_t n; 3401 | ngx_http_core_loc_conf_t *clcf; 3402 | ngx_http_script_compile_t sc; 3403 | 3404 | if (slcf->upstream.upstream || slcf->proxy_lengths) { 3405 | return "is duplicate"; 3406 | } 3407 | 3408 | clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); 3409 | 3410 | clcf->handler = ngx_http_socks_handler; 3411 | 3412 | if (clcf->name.data[clcf->name.len - 1] == '/') { 3413 | clcf->auto_redirect = 1; 3414 | } 3415 | 3416 | value = cf->args->elts; 3417 | 3418 | url = &value[1]; 3419 | 3420 | n = ngx_http_script_variables_count(url); 3421 | 3422 | if (n) { 3423 | 3424 | ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); 3425 | 3426 | sc.cf = cf; 3427 | sc.source = url; 3428 | sc.lengths = &slcf->proxy_lengths; 3429 | sc.values = &slcf->proxy_values; 3430 | sc.variables = n; 3431 | sc.complete_lengths = 1; 3432 | sc.complete_values = 1; 3433 | 3434 | if (ngx_http_script_compile(&sc) != NGX_OK) { 3435 | return NGX_CONF_ERROR; 3436 | } 3437 | 3438 | return NGX_CONF_OK; 3439 | } 3440 | 3441 | if (ngx_strncasecmp(url->data, (u_char *) "socks5://", 9) == 0) { 3442 | port = 1080; 3443 | } else { 3444 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid URL prefix"); 3445 | return NGX_CONF_ERROR; 3446 | } 3447 | 3448 | ngx_memzero(&u, sizeof(ngx_url_t)); 3449 | 3450 | u.url.len = url->len - 9; 3451 | u.url.data = url->data + 9; 3452 | u.default_port = port; 3453 | u.uri_part = 1; 3454 | u.no_resolve = 1; 3455 | 3456 | slcf->upstream.upstream = ngx_http_socks_upstream_add(cf, &u, 0); 3457 | if (slcf->upstream.upstream == NULL) { 3458 | return NGX_CONF_ERROR; 3459 | } 3460 | 3461 | slcf->vars.schema.len = 9; 3462 | slcf->vars.schema.data = url->data; 3463 | 3464 | ngx_http_socks_set_vars(&u, &slcf->vars); 3465 | 3466 | slcf->location = clcf->name; 3467 | 3468 | if (clcf->named 3469 | #if (NGX_PCRE) 3470 | || clcf->regex 3471 | #endif 3472 | || clcf->noname) 3473 | { 3474 | if (slcf->vars.uri.len) { 3475 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 3476 | "\"socks_pass\" cannot have URI part in " 3477 | "location given by regular expression, " 3478 | "or inside named location, " 3479 | "or inside \"if\" statement, " 3480 | "or inside \"limit_except\" block"); 3481 | return NGX_CONF_ERROR; 3482 | } 3483 | 3484 | slcf->location.len = 0; 3485 | } 3486 | 3487 | slcf->url = *url; 3488 | 3489 | return NGX_CONF_OK; 3490 | } 3491 | 3492 | 3493 | static char * 3494 | ngx_http_socks_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 3495 | { 3496 | ngx_http_socks_loc_conf_t *slcf = conf; 3497 | 3498 | u_char *p; 3499 | ngx_str_t *value; 3500 | ngx_http_socks_rewrite_t *pr; 3501 | ngx_http_compile_complex_value_t ccv; 3502 | 3503 | if (slcf->redirect == 0) { 3504 | return NGX_CONF_OK; 3505 | } 3506 | 3507 | slcf->redirect = 1; 3508 | 3509 | value = cf->args->elts; 3510 | 3511 | if (cf->args->nelts == 2) { 3512 | if (ngx_strcmp(value[1].data, "off") == 0) { 3513 | slcf->redirect = 0; 3514 | slcf->redirects = NULL; 3515 | return NGX_CONF_OK; 3516 | } 3517 | 3518 | if (ngx_strcmp(value[1].data, "false") == 0) { 3519 | ngx_conf_log_error(NGX_LOG_ERR, cf, 0, 3520 | "invalid parameter \"false\", use \"off\" instead"); 3521 | slcf->redirect = 0; 3522 | slcf->redirects = NULL; 3523 | return NGX_CONF_OK; 3524 | } 3525 | 3526 | if (ngx_strcmp(value[1].data, "default") != 0) { 3527 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 3528 | "invalid parameter \"%V\"", &value[1]); 3529 | return NGX_CONF_ERROR; 3530 | } 3531 | } 3532 | 3533 | if (slcf->redirects == NULL) { 3534 | slcf->redirects = ngx_array_create(cf->pool, 1, 3535 | sizeof(ngx_http_socks_rewrite_t)); 3536 | if (slcf->redirects == NULL) { 3537 | return NGX_CONF_ERROR; 3538 | } 3539 | } 3540 | 3541 | pr = ngx_array_push(slcf->redirects); 3542 | if (pr == NULL) { 3543 | return NGX_CONF_ERROR; 3544 | } 3545 | 3546 | if (ngx_strcmp(value[1].data, "default") == 0) { 3547 | if (slcf->proxy_lengths) { 3548 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 3549 | "\"socks_redirect default\" cannot be used " 3550 | "with \"socks_pass\" directive with variables"); 3551 | return NGX_CONF_ERROR; 3552 | } 3553 | 3554 | if (slcf->url.data == NULL) { 3555 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 3556 | "\"socks_redirect default\" should be placed " 3557 | "after the \"socks_pass\" directive"); 3558 | return NGX_CONF_ERROR; 3559 | } 3560 | 3561 | pr->handler = ngx_http_socks_rewrite_complex_handler; 3562 | 3563 | ngx_memzero(&pr->pattern.complex, sizeof(ngx_http_complex_value_t)); 3564 | 3565 | ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t)); 3566 | 3567 | if (slcf->vars.uri.len) { 3568 | pr->pattern.complex.value = slcf->url; 3569 | pr->replacement.value = slcf->location; 3570 | 3571 | } else { 3572 | pr->pattern.complex.value.len = slcf->url.len + sizeof("/") - 1; 3573 | 3574 | p = ngx_pnalloc(cf->pool, pr->pattern.complex.value.len); 3575 | if (p == NULL) { 3576 | return NGX_CONF_ERROR; 3577 | } 3578 | 3579 | pr->pattern.complex.value.data = p; 3580 | 3581 | p = ngx_cpymem(p, slcf->url.data, slcf->url.len); 3582 | *p = '/'; 3583 | 3584 | ngx_str_set(&pr->replacement.value, "/"); 3585 | } 3586 | 3587 | return NGX_CONF_OK; 3588 | } 3589 | 3590 | 3591 | if (value[1].data[0] == '~') { 3592 | value[1].len--; 3593 | value[1].data++; 3594 | 3595 | if (value[1].data[0] == '*') { 3596 | value[1].len--; 3597 | value[1].data++; 3598 | 3599 | if (ngx_http_socks_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) { 3600 | return NGX_CONF_ERROR; 3601 | } 3602 | 3603 | } else { 3604 | if (ngx_http_socks_rewrite_regex(cf, pr, &value[1], 0) != NGX_OK) { 3605 | return NGX_CONF_ERROR; 3606 | } 3607 | } 3608 | 3609 | } else { 3610 | 3611 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 3612 | 3613 | ccv.cf = cf; 3614 | ccv.value = &value[1]; 3615 | ccv.complex_value = &pr->pattern.complex; 3616 | 3617 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 3618 | return NGX_CONF_ERROR; 3619 | } 3620 | 3621 | pr->handler = ngx_http_socks_rewrite_complex_handler; 3622 | } 3623 | 3624 | 3625 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 3626 | 3627 | ccv.cf = cf; 3628 | ccv.value = &value[2]; 3629 | ccv.complex_value = &pr->replacement; 3630 | 3631 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 3632 | return NGX_CONF_ERROR; 3633 | } 3634 | 3635 | return NGX_CONF_OK; 3636 | } 3637 | 3638 | 3639 | static char * 3640 | ngx_http_socks_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 3641 | { 3642 | ngx_http_socks_loc_conf_t *slcf = conf; 3643 | 3644 | ngx_str_t *value; 3645 | ngx_http_socks_rewrite_t *pr; 3646 | ngx_http_compile_complex_value_t ccv; 3647 | 3648 | if (slcf->cookie_domains == NULL) { 3649 | return NGX_CONF_OK; 3650 | } 3651 | 3652 | value = cf->args->elts; 3653 | 3654 | if (cf->args->nelts == 2) { 3655 | 3656 | if (ngx_strcmp(value[1].data, "off") == 0) { 3657 | slcf->cookie_domains = NULL; 3658 | return NGX_CONF_OK; 3659 | } 3660 | 3661 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 3662 | "invalid parameter \"%V\"", &value[1]); 3663 | return NGX_CONF_ERROR; 3664 | } 3665 | 3666 | if (slcf->cookie_domains == NGX_CONF_UNSET_PTR) { 3667 | slcf->cookie_domains = ngx_array_create(cf->pool, 1, 3668 | sizeof(ngx_http_socks_rewrite_t)); 3669 | if (slcf->cookie_domains == NULL) { 3670 | return NGX_CONF_ERROR; 3671 | } 3672 | } 3673 | 3674 | pr = ngx_array_push(slcf->cookie_domains); 3675 | if (pr == NULL) { 3676 | return NGX_CONF_ERROR; 3677 | } 3678 | 3679 | if (value[1].data[0] == '~') { 3680 | value[1].len--; 3681 | value[1].data++; 3682 | 3683 | if (ngx_http_socks_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) { 3684 | return NGX_CONF_ERROR; 3685 | } 3686 | 3687 | } else { 3688 | 3689 | if (value[1].data[0] == '.') { 3690 | value[1].len--; 3691 | value[1].data++; 3692 | } 3693 | 3694 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 3695 | 3696 | ccv.cf = cf; 3697 | ccv.value = &value[1]; 3698 | ccv.complex_value = &pr->pattern.complex; 3699 | 3700 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 3701 | return NGX_CONF_ERROR; 3702 | } 3703 | 3704 | pr->handler = ngx_http_socks_rewrite_domain_handler; 3705 | 3706 | if (value[2].data[0] == '.') { 3707 | value[2].len--; 3708 | value[2].data++; 3709 | } 3710 | } 3711 | 3712 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 3713 | 3714 | ccv.cf = cf; 3715 | ccv.value = &value[2]; 3716 | ccv.complex_value = &pr->replacement; 3717 | 3718 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 3719 | return NGX_CONF_ERROR; 3720 | } 3721 | 3722 | return NGX_CONF_OK; 3723 | } 3724 | 3725 | 3726 | static char * 3727 | ngx_http_socks_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 3728 | { 3729 | ngx_http_socks_loc_conf_t *slcf = conf; 3730 | 3731 | ngx_str_t *value; 3732 | ngx_http_socks_rewrite_t *pr; 3733 | ngx_http_compile_complex_value_t ccv; 3734 | 3735 | if (slcf->cookie_paths == NULL) { 3736 | return NGX_CONF_OK; 3737 | } 3738 | 3739 | value = cf->args->elts; 3740 | 3741 | if (cf->args->nelts == 2) { 3742 | 3743 | if (ngx_strcmp(value[1].data, "off") == 0) { 3744 | slcf->cookie_paths = NULL; 3745 | return NGX_CONF_OK; 3746 | } 3747 | 3748 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 3749 | "invalid parameter \"%V\"", &value[1]); 3750 | return NGX_CONF_ERROR; 3751 | } 3752 | 3753 | if (slcf->cookie_paths == NGX_CONF_UNSET_PTR) { 3754 | slcf->cookie_paths = ngx_array_create(cf->pool, 1, 3755 | sizeof(ngx_http_socks_rewrite_t)); 3756 | if (slcf->cookie_paths == NULL) { 3757 | return NGX_CONF_ERROR; 3758 | } 3759 | } 3760 | 3761 | pr = ngx_array_push(slcf->cookie_paths); 3762 | if (pr == NULL) { 3763 | return NGX_CONF_ERROR; 3764 | } 3765 | 3766 | if (value[1].data[0] == '~') { 3767 | value[1].len--; 3768 | value[1].data++; 3769 | 3770 | if (value[1].data[0] == '*') { 3771 | value[1].len--; 3772 | value[1].data++; 3773 | 3774 | if (ngx_http_socks_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) { 3775 | return NGX_CONF_ERROR; 3776 | } 3777 | 3778 | } else { 3779 | if (ngx_http_socks_rewrite_regex(cf, pr, &value[1], 0) != NGX_OK) { 3780 | return NGX_CONF_ERROR; 3781 | } 3782 | } 3783 | 3784 | } else { 3785 | 3786 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 3787 | 3788 | ccv.cf = cf; 3789 | ccv.value = &value[1]; 3790 | ccv.complex_value = &pr->pattern.complex; 3791 | 3792 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 3793 | return NGX_CONF_ERROR; 3794 | } 3795 | 3796 | pr->handler = ngx_http_socks_rewrite_complex_handler; 3797 | } 3798 | 3799 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 3800 | 3801 | ccv.cf = cf; 3802 | ccv.value = &value[2]; 3803 | ccv.complex_value = &pr->replacement; 3804 | 3805 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 3806 | return NGX_CONF_ERROR; 3807 | } 3808 | 3809 | return NGX_CONF_OK; 3810 | } 3811 | 3812 | 3813 | static ngx_int_t 3814 | ngx_http_socks_rewrite_regex(ngx_conf_t *cf, ngx_http_socks_rewrite_t *pr, 3815 | ngx_str_t *regex, ngx_uint_t caseless) 3816 | { 3817 | #if (NGX_PCRE) 3818 | u_char errstr[NGX_MAX_CONF_ERRSTR]; 3819 | ngx_regex_compile_t rc; 3820 | 3821 | ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); 3822 | 3823 | rc.pattern = *regex; 3824 | rc.err.len = NGX_MAX_CONF_ERRSTR; 3825 | rc.err.data = errstr; 3826 | 3827 | if (caseless) { 3828 | rc.options = NGX_REGEX_CASELESS; 3829 | } 3830 | 3831 | pr->pattern.regex = ngx_http_regex_compile(cf, &rc); 3832 | if (pr->pattern.regex == NULL) { 3833 | return NGX_ERROR; 3834 | } 3835 | 3836 | pr->handler = ngx_http_socks_rewrite_regex_handler; 3837 | 3838 | return NGX_OK; 3839 | 3840 | #else 3841 | 3842 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 3843 | "using regex \"%V\" requires PCRE library", regex); 3844 | return NGX_ERROR; 3845 | 3846 | #endif 3847 | } 3848 | 3849 | 3850 | static char * 3851 | ngx_http_socks_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 3852 | { 3853 | ngx_http_socks_loc_conf_t *slcf = conf; 3854 | 3855 | ngx_str_t *value; 3856 | ngx_http_script_compile_t sc; 3857 | 3858 | if (slcf->upstream.store != NGX_CONF_UNSET) { 3859 | return "is duplicate"; 3860 | } 3861 | 3862 | value = cf->args->elts; 3863 | 3864 | if (ngx_strcmp(value[1].data, "off") == 0) { 3865 | slcf->upstream.store = 0; 3866 | return NGX_CONF_OK; 3867 | } 3868 | 3869 | #if (NGX_HTTP_CACHE) 3870 | if (slcf->upstream.cache > 0) { 3871 | return "is incompatible with \"socks_cache\""; 3872 | } 3873 | #endif 3874 | 3875 | slcf->upstream.store = 1; 3876 | 3877 | if (ngx_strcmp(value[1].data, "on") == 0) { 3878 | return NGX_CONF_OK; 3879 | } 3880 | 3881 | /* include the terminating '\0' into script */ 3882 | value[1].len++; 3883 | 3884 | ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); 3885 | 3886 | sc.cf = cf; 3887 | sc.source = &value[1]; 3888 | sc.lengths = &slcf->upstream.store_lengths; 3889 | sc.values = &slcf->upstream.store_values; 3890 | sc.variables = ngx_http_script_variables_count(&value[1]); 3891 | sc.complete_lengths = 1; 3892 | sc.complete_values = 1; 3893 | 3894 | if (ngx_http_script_compile(&sc) != NGX_OK) { 3895 | return NGX_CONF_ERROR; 3896 | } 3897 | 3898 | return NGX_CONF_OK; 3899 | } 3900 | 3901 | 3902 | #if (NGX_HTTP_CACHE) 3903 | 3904 | static char * 3905 | ngx_http_socks_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 3906 | { 3907 | ngx_http_socks_loc_conf_t *slcf = conf; 3908 | 3909 | ngx_str_t *value; 3910 | ngx_http_complex_value_t cv; 3911 | ngx_http_compile_complex_value_t ccv; 3912 | 3913 | value = cf->args->elts; 3914 | 3915 | if (slcf->upstream.cache != NGX_CONF_UNSET) { 3916 | return "is duplicate"; 3917 | } 3918 | 3919 | if (ngx_strcmp(value[1].data, "off") == 0) { 3920 | slcf->upstream.cache = 0; 3921 | return NGX_CONF_OK; 3922 | } 3923 | 3924 | if (slcf->upstream.store > 0) { 3925 | return "is incompatible with \"socks_store\""; 3926 | } 3927 | 3928 | slcf->upstream.cache = 1; 3929 | 3930 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 3931 | 3932 | ccv.cf = cf; 3933 | ccv.value = &value[1]; 3934 | ccv.complex_value = &cv; 3935 | 3936 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 3937 | return NGX_CONF_ERROR; 3938 | } 3939 | 3940 | if (cv.lengths != NULL) { 3941 | 3942 | slcf->upstream.cache_value = ngx_palloc(cf->pool, 3943 | sizeof(ngx_http_complex_value_t)); 3944 | if (slcf->upstream.cache_value == NULL) { 3945 | return NGX_CONF_ERROR; 3946 | } 3947 | 3948 | *slcf->upstream.cache_value = cv; 3949 | 3950 | return NGX_CONF_OK; 3951 | } 3952 | 3953 | slcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0, 3954 | &ngx_http_socks_module); 3955 | if (slcf->upstream.cache_zone == NULL) { 3956 | return NGX_CONF_ERROR; 3957 | } 3958 | 3959 | return NGX_CONF_OK; 3960 | } 3961 | 3962 | 3963 | static char * 3964 | ngx_http_socks_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 3965 | { 3966 | ngx_http_socks_loc_conf_t *slcf = conf; 3967 | 3968 | ngx_str_t *value; 3969 | ngx_http_compile_complex_value_t ccv; 3970 | 3971 | value = cf->args->elts; 3972 | 3973 | if (slcf->cache_key.value.data) { 3974 | return "is duplicate"; 3975 | } 3976 | 3977 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 3978 | 3979 | ccv.cf = cf; 3980 | ccv.value = &value[1]; 3981 | ccv.complex_value = &slcf->cache_key; 3982 | 3983 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 3984 | return NGX_CONF_ERROR; 3985 | } 3986 | 3987 | return NGX_CONF_OK; 3988 | } 3989 | 3990 | #endif 3991 | 3992 | static char * 3993 | ngx_http_socks_lowat_check(ngx_conf_t *cf, void *post, void *data) 3994 | { 3995 | #if (NGX_FREEBSD) 3996 | ssize_t *np = data; 3997 | 3998 | if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) { 3999 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 4000 | "\"socks_send_lowat\" must be less than %d " 4001 | "(sysctl net.inet.tcp.sendspace)", 4002 | ngx_freebsd_net_inet_tcp_sendspace); 4003 | 4004 | return NGX_CONF_ERROR; 4005 | } 4006 | 4007 | #elif !(NGX_HAVE_SO_SNDLOWAT) 4008 | ssize_t *np = data; 4009 | 4010 | ngx_conf_log_error(NGX_LOG_WARN, cf, 0, 4011 | "\"socks_send_lowat\" is not supported, ignored"); 4012 | 4013 | *np = 0; 4014 | 4015 | #endif 4016 | 4017 | return NGX_CONF_OK; 4018 | } 4019 | 4020 | 4021 | static void 4022 | ngx_http_socks_set_vars(ngx_url_t *u, ngx_http_socks_vars_t *v) 4023 | { 4024 | if (u->family != AF_UNIX) { 4025 | 4026 | if (u->no_port || u->port == u->default_port) { 4027 | 4028 | v->host_header = u->host; 4029 | 4030 | if (u->default_port == 80) { 4031 | ngx_str_set(&v->port, "80"); 4032 | 4033 | } else { 4034 | ngx_str_set(&v->port, "443"); 4035 | } 4036 | 4037 | } else { 4038 | v->host_header.len = u->host.len + 1 + u->port_text.len; 4039 | v->host_header.data = u->host.data; 4040 | v->port = u->port_text; 4041 | } 4042 | 4043 | } else { 4044 | ngx_str_set(&v->host_header, "localhost"); 4045 | ngx_str_null(&v->port); 4046 | } 4047 | 4048 | v->uri = u->uri; 4049 | } 4050 | 4051 | static char * 4052 | ngx_http_socks_tunnel_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { 4053 | ngx_http_socks_loc_conf_t *slcf = conf; 4054 | ngx_str_t *value; 4055 | 4056 | if (slcf->tunnel_header.data) { 4057 | return "is duplicate"; 4058 | } 4059 | 4060 | value = cf->args->elts; 4061 | ngx_strlow(value[1].data, value[1].data, value[1].len); 4062 | slcf->tunnel_header = value[1]; 4063 | 4064 | return NGX_CONF_OK; 4065 | } 4066 | 4067 | static char * 4068 | ngx_http_socks_set_host(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 4069 | { 4070 | ngx_http_socks_loc_conf_t *slcf = conf; 4071 | 4072 | ngx_str_t *value; 4073 | ngx_http_compile_complex_value_t ccv; 4074 | 4075 | value = cf->args->elts; 4076 | 4077 | if (slcf->host.value.data) { 4078 | return "is duplicate"; 4079 | } 4080 | 4081 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 4082 | 4083 | ccv.cf = cf; 4084 | ccv.value = &value[1]; 4085 | ccv.complex_value = &slcf->host; 4086 | 4087 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 4088 | return NGX_CONF_ERROR; 4089 | } 4090 | 4091 | return NGX_CONF_OK; 4092 | } -------------------------------------------------------------------------------- /src/ngx_http_socks_module.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Danila Poyarkov 4 | * 5 | * Based on ngx_http_proxy_module.c 6 | * Copyright (C) Igor Sysoev 7 | * Copyright (C) Nginx, Inc. 8 | */ 9 | 10 | 11 | #ifndef _NGX_HTTP_SOCKS_MODULE_H_INCLUDED_ 12 | #define _NGX_HTTP_SOCKS_MODULE_H_INCLUDED_ 13 | 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | ngx_module_t ngx_http_socks_module; 23 | 24 | typedef struct { 25 | ngx_array_t caches; /* ngx_http_file_cache_t * */ 26 | } ngx_http_socks_main_conf_t; 27 | 28 | 29 | typedef struct ngx_http_socks_rewrite_s ngx_http_socks_rewrite_t; 30 | 31 | typedef ngx_int_t (*ngx_http_socks_rewrite_pt)(ngx_http_request_t *r, 32 | ngx_table_elt_t *h, size_t prefix, size_t len, 33 | ngx_http_socks_rewrite_t *pr); 34 | 35 | struct ngx_http_socks_rewrite_s { 36 | ngx_http_socks_rewrite_pt handler; 37 | 38 | union { 39 | ngx_http_complex_value_t complex; 40 | #if (NGX_PCRE) 41 | ngx_http_regex_t *regex; 42 | #endif 43 | } pattern; 44 | 45 | ngx_http_complex_value_t replacement; 46 | }; 47 | 48 | 49 | typedef struct { 50 | ngx_str_t schema; 51 | ngx_str_t host_header; 52 | ngx_str_t port; 53 | ngx_str_t uri; 54 | } ngx_http_socks_vars_t; 55 | 56 | 57 | typedef struct { 58 | ngx_array_t *flushes; 59 | ngx_array_t *lengths; 60 | ngx_array_t *values; 61 | ngx_hash_t hash; 62 | } ngx_http_socks_headers_t; 63 | 64 | 65 | typedef struct { 66 | ngx_http_upstream_conf_t upstream; 67 | 68 | ngx_array_t *body_flushes; 69 | ngx_array_t *body_lengths; 70 | ngx_array_t *body_values; 71 | ngx_str_t body_source; 72 | 73 | ngx_http_socks_headers_t headers; 74 | #if (NGX_HTTP_CACHE) 75 | ngx_http_socks_headers_t headers_cache; 76 | #endif 77 | ngx_array_t *headers_source; 78 | 79 | ngx_array_t *proxy_lengths; 80 | ngx_array_t *proxy_values; 81 | 82 | ngx_array_t *redirects; 83 | ngx_array_t *cookie_domains; 84 | ngx_array_t *cookie_paths; 85 | 86 | ngx_str_t method; 87 | ngx_str_t location; 88 | ngx_str_t url; 89 | 90 | ngx_http_complex_value_t host; 91 | 92 | #if (NGX_HTTP_CACHE) 93 | ngx_http_complex_value_t cache_key; 94 | #endif 95 | 96 | ngx_http_socks_vars_t vars; 97 | 98 | ngx_flag_t redirect; 99 | 100 | ngx_uint_t http_version; 101 | 102 | ngx_uint_t headers_hash_max_size; 103 | ngx_uint_t headers_hash_bucket_size; 104 | 105 | ngx_str_t tunnel_header; 106 | } ngx_http_socks_loc_conf_t; 107 | 108 | 109 | typedef struct { 110 | ngx_http_status_t status; 111 | ngx_http_chunked_t chunked; 112 | ngx_http_socks_vars_t vars; 113 | off_t internal_body_length; 114 | 115 | ngx_chain_t *free; 116 | ngx_chain_t *busy; 117 | 118 | ngx_str_t host; 119 | 120 | unsigned head:1; 121 | unsigned internal_chunked:1; 122 | unsigned header_sent:1; 123 | unsigned tunnel:1; 124 | 125 | enum { 126 | socks_auth = 0, 127 | socks_connect, 128 | socks_done 129 | } state; 130 | } ngx_http_socks_ctx_t; 131 | 132 | 133 | ngx_int_t ngx_http_socks_upstream_cookie_variable(ngx_http_request_t *r, 134 | ngx_http_variable_value_t *v, uintptr_t data); 135 | ngx_int_t ngx_http_socks_upstream_header_variable(ngx_http_request_t *r, 136 | ngx_http_variable_value_t *v, uintptr_t data); 137 | 138 | ngx_int_t ngx_http_socks_upstream_create(ngx_http_request_t *r); 139 | void ngx_http_socks_upstream_init(ngx_http_request_t *r); 140 | ngx_http_upstream_srv_conf_t *ngx_http_socks_upstream_add(ngx_conf_t *cf, 141 | ngx_url_t *u, ngx_uint_t flags); 142 | char *ngx_http_socks_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, 143 | void *conf); 144 | char *ngx_http_socks_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, 145 | void *conf); 146 | ngx_int_t ngx_http_socks_upstream_hide_headers_hash(ngx_conf_t *cf, 147 | ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev, 148 | ngx_str_t *default_hide_headers, ngx_hash_init_t *hash); 149 | 150 | 151 | #endif /* _NGX_HTTP_SOCKS_MODULE_H_INCLUDED_ */ 152 | --------------------------------------------------------------------------------