Apache HTTPD
mod_remoteip.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * Portions of the input filter code for PROXY protocol support is
17 * Copyright 2014 Cloudzilla Inc.
18 */
19
20#include "ap_config.h"
21#include "ap_mmn.h"
22#include "ap_listen.h"
23#include "httpd.h"
24#include "http_config.h"
25#include "http_connection.h"
26#include "http_protocol.h"
27#include "http_log.h"
28#include "http_main.h"
29#include "apr_strings.h"
30#include "apr_lib.h"
31#define APR_WANT_BYTEFUNC
32#include "apr_want.h"
33#include "apr_network_io.h"
34#include "apr_version.h"
35
36module AP_MODULE_DECLARE_DATA remoteip_module;
37
44
50
70
71typedef struct {
75 const char *proxy_ips;
77 const char *proxied_remote;
79
80/* For PROXY protocol processing */
82
83typedef struct {
84 char line[108];
85} proxy_v1;
86
87typedef union {
88 struct { /* for TCP/UDP over IPv4, len = 12 */
93 } ip4;
94 struct { /* for TCP/UDP over IPv6, len = 36 */
95 apr_byte_t src_addr[16];
96 apr_byte_t dst_addr[16];
97 apr_uint16_t src_port;
98 apr_uint16_t dst_port;
99 } ip6;
100 struct { /* for AF_UNIX sockets, len = 216 */
101 apr_byte_t src_addr[108];
102 apr_byte_t dst_addr[108];
103 } unx;
105
106typedef struct {
107 apr_byte_t sig[12]; /* hex 0D 0A 0D 0A 00 0D 0A 51 55 49 54 0A */
108 apr_byte_t ver_cmd; /* protocol version and command */
109 apr_byte_t fam; /* protocol family and address */
110 apr_uint16_t len; /* number of following bytes part of the header */
112} proxy_v2;
113
118
119static const char v2sig[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
120#define MIN_V1_HDR_LEN 15
121#define MIN_V2_HDR_LEN 16
122#define MIN_HDR_LEN MIN_V1_HDR_LEN
123
124/* XXX: Unsure if this is needed if v6 support is not available on
125 this platform */
126#ifndef INET6_ADDRSTRLEN
127#define INET6_ADDRSTRLEN 46
128#endif
129
139
149
151
153{
154 remoteip_config_t *config = apr_pcalloc(p, sizeof(*config));
155 config->disabled_subnets = apr_array_make(p, 1, sizeof(apr_ipsubnet_t *));
156 /* config->header_name = NULL;
157 * config->proxies_header_name = NULL;
158 * config->proxy_protocol_enabled = NULL;
159 * config->proxy_protocol_disabled = NULL;
160 */
161 config->pool = p;
162 return config;
163}
164
166 void *serverv)
167{
170 remoteip_config_t *config;
171
172 config = (remoteip_config_t *) apr_palloc(p, sizeof(*config));
173 config->header_name = server->header_name
174 ? server->header_name
175 : global->header_name;
176 config->proxies_header_name = server->proxies_header_name
177 ? server->proxies_header_name
178 : global->proxies_header_name;
179 config->proxymatch_ip = server->proxymatch_ip
180 ? server->proxymatch_ip
181 : global->proxymatch_ip;
182 return config;
183}
184
185static const char *header_name_set(cmd_parms *cmd, void *dummy,
186 const char *arg)
187{
188 remoteip_config_t *config = ap_get_module_config(cmd->server->module_config,
189 &remoteip_module);
190 config->header_name = arg;
191 return NULL;
192}
193
194static const char *proxies_header_name_set(cmd_parms *cmd, void *dummy,
195 const char *arg)
196{
197 remoteip_config_t *config = ap_get_module_config(cmd->server->module_config,
198 &remoteip_module);
199 config->proxies_header_name = arg;
200 return NULL;
201}
202
203/* Would be quite nice if APR exported this */
204/* apr:network_io/unix/sockaddr.c */
205static int looks_like_ip(const char *ipstr)
206{
207 if (ap_strchr_c(ipstr, ':')) {
208 /* definitely not a hostname; assume it is intended to be an IPv6 address */
209 return 1;
210 }
211
212 /* simple IPv4 address string check */
213 while ((*ipstr == '.') || apr_isdigit(*ipstr))
214 ipstr++;
215 return (*ipstr == '\0');
216}
217
218static const char *proxies_set(cmd_parms *cmd, void *cfg,
219 const char *arg)
220{
221 remoteip_config_t *config = ap_get_module_config(cmd->server->module_config,
222 &remoteip_module);
224 apr_status_t rv;
225 char *ip = apr_pstrdup(cmd->temp_pool, arg);
226 char *s = ap_strchr(ip, '/');
227 if (s) {
228 *s++ = '\0';
229 }
230
231 if (!config->proxymatch_ip) {
232 config->proxymatch_ip = apr_array_make(cmd->pool, 1, sizeof(*match));
233 }
235 match->internal = cmd->info;
236
237 if (looks_like_ip(ip)) {
238 /* Note s may be null, that's fine (explicit host) */
239 rv = apr_ipsubnet_create(&match->ip, ip, s, cmd->pool);
240 }
241 else
242 {
244
245 if (s) {
246 return apr_pstrcat(cmd->pool, "RemoteIP: Error parsing IP ", arg,
247 " the subnet /", s, " is invalid for ",
248 cmd->cmd->name, NULL);
249 }
250
252 APR_IPV4_ADDR_OK, cmd->temp_pool);
253 while (rv == APR_SUCCESS)
254 {
256 rv = apr_ipsubnet_create(&match->ip, ip, NULL, cmd->pool);
257 if (!(temp_sa = temp_sa->next)) {
258 break;
259 }
262 match->internal = cmd->info;
263 }
264 }
265
266 if (rv != APR_SUCCESS) {
267 return apr_psprintf(cmd->pool,
268 "RemoteIP: Error parsing IP %s (%pm error) for %s",
269 arg, &rv, cmd->cmd->name);
270 }
271
272 return NULL;
273}
274
275static const char *proxylist_read(cmd_parms *cmd, void *cfg,
276 const char *filename)
277{
278 char lbuf[MAX_STRING_LEN];
279 char *arg;
280 const char *args;
281 const char *errmsg;
282 ap_configfile_t *cfp;
283 apr_status_t rv;
284
286 rv = ap_pcfg_openfile(&cfp, cmd->temp_pool, filename);
287 if (rv != APR_SUCCESS) {
288 return apr_psprintf(cmd->pool, "%s: Could not open file %s: %pm",
289 cmd->cmd->name, filename, &rv);
290 }
291
292 while (!(ap_cfg_getline(lbuf, MAX_STRING_LEN, cfp))) {
293 args = lbuf;
294 while (*(arg = ap_getword_conf(cmd->temp_pool, &args)) != '\0') {
295 if (*arg == '#') {
296 break;
297 }
298 errmsg = proxies_set(cmd, cfg, arg);
299 if (errmsg) {
300 ap_cfg_closefile(cfp);
301 errmsg = apr_psprintf(cmd->pool, "%s at line %d of %s",
302 errmsg, cfp->line_number, filename);
303 return errmsg;
304 }
305 }
306 }
307
308 ap_cfg_closefile(cfp);
309 return NULL;
310}
311
317
318#if !APR_VERSION_AT_LEAST(1,5,0)
319#define apr_sockaddr_is_wildcard sockaddr_is_wildcard
320/* XXX: temp build fix from apr 1.5.x */
322{
323 static const char inaddr_any[
324#if APR_HAVE_IPV6
325 sizeof(struct in6_addr)
326#else
328#endif
329 ] = {0};
330
331 if (addr->ipaddr_ptr /* IP address initialized */
332 && addr->ipaddr_len <= sizeof inaddr_any) { /* else bug elsewhere? */
334 return 1;
335 }
336#if APR_HAVE_IPV6
337 if (addr->family == AF_INET6
339 struct in_addr *v4 = (struct in_addr *)&((apr_uint32_t *)addr->ipaddr_ptr)[3];
340
341 if (!memcmp(inaddr_any, v4, sizeof *v4)) {
342 return 1;
343 }
344 }
345#endif
346 }
347 return 0;
348}
349#endif
350
351
356{
357 /* test exact address equality */
359 (addr1->port == addr2->port || addr1->port == 0 || addr2->port == 0)) {
360 return 1;
361 }
362
363 /* test address wildcards */
365 (addr1->port == 0 || addr1->port == addr2->port)) {
366 return 1;
367 }
368
370 (addr2->port == 0 || addr2->port == addr1->port)) {
371 return 1;
372 }
373
374 return 0;
375}
376
378{
379 for (; list; list = list->next) {
380 if (remoteip_sockaddr_compat(list->addr, addr)) {
381 return 1;
382 }
383 }
384
385 return 0;
386}
387
389{
390 char buf[INET6_ADDRSTRLEN];
391
392 apr_sockaddr_ip_getbuf(buf, sizeof(buf), prev->addr);
393
395 "RemoteIPProxyProtocol: previous setting for %s:%hu from virtual "
396 "host {%s:%hu in %s} is being overridden by virtual host "
397 "{%s:%hu in %s}; new setting is '%s'",
398 buf, prev->addr->port, prev->source->server_hostname,
399 prev->source->addrs->host_port, prev->source->defn_name,
400 new->server_hostname, new->addrs->host_port, new->defn_name,
401 flag ? "On" : "Off");
402}
403
404static const char *remoteip_enable_proxy_protocol(cmd_parms *cmd, void *config,
405 int flag)
406{
407 remoteip_config_t *conf;
409 remoteip_addr_info **add;
412
414 &remoteip_module);
415
416 if (flag) {
417 add = &conf->proxy_protocol_enabled;
419 }
420 else {
421 add = &conf->proxy_protocol_disabled;
423 }
424
425 for (addr = cmd->server->addrs; addr; addr = addr->next) {
426 /* remove address from opposite list */
427 if (*rem) {
428 if (remoteip_sockaddr_equal((*rem)->addr, addr->host_addr)) {
430 *rem = (*rem)->next;
431 }
432 else {
433 for (list = *rem; list->next; list = list->next) {
434 if (remoteip_sockaddr_equal(list->next->addr, addr->host_addr)) {
436 list->next = list->next->next;
437 break;
438 }
439 }
440 }
441 }
442
443 /* add address to desired list */
444 if (!remoteip_addr_in_list(*add, addr->host_addr)) {
445 remoteip_addr_info *info = apr_palloc(conf->pool, sizeof(*info));
446 info->addr = addr->host_addr;
447 info->source = cmd->server;
448 info->next = *add;
449 *add = info;
450 }
451 }
452
453 return NULL;
454}
455
456static const char *remoteip_disable_networks(cmd_parms *cmd, void *d,
457 int argc, char *const argv[])
458{
459 int i;
460 apr_pool_t *ptemp = cmd->temp_pool;
461 apr_pool_t *p = cmd->pool;
463 &remoteip_module);
464
465 if (argc == 0)
466 return apr_pstrcat(p, cmd->cmd->name, " requires an argument", NULL);
467
468
469 for (i=0; i<argc; i++) {
470 char *addr = apr_pstrdup(ptemp, argv[i]);
471 char *mask;
472 apr_status_t rv;
473 apr_ipsubnet_t **ip = apr_pcalloc(p, sizeof(apr_ipsubnet_t *));
474
475 if ((mask = ap_strchr(addr, '/')))
476 *mask++ = '\0';
477
478 rv = apr_ipsubnet_create(ip, addr, mask, p);
479
480 if (APR_STATUS_IS_EINVAL(rv)) {
481 /* looked nothing like an IP address */
482 return apr_psprintf(p, "ip address '%s' appears to be invalid", addr);
483 }
484 else if (rv != APR_SUCCESS) {
485 return apr_psprintf(p, "ip address '%s' appears to be invalid: %pm",
486 addr, &rv);
487 }
488
490 }
491
492 return NULL;
493}
494
496 apr_pool_t *ptemp, server_rec *s)
497{
498 remoteip_config_t *conf;
500 char buf[INET6_ADDRSTRLEN];
501
503 &remoteip_module);
504
505 for (info = conf->proxy_protocol_enabled; info; info = info->next) {
506 apr_sockaddr_ip_getbuf(buf, sizeof(buf), info->addr);
508 "RemoteIPProxyProtocol: enabled on %s:%hu", buf, info->addr->port);
509 }
510 for (info = conf->proxy_protocol_disabled; info; info = info->next) {
511 apr_sockaddr_ip_getbuf(buf, sizeof(buf), info->addr);
513 "RemoteIPProxyProtocol: disabled on %s:%hu", buf, info->addr->port);
514 }
515
516 return OK;
517}
518
520{
523 ap_get_module_config(r->server->module_config, &remoteip_module);
525 ap_get_module_config(r->connection->conn_config, &remoteip_module);
526
527 remoteip_req_t *req = NULL;
529
530 apr_status_t rv;
531 char *remote;
532 char *proxy_ips = NULL;
533 char *parse_remote;
534 char *eos;
535 unsigned char *addrbyte;
536
537 /* If no RemoteIPInternalProxy, RemoteIPInternalProxyList, RemoteIPTrustedProxy
538 or RemoteIPTrustedProxyList directive is configured,
539 all proxies will be considered as external trusted proxies.
540 */
541 void *internal = NULL;
542
543 /* No header defined or results from our input filter */
544 if (!config->header_name && !conn_config) {
545 return DECLINED;
546 }
547
548 /* Easy parsing case - just position the data we already have from PROXY
549 protocol handling allowing it to take precedence and return
550 */
551 if (conn_config) {
552 if (!conn_config->client_addr) {
554 "RemoteIPProxyProtocol data is missing, but required! Aborting request.");
555 return HTTP_BAD_REQUEST;
556 }
557
558 r->useragent_addr = conn_config->client_addr;
559 r->useragent_ip = conn_config->client_ip;
560
562 "Using %s as client's IP from PROXY protocol", r->useragent_ip);
563 return OK;
564 }
565
566 if (config->proxymatch_ip) {
567 /* This indicates that a RemoteIPInternalProxy, RemoteIPInternalProxyList, RemoteIPTrustedProxy
568 or RemoteIPTrustedProxyList directive is configured.
569 In this case, default to internal proxy.
570 */
571 internal = (void *) 1;
572 }
573
574 remote = (char *) apr_table_get(r->headers_in, config->header_name);
575 if (!remote) {
576 return OK;
577 }
578 remote = apr_pstrdup(r->pool, remote);
579
580 temp_sa = r->useragent_addr ? r->useragent_addr : c->client_addr;
581
582 while (remote) {
583
584 /* verify user agent IP against the trusted proxy list
585 */
586 if (config->proxymatch_ip) {
587 int i;
590 for (i = 0; i < config->proxymatch_ip->nelts; ++i) {
591 if (apr_ipsubnet_test(match[i].ip, temp_sa)) {
592 if (internal) {
593 /* Allow an internal proxy to present an external proxy,
594 but do not allow an external proxy to present an internal proxy.
595 In this case, the presented internal proxy will be considered external.
596 */
597 internal = match[i].internal;
598 }
599 break;
600 }
601 }
602 if (i && i >= config->proxymatch_ip->nelts) {
603 break;
604 }
605 }
606
607 if ((parse_remote = strrchr(remote, ',')) == NULL) {
608 parse_remote = remote;
609 remote = NULL;
610 }
611 else {
612 *(parse_remote++) = '\0';
613 }
614
615 while (*parse_remote == ' ') {
616 ++parse_remote;
617 }
618
619 eos = parse_remote + strlen(parse_remote) - 1;
620 while (eos >= parse_remote && *eos == ' ') {
621 *(eos--) = '\0';
622 }
623
624 if (eos < parse_remote) {
625 if (remote) {
626 *(remote + strlen(remote)) = ',';
627 }
628 else {
629 remote = parse_remote;
630 }
631 break;
632 }
633
634 /* We map as IPv4 rather than IPv6 for equivalent host names
635 * or IPV4OVERIPV6
636 */
638 APR_UNSPEC, temp_sa->port,
640 if (rv != APR_SUCCESS) {
642 "RemoteIP: Header %s value of %s cannot be parsed "
643 "as a client IP",
644 config->header_name, parse_remote);
645
646 if (remote) {
647 *(remote + strlen(remote)) = ',';
648 }
649 else {
650 remote = parse_remote;
651 }
652 break;
653 }
654
655 addrbyte = (unsigned char *) &temp_sa->sa.sin.sin_addr;
656
657 /* For intranet (Internal proxies) ignore all restrictions below */
658 if (!internal
659 && ((temp_sa->family == APR_INET
660 /* For internet (non-Internal proxies) deny all
661 * RFC3330 designated local/private subnets:
662 * 10.0.0.0/8 169.254.0.0/16 192.168.0.0/16
663 * 127.0.0.0/8 172.16.0.0/12
664 */
665 && (addrbyte[0] == 10
666 || addrbyte[0] == 127
667 || (addrbyte[0] == 169 && addrbyte[1] == 254)
668 || (addrbyte[0] == 172 && (addrbyte[1] & 0xf0) == 16)
669 || (addrbyte[0] == 192 && addrbyte[1] == 168)))
671 || (temp_sa->family == APR_INET6
672 /* For internet (non-Internal proxies) we translated
673 * IPv4-over-IPv6-mapped addresses as IPv4, above.
674 * Accept only Global Unicast 2000::/3 defined by RFC4291
675 */
676 && ((temp_sa->sa.sin6.sin6_addr.s6_addr[0] & 0xe0) != 0x20))
677#endif
678 )) {
680 "RemoteIP: Header %s value of %s appears to be "
681 "a private IP or nonsensical. Ignored",
682 config->header_name, parse_remote);
683 if (remote) {
684 *(remote + strlen(remote)) = ',';
685 }
686 else {
687 remote = parse_remote;
688 }
689
690 break;
691 }
692
693 /* save away our results */
694 if (!req) {
695 req = (remoteip_req_t *) apr_palloc(r->pool, sizeof(remoteip_req_t));
697 }
698
699 /* Set useragent_ip string */
700 if (!internal) {
701 if (proxy_ips) {
702 proxy_ips = apr_pstrcat(r->pool, proxy_ips, ", ",
703 req->useragent_ip, NULL);
704 }
705 else {
706 proxy_ips = req->useragent_ip;
707 }
708 }
709
710 req->useragent_addr = temp_sa;
712 }
713
714 /* Nothing happened? */
715 if (!req) {
716 return OK;
717 }
718
719 /* Port is not known so set it to zero; otherwise it can be misleading */
720 req->useragent_addr->port = 0;
721
722 req->proxied_remote = remote;
723 req->proxy_ips = proxy_ips;
724
725 if (req->proxied_remote) {
727 req->proxied_remote);
728 }
729 else {
731 }
732 if (req->proxy_ips) {
733 apr_table_setn(r->notes, "remoteip-proxy-ip-list", req->proxy_ips);
734 if (config->proxies_header_name) {
736 req->proxy_ips);
737 }
738 }
739
742
744 req->proxy_ips
745 ? "Using %s as client's IP by proxies %s"
746 : "Using %s as client's IP by internal proxies%s",
747 req->useragent_ip,
748 (req->proxy_ips ? req->proxy_ips : ""));
749 return OK;
750}
751
753{
755
756 for (lr = ap_listeners; lr; lr = lr->next) {
757 if (lr->bind_addr && lr->bind_addr->port == port) {
758 return 1;
759 }
760 }
761
762 return 0;
763}
764
765/*
766 * Human readable format:
767 * PROXY {TCP4|TCP6|UNKNOWN} <client-ip-addr> <dest-ip-addr> <client-port> <dest-port><CR><LF>
768 */
773{
774 char *end, *word, *host, *valid_addr_chars, *saveptr;
775 char buf[sizeof(hdr->v1.line)];
779
780#define GET_NEXT_WORD(field) \
781 word = apr_strtok(NULL, " ", &saveptr); \
782 if (!word) { \
783 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(03497) \
784 "RemoteIPProxyProtocol: no " field " found in header '%s'", \
785 hdr->v1.line); \
786 return HDR_ERROR; \
787 }
788
789 end = memchr(hdr->v1.line, '\r', len - 1);
790 if (!end || end[1] != '\n') {
791 return HDR_NEED_MORE; /* partial or invalid header */
792 }
793
794 *end = '\0';
795 *hdr_len = end + 2 - hdr->v1.line; /* skip header + CRLF */
796
797 /* parse in separate buffer so have the original for error messages */
798 strcpy(buf, hdr->v1.line);
799
800 apr_strtok(buf, " ", &saveptr);
801
802 /* parse family */
803 GET_NEXT_WORD("family")
804 if (strcmp(word, "UNKNOWN") == 0) {
805 conn_conf->client_addr = c->client_addr;
806 conn_conf->client_ip = c->client_ip;
807 return HDR_DONE;
808 }
809 else if (strcmp(word, "TCP4") == 0) {
811 valid_addr_chars = "0123456789.";
812 }
813 else if (strcmp(word, "TCP6") == 0) {
814#if APR_HAVE_IPV6
816 valid_addr_chars = "0123456789abcdefABCDEF:";
817#else
819 "RemoteIPProxyProtocol: Unable to parse v6 address - APR is not compiled with IPv6 support");
820 return HDR_ERROR;
821#endif
822 }
823 else {
825 "RemoteIPProxyProtocol: unknown family '%s' in header '%s'",
826 word, hdr->v1.line);
827 return HDR_ERROR;
828 }
829
830 /* parse client-addr */
831 GET_NEXT_WORD("client-address")
832
833 if (strspn(word, valid_addr_chars) != strlen(word)) {
835 "RemoteIPProxyProtocol: invalid client-address '%s' found in "
836 "header '%s'", word, hdr->v1.line);
837 return HDR_ERROR;
838 }
839
840 host = word;
841
842 /* parse dest-addr */
843 GET_NEXT_WORD("destination-address")
844
845 /* parse client-port */
846 GET_NEXT_WORD("client-port")
847 if (sscanf(word, "%hu", &port) != 1) {
849 "RemoteIPProxyProtocol: error parsing port '%s' in header '%s'",
850 word, hdr->v1.line);
851 return HDR_ERROR;
852 }
853
854 /* parse dest-port */
855 /* GET_NEXT_WORD("destination-port") - no-op since we don't care about it */
856
857 /* create a socketaddr from the info */
858 ret = apr_sockaddr_info_get(&conn_conf->client_addr, host, family, port, 0,
859 c->pool);
860 if (ret != APR_SUCCESS) {
861 conn_conf->client_addr = NULL;
863 "RemoteIPProxyProtocol: error converting family '%d', host '%s',"
864 " and port '%hu' to sockaddr; header was '%s'",
865 family, host, port, hdr->v1.line);
866 return HDR_ERROR;
867 }
868
869 conn_conf->client_ip = apr_pstrdup(c->pool, host);
870
871 return HDR_DONE;
872}
873
877{
878 remoteip_config_t *conf;
880 int i;
881
882 /* Establish master config in slave connections, so that request processing
883 * finds it. */
884 if (c->master != NULL) {
885 conn_conf = ap_get_module_config(c->master->conn_config, &remoteip_module);
886 if (conn_conf) {
887 ap_set_module_config(c->conn_config, &remoteip_module, conn_conf);
888 }
889 return DECLINED;
890 }
891
893 &remoteip_module);
894
895 /* check if we're enabled for this connection */
896 if (!remoteip_addr_in_list(conf->proxy_protocol_enabled, c->local_addr)
897 || remoteip_addr_in_list(conf->proxy_protocol_disabled, c->local_addr)) {
898
899 return DECLINED;
900 }
901
902 /* We are enabled for this IP/port, but check that we aren't
903 explicitly disabled */
904 for (i = 0; i < conf->disabled_subnets->nelts; i++) {
906
907 if (ip && apr_ipsubnet_test(ip, c->client_addr))
908 return DECLINED;
909 }
910
911 /* mod_proxy creates outgoing connections - we don't want those */
912 if (!remoteip_is_server_port(c->local_addr->port)) {
913 return DECLINED;
914 }
915
916 /* add our filter */
918 /* XXX: Shouldn't this WARN in log? */
919 return DECLINED;
920 }
921
923 "RemoteIPProxyProtocol: enabled on connection to %s:%hu",
924 c->local_ip, c->local_addr->port);
925
926 /* this holds the resolved proxy info for this connection */
927 conn_conf = apr_pcalloc(c->pool, sizeof(*conn_conf));
928
929 ap_set_module_config(c->conn_config, &remoteip_module, conn_conf);
930
931 return OK;
932}
933
934/* Binary format:
935 * <sig><cmd><proto><addr-len><addr>
936 * sig = \x0D \x0A \x0D \x0A \x00 \x0D \x0A \x51 \x55 \x49 \x54 \x0A
937 * cmd = <4-bits-version><4-bits-command>
938 * 4-bits-version = \x02
939 * 4-bits-command = {\x00|\x01} (\x00 = LOCAL: discard con info; \x01 = PROXY)
940 * proto = <4-bits-family><4-bits-protocol>
941 * 4-bits-family = {\x00|\x01|\x02|\x03} (AF_UNSPEC, AF_INET, AF_INET6, AF_UNIX)
942 * 4-bits-protocol = {\x00|\x01|\x02} (UNSPEC, STREAM, DGRAM)
943 */
947{
949
950 switch (hdr->v2.ver_cmd & 0xF) {
951 case 0x01: /* PROXY command */
952 switch (hdr->v2.fam) {
953 case 0x11: /* TCPv4 */
954 ret = apr_sockaddr_info_get(&conn_conf->client_addr, NULL,
955 APR_INET,
956 ntohs(hdr->v2.addr.ip4.src_port),
957 0, c->pool);
958 if (ret != APR_SUCCESS) {
959 conn_conf->client_addr = NULL;
961 "RemoteIPPProxyProtocol: error creating sockaddr");
962 return HDR_ERROR;
963 }
964
965 conn_conf->client_addr->sa.sin.sin_addr.s_addr =
966 hdr->v2.addr.ip4.src_addr;
967 break;
968
969 case 0x21: /* TCPv6 */
970#if APR_HAVE_IPV6
971 ret = apr_sockaddr_info_get(&conn_conf->client_addr, NULL,
972 APR_INET6,
973 ntohs(hdr->v2.addr.ip6.src_port),
974 0, c->pool);
975 if (ret != APR_SUCCESS) {
976 conn_conf->client_addr = NULL;
978 "RemoteIPProxyProtocol: error creating sockaddr");
979 return HDR_ERROR;
980 }
981 memcpy(&conn_conf->client_addr->sa.sin6.sin6_addr.s6_addr,
982 hdr->v2.addr.ip6.src_addr, 16);
983 break;
984#else
986 "RemoteIPProxyProtocol: APR is not compiled with IPv6 support");
987 return HDR_ERROR;
988#endif
989 default:
990 /* unsupported protocol */
992 "RemoteIPProxyProtocol: unsupported protocol %.2hx",
993 (unsigned short)hdr->v2.fam);
994 return HDR_ERROR;
995 }
996 break; /* we got a sockaddr now */
997 default:
998 /* not a supported command */
1000 "RemoteIPProxyProtocol: unsupported command %.2hx",
1001 (unsigned short)hdr->v2.ver_cmd);
1002 return HDR_ERROR;
1003 }
1004
1005 /* got address - compute the client_ip from it */
1006 ret = apr_sockaddr_ip_get(&conn_conf->client_ip, conn_conf->client_addr);
1007 if (ret != APR_SUCCESS) {
1008 conn_conf->client_addr = NULL;
1010 "RemoteIPProxyProtocol: error converting address to string");
1011 return HDR_ERROR;
1012 }
1013
1014 return HDR_DONE;
1015}
1016
1019{
1020 return ntohs(hdr->v2.len);
1021}
1022
1025static int remoteip_determine_version(conn_rec *c, const char *ptr)
1026{
1027 proxy_header *hdr = (proxy_header *) ptr;
1028
1029 /* assert len >= 14 */
1030
1031 if (memcmp(&hdr->v2, v2sig, sizeof(v2sig)) == 0 &&
1032 (hdr->v2.ver_cmd & 0xF0) == 0x20) {
1033 return 2;
1034 }
1035 else if (memcmp(hdr->v1.line, "PROXY ", 6) == 0) {
1036 return 1;
1037 }
1038 else {
1039 return -1;
1040 }
1041}
1042
1043/* Capture the first bytes on the protocol and parse the PROXY protocol header.
1044 * Removes itself when the header is complete.
1045 */
1051{
1055 apr_bucket *b;
1057 const char *ptr;
1059
1060 if (f->c->aborted) {
1061 return APR_ECONNABORTED;
1062 }
1063
1064 /* allocate/retrieve the context that holds our header */
1065 if (!ctx) {
1066 ctx = f->ctx = apr_palloc(f->c->pool, sizeof(*ctx));
1067 ctx->rcvd = 0;
1068 ctx->need = MIN_HDR_LEN;
1069 ctx->version = 0;
1070 ctx->mode = AP_MODE_READBYTES;
1071 ctx->bb = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
1072 ctx->done = 0;
1073 }
1074
1075 if (ctx->done) {
1076 /* Note: because we're a connection filter we can't remove ourselves
1077 * when we're done, so we have to stay in the chain and just go into
1078 * passthrough mode.
1079 */
1080 return ap_get_brigade(f->next, bb_out, mode, block, readbytes);
1081 }
1082
1083 conn_conf = ap_get_module_config(f->c->conn_config, &remoteip_module);
1084
1085 /* try to read a header's worth of data */
1086 while (!ctx->done) {
1087 if (APR_BRIGADE_EMPTY(ctx->bb)) {
1088 apr_off_t got, want = ctx->need - ctx->rcvd;
1089
1090 ret = ap_get_brigade(f->next, ctx->bb, ctx->mode, block, want);
1091 if (ret != APR_SUCCESS) {
1093 "failed reading input");
1094 return ret;
1095 }
1096
1097 ret = apr_brigade_length(ctx->bb, 1, &got);
1098 if (ret || got > want) {
1100 "RemoteIPProxyProtocol header too long, "
1101 "got %" APR_OFF_T_FMT " expected %" APR_OFF_T_FMT,
1102 got, want);
1103 f->c->aborted = 1;
1104 return APR_ECONNABORTED;
1105 }
1106 }
1107 if (APR_BRIGADE_EMPTY(ctx->bb)) {
1109 }
1110
1111 while (!ctx->done && !APR_BRIGADE_EMPTY(ctx->bb)) {
1112 b = APR_BRIGADE_FIRST(ctx->bb);
1113
1114 ret = apr_bucket_read(b, &ptr, &len, block);
1116 return APR_SUCCESS;
1117 }
1118 if (ret != APR_SUCCESS) {
1119 return ret;
1120 }
1121
1122 memcpy(ctx->header + ctx->rcvd, ptr, len);
1123 ctx->rcvd += len;
1124
1127
1128 if (ctx->version == 0) {
1129 /* reading initial chunk */
1130 if (ctx->rcvd >= MIN_HDR_LEN) {
1131 ctx->version = remoteip_determine_version(f->c, ctx->header);
1132 if (ctx->version < 0) {
1133 psts = HDR_ERROR;
1134 }
1135 else if (ctx->version == 1) {
1136 ctx->mode = AP_MODE_GETLINE;
1137 ctx->need = sizeof(proxy_v1);
1138 }
1139 else if (ctx->version == 2) {
1140 ctx->need = MIN_V2_HDR_LEN;
1141 }
1142 }
1143 }
1144 else if (ctx->version == 1) {
1146 (proxy_header *) ctx->header,
1147 ctx->rcvd, &ctx->need);
1148 }
1149 else if (ctx->version == 2) {
1150 if (ctx->rcvd >= MIN_V2_HDR_LEN) {
1151 ctx->need = MIN_V2_HDR_LEN +
1153 if (ctx->need > sizeof(proxy_v2)) {
1155 "RemoteIPProxyProtocol protocol header length too long");
1156 f->c->aborted = 1;
1158 return APR_ECONNABORTED;
1159 }
1160 }
1161 if (ctx->rcvd >= ctx->need) {
1163 (proxy_header *) ctx->header);
1164 }
1165 }
1166 else {
1168 "RemoteIPProxyProtocol: internal error: unknown version "
1169 "%d", ctx->version);
1170 f->c->aborted = 1;
1172 return APR_ECONNABORTED;
1173 }
1174
1175 switch (psts) {
1176 case HDR_ERROR:
1177 f->c->aborted = 1;
1179 return APR_ECONNABORTED;
1180
1181 case HDR_DONE:
1182 ctx->done = 1;
1183 break;
1184
1185 case HDR_NEED_MORE:
1186 break;
1187 }
1188 }
1189 }
1190
1191 /* we only get here when done == 1 */
1193 "RemoteIPProxyProtocol: received valid PROXY header: %s:%hu",
1194 conn_conf->client_ip, conn_conf->client_addr->port);
1195
1196 if (ctx->rcvd > ctx->need || !APR_BRIGADE_EMPTY(ctx->bb)) {
1198 "RemoteIPProxyProtocol: internal error: have data left over; "
1199 " need=%" APR_SIZE_T_FMT ", rcvd=%" APR_SIZE_T_FMT
1200 ", brigade-empty=%d", ctx->need, ctx->rcvd,
1201 APR_BRIGADE_EMPTY(ctx->bb));
1202 f->c->aborted = 1;
1204 return APR_ECONNABORTED;
1205 }
1206
1207 /* clean up */
1209 ctx->bb = NULL;
1210
1211 /* now do the real read for the upper layer */
1212 return ap_get_brigade(f->next, bb_out, mode, block, readbytes);
1213}
1214
1216{
1217 AP_INIT_TAKE1("RemoteIPHeader", header_name_set, NULL, RSRC_CONF,
1218 "Specifies a request header to trust as the client IP, "
1219 "e.g. X-Forwarded-For"),
1220 AP_INIT_TAKE1("RemoteIPProxiesHeader", proxies_header_name_set,
1221 NULL, RSRC_CONF,
1222 "Specifies a request header to record proxy IP's, "
1223 "e.g. X-Forwarded-By; if not given then do not record"),
1224 AP_INIT_ITERATE("RemoteIPTrustedProxy", proxies_set, 0, RSRC_CONF,
1225 "Specifies one or more proxies which are trusted "
1226 "to present IP headers"),
1227 AP_INIT_ITERATE("RemoteIPInternalProxy", proxies_set, (void*)1, RSRC_CONF,
1228 "Specifies one or more internal (transparent) proxies "
1229 "which are trusted to present IP headers"),
1230 AP_INIT_TAKE1("RemoteIPTrustedProxyList", proxylist_read, 0,
1232 "The filename to read the list of trusted proxies, "
1233 "see the RemoteIPTrustedProxy directive"),
1234 AP_INIT_TAKE1("RemoteIPInternalProxyList", proxylist_read, (void*)1,
1236 "The filename to read the list of internal proxies, "
1237 "see the RemoteIPInternalProxy directive"),
1238 AP_INIT_FLAG("RemoteIPProxyProtocol", remoteip_enable_proxy_protocol, NULL,
1239 RSRC_CONF, "Enable PROXY protocol handling ('on', 'off')"),
1240 AP_INIT_TAKE_ARGV("RemoteIPProxyProtocolExceptions",
1241 remoteip_disable_networks, NULL, RSRC_CONF, "Disable PROXY "
1242 "protocol handling for this list of networks in CIDR format"),
1243 { NULL }
1244};
1245
1247{
1248 /* mod_ssl is CONNECTION + 5, so we want something higher (earlier);
1249 * mod_reqtimeout is CONNECTION + 8, so we want something lower (later) */
1253
1257}
1258
1261 NULL, /* create per-directory config structure */
1262 NULL, /* merge per-directory config structures */
1263 create_remoteip_server_config, /* create per-server config structure */
1264 merge_remoteip_server_config, /* merge per-server config structures */
1265 remoteip_cmds, /* command apr_table_t */
1266 register_hooks /* register hooks */
1267};
Symbol export macros and hook functions.
Apache Listeners Library.
Module Magic Number.
int int const char ** match
Definition ap_regex.h:279
const char apr_size_t len
Definition ap_regex.h:187
APR general purpose library routines.
APR Network library.
apr_size_t const unsigned char unsigned int unsigned int d
Definition apr_siphash.h:72
APR Strings library.
APR Versioning Interface.
APR Standard Headers Support.
void ap_hook_pre_connection(ap_HOOK_pre_connection_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition connection.c:43
static apr_pool_t * pconf
Definition event.c:441
#define AP_INIT_TAKE1(directive, func, mconfig, where, help)
#define ap_get_module_config(v, m)
#define AP_INIT_TAKE_ARGV(directive, func, mconfig, where, help)
int ap_cfg_closefile(ap_configfile_t *cfp)
Definition util.c:931
void ap_hook_post_config(ap_HOOK_post_config_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:105
apr_status_t ap_pcfg_openfile(ap_configfile_t **ret_cfg, apr_pool_t *p, const char *name)
Definition util.c:957
#define AP_DECLARE_MODULE(foo)
#define AP_INIT_FLAG(directive, func, mconfig, where, help)
#define AP_INIT_ITERATE(directive, func, mconfig, where, help)
char * ap_server_root_relative(apr_pool_t *p, const char *fname)
Definition config.c:1594
#define ap_set_module_config(v, m, val)
request_rec * r
apr_status_t ap_cfg_getline(char *buf, apr_size_t bufsize, ap_configfile_t *cfp)
Definition util.c:1198
void * csd
#define MAX_STRING_LEN
Definition httpd.h:300
#define DECLINED
Definition httpd.h:457
#define OK
Definition httpd.h:456
ap_filter_t * ap_add_input_filter_handle(ap_filter_rec_t *f, void *ctx, request_rec *r, conn_rec *c)
apr_status_t ap_filter_rec_t * ap_register_input_filter(const char *name, ap_in_filter_func filter_func, ap_init_filter_func filter_init, ap_filter_type ftype)
apr_status_t ap_get_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
@ AP_FTYPE_CONNECTION
ap_listen_rec * ap_listeners
Definition listen.c:42
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_NOTICE
Definition http_log.h:69
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define ap_log_error
Definition http_log.h:370
#define ap_log_cerror
Definition http_log.h:498
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_WARNING
Definition http_log.h:68
#define APLOG_TRACE1
Definition http_log.h:72
#define APLOG_DEBUG
Definition http_log.h:71
server_rec * ap_server_conf
Definition config.c:62
const unsigned char * buf
Definition util_md5.h:50
void ap_hook_post_read_request(ap_HOOK_post_read_request_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition protocol.c:2585
const char apr_port_t port
Definition http_vhost.h:125
void * dummy
Definition http_vhost.h:62
const char * host
Definition http_vhost.h:124
void const char * arg
Definition http_vhost.h:63
#define APR_EOF
Definition apr_errno.h:461
#define APR_ECONNABORTED
Definition apr_errno.h:769
#define APR_STATUS_IS_EINVAL(s)
Definition apr_errno.h:1266
#define APR_STATUS_IS_EAGAIN(s)
Definition apr_errno.h:1272
apr_file_t * f
apr_read_type_e
Definition apr_buckets.h:57
#define APR_BRIGADE_EMPTY(b)
#define apr_bucket_delete(e)
apr_brigade_flush void * ctx
#define APR_BRIGADE_FIRST(b)
#define apr_bucket_read(e, str, len, block)
@ APR_NONBLOCK_READ
Definition apr_buckets.h:59
apr_dbd_transaction_t int mode
Definition apr_dbd.h:261
const char * mask
Definition apr_date.h:60
#define APR_HOOK_FIRST
Definition apr_hooks.h:301
#define APR_HOOK_MIDDLE
Definition apr_hooks.h:303
apr_memcache_server_t * server
apr_text_header * hdr
Definition apr_xml.h:77
#define RSRC_CONF
#define EXEC_ON_READ
#define HTTP_BAD_REQUEST
Definition httpd.h:508
#define STANDARD20_MODULE_STUFF
#define ap_strchr(s, c)
Definition httpd.h:2351
#define ap_strchr_c(s, c)
Definition httpd.h:2353
char * ap_getword_conf(apr_pool_t *p, const char **line)
Definition util.c:833
apr_size_t size
const apr_array_header_t * list
Definition apr_cstr.h:105
#define apr_isdigit(c)
Definition apr_lib.h:209
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
const char apr_int32_t flag
void * memchr(const void *s, int c, size_t n)
apr_pool_t int argc
Definition apr_getopt.h:104
apr_vformatter_buff_t * c
Definition apr_lib.h:175
apr_sockaddr_t * addr
const char * ipstr
int family
const apr_sockaddr_t * addr2
#define APR_IPV4_ADDR_OK
apr_uint16_t apr_port_t
#define APR_UNSPEC
#define APR_INET
apr_pool_t * b
Definition apr_pools.h:529
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
apr_size_t const char * filename
Definition apr_shm.h:72
const char char ** end
const char * s
Definition apr_strings.h:95
int sig
apr_cmdtype_e cmd
const char const char *const * args
Apache Configuration.
Apache connection library.
Apache Logging library.
Command line options.
HTTP protocol handling.
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
const char * argv[3]
static ap_filter_rec_t * remoteip_filter
static apr_size_t remoteip_get_v2_len(proxy_header *hdr)
static const char v2sig[12]
static int remoteip_determine_version(conn_rec *c, const char *ptr)
static void * merge_remoteip_server_config(apr_pool_t *p, void *globalv, void *serverv)
static int remoteip_modify_request(request_rec *r)
static const char * remoteip_disable_networks(cmd_parms *cmd, void *d, int argc, char *const argv[])
static remoteip_parse_status_t remoteip_process_v1_header(conn_rec *c, remoteip_conn_config_t *conn_conf, proxy_header *hdr, apr_size_t len, apr_size_t *hdr_len)
static const char * header_name_set(cmd_parms *cmd, void *dummy, const char *arg)
remoteip_parse_status_t
@ HDR_NEED_MORE
@ HDR_DONE
@ HDR_ERROR
#define MIN_HDR_LEN
static const command_rec remoteip_cmds[]
static apr_status_t remoteip_input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
static const char * proxies_header_name_set(cmd_parms *cmd, void *dummy, const char *arg)
static int remoteip_hook_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
static int remoteip_sockaddr_equal(apr_sockaddr_t *addr1, apr_sockaddr_t *addr2)
static int remoteip_hook_pre_connection(conn_rec *c, void *csd)
static void register_hooks(apr_pool_t *p)
static const char * remoteip_enable_proxy_protocol(cmd_parms *cmd, void *config, int flag)
#define MIN_V2_HDR_LEN
static int remoteip_sockaddr_compat(apr_sockaddr_t *addr1, apr_sockaddr_t *addr2)
static int remoteip_is_server_port(apr_port_t port)
static remoteip_parse_status_t remoteip_process_v2_header(conn_rec *c, remoteip_conn_config_t *conn_conf, proxy_header *hdr)
static void * create_remoteip_server_config(apr_pool_t *p, server_rec *s)
static const char * proxies_set(cmd_parms *cmd, void *cfg, const char *arg)
#define GET_NEXT_WORD(field)
static const char * proxylist_read(cmd_parms *cmd, void *cfg, const char *filename)
static int looks_like_ip(const char *ipstr)
static int remoteip_addr_in_list(remoteip_addr_info *list, apr_sockaddr_t *addr)
static void remoteip_warn_enable_conflict(remoteip_addr_info *prev, server_rec *new, int flag)
#define INET6_ADDRSTRLEN
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
unsigned line_number
This structure is used for recording information about the registered filters. It associates a name w...
The representation of a filter chain.
Apache's listeners record.
Definition ap_listen.h:47
ap_listen_rec * next
Definition ap_listen.h:51
apr_pool_t * pool
apr_sockaddr_t * next
apr_int32_t family
Structure to store things which are per connection.
Definition httpd.h:1152
struct ap_conf_vector_t * conn_config
Definition httpd.h:1190
apr_uint16_t len
apr_byte_t fam
apr_byte_t ver_cmd
proxy_v2_addr addr
server_rec * source
apr_sockaddr_t * addr
struct remoteip_addr_info * next
apr_pool_t * pool
const char * header_name
apr_array_header_t * disabled_subnets
apr_array_header_t * proxymatch_ip
remoteip_addr_info * proxy_protocol_enabled
const char * proxies_header_name
remoteip_addr_info * proxy_protocol_disabled
apr_sockaddr_t * client_addr
apr_bucket_brigade * bb
apr_ipsubnet_t * ip
char * useragent_ip
const char * proxied_remote
const char * proxy_ips
apr_sockaddr_t * useragent_addr
A structure that represents the current request.
Definition httpd.h:845
char * useragent_ip
Definition httpd.h:1101
apr_table_t * notes
Definition httpd.h:985
apr_pool_t * pool
Definition httpd.h:847
apr_sockaddr_t * useragent_addr
Definition httpd.h:1100
conn_rec * connection
Definition httpd.h:849
apr_table_t * headers_in
Definition httpd.h:976
server_rec * server
Definition httpd.h:851
A structure to be used for Per-vhost config.
Definition httpd.h:1301
apr_port_t host_port
Definition httpd.h:1309
A structure to store information for each virtual server.
Definition httpd.h:1322
const char * defn_name
Definition httpd.h:1346
server_addr_rec * addrs
Definition httpd.h:1370
char * server_hostname
Definition httpd.h:1365
struct ap_conf_vector_t * module_config
Definition httpd.h:1341
apr_uint32_t dst_addr
apr_uint16_t src_port
apr_uint16_t dst_port
apr_uint32_t src_addr
ap_input_mode_t
input filtering modes
Definition util_filter.h:41
@ AP_MODE_READBYTES
Definition util_filter.h:43
@ AP_MODE_GETLINE
Definition util_filter.h:48
INT info