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