]>
Commit | Line | Data |
---|---|---|
42f4e3c4 LP |
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> | |
542563ba | 10 | #include <net/if.h> |
42f4e3c4 LP |
11 | |
12 | #include "macro.h" | |
13 | #include "util.h" | |
14 | #include "socket-util.h" | |
15 | ||
542563ba | 16 | int socket_address_parse(SocketAddress *a, const char *s) { |
42f4e3c4 LP |
17 | int r; |
18 | char *e, *n; | |
19 | unsigned u; | |
20 | ||
21 | assert(a); | |
22 | assert(s); | |
23 | ||
9152c765 | 24 | zero(*a); |
542563ba | 25 | a->type = SOCK_STREAM; |
42f4e3c4 LP |
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); | |
42f4e3c4 LP |
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, ':'))) { | |
542563ba LP |
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; | |
42f4e3c4 | 94 | |
42f4e3c4 LP |
95 | if (!(n = strndup(s, e-s))) |
96 | return -ENOMEM; | |
97 | ||
542563ba LP |
98 | /* IPv4 in w.x.y.z:p notation? */ |
99 | if ((r = inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr)) < 0) { | |
42f4e3c4 | 100 | free(n); |
542563ba | 101 | return -errno; |
42f4e3c4 LP |
102 | } |
103 | ||
542563ba LP |
104 | if (r > 0) { |
105 | /* Gotcha, it's a traditional IPv4 address */ | |
106 | free(n); | |
42f4e3c4 | 107 | |
542563ba LP |
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; | |
42f4e3c4 | 113 | |
acbb0225 LP |
114 | if (strlen(n) > IF_NAMESIZE-1) { |
115 | free(n); | |
116 | return -EINVAL; | |
117 | } | |
118 | ||
542563ba LP |
119 | /* Uh, our last resort, an interface name */ |
120 | idx = if_nametoindex(n); | |
121 | free(n); | |
122 | ||
83c60c9f | 123 | if (idx == 0) |
542563ba | 124 | return -EINVAL; |
42f4e3c4 | 125 | |
542563ba LP |
126 | a->sockaddr.in6.sin6_family = AF_INET6; |
127 | a->sockaddr.in6.sin6_port = htons((uint16_t) u); | |
128 | a->sockaddr.in6.sin6_scope_id = idx; | |
83c60c9f | 129 | a->sockaddr.in6.sin6_addr = in6addr_any; |
542563ba | 130 | a->size = sizeof(struct sockaddr_in6); |
acbb0225 | 131 | |
542563ba | 132 | } |
42f4e3c4 LP |
133 | } else { |
134 | ||
135 | /* Just a port */ | |
136 | if ((r = safe_atou(s, &u)) < 0) | |
137 | return r; | |
138 | ||
139 | if (u <= 0 || u > 0xFFFF) | |
140 | return -EINVAL; | |
141 | ||
142 | a->sockaddr.in6.sin6_family = AF_INET6; | |
42f4e3c4 | 143 | a->sockaddr.in6.sin6_port = htons((uint16_t) u); |
83c60c9f | 144 | a->sockaddr.in6.sin6_addr = in6addr_any; |
42f4e3c4 LP |
145 | a->size = sizeof(struct sockaddr_in6); |
146 | } | |
147 | } | |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
542563ba | 152 | int socket_address_verify(const SocketAddress *a) { |
42f4e3c4 LP |
153 | assert(a); |
154 | ||
542563ba | 155 | switch (socket_address_family(a)) { |
42f4e3c4 LP |
156 | case AF_INET: |
157 | if (a->size != sizeof(struct sockaddr_in)) | |
158 | return -EINVAL; | |
159 | ||
160 | if (a->sockaddr.in4.sin_port == 0) | |
161 | return -EINVAL; | |
162 | ||
163 | return 0; | |
164 | ||
165 | case AF_INET6: | |
166 | if (a->size != sizeof(struct sockaddr_in6)) | |
167 | return -EINVAL; | |
168 | ||
169 | if (a->sockaddr.in6.sin6_port == 0) | |
170 | return -EINVAL; | |
171 | ||
172 | return 0; | |
173 | ||
174 | case AF_UNIX: | |
175 | if (a->size < sizeof(sa_family_t)) | |
176 | return -EINVAL; | |
177 | ||
178 | if (a->size > sizeof(sa_family_t)) { | |
179 | ||
180 | if (a->sockaddr.un.sun_path[0] == 0) { | |
181 | /* abstract */ | |
182 | if (a->size != sizeof(struct sockaddr_un)) | |
183 | return -EINVAL; | |
184 | } else { | |
185 | char *e; | |
186 | ||
187 | /* path */ | |
188 | if (!(e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path)))) | |
189 | return -EINVAL; | |
190 | ||
191 | if (a->size != sizeof(sa_family_t) + (e - a->sockaddr.un.sun_path) + 1) | |
192 | return -EINVAL; | |
193 | } | |
194 | } | |
195 | ||
196 | return 0; | |
197 | ||
198 | default: | |
199 | return -EAFNOSUPPORT; | |
200 | } | |
201 | } | |
202 | ||
542563ba | 203 | int socket_address_print(const SocketAddress *a, char **p) { |
42f4e3c4 LP |
204 | int r; |
205 | assert(a); | |
206 | assert(p); | |
207 | ||
542563ba | 208 | if ((r = socket_address_verify(a)) < 0) |
42f4e3c4 LP |
209 | return r; |
210 | ||
542563ba | 211 | switch (socket_address_family(a)) { |
42f4e3c4 LP |
212 | case AF_INET: { |
213 | char *ret; | |
214 | ||
215 | if (!(ret = new(char, INET_ADDRSTRLEN+1+5+1))) | |
216 | return -ENOMEM; | |
217 | ||
218 | if (!inet_ntop(AF_INET, &a->sockaddr.in4.sin_addr, ret, INET_ADDRSTRLEN)) { | |
219 | free(ret); | |
220 | return -errno; | |
221 | } | |
222 | ||
223 | sprintf(strchr(ret, 0), ":%u", ntohs(a->sockaddr.in4.sin_port)); | |
224 | *p = ret; | |
225 | return 0; | |
226 | } | |
227 | ||
228 | case AF_INET6: { | |
229 | char *ret; | |
230 | ||
231 | if (!(ret = new(char, 1+INET6_ADDRSTRLEN+2+5+1))) | |
232 | return -ENOMEM; | |
233 | ||
234 | ret[0] = '['; | |
235 | if (!inet_ntop(AF_INET6, &a->sockaddr.in6.sin6_addr, ret+1, INET6_ADDRSTRLEN)) { | |
236 | free(ret); | |
237 | return -errno; | |
238 | } | |
239 | ||
240 | sprintf(strchr(ret, 0), "]:%u", ntohs(a->sockaddr.in6.sin6_port)); | |
241 | *p = ret; | |
242 | return 0; | |
243 | } | |
244 | ||
245 | case AF_UNIX: { | |
246 | char *ret; | |
247 | ||
248 | if (a->size <= sizeof(sa_family_t)) { | |
249 | ||
250 | if (!(ret = strdup("<unamed>"))) | |
251 | return -ENOMEM; | |
252 | ||
253 | } else if (a->sockaddr.un.sun_path[0] == 0) { | |
254 | /* abstract */ | |
255 | ||
256 | /* FIXME: We assume we can print the | |
257 | * socket path here and that it hasn't | |
258 | * more than one NUL byte. That is | |
259 | * actually an invalid assumption */ | |
260 | ||
261 | if (!(ret = new(char, sizeof(a->sockaddr.un.sun_path)+1))) | |
262 | return -ENOMEM; | |
263 | ||
264 | ret[0] = '='; | |
265 | memcpy(ret+1, a->sockaddr.un.sun_path+1, sizeof(a->sockaddr.un.sun_path)-1); | |
266 | ret[sizeof(a->sockaddr.un.sun_path)] = 0; | |
267 | ||
268 | } else { | |
269 | ||
270 | if (!(ret = strdup(a->sockaddr.un.sun_path))) | |
271 | return -ENOMEM; | |
272 | } | |
273 | ||
274 | *p = ret; | |
275 | return 0; | |
276 | } | |
277 | ||
278 | default: | |
279 | return -EINVAL; | |
280 | } | |
281 | } | |
282 | ||
acbb0225 LP |
283 | int socket_address_listen(const SocketAddress *a, int backlog, SocketAddressBindIPv6Only only, const char *bind_to_device, int *ret) { |
284 | int r, fd, one; | |
42f4e3c4 | 285 | assert(a); |
83c60c9f | 286 | assert(ret); |
42f4e3c4 | 287 | |
542563ba | 288 | if ((r = socket_address_verify(a)) < 0) |
42f4e3c4 LP |
289 | return r; |
290 | ||
542563ba | 291 | if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0) |
42f4e3c4 LP |
292 | return -errno; |
293 | ||
542563ba LP |
294 | if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) { |
295 | int flag = only == SOCKET_ADDRESS_IPV6_ONLY; | |
296 | ||
acbb0225 LP |
297 | if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) |
298 | goto fail; | |
542563ba LP |
299 | } |
300 | ||
acbb0225 LP |
301 | if (bind_to_device) |
302 | if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0) | |
303 | goto fail; | |
304 | ||
305 | one = 1; | |
306 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) | |
307 | goto fail; | |
308 | ||
309 | if (bind(fd, &a->sockaddr.sa, a->size) < 0) | |
310 | goto fail; | |
42f4e3c4 LP |
311 | |
312 | if (a->type == SOCK_STREAM) | |
acbb0225 LP |
313 | if (listen(fd, backlog) < 0) |
314 | goto fail; | |
42f4e3c4 | 315 | |
83c60c9f | 316 | *ret = fd; |
42f4e3c4 | 317 | return 0; |
acbb0225 LP |
318 | |
319 | fail: | |
320 | r = -errno; | |
321 | close_nointr(fd); | |
322 | return r; | |
42f4e3c4 | 323 | } |