]>
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 | |
542563ba LP |
114 | /* Uh, our last resort, an interface name */ |
115 | idx = if_nametoindex(n); | |
116 | free(n); | |
117 | ||
83c60c9f | 118 | if (idx == 0) |
542563ba | 119 | return -EINVAL; |
42f4e3c4 | 120 | |
542563ba LP |
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; | |
83c60c9f | 124 | a->sockaddr.in6.sin6_addr = in6addr_any; |
542563ba LP |
125 | a->size = sizeof(struct sockaddr_in6); |
126 | } | |
42f4e3c4 LP |
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; | |
42f4e3c4 | 137 | a->sockaddr.in6.sin6_port = htons((uint16_t) u); |
83c60c9f | 138 | a->sockaddr.in6.sin6_addr = in6addr_any; |
42f4e3c4 LP |
139 | a->size = sizeof(struct sockaddr_in6); |
140 | } | |
141 | } | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
542563ba | 146 | int socket_address_verify(const SocketAddress *a) { |
42f4e3c4 LP |
147 | assert(a); |
148 | ||
542563ba | 149 | switch (socket_address_family(a)) { |
42f4e3c4 LP |
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 | ||
542563ba | 197 | int socket_address_print(const SocketAddress *a, char **p) { |
42f4e3c4 LP |
198 | int r; |
199 | assert(a); | |
200 | assert(p); | |
201 | ||
542563ba | 202 | if ((r = socket_address_verify(a)) < 0) |
42f4e3c4 LP |
203 | return r; |
204 | ||
542563ba | 205 | switch (socket_address_family(a)) { |
42f4e3c4 LP |
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 | ||
83c60c9f | 277 | int socket_address_listen(const SocketAddress *a, int backlog, SocketAddressBindIPv6Only only, int *ret) { |
42f4e3c4 LP |
278 | int r, fd; |
279 | assert(a); | |
83c60c9f | 280 | assert(ret); |
42f4e3c4 | 281 | |
542563ba | 282 | if ((r = socket_address_verify(a)) < 0) |
42f4e3c4 LP |
283 | return r; |
284 | ||
542563ba | 285 | if ((fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) < 0) |
42f4e3c4 LP |
286 | return -errno; |
287 | ||
542563ba LP |
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 | ||
42f4e3c4 LP |
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 | ||
83c60c9f | 308 | *ret = fd; |
42f4e3c4 LP |
309 | return 0; |
310 | } |