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