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