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 | |
22 | namespace dart { |
23 | namespace bin { |
24 | |
25 | int 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 | |
39 | intptr_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 | |
74 | intptr_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 | |
80 | bool 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 | |
105 | void 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 | |
130 | Dart_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 | |
163 | int16_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 | |
177 | void 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 | |
187 | intptr_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 | |
200 | Dart_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 | |
220 | CObjectUint8Array* 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 | } |
232 | void 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 | |
237 | intptr_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 | |
245 | void 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 | |
266 | void 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 | |
287 | void 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 | |
299 | void 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 | |
306 | bool 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) |
321 | intptr_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 | |