diff options
Diffstat (limited to 'src/global/haproxy_srvr.c')
-rw-r--r-- | src/global/haproxy_srvr.c | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/src/global/haproxy_srvr.c b/src/global/haproxy_srvr.c new file mode 100644 index 0000000..9779985 --- /dev/null +++ b/src/global/haproxy_srvr.c @@ -0,0 +1,202 @@ +/*++ +/* NAME +/* haproxy_srvr 3 +/* SUMMARY +/* server-side haproxy protocol support +/* SYNOPSIS +/* #include <haproxy_srvr.h> +/* +/* const char *haproxy_srvr_parse(str, +/* smtp_client_addr, smtp_client_port, +/* smtp_server_addr, smtp_server_port) +/* const char *str; +/* MAI_HOSTADDR_STR *smtp_client_addr, +/* MAI_SERVPORT_STR *smtp_client_port, +/* MAI_HOSTADDR_STR *smtp_server_addr, +/* MAI_SERVPORT_STR *smtp_server_port; +/* DESCRIPTION +/* haproxy_srvr_parse() parses a haproxy line. The result is +/* null in case of success, a pointer to text (with the error +/* type) in case of error. If both IPv6 and IPv4 support are +/* enabled, IPV4_IN_IPV6 address syntax (::ffff:1.2.3.4) is +/* converted to IPV4 syntax. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +#ifdef STRCASECMP_IN_STRINGS_H +#include <strings.h> +#endif + +/* Utility library. */ + +#include <msg.h> +#include <myaddrinfo.h> +#include <valid_hostname.h> +#include <stringops.h> +#include <mymalloc.h> +#include <inet_proto.h> + +/* Global library. */ + +#include <haproxy_srvr.h> + +/* Application-specific. */ + +static INET_PROTO_INFO *proto_info; + +#define STR_OR_NULL(str) ((str) ? (str) : "(null)") + +/* haproxy_srvr_parse_lit - extract and validate string literal */ + +static int haproxy_srvr_parse_lit(const char *str,...) +{ + va_list ap; + const char *cp; + int result = -1; + + if (msg_verbose) + msg_info("haproxy_srvr_parse: %s", STR_OR_NULL(str)); + + if (str != 0) { + va_start(ap, str); + while (result < 0 && (cp = va_arg(ap, const char *)) != 0) + if (strcmp(str, cp) == 0) + result = 0; + va_end(ap); + } + return (result); +} + +/* haproxy_srvr_parse_proto - parse and validate the protocol type */ + +static int haproxy_srvr_parse_proto(const char *str, int *addr_family) +{ + if (msg_verbose) + msg_info("haproxy_srvr_parse: proto=%s", STR_OR_NULL(str)); + + if (str == 0) + return (-1); +#ifdef AF_INET6 + if (strcasecmp(str, "TCP6") == 0) { + if (strchr((char *) proto_info->sa_family_list, AF_INET6) != 0) { + *addr_family = AF_INET6; + return (0); + } + } else +#endif + if (strcasecmp(str, "TCP4") == 0) { + if (strchr((char *) proto_info->sa_family_list, AF_INET) != 0) { + *addr_family = AF_INET; + return (0); + } + } + return (-1); +} + +/* haproxy_srvr_parse_addr - extract and validate IP address */ + +static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr, + int addr_family) +{ + if (msg_verbose) + msg_info("haproxy_srvr_parse: addr=%s proto=%d", + STR_OR_NULL(str), addr_family); + + if (str == 0 || strlen(str) >= sizeof(MAI_HOSTADDR_STR)) + return (-1); + + switch (addr_family) { +#ifdef AF_INET6 + case AF_INET6: + if (!valid_ipv6_hostaddr(str, DONT_GRIPE)) + return (-1); + if (strncasecmp("::ffff:", str, 7) == 0 + && strchr((char *) proto_info->sa_family_list, AF_INET) != 0) { + memcpy(addr->buf, str + 7, strlen(str) + 1 - 7); + return (0); + } else { + memcpy(addr->buf, str, strlen(str) + 1); + return (0); + } +#endif + case AF_INET: + if (!valid_ipv4_hostaddr(str, DONT_GRIPE)) + return (-1); + memcpy(addr->buf, str, strlen(str) + 1); + return (0); + default: + msg_panic("haproxy_srvr_parse: unexpected address family: %d", + addr_family); + } +} + +/* haproxy_srvr_parse_port - extract and validate TCP port */ + +static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port) +{ + if (msg_verbose) + msg_info("haproxy_srvr_parse: port=%s", STR_OR_NULL(str)); + if (str == 0 || strlen(str) >= sizeof(MAI_SERVPORT_STR) + || !valid_hostport(str, DONT_GRIPE)) { + return (-1); + } else { + memcpy(port->buf, str, strlen(str) + 1); + return (0); + } +} + +/* haproxy_srvr_parse - parse haproxy line */ + +const char *haproxy_srvr_parse(const char *str, + MAI_HOSTADDR_STR *smtp_client_addr, + MAI_SERVPORT_STR *smtp_client_port, + MAI_HOSTADDR_STR *smtp_server_addr, + MAI_SERVPORT_STR *smtp_server_port) +{ + char *saved_str = mystrdup(str); + char *cp = saved_str; + const char *err; + int addr_family; + + if (proto_info == 0) + proto_info = inet_proto_info(); + + /* + * XXX We don't accept connections with the "UNKNOWN" protocol type, + * because those would sidestep address-based access control mechanisms. + */ +#define NEXT_TOKEN mystrtok(&cp, " \r\n") + if (haproxy_srvr_parse_lit(NEXT_TOKEN, "PROXY", (char *) 0) < 0) + err = "unexpected protocol header"; + else if (haproxy_srvr_parse_proto(NEXT_TOKEN, &addr_family) < 0) + err = "unsupported protocol type"; + else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_client_addr, + addr_family) < 0) + err = "unexpected client address syntax"; + else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_server_addr, + addr_family) < 0) + err = "unexpected server address syntax"; + else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_client_port) < 0) + err = "unexpected client port syntax"; + else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_server_port) < 0) + err = "unexpected server port syntax"; + else + err = 0; + myfree(saved_str); + return (err); +} |