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
host_port(char * buf,char ** host,char * def_host,char ** port,char * def_service)117 const 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
main(int unused_argc,char ** unused_argv)179 int 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