1 /* grecs - Gray's Extensible Configuration System
2    Copyright (C) 2007-2017 Sergey Poznyakoff
3 
4    Grecs is free software; you can redistribute it and/or modify it
5    under the terms of the GNU General Public License as published by the
6    Free Software Foundation; either version 3 of the License, or (at your
7    option) any later version.
8 
9    Grecs is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with Grecs. If not, see <http://www.gnu.org/licenses/>. */
16 
17 /* Network-specific functions */
18 
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22 #include <stddef.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <netdb.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 #include "grecs.h"
34 
35 struct grecs_sockaddr *
grecs_sockaddr_new(size_t s)36 grecs_sockaddr_new(size_t s)
37 {
38 	struct grecs_sockaddr *sp = grecs_malloc(sizeof(*sp));
39 	sp->next = NULL;
40 	sp->str = NULL;
41 	sp->sa = grecs_zalloc(s);
42 	sp->len = s;
43 	return sp;
44 }
45 
46 void
grecs_sockaddr_free(struct grecs_sockaddr * p)47 grecs_sockaddr_free(struct grecs_sockaddr *p)
48 {
49 	while (p) {
50 		struct grecs_sockaddr *next = p->next;
51 		free(p->sa);
52 		free(p->str);
53 		free(p);
54 		p = next;
55 	}
56 }
57 
58 static int
parse_unix(struct grecs_sockaddr ** ret,const char * arg,const char * addrstr,struct grecs_sockaddr_hints * gh,grecs_locus_t const * locus)59 parse_unix(struct grecs_sockaddr **ret, const char *arg, const char *addrstr,
60 	   struct grecs_sockaddr_hints *gh, grecs_locus_t const *locus)
61 {
62 	struct sockaddr_un *s_un;
63 	size_t slen = strlen(addrstr);
64 	struct grecs_sockaddr *sp;
65 
66 	if (slen >= sizeof s_un->sun_path) {
67 		grecs_error(locus, 0, _("socket path name too long: %s"), arg);
68 		return -1;
69 	}
70 
71 	sp = grecs_sockaddr_new(sizeof(s_un[0]));
72 	s_un = (struct sockaddr_un *) sp->sa;
73 	s_un->sun_family = AF_UNIX;
74 	strcpy(s_un->sun_path, addrstr);
75 
76 	*ret = sp;
77 	return 0;
78 }
79 
80 static int
parse_inet(struct grecs_sockaddr ** ret,int family,const char * arg,const char * addrstr,struct grecs_sockaddr_hints * gh,grecs_locus_t const * locus)81 parse_inet(struct grecs_sockaddr **ret,
82 	   int family, const char *arg, const char *addrstr,
83 	   struct grecs_sockaddr_hints *gh, grecs_locus_t const *locus)
84 {
85 	int rc;
86 	struct addrinfo hints;
87 	struct addrinfo *res, *ap;
88 	const char *node = NULL;
89 	char *nodebuf = NULL;
90 	const char *service = NULL;
91 	struct grecs_sockaddr *head = NULL, *tail = NULL;
92 	char portbuf[64];
93 
94 	memset(&hints, 0, sizeof(hints));
95 	hints.ai_family = family;
96 	hints.ai_socktype = SOCK_STREAM;
97 
98 	if ((family == AF_INET6 || family == AF_UNSPEC)
99 	    && addrstr[0] == '[') {
100 		char *p = strchr(addrstr + 1, ']');
101 		if (p && p > addrstr + 1) {
102 			size_t len;
103 
104 			addrstr++;
105 			len = p - addrstr;
106 			nodebuf = grecs_malloc(len + 1);
107 			memcpy(nodebuf, addrstr, len);
108 			nodebuf[len] = 0;
109 			node = nodebuf;
110 			service = p + 1;
111 			family = AF_INET6;
112 		} else
113 			service = strchr(addrstr, ':');
114 	} else
115 		service = strrchr(addrstr, ':');
116 
117 	if (service && *service) {
118 		if (*service != ':') {
119 			grecs_error(locus, 0,
120 				    _("%s: garbage near %s"), arg, service);
121 			return -1;
122 		}
123 		service++;
124 	}
125 
126 	if (!node) {
127 		if (service) {
128 			size_t len = service - addrstr - 1;
129 
130 			if (len == 0)
131 				node = NULL;
132 			else {
133 				nodebuf = grecs_malloc(len + 1);
134 				memcpy(nodebuf, addrstr, len);
135 				nodebuf[len] = 0;
136 				node = nodebuf;
137 			}
138 		} else {
139 			if (grecs_str_is_ipaddr(addrstr))
140 				node = addrstr;
141 			else if (grecs_str_is_num(addrstr)) {
142 				service = addrstr;
143 				hints.ai_flags |= AI_NUMERICSERV;
144 			}
145 		}
146 	}
147 
148 	if (!service || !*service) {
149 		if (!node && addrstr[0])
150 			node = addrstr;
151 		if (gh->flags & GRECS_HINT_SERVICE) {
152 			service = gh->service;
153 		} else if (gh->flags & GRECS_HINT_PORT) {
154 			snprintf(portbuf, sizeof portbuf, "%hu", gh->port);
155 			service = portbuf;
156 			hints.ai_flags |= AI_NUMERICSERV;
157 		} else if (!(gh->flags & GRECS_AH_PASSIVE)) {
158 			grecs_error(locus, 0,
159 				    _("service not specified: %s"), arg);
160 			return -1;
161 		}
162 	}
163 
164 	if (!node) {
165 		if (gh->flags & GRECS_AH_PASSIVE)
166 			hints.ai_flags |= AI_PASSIVE;
167 	}
168 
169 	rc = getaddrinfo(node, service, &hints, &res);
170 	free(nodebuf);
171 	switch (rc) {
172 	case 0:
173 		break;
174 	case EAI_SYSTEM:
175 		grecs_error(locus, 0,
176 			    _("%s: cannot parse address: %s"),
177 			    arg, strerror(errno));
178 		break;
179 	case EAI_BADFLAGS:
180 	case EAI_SOCKTYPE:
181 		grecs_error(locus, 0,
182 			    "%s:%d: internal error converting %s",
183 			    __FILE__,__LINE__,arg);
184 		break;
185 	case EAI_MEMORY:
186 		grecs_alloc_die();
187 	default:
188 		grecs_error(locus, 0,
189 			    "%s: %s", arg, gai_strerror(rc));
190 		return -1;
191 	}
192 
193 	for (ap = res; ap; ap = ap->ai_next) {
194 		if (family == AF_UNSPEC || ap->ai_addr->sa_family == family) {
195 			struct grecs_sockaddr *sp =
196 				grecs_sockaddr_new(ap->ai_addrlen);
197 			memcpy(sp->sa, ap->ai_addr, ap->ai_addrlen);
198 			sp->len = ap->ai_addrlen;
199 			if (!head)
200 				head = sp;
201 			else
202 				tail->next = sp;
203 			tail = sp;
204 		}
205 	}
206 	freeaddrinfo(res);
207 	*ret = head;
208 	return 0;
209 }
210 
211 static int
parse_inet4(struct grecs_sockaddr ** ret,const char * arg,const char * addrstr,struct grecs_sockaddr_hints * gh,grecs_locus_t const * locus)212 parse_inet4(struct grecs_sockaddr **ret, const char *arg, const char *addrstr,
213 	    struct grecs_sockaddr_hints *gh, grecs_locus_t const *locus)
214 {
215 	return parse_inet(ret, AF_INET, arg, addrstr, gh, locus);
216 }
217 
218 static int
parse_inet6(struct grecs_sockaddr ** ret,const char * arg,const char * addrstr,struct grecs_sockaddr_hints * gh,grecs_locus_t const * locus)219 parse_inet6(struct grecs_sockaddr **ret, const char *arg, const char *addrstr,
220 	    struct grecs_sockaddr_hints *gh, grecs_locus_t const *locus)
221 {
222 	return parse_inet(ret, AF_INET6, arg, addrstr, gh, locus);
223 }
224 
225 struct schemetab {
226 	const char *scheme;
227 	size_t len;
228 	int (*parser)(struct grecs_sockaddr **ret,
229 		      const char *arg, const char *addr,
230 		      struct grecs_sockaddr_hints *gh,
231 		      grecs_locus_t const *locus);
232 };
233 
234 struct schemetab schemetab[] = {
235 	{ "inet",  4, parse_inet4 },
236 	{ "inet4", 5, parse_inet4 },
237 	{ "inet6", 5, parse_inet6 },
238 	{ "unix",  4, parse_unix },
239 	{ NULL }
240 };
241 
242 int
grecs_str_to_sockaddr(struct grecs_sockaddr ** sap,const char * arg,struct grecs_sockaddr_hints * gh,grecs_locus_t const * locus)243 grecs_str_to_sockaddr(struct grecs_sockaddr **sap,
244 		      const char *arg, struct grecs_sockaddr_hints *gh,
245 		      grecs_locus_t const *locus)
246 {
247 	char *p;
248 	struct grecs_sockaddr_hints ghints;
249 
250 	if (!gh) {
251 		memset(&ghints, 0, sizeof(ghints));
252 		if (grecs_default_port) {
253 			ghints.flags = GRECS_HINT_PORT;
254 			ghints.port = ntohs(grecs_default_port);
255 		}
256 		gh = &ghints;
257 	}
258 
259 	p = strchr(arg, ':');
260 	if (p && p > arg && p[1] == '/' && p[2] == '/') {
261 		size_t len = p - arg;
262 		struct schemetab *sp;
263 
264 		for (sp = schemetab; sp->scheme; sp++)
265 			if (len == sp->len &&
266 			    memcmp(arg, sp->scheme, len) == 0)
267 				return sp->parser(sap, arg, p + 3, gh, locus);
268 		grecs_error(locus, 0,
269 			    _("unknown or unsupported scheme: %s"), arg);
270 		return -1;
271 	}
272 
273 	if (arg[0] == '/')
274 		return parse_unix(sap, arg, arg, gh, locus);
275 	else if (strlen(arg) > 5 && memcmp(arg, "unix:", 5) == 0) {
276 		if (arg[5] != '/')
277 			grecs_error(locus, 0,
278 				    _("%s: UNIX socket must be an absolute file name"),
279 				    arg);
280 		return parse_unix(sap, arg, arg + 5, gh, locus);
281 	}
282 
283 	return parse_inet(sap, AF_UNSPEC, arg, arg, gh, locus);
284 }
285 
286 #define S_UN_NAME(sa, salen) \
287 	((salen < offsetof(struct sockaddr_un,sun_path)) ?      \
288 	 "" : (sa)->sun_path)
289 
290 static int
sockaddr_str(struct sockaddr * sa,socklen_t salen,char ** pbuf,size_t * psz)291 sockaddr_str(struct sockaddr *sa, socklen_t salen, char **pbuf, size_t *psz)
292 {
293 	int rc;
294 
295 	switch (sa->sa_family) {
296 	case AF_INET:
297 	case AF_INET6: {
298 		char host[NI_MAXHOST];
299 		char srv[NI_MAXSERV];
300 		if (getnameinfo(sa, salen,
301 				host, sizeof(host), srv, sizeof(srv),
302 				NI_NUMERICHOST|NI_NUMERICSERV) == 0)
303 			rc = grecs_asprintf(pbuf, psz, "%s://%s:%s",
304 					    sa->sa_family == AF_INET ?
305 					    "inet" : "inet6",
306 					    host, srv);
307 		else
308 			rc = grecs_asprintf(pbuf, psz,
309 					    "%s://[getnameinfo failed]",
310 					    sa->sa_family == AF_INET ?
311 					    "inet" : "inet6");
312 		break;
313 	}
314 	case AF_UNIX: {
315 		struct sockaddr_un *s_un = (struct sockaddr_un *)sa;
316 		if (S_UN_NAME(s_un, salen)[0] == 0)
317 			rc = grecs_asprintf(pbuf, psz,
318 					    "unix://[anonymous socket]");
319 		else
320 			rc = grecs_asprintf(pbuf, psz, "unix://%s",
321 					    s_un->sun_path);
322 		break;
323 	}
324 
325 	default:
326 		rc = grecs_asprintf(pbuf, psz, "family:%d", sa->sa_family);
327 	}
328 	return rc;
329 }
330 
331 char const *
grecs_sockaddr_str(struct grecs_sockaddr * sa)332 grecs_sockaddr_str(struct grecs_sockaddr *sa)
333 {
334 	if (!sa->str) {
335 		size_t sz = 0;
336 		if (sockaddr_str(sa->sa, sa->len, &sa->str, &sz)) {
337 			//FIXME
338 			abort();
339 		}
340 	}
341 	return sa->str;
342 }
343 
344