/*++ /* NAME /* host_port 3 /* SUMMARY /* split string into host and port, destroy string /* SYNOPSIS /* #include /* /* const char *host_port(string, host, def_host, port, def_service) /* char *string; /* char **host; /* char *def_host; /* char **port; /* char *def_service; /* DESCRIPTION /* host_port() splits a string into substrings with the host /* name or address, and the service name or port number. /* The input string is modified. /* /* Host/domain names are validated with valid_utf8_hostname(), /* and host addresses are validated with valid_hostaddr(). /* /* The following input formats are understood (null means /* a null pointer argument): /* /* When def_service is not null, and def_host is null: /* /* [host]:port, [host]:, [host] /* /* host:port, host:, host /* /* When def_host is not null, and def_service is null: /* /* :port, port /* /* Other combinations of def_service and def_host are /* not supported and produce undefined results. /* DIAGNOSTICS /* The result is a null pointer in case of success. /* In case of problems the result is a string pointer with /* the problem type. /* CLIENT EXAMPLE /* .ad /* .fi /* Typical client usage allows the user to omit the service port, /* in which case the client connects to a pre-determined default /* port: /* .nf /* .na /* /* buf = mystrdup(endpoint); /* if ((parse_error = host_port(buf, &host, NULL, &port, defport)) != 0) /* msg_fatal("%s in \"%s\"", parse_error, endpoint); /* if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res)) != 0) /* msg_fatal("%s: %s", endpoint, MAI_STRERROR(aierr)); /* myfree(buf); /* SERVER EXAMPLE /* .ad /* .fi /* Typical server usage allows the user to omit the host, meaning /* listen on all available network addresses: /* .nf /* .na /* /* buf = mystrdup(endpoint); /* if ((parse_error = host_port(buf, &host, "", &port, NULL)) != 0) /* msg_fatal("%s in \"%s\"", parse_error, endpoint); /* if (*host == 0) /* host = 0; /* if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res)) != 0) /* msg_fatal("%s: %s", endpoint, MAI_STRERROR(aierr)); /* myfree(buf); /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* AUTHOR(S) /* Wietse Venema /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA /*--*/ /* System library. */ #include #include #include /* Utility library. */ #include #include #include /* XXX util_utf8_enable */ #include /* Global library. */ #include /* * Point-fix workaround. The libutil library should be email agnostic, but * we can't rip up the library APIs in the stable releases. */ #include #ifdef STRCASECMP_IN_STRINGS_H #include #endif #define IPV6_COL "IPv6:" /* RFC 2821 */ #define IPV6_COL_LEN (sizeof(IPV6_COL) - 1) #define HAS_IPV6_COL(str) (strncasecmp((str), IPV6_COL, IPV6_COL_LEN) == 0) /* host_port - parse string into host and port, destroy string */ const char *host_port(char *buf, char **host, char *def_host, char **port, char *def_service) { char *cp = buf; int ipv6 = 0; /*- * [host]:port, [host]:, [host]. * [ipv6:ipv6addr]:port, [ipv6:ipv6addr]:, [ipv6:ipv6addr]. */ if (*cp == '[') { ++cp; if ((ipv6 = HAS_IPV6_COL(cp)) != 0) cp += IPV6_COL_LEN; *host = cp; if ((cp = split_at(cp, ']')) == 0) return ("missing \"]\""); if (*cp && *cp++ != ':') return ("garbage after \"]\""); if (ipv6 && !valid_ipv6_hostaddr(*host, DONT_GRIPE)) return ("malformed IPv6 address"); *port = *cp ? cp : def_service; } /* * host:port, host:, host, :port, port. */ else { if ((cp = split_at_right(buf, ':')) != 0) { *host = *buf ? buf : def_host; *port = *cp ? cp : def_service; } else { *host = def_host ? def_host : (*buf ? buf : 0); *port = def_service ? def_service : (*buf ? buf : 0); } } if (*host == 0) return ("missing host information"); if (*port == 0) return ("missing service information"); /* * Final sanity checks. We're still sloppy, allowing bare numerical * network addresses instead of requiring proper [ipaddress] forms. */ if (*host != def_host && !valid_utf8_hostname(util_utf8_enable, *host, DONT_GRIPE) && !valid_hostaddr(*host, DONT_GRIPE)) return ("valid hostname or network address required"); if (*port != def_service && ISDIGIT(**port) && !alldig(*port)) return ("garbage after numerical service"); return (0); } #ifdef TEST #include #include #include #define STR(x) vstring_str(x) int main(int unused_argc, char **unused_argv) { VSTRING *in_buf = vstring_alloc(10); VSTRING *parse_buf = vstring_alloc(10); char *host; char *port; const char *err; while (vstring_fgets_nonl(in_buf, VSTREAM_IN)) { vstream_printf(">> %s\n", STR(in_buf)); vstream_fflush(VSTREAM_OUT); if (*STR(in_buf) == '#') continue; vstring_strcpy(parse_buf, STR(in_buf)); if ((err = host_port(STR(parse_buf), &host, (char *) 0, &port, "default-service")) != 0) { msg_warn("%s in %s", err, STR(in_buf)); } else { vstream_printf("host %s port %s\n", host, port); vstream_fflush(VSTREAM_OUT); } } vstring_free(in_buf); vstring_free(parse_buf); return (0); } #endif