OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #if !defined(DART_IO_DISABLED) | 5 #if !defined(DART_IO_DISABLED) |
6 | 6 |
7 #include "platform/globals.h" | 7 #include "platform/globals.h" |
8 #if defined(HOST_OS_ANDROID) | 8 #if defined(HOST_OS_ANDROID) |
9 | 9 |
10 #include "bin/socket.h" | |
11 #include "bin/socket_android.h" | |
12 | |
13 #include <errno.h> // NOLINT | 10 #include <errno.h> // NOLINT |
14 #include <netinet/tcp.h> // NOLINT | 11 #include <netinet/tcp.h> // NOLINT |
15 #include <stdio.h> // NOLINT | 12 #include <stdio.h> // NOLINT |
16 #include <stdlib.h> // NOLINT | 13 #include <stdlib.h> // NOLINT |
17 #include <string.h> // NOLINT | 14 #include <string.h> // NOLINT |
18 #include <sys/stat.h> // NOLINT | 15 #include <sys/stat.h> // NOLINT |
19 #include <unistd.h> // NOLINT | 16 #include <unistd.h> // NOLINT |
20 | 17 |
21 #include "bin/fdutils.h" | 18 #include "bin/fdutils.h" |
22 #include "bin/file.h" | 19 #include "bin/file.h" |
| 20 #include "bin/socket.h" |
| 21 #include "bin/socket_base_android.h" |
23 #include "platform/signal_blocker.h" | 22 #include "platform/signal_blocker.h" |
24 | 23 |
25 namespace dart { | 24 namespace dart { |
26 namespace bin { | 25 namespace bin { |
27 | 26 |
28 SocketAddress::SocketAddress(struct sockaddr* sa) { | |
29 ASSERT(INET6_ADDRSTRLEN >= INET_ADDRSTRLEN); | |
30 if (!Socket::FormatNumericAddress(*reinterpret_cast<RawAddr*>(sa), as_string_, | |
31 INET6_ADDRSTRLEN)) { | |
32 as_string_[0] = 0; | |
33 } | |
34 socklen_t salen = GetAddrLength(*reinterpret_cast<RawAddr*>(sa)); | |
35 memmove(reinterpret_cast<void*>(&addr_), sa, salen); | |
36 } | |
37 | |
38 | |
39 bool Socket::FormatNumericAddress(const RawAddr& addr, char* address, int len) { | |
40 socklen_t salen = SocketAddress::GetAddrLength(addr); | |
41 return (NO_RETRY_EXPECTED(getnameinfo(&addr.addr, salen, address, len, NULL, | |
42 0, NI_NUMERICHOST)) == 0); | |
43 } | |
44 | |
45 | |
46 Socket::Socket(intptr_t fd) | 27 Socket::Socket(intptr_t fd) |
47 : ReferenceCounted(), fd_(fd), port_(ILLEGAL_PORT) {} | 28 : ReferenceCounted(), fd_(fd), port_(ILLEGAL_PORT) {} |
48 | 29 |
49 | 30 |
50 void Socket::SetClosedFd() { | 31 void Socket::SetClosedFd() { |
51 fd_ = kClosedFd; | 32 fd_ = kClosedFd; |
52 } | 33 } |
53 | 34 |
54 | 35 |
55 bool Socket::Initialize() { | |
56 // Nothing to do on Android. | |
57 return true; | |
58 } | |
59 | |
60 | |
61 static intptr_t Create(const RawAddr& addr) { | 36 static intptr_t Create(const RawAddr& addr) { |
62 intptr_t fd; | 37 intptr_t fd; |
63 fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, SOCK_STREAM, 0)); | 38 fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, SOCK_STREAM, 0)); |
64 if (fd < 0) { | 39 if (fd < 0) { |
65 return -1; | 40 return -1; |
66 } | 41 } |
67 if (!FDUtils::SetCloseOnExec(fd)) { | 42 if (!FDUtils::SetCloseOnExec(fd)) { |
68 FDUtils::SaveErrorAndClose(fd); | 43 FDUtils::SaveErrorAndClose(fd); |
69 return -1; | 44 return -1; |
70 } | 45 } |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
108 bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(source_addr))); | 83 bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(source_addr))); |
109 if ((result != 0) && (errno != EINPROGRESS)) { | 84 if ((result != 0) && (errno != EINPROGRESS)) { |
110 FDUtils::SaveErrorAndClose(fd); | 85 FDUtils::SaveErrorAndClose(fd); |
111 return -1; | 86 return -1; |
112 } | 87 } |
113 | 88 |
114 return Connect(fd, addr); | 89 return Connect(fd, addr); |
115 } | 90 } |
116 | 91 |
117 | 92 |
118 bool Socket::IsBindError(intptr_t error_number) { | |
119 return error_number == EADDRINUSE || error_number == EADDRNOTAVAIL || | |
120 error_number == EINVAL; | |
121 } | |
122 | |
123 | |
124 intptr_t Socket::Available(intptr_t fd) { | |
125 return FDUtils::AvailableBytes(fd); | |
126 } | |
127 | |
128 | |
129 intptr_t Socket::Read(intptr_t fd, void* buffer, intptr_t num_bytes) { | |
130 ASSERT(fd >= 0); | |
131 ssize_t read_bytes = TEMP_FAILURE_RETRY(read(fd, buffer, num_bytes)); | |
132 ASSERT(EAGAIN == EWOULDBLOCK); | |
133 if ((read_bytes == -1) && (errno == EWOULDBLOCK)) { | |
134 // If the read would block we need to retry and therefore return 0 | |
135 // as the number of bytes written. | |
136 read_bytes = 0; | |
137 } | |
138 return read_bytes; | |
139 } | |
140 | |
141 | |
142 intptr_t Socket::RecvFrom(intptr_t fd, | |
143 void* buffer, | |
144 intptr_t num_bytes, | |
145 RawAddr* addr) { | |
146 ASSERT(fd >= 0); | |
147 socklen_t addr_len = sizeof(addr->ss); | |
148 ssize_t read_bytes = TEMP_FAILURE_RETRY( | |
149 recvfrom(fd, buffer, num_bytes, 0, &addr->addr, &addr_len)); | |
150 if ((read_bytes == -1) && (errno == EWOULDBLOCK)) { | |
151 // If the read would block we need to retry and therefore return 0 | |
152 // as the number of bytes written. | |
153 read_bytes = 0; | |
154 } | |
155 return read_bytes; | |
156 } | |
157 | |
158 | |
159 intptr_t Socket::Write(intptr_t fd, const void* buffer, intptr_t num_bytes) { | |
160 ASSERT(fd >= 0); | |
161 ssize_t written_bytes = TEMP_FAILURE_RETRY(write(fd, buffer, num_bytes)); | |
162 ASSERT(EAGAIN == EWOULDBLOCK); | |
163 if ((written_bytes == -1) && (errno == EWOULDBLOCK)) { | |
164 // If the would block we need to retry and therefore return 0 as | |
165 // the number of bytes written. | |
166 written_bytes = 0; | |
167 } | |
168 return written_bytes; | |
169 } | |
170 | |
171 | |
172 intptr_t Socket::SendTo(intptr_t fd, | |
173 const void* buffer, | |
174 intptr_t num_bytes, | |
175 const RawAddr& addr) { | |
176 ASSERT(fd >= 0); | |
177 ssize_t written_bytes = | |
178 TEMP_FAILURE_RETRY(sendto(fd, buffer, num_bytes, 0, &addr.addr, | |
179 SocketAddress::GetAddrLength(addr))); | |
180 ASSERT(EAGAIN == EWOULDBLOCK); | |
181 if ((written_bytes == -1) && (errno == EWOULDBLOCK)) { | |
182 // If the would block we need to retry and therefore return 0 as | |
183 // the number of bytes written. | |
184 written_bytes = 0; | |
185 } | |
186 return written_bytes; | |
187 } | |
188 | |
189 | |
190 intptr_t Socket::GetPort(intptr_t fd) { | |
191 ASSERT(fd >= 0); | |
192 RawAddr raw; | |
193 socklen_t size = sizeof(raw); | |
194 if (NO_RETRY_EXPECTED(getsockname(fd, &raw.addr, &size))) { | |
195 return 0; | |
196 } | |
197 return SocketAddress::GetAddrPort(raw); | |
198 } | |
199 | |
200 | |
201 SocketAddress* Socket::GetRemotePeer(intptr_t fd, intptr_t* port) { | |
202 ASSERT(fd >= 0); | |
203 RawAddr raw; | |
204 socklen_t size = sizeof(raw); | |
205 if (NO_RETRY_EXPECTED(getpeername(fd, &raw.addr, &size))) { | |
206 return NULL; | |
207 } | |
208 *port = SocketAddress::GetAddrPort(raw); | |
209 return new SocketAddress(&raw.addr); | |
210 } | |
211 | |
212 | |
213 void Socket::GetError(intptr_t fd, OSError* os_error) { | |
214 int errorNumber; | |
215 socklen_t len = sizeof(errorNumber); | |
216 getsockopt(fd, SOL_SOCKET, SO_ERROR, reinterpret_cast<void*>(&errorNumber), | |
217 &len); | |
218 os_error->SetCodeAndMessage(OSError::kSystem, errorNumber); | |
219 } | |
220 | |
221 | |
222 int Socket::GetType(intptr_t fd) { | |
223 struct stat buf; | |
224 int result = fstat(fd, &buf); | |
225 if (result == -1) { | |
226 return -1; | |
227 } | |
228 if (S_ISCHR(buf.st_mode)) { | |
229 return File::kTerminal; | |
230 } | |
231 if (S_ISFIFO(buf.st_mode)) { | |
232 return File::kPipe; | |
233 } | |
234 if (S_ISREG(buf.st_mode)) { | |
235 return File::kFile; | |
236 } | |
237 return File::kOther; | |
238 } | |
239 | |
240 | |
241 intptr_t Socket::GetStdioHandle(intptr_t num) { | |
242 return num; | |
243 } | |
244 | |
245 | |
246 AddressList<SocketAddress>* Socket::LookupAddress(const char* host, | |
247 int type, | |
248 OSError** os_error) { | |
249 // Perform a name lookup for a host name. | |
250 struct addrinfo hints; | |
251 memset(&hints, 0, sizeof(hints)); | |
252 hints.ai_family = SocketAddress::FromType(type); | |
253 hints.ai_socktype = SOCK_STREAM; | |
254 hints.ai_flags = AI_ADDRCONFIG; | |
255 hints.ai_protocol = IPPROTO_TCP; | |
256 struct addrinfo* info = NULL; | |
257 int status = getaddrinfo(host, 0, &hints, &info); | |
258 if (status != 0) { | |
259 // We failed, try without AI_ADDRCONFIG. This can happen when looking up | |
260 // e.g. '::1', when there are no IPv6 addresses. | |
261 hints.ai_flags = 0; | |
262 status = getaddrinfo(host, 0, &hints, &info); | |
263 if (status != 0) { | |
264 ASSERT(*os_error == NULL); | |
265 *os_error = | |
266 new OSError(status, gai_strerror(status), OSError::kGetAddressInfo); | |
267 return NULL; | |
268 } | |
269 } | |
270 intptr_t count = 0; | |
271 for (struct addrinfo* c = info; c != NULL; c = c->ai_next) { | |
272 if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) { | |
273 count++; | |
274 } | |
275 } | |
276 intptr_t i = 0; | |
277 AddressList<SocketAddress>* addresses = new AddressList<SocketAddress>(count); | |
278 for (struct addrinfo* c = info; c != NULL; c = c->ai_next) { | |
279 if ((c->ai_family == AF_INET) || (c->ai_family == AF_INET6)) { | |
280 addresses->SetAt(i, new SocketAddress(c->ai_addr)); | |
281 i++; | |
282 } | |
283 } | |
284 freeaddrinfo(info); | |
285 return addresses; | |
286 } | |
287 | |
288 | |
289 bool Socket::ReverseLookup(const RawAddr& addr, | |
290 char* host, | |
291 intptr_t host_len, | |
292 OSError** os_error) { | |
293 ASSERT(host_len >= NI_MAXHOST); | |
294 int status = NO_RETRY_EXPECTED( | |
295 getnameinfo(&addr.addr, SocketAddress::GetAddrLength(addr), host, | |
296 host_len, NULL, 0, NI_NAMEREQD)); | |
297 if (status != 0) { | |
298 ASSERT(*os_error == NULL); | |
299 *os_error = | |
300 new OSError(status, gai_strerror(status), OSError::kGetAddressInfo); | |
301 return false; | |
302 } | |
303 return true; | |
304 } | |
305 | |
306 | |
307 bool Socket::ParseAddress(int type, const char* address, RawAddr* addr) { | |
308 int result; | |
309 if (type == SocketAddress::TYPE_IPV4) { | |
310 result = inet_pton(AF_INET, address, &addr->in.sin_addr); | |
311 } else { | |
312 ASSERT(type == SocketAddress::TYPE_IPV6); | |
313 result = inet_pton(AF_INET6, address, &addr->in6.sin6_addr); | |
314 } | |
315 return (result == 1); | |
316 } | |
317 | |
318 | |
319 intptr_t Socket::CreateBindDatagram(const RawAddr& addr, bool reuseAddress) { | 93 intptr_t Socket::CreateBindDatagram(const RawAddr& addr, bool reuseAddress) { |
320 intptr_t fd; | 94 intptr_t fd; |
321 | 95 |
322 fd = NO_RETRY_EXPECTED(socket(addr.addr.sa_family, SOCK_DGRAM, IPPROTO_UDP)); | 96 fd = NO_RETRY_EXPECTED(socket(addr.addr.sa_family, SOCK_DGRAM, IPPROTO_UDP)); |
323 if (fd < 0) { | 97 if (fd < 0) { |
324 return -1; | 98 return -1; |
325 } | 99 } |
326 | 100 |
327 if (!FDUtils::SetCloseOnExec(fd)) { | 101 if (!FDUtils::SetCloseOnExec(fd)) { |
328 FDUtils::SaveErrorAndClose(fd); | 102 FDUtils::SaveErrorAndClose(fd); |
(...skipping 13 matching lines...) Expand all Loading... |
342 } | 116 } |
343 | 117 |
344 if (!FDUtils::SetNonBlocking(fd)) { | 118 if (!FDUtils::SetNonBlocking(fd)) { |
345 FDUtils::SaveErrorAndClose(fd); | 119 FDUtils::SaveErrorAndClose(fd); |
346 return -1; | 120 return -1; |
347 } | 121 } |
348 return fd; | 122 return fd; |
349 } | 123 } |
350 | 124 |
351 | 125 |
352 bool Socket::ListInterfacesSupported() { | |
353 return false; | |
354 } | |
355 | |
356 | |
357 AddressList<InterfaceSocketAddress>* Socket::ListInterfaces( | |
358 int type, | |
359 OSError** os_error) { | |
360 // The ifaddrs.h header is not provided on Android. An Android | |
361 // implementation would have to use IOCTL or netlink. | |
362 ASSERT(*os_error == NULL); | |
363 *os_error = new OSError(-1, | |
364 "Listing interfaces is not supported " | |
365 "on this platform", | |
366 OSError::kSystem); | |
367 return NULL; | |
368 } | |
369 | |
370 | |
371 intptr_t ServerSocket::CreateBindListen(const RawAddr& addr, | 126 intptr_t ServerSocket::CreateBindListen(const RawAddr& addr, |
372 intptr_t backlog, | 127 intptr_t backlog, |
373 bool v6_only) { | 128 bool v6_only) { |
374 intptr_t fd; | 129 intptr_t fd; |
375 | 130 |
376 fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, SOCK_STREAM, 0)); | 131 fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, SOCK_STREAM, 0)); |
377 if (fd < 0) { | 132 if (fd < 0) { |
378 return -1; | 133 return -1; |
379 } | 134 } |
380 | 135 |
(...skipping 13 matching lines...) Expand all Loading... |
394 } | 149 } |
395 | 150 |
396 if (NO_RETRY_EXPECTED( | 151 if (NO_RETRY_EXPECTED( |
397 bind(fd, &addr.addr, SocketAddress::GetAddrLength(addr))) < 0) { | 152 bind(fd, &addr.addr, SocketAddress::GetAddrLength(addr))) < 0) { |
398 FDUtils::SaveErrorAndClose(fd); | 153 FDUtils::SaveErrorAndClose(fd); |
399 return -1; | 154 return -1; |
400 } | 155 } |
401 | 156 |
402 // Test for invalid socket port 65535 (some browsers disallow it). | 157 // Test for invalid socket port 65535 (some browsers disallow it). |
403 if ((SocketAddress::GetAddrPort(addr)) == 0 && | 158 if ((SocketAddress::GetAddrPort(addr)) == 0 && |
404 (Socket::GetPort(fd) == 65535)) { | 159 (SocketBase::GetPort(fd) == 65535)) { |
405 // Don't close the socket until we have created a new socket, ensuring | 160 // Don't close the socket until we have created a new socket, ensuring |
406 // that we do not get the bad port number again. | 161 // that we do not get the bad port number again. |
407 intptr_t new_fd = CreateBindListen(addr, backlog, v6_only); | 162 intptr_t new_fd = CreateBindListen(addr, backlog, v6_only); |
408 FDUtils::SaveErrorAndClose(fd); | 163 FDUtils::SaveErrorAndClose(fd); |
409 return new_fd; | 164 return new_fd; |
410 } | 165 } |
411 | 166 |
412 if (NO_RETRY_EXPECTED(listen(fd, backlog > 0 ? backlog : SOMAXCONN)) != 0) { | 167 if (NO_RETRY_EXPECTED(listen(fd, backlog > 0 ? backlog : SOMAXCONN)) != 0) { |
413 FDUtils::SaveErrorAndClose(fd); | 168 FDUtils::SaveErrorAndClose(fd); |
414 return -1; | 169 return -1; |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
457 return -1; | 212 return -1; |
458 } | 213 } |
459 if (!FDUtils::SetNonBlocking(socket)) { | 214 if (!FDUtils::SetNonBlocking(socket)) { |
460 FDUtils::SaveErrorAndClose(socket); | 215 FDUtils::SaveErrorAndClose(socket); |
461 return -1; | 216 return -1; |
462 } | 217 } |
463 } | 218 } |
464 return socket; | 219 return socket; |
465 } | 220 } |
466 | 221 |
467 | |
468 void Socket::Close(intptr_t fd) { | |
469 ASSERT(fd >= 0); | |
470 VOID_TEMP_FAILURE_RETRY(close(fd)); | |
471 } | |
472 | |
473 | |
474 bool Socket::GetNoDelay(intptr_t fd, bool* enabled) { | |
475 int on; | |
476 socklen_t len = sizeof(on); | |
477 int err = NO_RETRY_EXPECTED(getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, | |
478 reinterpret_cast<void*>(&on), &len)); | |
479 if (err == 0) { | |
480 *enabled = (on == 1); | |
481 } | |
482 return (err == 0); | |
483 } | |
484 | |
485 | |
486 bool Socket::SetNoDelay(intptr_t fd, bool enabled) { | |
487 int on = enabled ? 1 : 0; | |
488 return NO_RETRY_EXPECTED(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, | |
489 reinterpret_cast<char*>(&on), | |
490 sizeof(on))) == 0; | |
491 } | |
492 | |
493 | |
494 bool Socket::GetMulticastLoop(intptr_t fd, intptr_t protocol, bool* enabled) { | |
495 uint8_t on; | |
496 socklen_t len = sizeof(on); | |
497 int level = protocol == SocketAddress::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6; | |
498 int optname = protocol == SocketAddress::TYPE_IPV4 ? IP_MULTICAST_LOOP | |
499 : IPV6_MULTICAST_LOOP; | |
500 if (NO_RETRY_EXPECTED(getsockopt(fd, level, optname, | |
501 reinterpret_cast<char*>(&on), &len)) == 0) { | |
502 *enabled = (on == 1); | |
503 return true; | |
504 } | |
505 return false; | |
506 } | |
507 | |
508 | |
509 bool Socket::SetMulticastLoop(intptr_t fd, intptr_t protocol, bool enabled) { | |
510 int on = enabled ? 1 : 0; | |
511 int level = protocol == SocketAddress::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6; | |
512 int optname = protocol == SocketAddress::TYPE_IPV4 ? IP_MULTICAST_LOOP | |
513 : IPV6_MULTICAST_LOOP; | |
514 return NO_RETRY_EXPECTED(setsockopt( | |
515 fd, level, optname, reinterpret_cast<char*>(&on), sizeof(on))) == | |
516 0; | |
517 } | |
518 | |
519 bool Socket::GetMulticastHops(intptr_t fd, intptr_t protocol, int* value) { | |
520 uint8_t v; | |
521 socklen_t len = sizeof(v); | |
522 int level = protocol == SocketAddress::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6; | |
523 int optname = protocol == SocketAddress::TYPE_IPV4 ? IP_MULTICAST_TTL | |
524 : IPV6_MULTICAST_HOPS; | |
525 if (NO_RETRY_EXPECTED(getsockopt(fd, level, optname, | |
526 reinterpret_cast<char*>(&v), &len)) == 0) { | |
527 *value = v; | |
528 return true; | |
529 } | |
530 return false; | |
531 } | |
532 | |
533 | |
534 bool Socket::SetMulticastHops(intptr_t fd, intptr_t protocol, int value) { | |
535 int v = value; | |
536 int level = protocol == SocketAddress::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6; | |
537 int optname = protocol == SocketAddress::TYPE_IPV4 ? IP_MULTICAST_TTL | |
538 : IPV6_MULTICAST_HOPS; | |
539 return NO_RETRY_EXPECTED(setsockopt( | |
540 fd, level, optname, reinterpret_cast<char*>(&v), sizeof(v))) == 0; | |
541 } | |
542 | |
543 | |
544 bool Socket::GetBroadcast(intptr_t fd, bool* enabled) { | |
545 int on; | |
546 socklen_t len = sizeof(on); | |
547 int err = NO_RETRY_EXPECTED(getsockopt(fd, SOL_SOCKET, SO_BROADCAST, | |
548 reinterpret_cast<char*>(&on), &len)); | |
549 if (err == 0) { | |
550 *enabled = (on == 1); | |
551 } | |
552 return (err == 0); | |
553 } | |
554 | |
555 | |
556 bool Socket::SetBroadcast(intptr_t fd, bool enabled) { | |
557 int on = enabled ? 1 : 0; | |
558 return NO_RETRY_EXPECTED(setsockopt(fd, SOL_SOCKET, SO_BROADCAST, | |
559 reinterpret_cast<char*>(&on), | |
560 sizeof(on))) == 0; | |
561 } | |
562 | |
563 | |
564 bool Socket::JoinMulticast(intptr_t fd, | |
565 const RawAddr& addr, | |
566 const RawAddr&, | |
567 int interfaceIndex) { | |
568 int proto = (addr.addr.sa_family == AF_INET) ? IPPROTO_IP : IPPROTO_IPV6; | |
569 struct group_req mreq; | |
570 mreq.gr_interface = interfaceIndex; | |
571 memmove(&mreq.gr_group, &addr.ss, SocketAddress::GetAddrLength(addr)); | |
572 return NO_RETRY_EXPECTED( | |
573 setsockopt(fd, proto, MCAST_JOIN_GROUP, &mreq, sizeof(mreq))) == 0; | |
574 } | |
575 | |
576 | |
577 bool Socket::LeaveMulticast(intptr_t fd, | |
578 const RawAddr& addr, | |
579 const RawAddr&, | |
580 int interfaceIndex) { | |
581 int proto = (addr.addr.sa_family == AF_INET) ? IPPROTO_IP : IPPROTO_IPV6; | |
582 struct group_req mreq; | |
583 mreq.gr_interface = interfaceIndex; | |
584 memmove(&mreq.gr_group, &addr.ss, SocketAddress::GetAddrLength(addr)); | |
585 return NO_RETRY_EXPECTED(setsockopt(fd, proto, MCAST_LEAVE_GROUP, &mreq, | |
586 sizeof(mreq))) == 0; | |
587 } | |
588 | |
589 } // namespace bin | 222 } // namespace bin |
590 } // namespace dart | 223 } // namespace dart |
591 | 224 |
592 #endif // defined(HOST_OS_ANDROID) | 225 #endif // defined(HOST_OS_ANDROID) |
593 | 226 |
594 #endif // !defined(DART_IO_DISABLED) | 227 #endif // !defined(DART_IO_DISABLED) |
OLD | NEW |