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