1/* $NetBSD: host_port.c,v 1.2 2017/02/14 01:16:49 christos Exp $ */ 2 3/*++ 4/* NAME 5/* host_port 3 6/* SUMMARY 7/* split string into host and port, destroy string 8/* SYNOPSIS 9/* #include <host_port.h> 10/* 11/* const char *host_port(string, host, def_host, port, def_service) 12/* char *string; 13/* char **host; 14/* char *def_host; 15/* char **port; 16/* char *def_service; 17/* DESCRIPTION 18/* host_port() splits a string into substrings with the host 19/* name or address, and the service name or port number. 20/* The input string is modified. 21/* 22/* Host/domain names are validated with valid_utf8_hostname(), 23/* and host addresses are validated with valid_hostaddr(). 24/* 25/* The following input formats are understood (null means 26/* a null pointer argument): 27/* 28/* When def_service is not null, and def_host is null: 29/* 30/* [host]:port, [host]:, [host] 31/* 32/* host:port, host:, host 33/* 34/* When def_host is not null, and def_service is null: 35/* 36/* :port, port 37/* 38/* Other combinations of def_service and def_host are 39/* not supported and produce undefined results. 40/* DIAGNOSTICS 41/* The result is a null pointer in case of success. 42/* In case of problems the result is a string pointer with 43/* the problem type. 44/* CLIENT EXAMPLE 45/* .ad 46/* .fi 47/* Typical client usage allows the user to omit the service port, 48/* in which case the client connects to a pre-determined default 49/* port: 50/* .nf 51/* .na 52/* 53/* buf = mystrdup(endpoint); 54/* if ((parse_error = host_port(buf, &host, NULL, &port, defport)) != 0) 55/* msg_fatal("%s in \"%s\"", parse_error, endpoint); 56/* if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res)) != 0) 57/* msg_fatal("%s: %s", endpoint, MAI_STRERROR(aierr)); 58/* myfree(buf); 59/* SERVER EXAMPLE 60/* .ad 61/* .fi 62/* Typical server usage allows the user to omit the host, meaning 63/* listen on all available network addresses: 64/* .nf 65/* .na 66/* 67/* buf = mystrdup(endpoint); 68/* if ((parse_error = host_port(buf, &host, "", &port, NULL)) != 0) 69/* msg_fatal("%s in \"%s\"", parse_error, endpoint); 70/* if (*host == 0) 71/* host = 0; 72/* if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res)) != 0) 73/* msg_fatal("%s: %s", endpoint, MAI_STRERROR(aierr)); 74/* myfree(buf); 75/* LICENSE 76/* .ad 77/* .fi 78/* The Secure Mailer license must be distributed with this software. 79/* AUTHOR(S) 80/* Wietse Venema 81/* IBM T.J. Watson Research 82/* P.O. Box 704 83/* Yorktown Heights, NY 10598, USA 84/*--*/ 85 86/* System library. */ 87 88#include <sys_defs.h> 89#include <string.h> 90#include <ctype.h> 91 92/* Utility library. */ 93 94#include <msg.h> 95#include <split_at.h> 96#include <stringops.h> /* XXX util_utf8_enable */ 97#include <valid_utf8_hostname.h> 98 99/* Global library. */ 100 101#include <host_port.h> 102 103 /* 104 * Point-fix workaround. The libutil library should be email agnostic, but 105 * we can't rip up the library APIs in the stable releases. 106 */ 107#include <string.h> 108#ifdef STRCASECMP_IN_STRINGS_H 109#include <strings.h> 110#endif 111#define IPV6_COL "IPv6:" /* RFC 2821 */ 112#define IPV6_COL_LEN (sizeof(IPV6_COL) - 1) 113#define HAS_IPV6_COL(str) (strncasecmp((str), IPV6_COL, IPV6_COL_LEN) == 0) 114 115/* host_port - parse string into host and port, destroy string */ 116 117const char *host_port(char *buf, char **host, char *def_host, 118 char **port, char *def_service) 119{ 120 char *cp = buf; 121 int ipv6 = 0; 122 123 /*- 124 * [host]:port, [host]:, [host]. 125 * [ipv6:ipv6addr]:port, [ipv6:ipv6addr]:, [ipv6:ipv6addr]. 126 */ 127 if (*cp == '[') { 128 ++cp; 129 if ((ipv6 = HAS_IPV6_COL(cp)) != 0) 130 cp += IPV6_COL_LEN; 131 *host = cp; 132 if ((cp = split_at(cp, ']')) == 0) 133 return ("missing \"]\""); 134 if (*cp && *cp++ != ':') 135 return ("garbage after \"]\""); 136 if (ipv6 && !valid_ipv6_hostaddr(*host, DONT_GRIPE)) 137 return ("malformed IPv6 address"); 138 *port = *cp ? cp : def_service; 139 } 140 141 /* 142 * host:port, host:, host, :port, port. 143 */ 144 else { 145 if ((cp = split_at_right(buf, ':')) != 0) { 146 *host = *buf ? buf : def_host; 147 *port = *cp ? cp : def_service; 148 } else { 149 *host = def_host ? def_host : (*buf ? buf : 0); 150 *port = def_service ? def_service : (*buf ? buf : 0); 151 } 152 } 153 if (*host == 0) 154 return ("missing host information"); 155 if (*port == 0) 156 return ("missing service information"); 157 158 /* 159 * Final sanity checks. We're still sloppy, allowing bare numerical 160 * network addresses instead of requiring proper [ipaddress] forms. 161 */ 162 if (*host != def_host 163 && !valid_utf8_hostname(util_utf8_enable, *host, DONT_GRIPE) 164 && !valid_hostaddr(*host, DONT_GRIPE)) 165 return ("valid hostname or network address required"); 166 if (*port != def_service && ISDIGIT(**port) && !alldig(*port)) 167 return ("garbage after numerical service"); 168 return (0); 169} 170 171#ifdef TEST 172 173#include <vstream.h> 174#include <vstring.h> 175#include <vstring_vstream.h> 176 177#define STR(x) vstring_str(x) 178 179int main(int unused_argc, char **unused_argv) 180{ 181 VSTRING *in_buf = vstring_alloc(10); 182 VSTRING *parse_buf = vstring_alloc(10); 183 char *host; 184 char *port; 185 const char *err; 186 187 while (vstring_fgets_nonl(in_buf, VSTREAM_IN)) { 188 vstream_printf(">> %s\n", STR(in_buf)); 189 vstream_fflush(VSTREAM_OUT); 190 if (*STR(in_buf) == '#') 191 continue; 192 vstring_strcpy(parse_buf, STR(in_buf)); 193 if ((err = host_port(STR(parse_buf), &host, (char *) 0, &port, "default-service")) != 0) { 194 msg_warn("%s in %s", err, STR(in_buf)); 195 } else { 196 vstream_printf("host %s port %s\n", host, port); 197 vstream_fflush(VSTREAM_OUT); 198 } 199 } 200 vstring_free(in_buf); 201 vstring_free(parse_buf); 202 return (0); 203} 204 205#endif 206