1// Copyright (c) 2017, 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 "bin/socket_base.h"
6
7#include <errno.h> // NOLINT
8
9#include "bin/dartutils.h"
10#include "bin/io_buffer.h"
11#include "bin/isolate_data.h"
12#include "bin/lockers.h"
13#include "bin/thread.h"
14#include "bin/typed_data_utils.h"
15#include "bin/utils.h"
16
17#include "include/dart_api.h"
18
19#include "platform/globals.h"
20#include "platform/utils.h"
21
22namespace dart {
23namespace bin {
24
25int SocketAddress::GetType() {
26 switch (addr_.ss.ss_family) {
27 case AF_INET6:
28 return TYPE_IPV6;
29 case AF_INET:
30 return TYPE_IPV4;
31 case AF_UNIX:
32 return TYPE_UNIX;
33 default:
34 UNREACHABLE();
35 return TYPE_ANY;
36 }
37}
38
39intptr_t SocketAddress::GetAddrLength(const RawAddr& addr,
40 bool unnamed_unix_socket) {
41 ASSERT((addr.ss.ss_family == AF_INET) || (addr.ss.ss_family == AF_INET6) ||
42 (addr.ss.ss_family == AF_UNIX));
43 switch (addr.ss.ss_family) {
44 case AF_INET6:
45 return sizeof(struct sockaddr_in6);
46 case AF_INET:
47 return sizeof(struct sockaddr_in);
48 case AF_UNIX: {
49 // For an abstract UNIX socket, trailing null bytes in the name are
50 // meaningful. That is, the bytes '\0/tmp/dbus-xxxx' are a different name
51 // than '\0/tmp/dbus-xxxx\0\0\0...'. The length of the address structure
52 // passed to connect() etc. tells those calls how many bytes of the name
53 // to look at. Therefore, when computing the length of the address in
54 // this case, any trailing null bytes are trimmed.
55 // TODO(dart:io): Support abstract UNIX socket addresses that have
56 // trailing null bytes on purpose.
57 // https://github.com/dart-lang/sdk/issues/46158
58 intptr_t nulls = 0;
59 if (!unnamed_unix_socket && addr.un.sun_path[0] == '\0') {
60 intptr_t i = sizeof(addr.un.sun_path) - 1;
61 while (addr.un.sun_path[i] == '\0') {
62 nulls++;
63 i--;
64 }
65 }
66 return sizeof(struct sockaddr_un) - nulls;
67 }
68 default:
69 UNREACHABLE();
70 return 0;
71 }
72}
73
74intptr_t SocketAddress::GetInAddrLength(const RawAddr& addr) {
75 ASSERT((addr.ss.ss_family == AF_INET) || (addr.ss.ss_family == AF_INET6));
76 return (addr.ss.ss_family == AF_INET6) ? sizeof(struct in6_addr)
77 : sizeof(struct in_addr);
78}
79
80bool SocketAddress::AreAddressesEqual(const RawAddr& a, const RawAddr& b) {
81 if (a.ss.ss_family != b.ss.ss_family) {
82 return false;
83 }
84 if (a.ss.ss_family == AF_INET) {
85 return memcmp(&a.in.sin_addr, &b.in.sin_addr, sizeof(a.in.sin_addr)) == 0;
86 } else if (a.ss.ss_family == AF_INET6) {
87 return memcmp(&a.in6.sin6_addr, &b.in6.sin6_addr,
88 sizeof(a.in6.sin6_addr)) == 0 &&
89 a.in6.sin6_scope_id == b.in6.sin6_scope_id;
90 } else if (a.ss.ss_family == AF_UNIX) {
91 // This is not used anywhere. The comparison of file path is done via
92 // File::AreIdentical().
93 int len = sizeof(a.un.sun_path);
94 for (int i = 0; i < len; i++) {
95 if (a.un.sun_path[i] != b.un.sun_path[i]) return false;
96 if (a.un.sun_path[i] == '\0') return true;
97 }
98 return true;
99 } else {
100 UNREACHABLE();
101 return false;
102 }
103}
104
105void SocketAddress::GetSockAddr(Dart_Handle obj, RawAddr* addr) {
106 Dart_TypedData_Type data_type;
107 uint8_t* data = nullptr;
108 intptr_t len;
109 Dart_Handle result = Dart_TypedDataAcquireData(
110 object: obj, type: &data_type, data: reinterpret_cast<void**>(&data), len: &len);
111 if (Dart_IsError(handle: result)) {
112 Dart_PropagateError(handle: result);
113 }
114 if ((data_type != Dart_TypedData_kUint8) ||
115 ((len != sizeof(in_addr)) && (len != sizeof(in6_addr)))) {
116 Dart_PropagateError(handle: Dart_NewApiError(error: "Unexpected type for socket address"));
117 }
118 memset(reinterpret_cast<void*>(addr), 0, sizeof(RawAddr));
119 if (len == sizeof(in_addr)) {
120 addr->in.sin_family = AF_INET;
121 memmove(reinterpret_cast<void*>(&addr->in.sin_addr), data, len);
122 } else {
123 ASSERT(len == sizeof(in6_addr));
124 addr->in6.sin6_family = AF_INET6;
125 memmove(reinterpret_cast<void*>(&addr->in6.sin6_addr), data, len);
126 }
127 Dart_TypedDataReleaseData(object: obj);
128}
129
130Dart_Handle SocketAddress::GetUnixDomainSockAddr(const char* path,
131 Namespace* namespc,
132 RawAddr* addr) {
133#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
134 NamespaceScope ns(namespc, path);
135 path = ns.path();
136 bool is_abstract = (path[0] == '@');
137 if (is_abstract) {
138 // The following 107 bytes after the leading null byte represents the name
139 // of unix domain socket. Without reseting, even users provide the same path
140 // for bind and connect, they actually represent two different address and
141 // connection will be rejected.
142 bzero(addr->un.sun_path, sizeof(addr->un.sun_path));
143 }
144#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
145 if (sizeof(path) > sizeof(addr->un.sun_path)) {
146 OSError os_error(-1,
147 "The length of path exceeds the limit. "
148 "Check out man 7 unix page",
149 OSError::kUnknown);
150 return DartUtils::NewDartOSError(os_error: &os_error);
151 }
152 addr->un.sun_family = AF_UNIX;
153 Utils::SNPrint(str: addr->un.sun_path, size: sizeof(addr->un.sun_path), format: "%s", path);
154#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
155 // In case of abstract namespace, transfer the leading '@' into a null byte.
156 if (is_abstract) {
157 addr->un.sun_path[0] = '\0';
158 }
159#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
160 return Dart_Null();
161}
162
163int16_t SocketAddress::FromType(int type) {
164 if (type == TYPE_ANY) {
165 return AF_UNSPEC;
166 }
167 if (type == TYPE_IPV4) {
168 return AF_INET;
169 }
170 if (type == TYPE_UNIX) {
171 return AF_UNIX;
172 }
173 ASSERT((type == TYPE_IPV6) && "Invalid type");
174 return AF_INET6;
175}
176
177void SocketAddress::SetAddrPort(RawAddr* addr, intptr_t port) {
178 if (addr->ss.ss_family == AF_INET) {
179 addr->in.sin_port = htons(port);
180 } else if (addr->ss.ss_family == AF_INET6) {
181 addr->in6.sin6_port = htons(port);
182 } else {
183 UNREACHABLE();
184 }
185}
186
187intptr_t SocketAddress::GetAddrPort(const RawAddr& addr) {
188 if (addr.ss.ss_family == AF_INET) {
189 return ntohs(addr.in.sin_port);
190 } else if (addr.ss.ss_family == AF_INET6) {
191 return ntohs(addr.in6.sin6_port);
192 } else if (addr.ss.ss_family == AF_UNIX) {
193 return 0;
194 } else {
195 UNREACHABLE();
196 return -1;
197 }
198}
199
200Dart_Handle SocketAddress::ToTypedData(const RawAddr& addr) {
201 int len = GetInAddrLength(addr);
202 Dart_Handle result = Dart_NewTypedData(type: Dart_TypedData_kUint8, length: len);
203 if (Dart_IsError(handle: result)) {
204 Dart_PropagateError(handle: result);
205 }
206 Dart_Handle err;
207 if (addr.addr.sa_family == AF_INET6) {
208 err = Dart_ListSetAsBytes(
209 list: result, offset: 0, native_array: reinterpret_cast<const uint8_t*>(&addr.in6.sin6_addr), length: len);
210 } else {
211 err = Dart_ListSetAsBytes(
212 list: result, offset: 0, native_array: reinterpret_cast<const uint8_t*>(&addr.in.sin_addr), length: len);
213 }
214 if (Dart_IsError(handle: err)) {
215 Dart_PropagateError(handle: err);
216 }
217 return result;
218}
219
220CObjectUint8Array* SocketAddress::ToCObject(const RawAddr& addr) {
221 int in_addr_len = SocketAddress::GetInAddrLength(addr);
222 const void* in_addr;
223 if (addr.addr.sa_family == AF_INET6) {
224 in_addr = reinterpret_cast<const void*>(&addr.in6.sin6_addr);
225 } else {
226 in_addr = reinterpret_cast<const void*>(&addr.in.sin_addr);
227 }
228 CObjectUint8Array* data =
229 new CObjectUint8Array(CObject::NewUint8Array(data: in_addr, length: in_addr_len));
230 return data;
231}
232void SocketAddress::SetAddrScope(RawAddr* addr, intptr_t scope_id) {
233 if (addr->addr.sa_family != AF_INET6) return;
234 addr->in6.sin6_scope_id = scope_id;
235}
236
237intptr_t SocketAddress::GetAddrScope(const RawAddr& addr) {
238 if (addr.addr.sa_family == AF_INET6) {
239 return addr.in6.sin6_scope_id;
240 } else {
241 return 0;
242 }
243}
244
245void FUNCTION_NAME(InternetAddress_Parse)(Dart_NativeArguments args) {
246 const char* address =
247 DartUtils::GetStringValue(str_obj: Dart_GetNativeArgument(args, index: 0));
248 ASSERT(address != nullptr);
249 RawAddr raw;
250 memset(&raw, 0, sizeof(raw));
251 int type = strchr(address, ':') == nullptr ? SocketAddress::TYPE_IPV4
252 : SocketAddress::TYPE_IPV6;
253 if (type == SocketAddress::TYPE_IPV4) {
254 raw.addr.sa_family = AF_INET;
255 } else {
256 raw.addr.sa_family = AF_INET6;
257 }
258 bool ok = SocketBase::ParseAddress(type, address, addr: &raw);
259 if (!ok) {
260 Dart_SetReturnValue(args, retval: Dart_Null());
261 } else {
262 Dart_SetReturnValue(args, retval: SocketAddress::ToTypedData(addr: raw));
263 }
264}
265
266void FUNCTION_NAME(InternetAddress_ParseScopedLinkLocalAddress)(
267 Dart_NativeArguments args) {
268 const char* address =
269 DartUtils::GetStringValue(str_obj: Dart_GetNativeArgument(args, index: 0));
270 // This must be an IPv6 address.
271 intptr_t type = 1;
272 ASSERT(address != nullptr);
273 OSError* os_error = nullptr;
274 AddressList<SocketAddress>* addresses =
275 SocketBase::LookupAddress(host: address, type, os_error: &os_error);
276 if (addresses != nullptr) {
277 SocketAddress* addr = addresses->GetAt(i: 0);
278 Dart_SetReturnValue(
279 args, retval: Dart_NewInteger(value: SocketAddress::GetAddrScope(addr: addr->addr())));
280 delete addresses;
281 } else {
282 Dart_SetReturnValue(args, retval: DartUtils::NewDartOSError(os_error));
283 delete os_error;
284 }
285}
286
287void FUNCTION_NAME(InternetAddress_RawAddrToString)(Dart_NativeArguments args) {
288 RawAddr addr;
289 SocketAddress::GetSockAddr(obj: Dart_GetNativeArgument(args, index: 0), addr: &addr);
290 // INET6_ADDRSTRLEN is larger than INET_ADDRSTRLEN
291 char str[INET6_ADDRSTRLEN];
292 bool ok = SocketBase::RawAddrToString(addr: &addr, str: str);
293 if (!ok) {
294 str[0] = '\0';
295 }
296 Dart_SetReturnValue(args, ThrowIfError(DartUtils::NewString(str: str)));
297}
298
299void FUNCTION_NAME(SocketBase_IsBindError)(Dart_NativeArguments args) {
300 intptr_t error_number =
301 DartUtils::GetIntptrValue(value_obj: Dart_GetNativeArgument(args, index: 1));
302 bool is_bind_error = SocketBase::IsBindError(error_number);
303 Dart_SetBooleanReturnValue(args, retval: is_bind_error ? true : false);
304}
305
306bool SocketBase::IsValidAddress(const char* address) {
307 ASSERT(address != nullptr);
308 RawAddr raw;
309 memset(&raw, 0, sizeof(raw));
310 int type = strchr(address, ':') == nullptr ? SocketAddress::TYPE_IPV4
311 : SocketAddress::TYPE_IPV6;
312 if (type == SocketAddress::TYPE_IPV4) {
313 raw.addr.sa_family = AF_INET;
314 } else {
315 raw.addr.sa_family = AF_INET6;
316 }
317 return SocketBase::ParseAddress(type, address, addr: &raw);
318}
319
320#if !defined(DART_HOST_OS_WINDOWS)
321intptr_t SocketBase::Write(intptr_t fd,
322 const void* buffer,
323 intptr_t num_bytes,
324 SocketOpKind sync) {
325 // For non-blocking sockets we must write as many bytes as possible into
326 // the output to trigger EAGAIN otherwise we are not guaranteed to
327 // receive an event from epoll which we are using in edge-triggering
328 // (EPOLLET) mode. See man epoll for more information and guidelines.
329 ssize_t num_bytes_left = num_bytes;
330 while (num_bytes_left > 0) {
331 ssize_t written_bytes = WriteImpl(fd, buffer, num_bytes_left, sync);
332 static_assert(EAGAIN == EWOULDBLOCK);
333 if (written_bytes == -1) {
334 if ((sync == kAsync) && (errno == EWOULDBLOCK)) {
335 break;
336 }
337
338 return -1; // Error occurred.
339 }
340
341 num_bytes_left -= written_bytes;
342 buffer = static_cast<const char*>(buffer) + written_bytes;
343 }
344
345 return num_bytes - num_bytes_left;
346}
347#endif
348
349} // namespace bin
350} // namespace dart
351

source code of dart_sdk/runtime/bin/socket_base.cc