1// Copyright (c) 2013, 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#ifndef RUNTIME_BIN_SOCKET_H_
6#define RUNTIME_BIN_SOCKET_H_
7
8#include "bin/builtin.h"
9#include "bin/dartutils.h"
10#include "bin/file.h"
11#include "bin/reference_counting.h"
12#include "bin/socket_base.h"
13#include "bin/thread.h"
14#include "bin/utils.h"
15#include "platform/hashmap.h"
16
17namespace dart {
18namespace bin {
19
20// TODO(bkonyi): Socket should also inherit from SocketBase once it is
21// refactored to use instance methods when possible.
22
23// We write Sockets into the native field of the _NativeSocket object
24// on the Dart side. They are allocated in SetSocketIdNativeField(), and are
25// deallocated either from the finalizer attached to _NativeSockets there, or
26// from the eventhandler, whichever drops the last reference.
27class Socket : public ReferenceCounted<Socket> {
28 public:
29 enum SocketRequest {
30 kLookupRequest = 0,
31 kListInterfacesRequest = 1,
32 kReverseLookupRequest = 2,
33 };
34
35 enum SocketFinalizer {
36 kFinalizerNormal,
37 kFinalizerListening,
38 kFinalizerStdio,
39 kFinalizerSignal,
40 };
41
42 // Keep in sync with constants in _NativeSocket in socket_patch.dart.
43 enum SocketType {
44 kTcpSocket = 18,
45 kUdpSocket = 19,
46 kInternalSocket = 20,
47 kInternalSignalSocket = 21,
48 };
49
50 explicit Socket(intptr_t fd);
51
52 intptr_t fd() const { return fd_; }
53
54 // Close fd and may need to decrement the count of handle by calling
55 // release().
56 void CloseFd();
57 // Set fd_ to closed. On fuchsia and win, shared socket should not
58 // release handle but only SetClosedFd().
59 void SetClosedFd();
60
61 Dart_Port isolate_port() const { return isolate_port_; }
62
63 Dart_Port port() const { return port_; }
64 void set_port(Dart_Port port) { port_ = port; }
65
66 uint8_t* udp_receive_buffer() const { return udp_receive_buffer_; }
67 void set_udp_receive_buffer(uint8_t* buffer) { udp_receive_buffer_ = buffer; }
68
69 static bool Initialize();
70
71 // Creates a socket which is bound and connected. The port to connect to is
72 // specified as the port component of the passed RawAddr structure.
73 static intptr_t CreateConnect(const RawAddr& addr);
74 static intptr_t CreateUnixDomainConnect(const RawAddr& addr);
75 // Creates a socket which is bound and connected. The port to connect to is
76 // specified as the port component of the passed RawAddr structure.
77 static intptr_t CreateBindConnect(const RawAddr& addr,
78 const RawAddr& source_addr);
79 static intptr_t CreateUnixDomainBindConnect(const RawAddr& addr,
80 const RawAddr& source_addr);
81 // Creates a datagram socket which is bound. The port to bind
82 // to is specified as the port component of the RawAddr structure.
83 static intptr_t CreateBindDatagram(const RawAddr& addr,
84 bool reuseAddress,
85 bool reusePort,
86 int ttl = 1);
87
88 static CObject* LookupRequest(const CObjectArray& request);
89 static CObject* ListInterfacesRequest(const CObjectArray& request);
90 static CObject* ReverseLookupRequest(const CObjectArray& request);
91
92 static Dart_Port GetServicePort();
93
94 static void SetSocketIdNativeField(Dart_Handle handle,
95 intptr_t id,
96 SocketFinalizer finalizer);
97 static void ReuseSocketIdNativeField(Dart_Handle handle,
98 Socket* socket,
99 SocketFinalizer finalizer);
100 static Socket* GetSocketIdNativeField(Dart_Handle socket);
101
102 static bool short_socket_read() { return short_socket_read_; }
103 static void set_short_socket_read(bool short_socket_read) {
104 short_socket_read_ = short_socket_read;
105 }
106 static bool short_socket_write() { return short_socket_write_; }
107 static void set_short_socket_write(bool short_socket_write) {
108 short_socket_write_ = short_socket_write;
109 }
110
111 static bool IsSignalSocketFlag(intptr_t flag) {
112 return ((flag & (0x1 << kInternalSignalSocket)) != 0);
113 }
114
115 private:
116 ~Socket() {
117 ASSERT(fd_ == kClosedFd);
118 free(udp_receive_buffer_);
119 udp_receive_buffer_ = NULL;
120 }
121
122 static const int kClosedFd = -1;
123
124 static bool short_socket_read_;
125 static bool short_socket_write_;
126
127 intptr_t fd_;
128 Dart_Port isolate_port_;
129 Dart_Port port_;
130 uint8_t* udp_receive_buffer_;
131
132 friend class ReferenceCounted<Socket>;
133 DISALLOW_COPY_AND_ASSIGN(Socket);
134};
135
136class ServerSocket {
137 public:
138 static const intptr_t kTemporaryFailure = -2;
139
140 static intptr_t Accept(intptr_t fd);
141
142 // Creates a socket which is bound and listens. The port to listen on is
143 // specified in the port component of the passed RawAddr structure.
144 //
145 // Returns a positive integer if the call is successful. In case of failure
146 // it returns:
147 //
148 // -1: system error (errno set)
149 // -5: invalid bindAddress
150 static intptr_t CreateBindListen(const RawAddr& addr,
151 intptr_t backlog,
152 bool v6_only = false);
153 static intptr_t CreateUnixDomainBindListen(const RawAddr& addr,
154 intptr_t backlog);
155
156 // Start accepting on a newly created listening socket. If it was unable to
157 // start accepting incoming sockets, the fd is invalidated.
158 static bool StartAccept(intptr_t fd);
159
160 private:
161 DISALLOW_ALLOCATION();
162 DISALLOW_IMPLICIT_CONSTRUCTORS(ServerSocket);
163};
164
165class ListeningSocketRegistry {
166 public:
167 ListeningSocketRegistry()
168 : sockets_by_port_(SameIntptrValue, kInitialSocketsCount),
169 sockets_by_fd_(SameIntptrValue, kInitialSocketsCount),
170 unix_domain_sockets_(nullptr),
171 mutex_() {}
172
173 ~ListeningSocketRegistry() {
174 CloseAllSafe();
175 }
176
177 static void Initialize();
178
179 static ListeningSocketRegistry* Instance();
180
181 static void Cleanup();
182
183 // Bind `socket_object` to `addr`.
184 // Return Dart_True() if succeed.
185 // This function should be called from a dart runtime call in order to create
186 // a new (potentially shared) socket.
187 Dart_Handle CreateBindListen(Dart_Handle socket_object,
188 RawAddr addr,
189 intptr_t backlog,
190 bool v6_only,
191 bool shared);
192 // Bind unix domain socket`socket_object` to `path`.
193 // Return Dart_True() if succeed.
194 // This function should be called from a dart runtime call in order to create
195 // a new socket.
196 Dart_Handle CreateUnixDomainBindListen(Dart_Handle socket_object,
197 Namespace* namespc,
198 const char* path,
199 intptr_t backlog,
200 bool shared);
201
202 // This should be called from the event handler for every kCloseEvent it gets
203 // on listening sockets.
204 //
205 // Returns `true` if the last reference has been dropped and the underlying
206 // socket can be closed.
207 //
208 // The caller is responsible for obtaining the mutex first, before calling
209 // this function.
210 bool CloseSafe(Socket* socketfd);
211
212 Mutex* mutex() { return &mutex_; }
213
214 private:
215 struct OSSocket {
216 RawAddr address;
217 int port;
218 bool v6_only;
219 bool shared;
220 int ref_count;
221 intptr_t fd;
222
223 // Only applicable to Unix domain socket, where address.addr.sa_family
224 // == AF_UNIX.
225 Namespace* namespc;
226
227 // Singly linked lists of OSSocket instances which listen on the same port
228 // but on different addresses.
229 OSSocket* next;
230
231 OSSocket(RawAddr address,
232 int port,
233 bool v6_only,
234 bool shared,
235 Socket* socketfd,
236 Namespace* namespc)
237 : address(address),
238 port(port),
239 v6_only(v6_only),
240 shared(shared),
241 ref_count(0),
242 namespc(namespc),
243 next(NULL) {
244 fd = socketfd->fd();
245 }
246 };
247
248 static const intptr_t kInitialSocketsCount = 8;
249
250 OSSocket* FindOSSocketWithAddress(OSSocket* current, const RawAddr& addr) {
251 while (current != NULL) {
252 if (SocketAddress::AreAddressesEqual(current->address, addr)) {
253 return current;
254 }
255 current = current->next;
256 }
257 return NULL;
258 }
259
260 OSSocket* FindOSSocketWithPath(OSSocket* current,
261 Namespace* namespc,
262 const char* path) {
263 while (current != NULL) {
264 ASSERT(current->address.addr.sa_family == AF_UNIX);
265#if defined(HOST_OS_LINUX) || defined(HOST_OS_ANDROID)
266 bool condition;
267 if (path[0] == '\0') {
268 condition = current->address.un.sun_path[0] == '\0' &&
269 strcmp(&(current->address.un.sun_path[1]), path + 1) == 0;
270 } else {
271 condition =
272 File::AreIdentical(current->namespc, current->address.un.sun_path,
273 namespc, path) == File::kIdentical;
274 }
275 if (condition) {
276 return current;
277 }
278#else
279 if (File::AreIdentical(current->namespc, current->address.un.sun_path,
280 namespc, path) == File::kIdentical) {
281 return current;
282 }
283#endif // defined(HOST_OS_LINUX) || defined(HOST_OS_ANDROID)
284 current = current->next;
285 }
286 return NULL;
287 }
288
289 static bool SameIntptrValue(void* key1, void* key2) {
290 return reinterpret_cast<intptr_t>(key1) == reinterpret_cast<intptr_t>(key2);
291 }
292
293 static uint32_t GetHashmapHashFromIntptr(intptr_t i) {
294 return static_cast<uint32_t>((i + 1) & 0xFFFFFFFF);
295 }
296
297 static void* GetHashmapKeyFromIntptr(intptr_t i) {
298 return reinterpret_cast<void*>(i + 1);
299 }
300
301 OSSocket* LookupByPort(intptr_t port);
302 void InsertByPort(intptr_t port, OSSocket* socket);
303 void RemoveByPort(intptr_t port);
304
305 OSSocket* LookupByFd(Socket* fd);
306 void InsertByFd(Socket* fd, OSSocket* socket);
307 void RemoveByFd(Socket* fd);
308
309 bool CloseOneSafe(OSSocket* os_socket, Socket* socket);
310 void CloseAllSafe();
311
312 SimpleHashMap sockets_by_port_;
313 SimpleHashMap sockets_by_fd_;
314
315 OSSocket* unix_domain_sockets_;
316
317 Mutex mutex_;
318
319 DISALLOW_COPY_AND_ASSIGN(ListeningSocketRegistry);
320};
321
322} // namespace bin
323} // namespace dart
324
325#endif // RUNTIME_BIN_SOCKET_H_
326