chiark / gitweb /
improve dump output for sockets
[elogind.git] / socket-util.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <assert.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <errno.h>
7 #include <stdlib.h>
8 #include <arpa/inet.h>
9 #include <stdio.h>
10 #include <net/if.h>
11
12 #include "macro.h"
13 #include "util.h"
14 #include "socket-util.h"
15
16 int socket_address_parse(SocketAddress *a, const char *s) {
17         int r;
18         char *e, *n;
19         unsigned u;
20
21         assert(a);
22         assert(s);
23
24         zero(*a);
25         a->type = SOCK_STREAM;
26
27         if (*s == '[') {
28                 /* IPv6 in [x:.....:z]:p notation */
29
30                 if (!(e = strchr(s+1, ']')))
31                         return -EINVAL;
32
33                 if (!(n = strndup(s+1, e-s-1)))
34                         return -ENOMEM;
35
36                 errno = 0;
37                 if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) {
38                         free(n);
39                         return errno != 0 ? -errno : -EINVAL;
40                 }
41
42                 free(n);
43
44                 e++;
45                 if (*e != ':')
46                         return -EINVAL;
47
48                 e++;
49                 if ((r = safe_atou(e, &u)) < 0)
50                         return r;
51
52                 if (u <= 0 || u > 0xFFFF)
53                         return -EINVAL;
54
55                 a->sockaddr.in6.sin6_family = AF_INET6;
56                 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
57                 a->size = sizeof(struct sockaddr_in6);
58
59         } else if (*s == '/') {
60                 /* AF_UNIX socket */
61
62                 size_t l;
63
64                 l = strlen(s);
65                 if (l >= sizeof(a->sockaddr.un.sun_path))
66                         return -EINVAL;
67
68                 a->sockaddr.un.sun_family = AF_UNIX;
69                 memcpy(a->sockaddr.un.sun_path, s, l);
70                 a->size = sizeof(sa_family_t) + l + 1;
71
72         } else if (*s == '=') {
73                 /* Abstract AF_UNIX socket */
74                 size_t l;
75
76                 l = strlen(s+1);
77                 if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
78                         return -EINVAL;
79
80                 a->sockaddr.un.sun_family = AF_UNIX;
81                 memcpy(a->sockaddr.un.sun_path+1, s+1, l);
82                 a->size = sizeof(struct sockaddr_un);
83
84         } else {
85
86                 if ((e = strchr(s, ':'))) {
87                         int r;
88
89                         if ((r = safe_atou(e+1, &u)) < 0)
90                                 return r;
91
92                         if (u <= 0 || u > 0xFFFF)
93                                 return -EINVAL;
94
95                         if (!(n = strndup(s, e-s)))
96                                 return -ENOMEM;
97
98                         /* IPv4 in w.x.y.z:p notation? */
99                         if ((r = inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr)) < 0) {
100                                 free(n);
101                                 return -errno;
102                         }
103
104                         if (r > 0) {
105                                 /* Gotcha, it's a traditional IPv4 address */
106                                 free(n);
107
108                                 a->sockaddr.in4.sin_family = AF_INET;
109                                 a->sockaddr.in4.sin_port = htons((uint16_t) u);
110                                 a->size = sizeof(struct sockaddr_in);
111                         } else {
112                                 unsigned idx;
113
114                                 /* Uh, our last resort, an interface name */
115                                 idx = if_nametoindex(n);
116                                 free(n);
117
118                                 if (idx == 0)
119                                         return -EINVAL;
120
121                                 a->sockaddr.in6.sin6_family = AF_INET6;
122                                 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
123                                 a->sockaddr.in6.sin6_scope_id = idx;
124                                 a->sockaddr.in6.sin6_addr = in6addr_any;
125                                 a->size = sizeof(struct sockaddr_in6);
126                         }
127                 } else {
128
129                         /* Just a port */
130                         if ((r = safe_atou(s, &u)) < 0)
131                                 return r;
132
133                         if (u <= 0 || u > 0xFFFF)
134                                 return -EINVAL;
135
136                         a->sockaddr.in6.sin6_family = AF_INET6;
137                         a->sockaddr.in6.sin6_port = htons((uint16_t) u);
138                         a->sockaddr.in6.sin6_addr = in6addr_any;
139                         a->size = sizeof(struct sockaddr_in6);
140                 }
141         }
142
143         return 0;
144 }
145
146 int socket_address_verify(const SocketAddress *a) {
147         assert(a);
148
149         switch (socket_address_family(a)) {
150                 case AF_INET:
151                         if (a->size != sizeof(struct sockaddr_in))
152                                 return -EINVAL;
153
154                         if (a->sockaddr.in4.sin_port == 0)
155                                 return -EINVAL;
156
157                         return 0;
158
159                 case AF_INET6:
160                         if (a->size != sizeof(struct sockaddr_in6))
161                                 return -EINVAL;
162
163                         if (a->sockaddr.in6.sin6_port == 0)
164                                 return -EINVAL;
165
166                         return 0;
167
168                 case AF_UNIX:
169                         if (a->size < sizeof(sa_family_t))
170                                 return -EINVAL;
171
172                         if (a->size > sizeof(sa_family_t)) {
173
174                                 if (a->sockaddr.un.sun_path[0] == 0) {
175                                         /* abstract */
176                                         if (a->size != sizeof(struct sockaddr_un))
177                                                 return -EINVAL;
178                                 } else {
179                                         char *e;
180
181                                         /* path */
182                                         if (!(e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path))))
183                                                 return -EINVAL;
184
185                                         if (a->size != sizeof(sa_family_t) + (e - a->sockaddr.un.sun_path) + 1)
186                                                 return -EINVAL;
187                                 }
188                         }
189
190                         return 0;
191
192                 default:
193                         return -EAFNOSUPPORT;
194         }
195 }
196
197 int socket_address_print(const SocketAddress *a, char **p) {
198         int r;
199         assert(a);
200         assert(p);
201
202         if ((r = socket_address_verify(a)) < 0)
203                 return r;
204
205         switch (socket_address_family(a)) {
206                 case AF_INET: {
207                         char *ret;
208
209                         if (!(ret = new(char, INET_ADDRSTRLEN+1+5+1)))
210                                 return -ENOMEM;
211
212                         if (!inet_ntop(AF_INET, &a->sockaddr.in4.sin_addr, ret, INET_ADDRSTRLEN)) {
213                                 free(ret);
214                                 return -errno;
215                         }
216
217                         sprintf(strchr(ret, 0), ":%u", ntohs(a->sockaddr.in4.sin_port));
218                         *p = ret;
219                         return 0;
220                 }
221
222                 case AF_INET6: {
223                         char *ret;
224
225                         if (!(ret = new(char, 1+INET6_ADDRSTRLEN+2+5+1)))
226                                 return -ENOMEM;
227
228                         ret[0] = '[';
229                         if (!inet_ntop(AF_INET6, &a->sockaddr.in6.sin6_addr, ret+1, INET6_ADDRSTRLEN)) {
230                                 free(ret);
231                                 return -errno;
232                         }
233
234                         sprintf(strchr(ret, 0), "]:%u", ntohs(a->sockaddr.in6.sin6_port));
235                         *p = ret;
236                         return 0;
237                 }
238
239                 case AF_UNIX: {
240                         char *ret;
241
242                         if (a->size <= sizeof(sa_family_t)) {
243
244                                 if (!(ret = strdup("<unamed>")))
245                                         return -ENOMEM;
246
247                         } else if (a->sockaddr.un.sun_path[0] == 0) {
248                                 /* abstract */
249
250                                 /* FIXME: We assume we can print the
251                                  * socket path here and that it hasn't
252                                  * more than one NUL byte. That is
253                                  * actually an invalid assumption */
254
255                                 if (!(ret = new(char, sizeof(a->sockaddr.un.sun_path)+1)))
256                                         return -ENOMEM;
257
258                                 ret[0] = '=';
259                                 memcpy(ret+1, a->sockaddr.un.sun_path+1, sizeof(a->sockaddr.un.sun_path)-1);
260                                 ret[sizeof(a->sockaddr.un.sun_path)] = 0;
261
262                         } else {
263
264                                 if (!(ret = strdup(a->sockaddr.un.sun_path)))
265                                         return -ENOMEM;
266                         }
267
268                         *p = ret;
269                         return 0;
270                 }
271
272                 default:
273                         return -EINVAL;
274         }
275 }
276
277 int socket_address_listen(const SocketAddress *a, int backlog, SocketAddressBindIPv6Only only, int *ret) {
278         int r, fd;
279         assert(a);
280         assert(ret);
281
282         if ((r = socket_address_verify(a)) < 0)
283                 return r;
284
285         if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0)
286                 return -errno;
287
288         if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
289                 int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
290
291                 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) {
292                         close_nointr(fd);
293                         return -errno;
294                 }
295         }
296
297         if (bind(fd, &a->sockaddr.sa, a->size) < 0) {
298                 close_nointr(fd);
299                 return -errno;
300         }
301
302         if (a->type == SOCK_STREAM)
303                 if (listen(fd, backlog) < 0) {
304                         close_nointr(fd);
305                         return -errno;
306                 }
307
308         *ret = fd;
309         return 0;
310 }