summaryrefslogtreecommitdiffstats
path: root/src/global/haproxy_srvr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/global/haproxy_srvr.c')
-rw-r--r--src/global/haproxy_srvr.c202
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);
+}