1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net; 18 19 import android.compat.annotation.UnsupportedAppUsage; 20 import android.os.Build; 21 import android.system.ErrnoException; 22 import android.system.Os; 23 import android.system.OsConstants; 24 import android.system.StructLinger; 25 import android.system.StructTimeval; 26 27 import java.io.FileDescriptor; 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.io.OutputStream; 31 import java.net.SocketOptions; 32 33 /** 34 * Socket implementation used for android.net.LocalSocket and 35 * android.net.LocalServerSocket. Supports only AF_LOCAL sockets. 36 */ 37 class LocalSocketImpl 38 { 39 private SocketInputStream fis; 40 private SocketOutputStream fos; 41 private Object readMonitor = new Object(); 42 private Object writeMonitor = new Object(); 43 44 /** null if closed or not yet created */ 45 private FileDescriptor fd; 46 /** whether fd is created internally */ 47 private boolean mFdCreatedInternally; 48 49 // These fields are accessed by native code; 50 /** file descriptor array received during a previous read */ 51 @UnsupportedAppUsage 52 FileDescriptor[] inboundFileDescriptors; 53 /** file descriptor array that should be written during next write */ 54 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 55 FileDescriptor[] outboundFileDescriptors; 56 57 /** 58 * An input stream for local sockets. Needed because we may 59 * need to read ancillary data. 60 */ 61 class SocketInputStream extends InputStream { 62 /** {@inheritDoc} */ 63 @Override available()64 public int available() throws IOException { 65 FileDescriptor myFd = fd; 66 if (myFd == null) throw new IOException("socket closed"); 67 try { 68 return Os.ioctlInt(myFd, OsConstants.FIONREAD); 69 } catch (ErrnoException e) { 70 throw e.rethrowAsIOException(); 71 } 72 } 73 74 /** {@inheritDoc} */ 75 @Override close()76 public void close() throws IOException { 77 LocalSocketImpl.this.close(); 78 } 79 80 /** {@inheritDoc} */ 81 @Override read()82 public int read() throws IOException { 83 int ret; 84 synchronized (readMonitor) { 85 FileDescriptor myFd = fd; 86 if (myFd == null) throw new IOException("socket closed"); 87 88 ret = read_native(myFd); 89 return ret; 90 } 91 } 92 93 /** {@inheritDoc} */ 94 @Override read(byte[] b)95 public int read(byte[] b) throws IOException { 96 return read(b, 0, b.length); 97 } 98 99 /** {@inheritDoc} */ 100 @Override read(byte[] b, int off, int len)101 public int read(byte[] b, int off, int len) throws IOException { 102 synchronized (readMonitor) { 103 FileDescriptor myFd = fd; 104 if (myFd == null) throw new IOException("socket closed"); 105 106 if (off < 0 || len < 0 || (off + len) > b.length ) { 107 throw new ArrayIndexOutOfBoundsException(); 108 } 109 110 int ret = readba_native(b, off, len, myFd); 111 112 return ret; 113 } 114 } 115 } 116 117 /** 118 * An output stream for local sockets. Needed because we may 119 * need to read ancillary data. 120 */ 121 class SocketOutputStream extends OutputStream { 122 /** {@inheritDoc} */ 123 @Override close()124 public void close() throws IOException { 125 LocalSocketImpl.this.close(); 126 } 127 128 /** {@inheritDoc} */ 129 @Override write(byte[] b)130 public void write (byte[] b) throws IOException { 131 write(b, 0, b.length); 132 } 133 134 /** {@inheritDoc} */ 135 @Override write(byte[] b, int off, int len)136 public void write (byte[] b, int off, int len) throws IOException { 137 synchronized (writeMonitor) { 138 FileDescriptor myFd = fd; 139 if (myFd == null) throw new IOException("socket closed"); 140 141 if (off < 0 || len < 0 || (off + len) > b.length ) { 142 throw new ArrayIndexOutOfBoundsException(); 143 } 144 writeba_native(b, off, len, myFd); 145 } 146 } 147 148 /** {@inheritDoc} */ 149 @Override write(int b)150 public void write (int b) throws IOException { 151 synchronized (writeMonitor) { 152 FileDescriptor myFd = fd; 153 if (myFd == null) throw new IOException("socket closed"); 154 write_native(b, myFd); 155 } 156 } 157 } 158 read_native(FileDescriptor fd)159 private native int read_native(FileDescriptor fd) throws IOException; readba_native(byte[] b, int off, int len, FileDescriptor fd)160 private native int readba_native(byte[] b, int off, int len, 161 FileDescriptor fd) throws IOException; writeba_native(byte[] b, int off, int len, FileDescriptor fd)162 private native void writeba_native(byte[] b, int off, int len, 163 FileDescriptor fd) throws IOException; write_native(int b, FileDescriptor fd)164 private native void write_native(int b, FileDescriptor fd) 165 throws IOException; connectLocal(FileDescriptor fd, String name, int namespace)166 private native void connectLocal(FileDescriptor fd, String name, 167 int namespace) throws IOException; bindLocal(FileDescriptor fd, String name, int namespace)168 private native void bindLocal(FileDescriptor fd, String name, int namespace) 169 throws IOException; getPeerCredentials_native( FileDescriptor fd)170 private native Credentials getPeerCredentials_native( 171 FileDescriptor fd) throws IOException; 172 173 /** 174 * Create a new instance. 175 */ 176 @UnsupportedAppUsage LocalSocketImpl()177 /*package*/ LocalSocketImpl() 178 { 179 } 180 181 /** 182 * Create a new instance from a file descriptor representing 183 * a bound socket. The state of the file descriptor is not checked here 184 * but the caller can verify socket state by calling listen(). 185 * 186 * @param fd non-null; bound file descriptor 187 */ LocalSocketImpl(FileDescriptor fd)188 /*package*/ LocalSocketImpl(FileDescriptor fd) 189 { 190 this.fd = fd; 191 } 192 toString()193 public String toString() { 194 return super.toString() + " fd:" + fd; 195 } 196 197 /** 198 * Creates a socket in the underlying OS. 199 * 200 * @param sockType either {@link LocalSocket#SOCKET_DGRAM}, {@link LocalSocket#SOCKET_STREAM} 201 * or {@link LocalSocket#SOCKET_SEQPACKET} 202 * @throws IOException 203 */ create(int sockType)204 public void create(int sockType) throws IOException { 205 if (fd != null) { 206 throw new IOException("LocalSocketImpl already has an fd"); 207 } 208 209 int osType; 210 switch (sockType) { 211 case LocalSocket.SOCKET_DGRAM: 212 osType = OsConstants.SOCK_DGRAM; 213 break; 214 case LocalSocket.SOCKET_STREAM: 215 osType = OsConstants.SOCK_STREAM; 216 break; 217 case LocalSocket.SOCKET_SEQPACKET: 218 osType = OsConstants.SOCK_SEQPACKET; 219 break; 220 default: 221 throw new IllegalStateException("unknown sockType"); 222 } 223 try { 224 fd = Os.socket(OsConstants.AF_UNIX, osType, 0); 225 mFdCreatedInternally = true; 226 } catch (ErrnoException e) { 227 e.rethrowAsIOException(); 228 } 229 } 230 231 /** 232 * Closes the socket. 233 * 234 * @throws IOException 235 */ close()236 public void close() throws IOException { 237 synchronized (LocalSocketImpl.this) { 238 if ((fd == null) || (mFdCreatedInternally == false)) { 239 fd = null; 240 return; 241 } 242 try { 243 Os.close(fd); 244 } catch (ErrnoException e) { 245 e.rethrowAsIOException(); 246 } 247 fd = null; 248 } 249 } 250 251 /** note timeout presently ignored */ connect(LocalSocketAddress address, int timeout)252 protected void connect(LocalSocketAddress address, int timeout) 253 throws IOException 254 { 255 if (fd == null) { 256 throw new IOException("socket not created"); 257 } 258 259 connectLocal(fd, address.getName(), address.getNamespace().getId()); 260 } 261 262 /** 263 * Binds this socket to an endpoint name. May only be called on an instance 264 * that has not yet been bound. 265 * 266 * @param endpoint endpoint address 267 * @throws IOException 268 */ bind(LocalSocketAddress endpoint)269 public void bind(LocalSocketAddress endpoint) throws IOException 270 { 271 if (fd == null) { 272 throw new IOException("socket not created"); 273 } 274 275 bindLocal(fd, endpoint.getName(), endpoint.getNamespace().getId()); 276 } 277 listen(int backlog)278 protected void listen(int backlog) throws IOException 279 { 280 if (fd == null) { 281 throw new IOException("socket not created"); 282 } 283 try { 284 Os.listen(fd, backlog); 285 } catch (ErrnoException e) { 286 throw e.rethrowAsIOException(); 287 } 288 } 289 290 /** 291 * Accepts a new connection to the socket. Blocks until a new 292 * connection arrives. 293 * 294 * @param s a socket that will be used to represent the new connection. 295 * @throws IOException 296 */ accept(LocalSocketImpl s)297 protected void accept(LocalSocketImpl s) throws IOException { 298 if (fd == null) { 299 throw new IOException("socket not created"); 300 } 301 302 try { 303 s.fd = Os.accept(fd, null /* address */); 304 s.mFdCreatedInternally = true; 305 } catch (ErrnoException e) { 306 throw e.rethrowAsIOException(); 307 } 308 } 309 310 /** 311 * Retrieves the input stream for this instance. 312 * 313 * @return input stream 314 * @throws IOException if socket has been closed or cannot be created. 315 */ getInputStream()316 protected InputStream getInputStream() throws IOException 317 { 318 if (fd == null) { 319 throw new IOException("socket not created"); 320 } 321 322 synchronized (this) { 323 if (fis == null) { 324 fis = new SocketInputStream(); 325 } 326 327 return fis; 328 } 329 } 330 331 /** 332 * Retrieves the output stream for this instance. 333 * 334 * @return output stream 335 * @throws IOException if socket has been closed or cannot be created. 336 */ getOutputStream()337 protected OutputStream getOutputStream() throws IOException 338 { 339 if (fd == null) { 340 throw new IOException("socket not created"); 341 } 342 343 synchronized (this) { 344 if (fos == null) { 345 fos = new SocketOutputStream(); 346 } 347 348 return fos; 349 } 350 } 351 352 /** 353 * Returns the number of bytes available for reading without blocking. 354 * 355 * @return >= 0 count bytes available 356 * @throws IOException 357 */ available()358 protected int available() throws IOException 359 { 360 return getInputStream().available(); 361 } 362 363 /** 364 * Shuts down the input side of the socket. 365 * 366 * @throws IOException 367 */ shutdownInput()368 protected void shutdownInput() throws IOException 369 { 370 if (fd == null) { 371 throw new IOException("socket not created"); 372 } 373 374 try { 375 Os.shutdown(fd, OsConstants.SHUT_RD); 376 } catch (ErrnoException e) { 377 throw e.rethrowAsIOException(); 378 } 379 } 380 381 /** 382 * Shuts down the output side of the socket. 383 * 384 * @throws IOException 385 */ shutdownOutput()386 protected void shutdownOutput() throws IOException 387 { 388 if (fd == null) { 389 throw new IOException("socket not created"); 390 } 391 392 try { 393 Os.shutdown(fd, OsConstants.SHUT_WR); 394 } catch (ErrnoException e) { 395 throw e.rethrowAsIOException(); 396 } 397 } 398 getFileDescriptor()399 protected FileDescriptor getFileDescriptor() 400 { 401 return fd; 402 } 403 supportsUrgentData()404 protected boolean supportsUrgentData() 405 { 406 return false; 407 } 408 sendUrgentData(int data)409 protected void sendUrgentData(int data) throws IOException 410 { 411 throw new RuntimeException ("not impled"); 412 } 413 getOption(int optID)414 public Object getOption(int optID) throws IOException 415 { 416 if (fd == null) { 417 throw new IOException("socket not created"); 418 } 419 420 try { 421 Object toReturn; 422 switch (optID) { 423 case SocketOptions.SO_TIMEOUT: 424 StructTimeval timeval = Os.getsockoptTimeval(fd, OsConstants.SOL_SOCKET, 425 OsConstants.SO_SNDTIMEO); 426 toReturn = (int) timeval.toMillis(); 427 break; 428 case SocketOptions.SO_RCVBUF: 429 case SocketOptions.SO_SNDBUF: 430 case SocketOptions.SO_REUSEADDR: 431 int osOpt = javaSoToOsOpt(optID); 432 toReturn = Os.getsockoptInt(fd, OsConstants.SOL_SOCKET, osOpt); 433 break; 434 case SocketOptions.SO_LINGER: 435 StructLinger linger= 436 Os.getsockoptLinger(fd, OsConstants.SOL_SOCKET, OsConstants.SO_LINGER); 437 if (!linger.isOn()) { 438 toReturn = -1; 439 } else { 440 toReturn = linger.l_linger; 441 } 442 break; 443 case SocketOptions.TCP_NODELAY: 444 toReturn = Os.getsockoptInt(fd, OsConstants.IPPROTO_TCP, 445 OsConstants.TCP_NODELAY); 446 break; 447 default: 448 throw new IOException("Unknown option: " + optID); 449 } 450 return toReturn; 451 } catch (ErrnoException e) { 452 throw e.rethrowAsIOException(); 453 } 454 } 455 setOption(int optID, Object value)456 public void setOption(int optID, Object value) 457 throws IOException { 458 459 if (fd == null) { 460 throw new IOException("socket not created"); 461 } 462 463 /* 464 * Boolean.FALSE is used to disable some options, so it 465 * is important to distinguish between FALSE and unset. 466 * We define it here that -1 is unset, 0 is FALSE, and 1 467 * is TRUE. 468 */ 469 int boolValue = -1; 470 int intValue = 0; 471 if (value instanceof Integer) { 472 intValue = (Integer)value; 473 } else if (value instanceof Boolean) { 474 boolValue = ((Boolean) value)? 1 : 0; 475 } else { 476 throw new IOException("bad value: " + value); 477 } 478 479 try { 480 switch (optID) { 481 case SocketOptions.SO_LINGER: 482 StructLinger linger = new StructLinger(boolValue, intValue); 483 Os.setsockoptLinger(fd, OsConstants.SOL_SOCKET, OsConstants.SO_LINGER, linger); 484 break; 485 case SocketOptions.SO_TIMEOUT: 486 // The option must set both send and receive timeouts. 487 // Note: The incoming timeout value is in milliseconds. 488 StructTimeval timeval = StructTimeval.fromMillis(intValue); 489 Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO, 490 timeval); 491 Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO, 492 timeval); 493 break; 494 case SocketOptions.SO_RCVBUF: 495 case SocketOptions.SO_SNDBUF: 496 case SocketOptions.SO_REUSEADDR: 497 int osOpt = javaSoToOsOpt(optID); 498 Os.setsockoptInt(fd, OsConstants.SOL_SOCKET, osOpt, intValue); 499 break; 500 case SocketOptions.TCP_NODELAY: 501 Os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_NODELAY, 502 intValue); 503 break; 504 default: 505 throw new IOException("Unknown option: " + optID); 506 } 507 } catch (ErrnoException e) { 508 throw e.rethrowAsIOException(); 509 } 510 } 511 512 /** 513 * Enqueues a set of file descriptors to send to the peer. The queue 514 * is one deep. The file descriptors will be sent with the next write 515 * of normal data, and will be delivered in a single ancillary message. 516 * See "man 7 unix" SCM_RIGHTS on a desktop Linux machine. 517 * 518 * @param fds non-null; file descriptors to send. 519 * @throws IOException 520 */ setFileDescriptorsForSend(FileDescriptor[] fds)521 public void setFileDescriptorsForSend(FileDescriptor[] fds) { 522 synchronized(writeMonitor) { 523 outboundFileDescriptors = fds; 524 } 525 } 526 527 /** 528 * Retrieves a set of file descriptors that a peer has sent through 529 * an ancillary message. This method retrieves the most recent set sent, 530 * and then returns null until a new set arrives. 531 * File descriptors may only be passed along with regular data, so this 532 * method can only return a non-null after a read operation. 533 * 534 * @return null or file descriptor array 535 * @throws IOException 536 */ getAncillaryFileDescriptors()537 public FileDescriptor[] getAncillaryFileDescriptors() throws IOException { 538 synchronized(readMonitor) { 539 FileDescriptor[] result = inboundFileDescriptors; 540 541 inboundFileDescriptors = null; 542 return result; 543 } 544 } 545 546 /** 547 * Retrieves the credentials of this socket's peer. Only valid on 548 * connected sockets. 549 * 550 * @return non-null; peer credentials 551 * @throws IOException 552 */ getPeerCredentials()553 public Credentials getPeerCredentials() throws IOException { 554 return getPeerCredentials_native(fd); 555 } 556 557 /** 558 * Retrieves the socket name from the OS. 559 * 560 * @return non-null; socket name 561 * @throws IOException on failure 562 */ getSockAddress()563 public LocalSocketAddress getSockAddress() throws IOException { 564 // This method has never been implemented. 565 return null; 566 } 567 568 @Override finalize()569 protected void finalize() throws IOException { 570 close(); 571 } 572 javaSoToOsOpt(int optID)573 private static int javaSoToOsOpt(int optID) { 574 switch (optID) { 575 case SocketOptions.SO_SNDBUF: 576 return OsConstants.SO_SNDBUF; 577 case SocketOptions.SO_RCVBUF: 578 return OsConstants.SO_RCVBUF; 579 case SocketOptions.SO_REUSEADDR: 580 return OsConstants.SO_REUSEADDR; 581 default: 582 throw new UnsupportedOperationException("Unknown option: " + optID); 583 } 584 } 585 } 586