master-service-haproxy.c revision 8761992b5aa05862e7ec3a460cdc17af41a4a0f5
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen/* Copyright (c) 2013-2015 Dovecot authors, see the included COPYING file */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen struct { /* for TCP/UDP over IPv4, len = 12 */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen struct { /* for TCP/UDP over IPv6, len = 36 */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen struct { /* for AF_UNIX sockets, len = 216 */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen struct master_service_haproxy_conn *prev, *next;
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainenmaster_service_haproxy_conn_free(struct master_service_haproxy_conn *hpconn)
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen struct master_service *service = hpconn->service;
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen DLLIST_REMOVE(&service->haproxy_conns, hpconn);
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainenmaster_service_haproxy_conn_failure(struct master_service_haproxy_conn *hpconn)
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen struct master_service *service = hpconn->service;
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen struct master_service_connection conn = hpconn->conn;
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen master_service_client_connection_handled(service, &conn);
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainenmaster_service_haproxy_conn_success(struct master_service_haproxy_conn *hpconn)
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen struct master_service *service = hpconn->service;
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen struct master_service_connection conn = hpconn->conn;
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen master_service_client_connection_callback(service, &conn);
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainenmaster_service_haproxy_timeout(struct master_service_haproxy_conn *hpconn)
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainenmaster_service_haproxy_read(struct master_service_haproxy_conn *hpconn)
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen static union {
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen unsigned char v1_data[HAPROXY_V1_MAX_HEADER_SIZE];
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen struct ip_addr *real_remote_ip = &hpconn->conn.remote_ip;
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* the protocol specification explicitly states that the protocol header
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen must be sent as one TCP frame, meaning that we will get it in full
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen with the first recv() call.
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen FIXME: still, it would be cleaner to allow reading it incrementally.
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen i_info("haproxy: Client disconnected (rip=%s)",
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* don't update true connection data until we succeed */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* protocol version 2 */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen const struct haproxy_header_v2 *hdr = &buf.v2.hdr;
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen const struct haproxy_data_v2 *data = &buf.v2.data;
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Unsupported protocol version (version=%02x, rip=%s)",
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Protocol payload length does not match header "
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "(got=%"PRIuSIZE_T", expect=%"PRIuSIZE_T", rip=%s)",
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen (size_t)ret, size, net_ip2addr(real_remote_ip));
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* keep local connection address for LOCAL */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /*i_debug("haproxy(v2): Local connection (rip=%s)",
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen net_ip2addr(real_remote_ip));*/
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen if ((hdr->fam & 0x0f) != HAPROXY_SOCK_STREAM) {
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* UDP makes no sense currently */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Not using TCP (type=%02x, rip=%s)",
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen (hdr->fam & 0x0f), net_ip2addr(real_remote_ip));
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "IPv4 data is incomplete (rip=%s)",
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen local_ip.u.ip4.s_addr = data->addr.ip4.dst_addr;
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen remote_ip.u.ip4.s_addr = data->addr.ip4.src_addr;
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "IPv6 data is incomplete (rip=%s)",
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen memcpy(&local_ip.u.ip6.s6_addr, data->addr.ip6.dst_addr, 16);
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen memcpy(&remote_ip.u.ip6.s6_addr, data->addr.ip6.src_addr, 16);
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* unsupported; ignored */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen i_error("haproxy(v2): Unsupported address family "
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "(family=%02x, rip=%s)", (hdr->fam & 0xf0) >> 4,
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* unsupported; error */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Unknown address family "
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "(family=%02x, rip=%s)", (hdr->fam & 0xf0) >> 4,
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Invalid command (cmd=%02x, rip=%s)",
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen // FIXME: TLV vectors are ignored
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen // (useful to see whether proxied client is using SSL)
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* protocol version 1 (soon obsolete) */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen } else if (ret >= 8 && memcmp(buf.v1_data, "PROXY", 5) == 0) {
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen const char *const *fields;
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen unsigned int family = 0;
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* find end of header line */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* protocol */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Field for proxied protocol is missing "
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Unknown proxied protocol "
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "(protocol=`%s', rip=%s)", str_sanitize(*fields, 64),
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* remote address */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Field for proxied remote address is missing "
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Proxied remote address is invalid "
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "(address=`%s', rip=%s)", str_sanitize(*fields, 64),
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* local address */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Field for proxied local address is missing "
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Proxied local address is invalid "
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "(address=`%s', rip=%s)", str_sanitize(*fields, 64),
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* remote port */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Field for proxied local port is missing "
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Proxied remote port is invalid "
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "(port=`%s', rip=%s)", str_sanitize(*fields, 64),
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* local port */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Field for proxied local port is missing "
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Proxied local port is invalid "
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "(port=`%s', rip=%s)", str_sanitize(*fields, 64),
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Header line has spurius extra field "
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "(field=`%s', rip=%s)", str_sanitize(*fields, 64),
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* invalid protocol */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "No valid proxy header found (rip=%s)",
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* remove proxy protocol header from socket buffer */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen i_info("haproxy: Client disconnected (rip=%s)",
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* not supposed to happen */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen "Failed to read full header (rip=%s)",
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen /* assign data from proxy */
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainenmaster_service_haproxy_input(struct master_service_haproxy_conn *hpconn)
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen if ((ret = master_service_haproxy_read(hpconn)) <= 0) {
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainenmaster_service_haproxy_conn_is_trusted(struct master_service *service,
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen const char *const *net;
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen unsigned int bits;
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen if (service->set->haproxy_trusted_networks == NULL)
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen net = t_strsplit_spaces(service->set->haproxy_trusted_networks, ", ");
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen if (net_parse_range(*net, &net_ip, &bits) < 0) {
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen if (net_is_in_network(&conn->real_remote_ip, &net_ip, bits))
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainenvoid master_service_haproxy_new(struct master_service *service,
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen if (!master_service_haproxy_conn_is_trusted(service, conn)) {
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen i_warning("haproxy: Client not trusted (rip=%s)",
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen master_service_client_connection_handled(service, conn);
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen hpconn = i_new(struct master_service_haproxy_conn, 1);
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen DLLIST_PREPEND(&service->haproxy_conns, hpconn);
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen hpconn->to = timeout_add(service->set->haproxy_timeout*1000,
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainenvoid master_service_haproxy_abort(struct master_service *service)
8761992b5aa05862e7ec3a460cdc17af41a4a0f5Timo Sirainen i_error("haproxy: close(service connection) failed: %m");