1 | // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "platform/globals.h" |
6 | #if defined(HOST_OS_FUCHSIA) |
7 | |
8 | #include "bin/socket_base.h" |
9 | |
10 | #include <errno.h> |
11 | #include <fuchsia/netstack/cpp/fidl.h> |
12 | #include <ifaddrs.h> |
13 | #include <lib/sys/cpp/service_directory.h> |
14 | #include <net/if.h> |
15 | #include <netinet/tcp.h> |
16 | #include <stdio.h> |
17 | #include <stdlib.h> |
18 | #include <string.h> |
19 | #include <sys/stat.h> |
20 | #include <unistd.h> |
21 | #include <vector> |
22 | |
23 | #include "bin/eventhandler.h" |
24 | #include "bin/fdutils.h" |
25 | #include "bin/file.h" |
26 | #include "bin/socket_base_fuchsia.h" |
27 | #include "platform/signal_blocker.h" |
28 | |
29 | // #define SOCKET_LOG_INFO 1 |
30 | // #define SOCKET_LOG_ERROR 1 |
31 | |
32 | // define SOCKET_LOG_ERROR to get log messages only for errors. |
33 | // define SOCKET_LOG_INFO to get log messages for both information and errors. |
34 | #if defined(SOCKET_LOG_INFO) || defined(SOCKET_LOG_ERROR) |
35 | #define LOG_ERR(msg, ...) \ |
36 | { \ |
37 | int err = errno; \ |
38 | Syslog::PrintErr("Dart Socket ERROR: %s:%d: " msg, __FILE__, __LINE__, \ |
39 | ##__VA_ARGS__); \ |
40 | errno = err; \ |
41 | } |
42 | #if defined(SOCKET_LOG_INFO) |
43 | #define LOG_INFO(msg, ...) \ |
44 | Syslog::Print("Dart Socket INFO: %s:%d: " msg, __FILE__, __LINE__, \ |
45 | ##__VA_ARGS__) |
46 | #else |
47 | #define LOG_INFO(msg, ...) |
48 | #endif // defined(SOCKET_LOG_INFO) |
49 | #else |
50 | #define LOG_ERR(msg, ...) |
51 | #define LOG_INFO(msg, ...) |
52 | #endif // defined(SOCKET_LOG_INFO) || defined(SOCKET_LOG_ERROR) |
53 | |
54 | namespace dart { |
55 | namespace bin { |
56 | |
57 | SocketAddress::SocketAddress(struct sockaddr* sa, bool unnamed_unix_socket) { |
58 | // Fuchsia does not support unix domain sockets. |
59 | if (unnamed_unix_socket) { |
60 | FATAL("Fuchsia does not support unix domain sockets." ); |
61 | } |
62 | ASSERT(INET6_ADDRSTRLEN >= INET_ADDRSTRLEN); |
63 | if (!SocketBase::FormatNumericAddress(*reinterpret_cast<RawAddr*>(sa), |
64 | as_string_, INET6_ADDRSTRLEN)) { |
65 | as_string_[0] = 0; |
66 | } |
67 | socklen_t salen = GetAddrLength(*reinterpret_cast<RawAddr*>(sa)); |
68 | memmove(reinterpret_cast<void*>(&addr_), sa, salen); |
69 | } |
70 | |
71 | static fidl::SynchronousInterfacePtr<fuchsia::netstack::Netstack> netstack; |
72 | static std::once_flag once; |
73 | |
74 | bool SocketBase::Initialize() { |
75 | static zx_status_t status; |
76 | std::call_once(once, [&]() { |
77 | auto directory = sys::ServiceDirectory::CreateFromNamespace(); |
78 | status = directory->Connect(netstack.NewRequest()); |
79 | if (status != ZX_OK) { |
80 | Syslog::PrintErr( |
81 | "Initialize: connecting to fuchsia.netstack failed: %s\n" , |
82 | zx_status_get_string(status)); |
83 | } |
84 | }); |
85 | return status == ZX_OK; |
86 | } |
87 | |
88 | bool SocketBase::FormatNumericAddress(const RawAddr& addr, |
89 | char* address, |
90 | int len) { |
91 | socklen_t salen = SocketAddress::GetAddrLength(addr); |
92 | LOG_INFO("SocketBase::FormatNumericAddress: calling getnameinfo\n" ); |
93 | return (NO_RETRY_EXPECTED(getnameinfo(&addr.addr, salen, address, len, NULL, |
94 | 0, NI_NUMERICHOST) == 0)); |
95 | } |
96 | |
97 | bool SocketBase::IsBindError(intptr_t error_number) { |
98 | return error_number == EADDRINUSE || error_number == EADDRNOTAVAIL || |
99 | error_number == EINVAL; |
100 | } |
101 | |
102 | intptr_t SocketBase::Available(intptr_t fd) { |
103 | IOHandle* handle = reinterpret_cast<IOHandle*>(fd); |
104 | return handle->AvailableBytes(); |
105 | } |
106 | |
107 | intptr_t SocketBase::Read(intptr_t fd, |
108 | void* buffer, |
109 | intptr_t num_bytes, |
110 | SocketOpKind sync) { |
111 | IOHandle* handle = reinterpret_cast<IOHandle*>(fd); |
112 | ASSERT(handle->fd() >= 0); |
113 | LOG_INFO("SocketBase::Read: calling read(%ld, %p, %ld)\n" , handle->fd(), |
114 | buffer, num_bytes); |
115 | intptr_t read_bytes = handle->Read(buffer, num_bytes); |
116 | ASSERT(EAGAIN == EWOULDBLOCK); |
117 | if ((sync == kAsync) && (read_bytes == -1) && (errno == EWOULDBLOCK)) { |
118 | // If the read would block we need to retry and therefore return 0 |
119 | // as the number of bytes written. |
120 | read_bytes = 0; |
121 | } else if (read_bytes == -1) { |
122 | LOG_ERR("SocketBase::Read: read(%ld, %p, %ld) failed\n" , handle->fd(), |
123 | buffer, num_bytes); |
124 | } else { |
125 | LOG_INFO("SocketBase::Read: read(%ld, %p, %ld) succeeded\n" , handle->fd(), |
126 | buffer, num_bytes); |
127 | } |
128 | return read_bytes; |
129 | } |
130 | |
131 | intptr_t SocketBase::RecvFrom(intptr_t fd, |
132 | void* buffer, |
133 | intptr_t num_bytes, |
134 | RawAddr* addr, |
135 | SocketOpKind sync) { |
136 | errno = ENOSYS; |
137 | return -1; |
138 | } |
139 | |
140 | bool SocketBase::AvailableDatagram(intptr_t fd, |
141 | void* buffer, |
142 | intptr_t num_bytes) { |
143 | return false; |
144 | } |
145 | |
146 | intptr_t SocketBase::Write(intptr_t fd, |
147 | const void* buffer, |
148 | intptr_t num_bytes, |
149 | SocketOpKind sync) { |
150 | IOHandle* handle = reinterpret_cast<IOHandle*>(fd); |
151 | ASSERT(handle->fd() >= 0); |
152 | LOG_INFO("SocketBase::Write: calling write(%ld, %p, %ld)\n" , handle->fd(), |
153 | buffer, num_bytes); |
154 | intptr_t written_bytes = handle->Write(buffer, num_bytes); |
155 | ASSERT(EAGAIN == EWOULDBLOCK); |
156 | if ((sync == kAsync) && (written_bytes == -1) && (errno == EWOULDBLOCK)) { |
157 | // If the would block we need to retry and therefore return 0 as |
158 | // the number of bytes written. |
159 | written_bytes = 0; |
160 | } else if (written_bytes == -1) { |
161 | LOG_ERR("SocketBase::Write: write(%ld, %p, %ld) failed\n" , handle->fd(), |
162 | buffer, num_bytes); |
163 | } else { |
164 | LOG_INFO("SocketBase::Write: write(%ld, %p, %ld) succeeded\n" , handle->fd(), |
165 | buffer, num_bytes); |
166 | } |
167 | return written_bytes; |
168 | } |
169 | |
170 | intptr_t SocketBase::SendTo(intptr_t fd, |
171 | const void* buffer, |
172 | intptr_t num_bytes, |
173 | const RawAddr& addr, |
174 | SocketOpKind sync) { |
175 | errno = ENOSYS; |
176 | return -1; |
177 | } |
178 | |
179 | intptr_t SocketBase::GetPort(intptr_t fd) { |
180 | IOHandle* handle = reinterpret_cast<IOHandle*>(fd); |
181 | ASSERT(handle->fd() >= 0); |
182 | RawAddr raw; |
183 | socklen_t size = sizeof(raw); |
184 | LOG_INFO("SocketBase::GetPort: calling getsockname(%ld)\n" , handle->fd()); |
185 | if (NO_RETRY_EXPECTED(getsockname(handle->fd(), &raw.addr, &size))) { |
186 | return 0; |
187 | } |
188 | return SocketAddress::GetAddrPort(raw); |
189 | } |
190 | |
191 | SocketAddress* SocketBase::GetRemotePeer(intptr_t fd, intptr_t* port) { |
192 | IOHandle* handle = reinterpret_cast<IOHandle*>(fd); |
193 | ASSERT(handle->fd() >= 0); |
194 | RawAddr raw; |
195 | socklen_t size = sizeof(raw); |
196 | if (NO_RETRY_EXPECTED(getpeername(handle->fd(), &raw.addr, &size))) { |
197 | return NULL; |
198 | } |
199 | *port = SocketAddress::GetAddrPort(raw); |
200 | return new SocketAddress(&raw.addr); |
201 | } |
202 | |
203 | void SocketBase::GetError(intptr_t fd, OSError* os_error) { |
204 | IOHandle* handle = reinterpret_cast<IOHandle*>(fd); |
205 | ASSERT(handle->fd() >= 0); |
206 | int len = sizeof(errno); |
207 | int err = 0; |
208 | VOID_NO_RETRY_EXPECTED(getsockopt(handle->fd(), SOL_SOCKET, SO_ERROR, &err, |
209 | reinterpret_cast<socklen_t*>(&len))); |
210 | errno = err; |
211 | os_error->SetCodeAndMessage(OSError::kSystem, errno); |
212 | } |
213 | |
214 | int SocketBase::GetType(intptr_t fd) { |
215 | errno = ENOSYS; |
216 | return -1; |
217 | } |
218 | |
219 | intptr_t SocketBase::GetStdioHandle(intptr_t num) { |
220 | return num; |
221 | } |
222 | |
223 | AddressList<SocketAddress>* SocketBase::LookupAddress(const char* host, |
224 | int type, |
225 | OSError** os_error) { |
226 | // Perform a name lookup for a host name. |
227 | struct addrinfo hints; |
228 | memset(&hints, 0, sizeof(hints)); |
229 | hints.ai_family = SocketAddress::FromType(type); |
230 | hints.ai_socktype = SOCK_STREAM; |
231 | hints.ai_flags = AI_ADDRCONFIG; |
232 | hints.ai_protocol = IPPROTO_TCP; |
233 | struct addrinfo* info = NULL; |
234 | LOG_INFO("SocketBase::LookupAddress: calling getaddrinfo\n" ); |
235 | int status = NO_RETRY_EXPECTED(getaddrinfo(host, 0, &hints, &info)); |
236 | if (status != 0) { |
237 | // We failed, try without AI_ADDRCONFIG. This can happen when looking up |
238 | // e.g. '::1', when there are no global IPv6 addresses. |
239 | hints.ai_flags = 0; |
240 | LOG_INFO("SocketBase::LookupAddress: calling getaddrinfo again\n" ); |
241 | status = NO_RETRY_EXPECTED(getaddrinfo(host, 0, &hints, &info)); |
242 | if (status != 0) { |
243 | ASSERT(*os_error == NULL); |
244 | *os_error = |
245 | new OSError(status, gai_strerror(status), OSError::kGetAddressInfo); |
246 | return NULL; |
247 | } |
248 | } |
249 | intptr_t count = 0; |
250 | for (struct addrinfo* c = info; c != NULL; c = c->ai_next) { |
251 | if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) { |
252 | count++; |
253 | } |
254 | } |
255 | intptr_t i = 0; |
256 | AddressList<SocketAddress>* addresses = new AddressList<SocketAddress>(count); |
257 | for (struct addrinfo* c = info; c != NULL; c = c->ai_next) { |
258 | if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) { |
259 | addresses->SetAt(i, new SocketAddress(c->ai_addr)); |
260 | i++; |
261 | } |
262 | } |
263 | freeaddrinfo(info); |
264 | return addresses; |
265 | } |
266 | |
267 | bool SocketBase::ReverseLookup(const RawAddr& addr, |
268 | char* host, |
269 | intptr_t host_len, |
270 | OSError** os_error) { |
271 | errno = ENOSYS; |
272 | return false; |
273 | } |
274 | |
275 | bool SocketBase::ParseAddress(int type, const char* address, RawAddr* addr) { |
276 | int result; |
277 | if (type == SocketAddress::TYPE_IPV4) { |
278 | result = NO_RETRY_EXPECTED(inet_pton(AF_INET, address, &addr->in.sin_addr)); |
279 | } else { |
280 | ASSERT(type == SocketAddress::TYPE_IPV6); |
281 | result = |
282 | NO_RETRY_EXPECTED(inet_pton(AF_INET6, address, &addr->in6.sin6_addr)); |
283 | } |
284 | return (result == 1); |
285 | } |
286 | |
287 | bool SocketBase::RawAddrToString(RawAddr* addr, char* str) { |
288 | if (addr->addr.sa_family == AF_INET) { |
289 | return inet_ntop(AF_INET, &addr->in.sin_addr, str, INET_ADDRSTRLEN) != NULL; |
290 | } else { |
291 | ASSERT(addr->addr.sa_family == AF_INET6); |
292 | return inet_ntop(AF_INET6, &addr->in6.sin6_addr, str, INET6_ADDRSTRLEN) != |
293 | NULL; |
294 | } |
295 | } |
296 | |
297 | bool SocketBase::ListInterfacesSupported() { |
298 | return true; |
299 | } |
300 | |
301 | AddressList<InterfaceSocketAddress>* SocketBase::ListInterfaces( |
302 | int type, |
303 | OSError** os_error) { |
304 | std::vector<fuchsia::netstack::NetInterface2> interfaces; |
305 | zx_status_t status = netstack->GetInterfaces2(&interfaces); |
306 | if (status != ZX_OK) { |
307 | LOG_ERR("ListInterfaces: fuchsia.netstack.GetInterfaces2 failed: %s\n" , |
308 | zx_status_get_string(status)); |
309 | errno = EIO; |
310 | return NULL; |
311 | } |
312 | |
313 | // Process the results. |
314 | const int lookup_family = SocketAddress::FromType(type); |
315 | |
316 | std::remove_if( |
317 | interfaces.begin(), interfaces.end(), |
318 | [lookup_family](const auto& interface) { |
319 | switch (interface.addr.Which()) { |
320 | case fuchsia::net::IpAddress::Tag::kIpv4: |
321 | return !(lookup_family == AF_UNSPEC || lookup_family == AF_INET); |
322 | case fuchsia::net::IpAddress::Tag::kIpv6: |
323 | return !(lookup_family == AF_UNSPEC || lookup_family == AF_INET6); |
324 | case fuchsia::net::IpAddress::Tag::Invalid: |
325 | return true; |
326 | } |
327 | }); |
328 | |
329 | auto addresses = new AddressList<InterfaceSocketAddress>(interfaces.size()); |
330 | int addresses_idx = 0; |
331 | for (const auto& interface : interfaces) { |
332 | struct sockaddr_storage addr = {}; |
333 | auto addr_in = reinterpret_cast<struct sockaddr_in*>(&addr); |
334 | auto addr_in6 = reinterpret_cast<struct sockaddr_in6*>(&addr); |
335 | switch (interface.addr.Which()) { |
336 | case fuchsia::net::IpAddress::Tag::kIpv4: |
337 | addr_in->sin_family = AF_INET; |
338 | memmove(&addr_in->sin_addr, interface.addr.ipv4().addr.data(), |
339 | sizeof(addr_in->sin_addr)); |
340 | break; |
341 | case fuchsia::net::IpAddress::Tag::kIpv6: |
342 | addr_in6->sin6_family = AF_INET6; |
343 | memmove(&addr_in6->sin6_addr, interface.addr.ipv6().addr.data(), |
344 | sizeof(addr_in6->sin6_addr)); |
345 | break; |
346 | case fuchsia::net::IpAddress::Tag::Invalid: |
347 | // Should have been filtered out above. |
348 | UNREACHABLE(); |
349 | } |
350 | addresses->SetAt(addresses_idx, |
351 | new InterfaceSocketAddress( |
352 | reinterpret_cast<sockaddr*>(&addr), |
353 | DartUtils::ScopedCopyCString(interface.name.c_str()), |
354 | if_nametoindex(interface.name.c_str()))); |
355 | addresses_idx++; |
356 | } |
357 | return addresses; |
358 | } |
359 | |
360 | void SocketBase::Close(intptr_t fd) { |
361 | IOHandle* handle = reinterpret_cast<IOHandle*>(fd); |
362 | ASSERT(handle->fd() >= 0); |
363 | NO_RETRY_EXPECTED(close(handle->fd())); |
364 | } |
365 | |
366 | bool SocketBase::GetNoDelay(intptr_t fd, bool* enabled) { |
367 | errno = ENOSYS; |
368 | return false; |
369 | } |
370 | |
371 | bool SocketBase::SetNoDelay(intptr_t fd, bool enabled) { |
372 | IOHandle* handle = reinterpret_cast<IOHandle*>(fd); |
373 | int on = enabled ? 1 : 0; |
374 | return NO_RETRY_EXPECTED(setsockopt(handle->fd(), IPPROTO_TCP, TCP_NODELAY, |
375 | reinterpret_cast<char*>(&on), |
376 | sizeof(on))) == 0; |
377 | } |
378 | |
379 | bool SocketBase::GetMulticastLoop(intptr_t fd, |
380 | intptr_t protocol, |
381 | bool* enabled) { |
382 | errno = ENOSYS; |
383 | return false; |
384 | } |
385 | |
386 | bool SocketBase::SetMulticastLoop(intptr_t fd, |
387 | intptr_t protocol, |
388 | bool enabled) { |
389 | errno = ENOSYS; |
390 | return false; |
391 | } |
392 | |
393 | bool SocketBase::GetMulticastHops(intptr_t fd, intptr_t protocol, int* value) { |
394 | errno = ENOSYS; |
395 | return false; |
396 | } |
397 | |
398 | bool SocketBase::SetMulticastHops(intptr_t fd, intptr_t protocol, int value) { |
399 | errno = ENOSYS; |
400 | return false; |
401 | } |
402 | |
403 | bool SocketBase::GetBroadcast(intptr_t fd, bool* enabled) { |
404 | errno = ENOSYS; |
405 | return false; |
406 | } |
407 | |
408 | bool SocketBase::SetBroadcast(intptr_t fd, bool enabled) { |
409 | errno = ENOSYS; |
410 | return false; |
411 | } |
412 | |
413 | bool SocketBase::SetOption(intptr_t fd, |
414 | int level, |
415 | int option, |
416 | const char* data, |
417 | int length) { |
418 | IOHandle* handle = reinterpret_cast<IOHandle*>(fd); |
419 | return NO_RETRY_EXPECTED( |
420 | setsockopt(handle->fd(), level, option, data, length)) == 0; |
421 | } |
422 | |
423 | bool SocketBase::GetOption(intptr_t fd, |
424 | int level, |
425 | int option, |
426 | char* data, |
427 | unsigned int* length) { |
428 | IOHandle* handle = reinterpret_cast<IOHandle*>(fd); |
429 | socklen_t optlen = static_cast<socklen_t>(*length); |
430 | auto result = |
431 | NO_RETRY_EXPECTED(getsockopt(handle->fd(), level, option, data, &optlen)); |
432 | *length = static_cast<unsigned int>(optlen); |
433 | return result == 0; |
434 | } |
435 | |
436 | bool SocketBase::JoinMulticast(intptr_t fd, |
437 | const RawAddr& addr, |
438 | const RawAddr&, |
439 | int interfaceIndex) { |
440 | errno = ENOSYS; |
441 | return false; |
442 | } |
443 | |
444 | bool SocketBase::LeaveMulticast(intptr_t fd, |
445 | const RawAddr& addr, |
446 | const RawAddr&, |
447 | int interfaceIndex) { |
448 | errno = ENOSYS; |
449 | return false; |
450 | } |
451 | |
452 | } // namespace bin |
453 | } // namespace dart |
454 | |
455 | #endif // defined(HOST_OS_FUCHSIA) |
456 | |