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