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