/* * Pound - the reverse-proxy load-balancer * Copyright (C) 2002-2010 Apsis GmbH * * This file is part of Pound. * * Pound is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Pound is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Contact information: * Apsis GmbH * P.O.Box * 8707 Uetikon am See * Switzerland * EMail: roseg@apsis.ch */ #include "config.h" #include #include #if HAVE_STDLIB_H #include #else #error "Pound needs stdlib.h" #endif #if HAVE_UNISTD_H #include #else #error "Pound needs unistd.h" #endif #if HAVE_GETOPT_H #include #endif #if HAVE_PTHREAD_H #include #else #error "Pound needs pthread.h" #endif #if HAVE_STRING_H #include #else #error "Pound needs string.h" #endif #if TIME_WITH_SYS_TIME #if HAVE_SYS_TIME_H #include #else #error "Pound needs sys/time.h" #endif #if HAVE_TIME_H #include #else #error "Pound needs time.h" #endif #else /* may not mix sys/time.h and time.h */ #if HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #else #error "Pound needs time.h" #endif #endif /* mix */ #if HAVE_SYS_TYPES_H #include #else #error "Pound needs sys/types.h" #endif #if HAVE_SYS_SOCKET_H #include #else #error "Pound needs sys/socket.h" #endif #if HAVE_SYS_UN_H #include #else #error "Pound needs sys/un.h" #endif #ifndef UNIX_PATH_MAX /* on Linux this is defined in linux/un.h rather than sys/un.h - go figure */ #define UNIX_PATH_MAX 108 #endif #if HAVE_NETINET_IN_H #include #else #error "Pound needs netinet/in.h" #endif #if HAVE_NETINET_TCP_H #include #else #error "Pound needs netinet/tcp.h" #endif #if HAVE_ARPA_INET_H #include #else #error "Pound needs arpa/inet.h" #endif #if HAVE_NETDB_H #include #else #error "Pound needs netdb.h" #endif #if HAVE_SYS_POLL_H #include #else #error "Pound needs sys/poll.h" #endif #if HAVE_OPENSSL_SSL_H #define OPENSSL_THREAD_DEFINES #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x00907000L #ifndef OPENSSL_THREADS #error "Pound requires OpenSSL with thread support" #endif #else #ifndef THREADS #error "Pound requires OpenSSL with thread support" #endif #endif #else #error "Pound needs openssl/ssl.h" #endif #if HAVE_OPENSSL_ENGINE_H #include #endif #if HAVE_PWD_H #include #else #error "Pound needs pwd.h" #endif #if HAVE_GRP_H #include #else #error "Pound needs grp.h" #endif #if HAVE_SYSLOG_H #include #endif #if HAVE_SYS_SYSLOG_H #include #endif #if HAVE_SIGNAL_H #include #else #error "Pound needs signal.h" #endif #if HAVE_LIBPCREPOSIX #if HAVE_PCREPOSIX_H #include #elif HAVE_PCRE_PCREPOSIX_H #include #else #error "You have libpcreposix, but the header files are missing. Use --disable-pcreposix" #endif #elif HAVE_REGEX_H #include #else #error "Pound needs regex.h" #endif #if HAVE_CTYPE_H #include #else #error "Pound needs ctype.h" #endif #if HAVE_ERRNO_H #include #else #error "Pound needs errno.h" #endif #if HAVE_WAIT_H #include #elif HAVE_SYS_WAIT_H #include #else #error "Pound needs sys/wait.h" #endif #if HAVE_SYS_STAT_H #include #else #error "Pound needs sys/stat.h" #endif #if HAVE_FCNTL_H #include #else #error "Pound needs fcntl.h" #endif #if HAVE_STDARG_H #include #else #include #endif #if HAVE_FNMATCH_H #include #else #error "Pound needs fnmatch.h" #endif #ifndef __STDC__ #define const #endif #ifdef HAVE_LONG_LONG_INT #define LONG long long #define L0 0LL #define L_1 -1LL #define STRTOL strtoll #define ATOL atoll #else #define LONG long #define L0 0L #define L_1 -1L #define STRTOL strtol #define ATOL atol #endif #ifndef NO_EXTERNALS /* * Global variables needed by everybody */ extern char *user, /* user to run as */ *group, /* group to run as */ *root_jail, /* directory to chroot to */ *pid_name, /* file to record pid in */ *ctrl_name; /* control socket name */ extern int numthreads, /* number of worker threads */ anonymise, /* anonymise client address */ alive_to, /* check interval for resurrection */ daemonize, /* run as daemon */ log_facility, /* log facility to use */ print_log, /* print log messages to stdout/stderr */ grace, /* grace period before shutdown */ control_sock; /* control socket */ extern regex_t HEADER, /* Allowed header */ CONN_UPGRD, /* upgrade in connection header */ CHUNK_HEAD, /* chunk header line */ RESP_SKIP, /* responses for which we skip response */ RESP_IGN, /* responses for which we ignore content */ LOCATION, /* the host we are redirected to */ AUTHORIZATION; /* the Authorisation header */ #ifndef SOL_TCP /* for systems without the definition */ extern int SOL_TCP; #endif #endif /* NO_EXTERNALS */ #ifndef MAXBUF #define MAXBUF 4096 #endif #define MAXHEADERS 128 #ifndef F_CONF #define F_CONF "/usr/local/etc/pound.cfg" #endif #ifndef F_PID #define F_PID "/var/run/pound.pid" #endif /* matcher chain */ typedef struct _matcher { regex_t pat; /* pattern to match the request/header against */ struct _matcher *next; } MATCHER; /* back-end types */ typedef enum { SESS_NONE, SESS_IP, SESS_COOKIE, SESS_URL, SESS_PARM, SESS_HEADER, SESS_BASIC } SESS_TYPE; /* back-end definition */ typedef struct _backend { int be_type; /* 0 if real back-end, otherwise code (301, 302/default, 307) */ struct addrinfo addr; /* IPv4/6 address */ int priority; /* priority */ int to; /* read/write time-out */ int conn_to; /* connection time-out */ int ws_to; /* websocket time-out */ struct addrinfo ha_addr; /* HA address/port */ char *url; /* for redirectors */ int redir_req; /* the redirect should include the request path */ SSL_CTX *ctx; /* CTX for SSL connections */ pthread_mutex_t mut; /* mutex for this back-end */ int n_requests; /* number of requests seen */ double t_requests; /* time to answer these requests */ double t_average; /* average time to answer requests */ int alive; /* false if the back-end is dead */ int resurrect; /* this back-end is to be resurrected */ int disabled; /* true if the back-end is disabled */ struct _backend *next; } BACKEND; typedef struct _tn { char *key; void *content; time_t last_acc; } TABNODE; #define n_children(N) ((N)? (N)->children: 0) /* maximal session key size */ #define KEY_SIZE 127 #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) DEFINE_LHASH_OF(TABNODE); #elif OPENSSL_VERSION_NUMBER >= 0x10000000L DECLARE_LHASH_OF(TABNODE); #endif /* service definition */ typedef struct _service { char name[KEY_SIZE + 1]; /* symbolic name */ MATCHER *url, /* request matcher */ *req_head, /* required headers */ *deny_head; /* forbidden headers */ BACKEND *backends; BACKEND *emergency; int abs_pri; /* abs total priority for all back-ends */ int tot_pri; /* total priority for current back-ends */ pthread_mutex_t mut; /* mutex for this service */ SESS_TYPE sess_type; int sess_ttl; /* session time-to-live */ regex_t sess_start; /* pattern to identify the session data */ regex_t sess_pat; /* pattern to match the session data */ #if OPENSSL_VERSION_NUMBER >= 0x10000000L LHASH_OF(TABNODE) *sessions; /* currently active sessions */ #else LHASH *sessions; /* currently active sessions */ #endif int disabled; /* true if the service is disabled */ struct _service *next; } SERVICE; #ifndef NO_EXTERNALS extern SERVICE *services; /* global services (if any) */ #endif /* NO_EXTERNALS */ typedef struct _pound_ctx { SSL_CTX *ctx; char *server_name; unsigned char **subjectAltNames; unsigned int subjectAltNameCount; struct _pound_ctx *next; } POUND_CTX; /* Listener definition */ typedef struct _listener { struct addrinfo addr; /* IPv4/6 address */ int sock; /* listening socket */ POUND_CTX *ctx; /* CTX for SSL connections */ int clnt_check; /* client verification mode */ int noHTTPS11; /* HTTP 1.1 mode for SSL */ char *add_head; /* extra SSL header */ regex_t verb; /* pattern to match the request verb against */ int to; /* client time-out */ int has_pat; /* was a URL pattern defined? */ regex_t url_pat; /* pattern to match the request URL against */ char *err414, /* error messages */ *err500, *err501, *err503; LONG max_req; /* max. request size */ MATCHER *head_off; /* headers to remove */ int rewr_loc; /* rewrite location response */ int rewr_dest; /* rewrite destination header */ int disabled; /* true if the listener is disabled */ int log_level; /* log level for this listener */ int allow_client_reneg; /* Allow Client SSL Renegotiation */ int disable_ssl_v2; /* Disable SSL version 2 */ SERVICE *services; struct _listener *next; } LISTENER; #ifndef NO_EXTERNALS extern LISTENER *listeners; /* all available listeners */ #endif /* NO_EXTERNALS */ typedef struct _thr_arg { int sock; LISTENER *lstn; struct addrinfo from_host; struct _thr_arg *next; } thr_arg; /* argument to processing threads: socket, origin */ /* Track SSL handshare/renegotiation so we can reject client-renegotiations. */ typedef enum { RENEG_INIT=0, RENEG_REJECT, RENEG_ALLOW, RENEG_ABORT } RENEG_STATE; /* Header types */ #define HEADER_ILLEGAL -1 #define HEADER_OTHER 0 #define HEADER_TRANSFER_ENCODING 1 #define HEADER_CONTENT_LENGTH 2 #define HEADER_CONNECTION 3 #define HEADER_LOCATION 4 #define HEADER_CONTLOCATION 5 #define HEADER_HOST 6 #define HEADER_REFERER 7 #define HEADER_USER_AGENT 8 #define HEADER_URI 9 #define HEADER_DESTINATION 10 #define HEADER_EXPECT 11 #define HEADER_UPGRADE 13 /* control request stuff */ typedef enum { CTRL_LST, CTRL_EN_LSTN, CTRL_DE_LSTN, CTRL_EN_SVC, CTRL_DE_SVC, CTRL_EN_BE, CTRL_DE_BE, CTRL_ADD_SESS, CTRL_DEL_SESS } CTRL_CODE; typedef struct { CTRL_CODE cmd; int listener; int service; int backend; char key[KEY_SIZE + 1]; } CTRL_CMD; #ifdef NEED_INADDRT /* for oldish Unices - normally this is in /usr/include/netinet/in.h */ typedef u_int32_t in_addr_t; #endif #ifdef NEED_INPORTT /* for oldish Unices - normally this is in /usr/include/netinet/in.h */ typedef u_int16_t in_port_t; #endif #ifdef NEED_TIMET /* for oldish Unices - normally this is in /usr/include/time.h */ typedef u_int32_t time_t; #endif /* * add a request to the queue */ extern int put_thr_arg(thr_arg *); /* * get a request from the queue */ extern thr_arg *get_thr_arg(void); /* * get the current queue length */ extern int get_thr_qlen(void); /* * handle an HTTP request */ extern void *thr_http(void *); /* * Log an error to the syslog or to stderr */ extern void logmsg(const int, const char *, ...); /* * Parse a URL, possibly decoding hexadecimal-encoded characters */ extern int cpURL(char *, char *, int); /* * Translate inet/inet6 address into a string */ extern void addr2str(char *, const int, const struct addrinfo *, const int); /* * Return a string representation for a back-end address */ #define str_be(BUF, LEN, BE) addr2str((BUF), (LEN), &(BE)->addr, 0) /* * Find the right service for a request */ extern SERVICE *get_service(const LISTENER *, const char *, char **const); /* * Find the right back-end for a request */ extern BACKEND *get_backend(SERVICE *const, const struct addrinfo *, const char *, char **const); /* * Search for a host name, return the addrinfo for it */ extern int get_host(char *const, struct addrinfo *, int); /* * Find if a redirect needs rewriting * In general we have two possibilities that require it: * (1) if the redirect was done to the correct location with the wrong protocol * (2) if the redirect was done to the back-end rather than the listener */ extern int need_rewrite(const int, char *const, char *const, const char *, const LISTENER *, const BACKEND *); /* * (for cookies only) possibly create session based on response headers */ extern void upd_session(SERVICE *const, char **const, BACKEND *const); /* * Parse a header */ extern int check_header(const char *, char *); #define BE_DISABLE -1 #define BE_KILL 1 #define BE_ENABLE 0 /* * mark a backend host as dead; * do nothing if no resurection code is active */ extern void kill_be(SERVICE *const, const BACKEND *, const int); /* * Update the number of requests and time to answer for a given back-end */ extern void upd_be(SERVICE *const svc, BACKEND *const be, const double); /* * Non-blocking version of connect(2). Does the same as connect(2) but * ensures it will time-out after a much shorter time period CONN_TO. */ extern int connect_nb(const int, const struct addrinfo *, const int); /* * Parse arguments/config file */ extern void config_parse(const int, char **const); /* * RSA ephemeral keys: how many and how often */ #define N_RSA_KEYS 11 #ifndef T_RSA_KEYS #define T_RSA_KEYS 7200 #endif /* * return a pre-generated RSA key */ extern RSA *RSA_tmp_callback(SSL *, int, int); /* * return a pre-generated RSA key */ extern DH *DH_tmp_callback(SSL *, int, int); /* * Renegotiation callback */ extern void SSLINFO_callback(const SSL *s, int where, int rc); /* * expiration stuff */ #ifndef EXPIRE_TO #define EXPIRE_TO 60 #endif #ifndef HOST_TO #define HOST_TO 300 #endif /* * initialise the timer functions: * - host_mut * - RSA_mut and keys */ extern void init_timer(void); /* * run timed functions: * - RSAgen every T_RSA_KEYS seconds * - resurrect every alive_to seconds * - expire every EXPIRE_TO seconds */ extern void *thr_timer(void *); /* * The controlling thread * listens to client requests and calls the appropriate functions */ extern void *thr_control(void *);