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