1/*++
2/* NAME
3/*	haproxy_srvr 3
4/* SUMMARY
5/*	server-side haproxy protocol support
6/* SYNOPSIS
7/*	#include <haproxy_srvr.h>
8/*
9/*	const char *haproxy_srvr_parse(str,
10/*			smtp_client_addr, smtp_client_port,
11/*			smtp_server_addr, smtp_server_port)
12/*	const char *str;
13/*	MAI_HOSTADDR_STR *smtp_client_addr,
14/*	MAI_SERVPORT_STR *smtp_client_port,
15/*	MAI_HOSTADDR_STR *smtp_server_addr,
16/*	MAI_SERVPORT_STR *smtp_server_port;
17/* DESCRIPTION
18/*	haproxy_srvr_parse() parses a haproxy line. The result is
19/*	null in case of success, a pointer to text (with the error
20/*	type) in case of error. If both IPv6 and IPv4 support are
21/*	enabled, IPV4_IN_IPV6 address syntax (::ffff:1.2.3.4) is
22/*	converted to IPV4 syntax.
23/* LICENSE
24/* .ad
25/* .fi
26/*	The Secure Mailer license must be distributed with this software.
27/* AUTHOR(S)
28/*	Wietse Venema
29/*	IBM T.J. Watson Research
30/*	P.O. Box 704
31/*	Yorktown Heights, NY 10598, USA
32/*--*/
33
34/* System library. */
35
36#include <sys_defs.h>
37#include <stdarg.h>
38#include <stdlib.h>
39#include <string.h>
40
41#ifdef STRCASECMP_IN_STRINGS_H
42#include <strings.h>
43#endif
44
45/* Utility library. */
46
47#include <msg.h>
48#include <myaddrinfo.h>
49#include <valid_hostname.h>
50#include <stringops.h>
51#include <mymalloc.h>
52#include <inet_proto.h>
53
54/* Global library. */
55
56#include <haproxy_srvr.h>
57
58/* Application-specific. */
59
60static INET_PROTO_INFO *proto_info;
61
62/* haproxy_srvr_parse_lit - extract and validate string literal */
63
64static int haproxy_srvr_parse_lit(const char *str,...)
65{
66    va_list ap;
67    const char *cp;
68    int     result = -1;
69
70    if (msg_verbose)
71	msg_info("haproxy_srvr_parse: %s", str);
72
73    if (str != 0) {
74	va_start(ap, str);
75	while (result < 0 && (cp = va_arg(ap, const char *)) != 0)
76	    if (strcmp(str, cp) == 0)
77		result = 0;
78	va_end(ap);
79    }
80    return (result);
81}
82
83/* haproxy_srvr_parse_proto - parse and validate the protocol type */
84
85static int haproxy_srvr_parse_proto(const char *str, int *addr_family)
86{
87    if (msg_verbose)
88	msg_info("haproxy_srvr_parse: proto=%s", str);
89
90#ifdef AF_INET6
91    if (strcasecmp(str, "TCP6") == 0) {
92	if (strchr((char *) proto_info->sa_family_list, AF_INET6) != 0) {
93	    *addr_family = AF_INET6;
94	    return (0);
95	}
96    } else
97#endif
98    if (strcasecmp(str, "TCP4") == 0) {
99	if (strchr((char *) proto_info->sa_family_list, AF_INET) != 0) {
100	    *addr_family = AF_INET;
101	    return (0);
102	}
103    }
104    return (-1);
105}
106
107/* haproxy_srvr_parse_addr - extract and validate IP address */
108
109static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr,
110				           int addr_family)
111{
112    if (msg_verbose)
113	msg_info("haproxy_srvr_parse: addr=%s proto=%d", str, addr_family);
114
115    if (str == 0 || strlen(str) >= sizeof(MAI_HOSTADDR_STR))
116	return (-1);
117
118    switch (addr_family) {
119#ifdef AF_INET6
120    case AF_INET6:
121	if (!valid_ipv6_hostaddr(str, DONT_GRIPE))
122	    return (-1);
123	if (strncasecmp("::ffff:", str, 7) == 0
124	    && strchr((char *) proto_info->sa_family_list, AF_INET) != 0) {
125	    memcpy(addr->buf, str + 7, strlen(str) + 1 - 7);
126	    return (0);
127	} else {
128	    memcpy(addr->buf, str, strlen(str) + 1);
129	    return (0);
130	}
131#endif
132    case AF_INET:
133	if (!valid_ipv4_hostaddr(str, DONT_GRIPE))
134	    return (-1);
135	memcpy(addr->buf, str, strlen(str) + 1);
136	return (0);
137    default:
138	msg_panic("haproxy_srvr_parse: unexpected address family: %d",
139		  addr_family);
140    }
141}
142
143/* haproxy_srvr_parse_port - extract and validate TCP port */
144
145static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port)
146{
147    if (msg_verbose)
148	msg_info("haproxy_srvr_parse: port=%s", str);
149    if (str == 0 || strlen(str) >= sizeof(MAI_SERVPORT_STR)
150	|| !valid_hostport(str, DONT_GRIPE)) {
151	return (-1);
152    } else {
153	memcpy(port->buf, str, strlen(str) + 1);
154	return (0);
155    }
156}
157
158/* haproxy_srvr_parse - parse haproxy line */
159
160const char *haproxy_srvr_parse(const char *str,
161			               MAI_HOSTADDR_STR *smtp_client_addr,
162			               MAI_SERVPORT_STR *smtp_client_port,
163			               MAI_HOSTADDR_STR *smtp_server_addr,
164			               MAI_SERVPORT_STR *smtp_server_port)
165{
166    char   *saved_str = mystrdup(str);
167    char   *cp = saved_str;
168    const char *err;
169    int     addr_family;
170
171    if (proto_info == 0)
172	proto_info = inet_proto_info();
173
174    /*
175     * XXX We don't accept connections with the "UNKNOWN" protocol type,
176     * because those would sidestep address-based access control mechanisms.
177     */
178#define NEXT_TOKEN mystrtok(&cp, " \r\n")
179    if (haproxy_srvr_parse_lit(NEXT_TOKEN, "PROXY", (char *) 0) < 0)
180	err = "unexpected protocol header";
181    else if (haproxy_srvr_parse_proto(NEXT_TOKEN, &addr_family) < 0)
182	err = "unsupported protocol type";
183    else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_client_addr,
184				     addr_family) < 0)
185	err = "unexpected client address syntax";
186    else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_server_addr,
187				     addr_family) < 0)
188	err = "unexpected server address syntax";
189    else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_client_port) < 0)
190	err = "unexpected client port syntax";
191    else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_server_port) < 0)
192	err = "unexpected server port syntax";
193    else
194	err = 0;
195    myfree(saved_str);
196    return (err);
197}
198