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