blob: 0eee04110c9b4489a44f836faba797d0680b81c8 [file] [log] [blame]
/* GENERATED SOURCE. DO NOT MODIFY. */
/*
* Copyright 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.org.conscrypt;
import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_CLOSED;
import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_HANDSHAKE_COMPLETED;
import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_HANDSHAKE_STARTED;
import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_NEW;
import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_READY;
import static com.android.org.conscrypt.SSLUtils.EngineStates.STATE_READY_HANDSHAKE_CUT_THROUGH;
import static javax.net.ssl.SSLEngineResult.Status.CLOSED;
import static javax.net.ssl.SSLEngineResult.Status.OK;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
/**
* Implements crypto handling by delegating to {@link ConscryptEngine}.
*/
class ConscryptEngineSocket extends OpenSSLSocketImpl implements SSLParametersImpl.AliasChooser {
private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
private final ConscryptEngine engine;
private final Object stateLock = new Object();
private final Object handshakeLock = new Object();
private SSLOutputStream out;
private SSLInputStream in;
private BufferAllocator bufferAllocator = ConscryptEngine.getDefaultBufferAllocator();
// @GuardedBy("stateLock");
private int state = STATE_NEW;
// The constructors should not be called except from the Platform class, because we may
// want to construct a subclass instead.
ConscryptEngineSocket(SSLParametersImpl sslParameters) throws IOException {
engine = newEngine(sslParameters, this);
}
ConscryptEngineSocket(String hostname, int port, SSLParametersImpl sslParameters)
throws IOException {
super(hostname, port);
engine = newEngine(sslParameters, this);
}
ConscryptEngineSocket(InetAddress address, int port, SSLParametersImpl sslParameters)
throws IOException {
super(address, port);
engine = newEngine(sslParameters, this);
}
ConscryptEngineSocket(String hostname, int port, InetAddress clientAddress, int clientPort,
SSLParametersImpl sslParameters) throws IOException {
super(hostname, port, clientAddress, clientPort);
engine = newEngine(sslParameters, this);
}
ConscryptEngineSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort,
SSLParametersImpl sslParameters) throws IOException {
super(address, port, clientAddress, clientPort);
engine = newEngine(sslParameters, this);
}
ConscryptEngineSocket(Socket socket, String hostname, int port, boolean autoClose,
SSLParametersImpl sslParameters) throws IOException {
super(socket, hostname, port, autoClose);
engine = newEngine(sslParameters, this);
}
private static ConscryptEngine newEngine(
SSLParametersImpl sslParameters, final ConscryptEngineSocket socket) {
SSLParametersImpl modifiedParams;
if (Platform.supportsX509ExtendedTrustManager()) {
modifiedParams = sslParameters.cloneWithTrustManager(
getDelegatingTrustManager(sslParameters.getX509TrustManager(), socket));
} else {
modifiedParams = sslParameters;
}
ConscryptEngine engine =
new ConscryptEngine(modifiedParams, socket.peerInfoProvider(), socket);
// When the handshake completes, notify any listeners.
engine.setHandshakeListener(new HandshakeListener() {
/**
* Protected by {@code stateLock}
*/
@Override
public void onHandshakeFinished() {
// Just call the outer class method.
socket.onHandshakeFinished();
}
});
// Transition the engine state to MODE_SET
engine.setUseClientMode(sslParameters.getUseClientMode());
return engine;
}
// Returns a trust manager that delegates to the given trust manager, but maps SSLEngine
// references to the given ConscryptEngineSocket. Our internal engine will call
// the SSLEngine-receiving methods, but our callers expect the SSLSocket-receiving
// methods to get called.
private static X509TrustManager getDelegatingTrustManager(
final X509TrustManager delegate, final ConscryptEngineSocket socket) {
if (delegate instanceof X509ExtendedTrustManager) {
final X509ExtendedTrustManager extendedDelegate = (X509ExtendedTrustManager) delegate;
return new X509ExtendedTrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s,
Socket socket) throws CertificateException {
throw new AssertionError("Should not be called");
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s,
Socket socket) throws CertificateException {
throw new AssertionError("Should not be called");
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s,
SSLEngine sslEngine) throws CertificateException {
extendedDelegate.checkClientTrusted(x509Certificates, s, socket);
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s,
SSLEngine sslEngine) throws CertificateException {
extendedDelegate.checkServerTrusted(x509Certificates, s, socket);
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
throws CertificateException {
extendedDelegate.checkClientTrusted(x509Certificates, s);
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
throws CertificateException {
extendedDelegate.checkServerTrusted(x509Certificates, s);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return extendedDelegate.getAcceptedIssuers();
}
};
}
return delegate;
}
@Override
public final SSLParameters getSSLParameters() {
return engine.getSSLParameters();
}
@Override
public final void setSSLParameters(SSLParameters sslParameters) {
engine.setSSLParameters(sslParameters);
}
@Override
public final void startHandshake() throws IOException {
checkOpen();
try {
synchronized (handshakeLock) {
// Only lock stateLock when we begin the handshake. This is done so that we don't
// hold the stateLock when we invoke the handshake completion listeners.
synchronized (stateLock) {
// Initialize the handshake if we haven't already.
if (state == STATE_NEW) {
state = STATE_HANDSHAKE_STARTED;
engine.beginHandshake();
in = new SSLInputStream();
out = new SSLOutputStream();
} else {
// We've either started the handshake already or have been closed.
// Do nothing in both cases.
//
// NOTE: BoringSSL does not support initiating renegotiation, so we always
// ignore addition handshake calls.
return;
}
}
doHandshake();
}
} catch (SSLException e) {
close();
throw e;
} catch (IOException e) {
close();
throw e;
} catch (Exception e) {
close();
// Convert anything else to a handshake exception.
throw SSLUtils.toSSLHandshakeException(e);
}
}
private void doHandshake() throws IOException {
try {
boolean finished = false;
while (!finished) {
switch (engine.getHandshakeStatus()) {
case NEED_UNWRAP:
if (in.processDataFromSocket(EmptyArray.BYTE, 0, 0) < 0) {
// Can't complete the handshake due to EOF.
throw SSLUtils.toSSLHandshakeException(
new EOFException("connection closed"));
}
break;
case NEED_WRAP: {
out.writeInternal(EMPTY_BUFFER);
// Always flush handshake frames immediately.
out.flushInternal();
break;
}
case NEED_TASK: {
// Should never get here, since our engine never provides tasks.
throw new IllegalStateException("Engine tasks are unsupported");
}
case NOT_HANDSHAKING:
case FINISHED: {
// Handshake is complete.
finished = true;
break;
}
default: {
throw new IllegalStateException(
"Unknown handshake status: " + engine.getHandshakeStatus());
}
}
}
} catch (SSLException e) {
drainOutgoingQueue();
close();
throw e;
} catch (IOException e) {
close();
throw e;
} catch (Exception e) {
close();
// Convert anything else to a handshake exception.
throw SSLUtils.toSSLHandshakeException(e);
}
}
@Override
public final InputStream getInputStream() throws IOException {
checkOpen();
// Block waiting for a handshake without a lock held. It's possible that the socket
// is closed at this point. If that happens, we'll still return the input stream but
// all reads on it will throw.
waitForHandshake();
return in;
}
@Override
public final OutputStream getOutputStream() throws IOException {
checkOpen();
// Block waiting for a handshake without a lock held. It's possible that the socket
// is closed at this point. If that happens, we'll still return the input stream but
// all reads on it will throw.
waitForHandshake();
return out;
}
@Override
public final SSLSession getHandshakeSession() {
return engine.handshakeSession();
}
@Override
public final SSLSession getSession() {
if (isConnected()) {
try {
waitForHandshake();
} catch (IOException e) {
// Fall through
}
}
return engine.getSession();
}
@Override
final SSLSession getActiveSession() {
return engine.getSession();
}
@Override
public final boolean getEnableSessionCreation() {
return engine.getEnableSessionCreation();
}
@Override
public final void setEnableSessionCreation(boolean flag) {
engine.setEnableSessionCreation(flag);
}
@Override
public final String[] getSupportedCipherSuites() {
return engine.getSupportedCipherSuites();
}
@Override
public final String[] getEnabledCipherSuites() {
return engine.getEnabledCipherSuites();
}
@Override
public final void setEnabledCipherSuites(String[] suites) {
engine.setEnabledCipherSuites(suites);
}
@Override
public final String[] getSupportedProtocols() {
return engine.getSupportedProtocols();
}
@Override
public final String[] getEnabledProtocols() {
return engine.getEnabledProtocols();
}
@Override
public final void setEnabledProtocols(String[] protocols) {
engine.setEnabledProtocols(protocols);
}
/**
* This method enables Server Name Indication. If the hostname is not a valid SNI hostname,
* the SNI extension will be omitted from the handshake.
*
* @param hostname the desired SNI hostname, or null to disable
*/
@android.compat.annotation.
UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q,
publicAlternatives = "Use {@link javax.net.ssl.SSLParameters#setServerNames}.")
@Override
public final void
setHostname(String hostname) {
engine.setHostname(hostname);
super.setHostname(hostname);
}
@android.compat.annotation.
UnsupportedAppUsage(maxTargetSdk = dalvik.annotation.compat.VersionCodes.Q,
publicAlternatives = "Use {@link android.net.ssl.SSLSockets#setUseSessionTickets}.")
@Override
public final void
setUseSessionTickets(boolean useSessionTickets) {
engine.setUseSessionTickets(useSessionTickets);
}
@Override
public final void setChannelIdEnabled(boolean enabled) {
engine.setChannelIdEnabled(enabled);
}
@Override
public final byte[] getChannelId() throws SSLException {
return engine.getChannelId();
}
@Override
public final void setChannelIdPrivateKey(PrivateKey privateKey) {
engine.setChannelIdPrivateKey(privateKey);
}
@Override
byte[] getTlsUnique() {
return engine.getTlsUnique();
}
@Override
byte[] exportKeyingMaterial(String label, byte[] context, int length) throws SSLException {
return engine.exportKeyingMaterial(label, context, length);
}
@Override
public final boolean getUseClientMode() {
return engine.getUseClientMode();
}
@Override
public final void setUseClientMode(boolean mode) {
engine.setUseClientMode(mode);
}
@Override
public final boolean getWantClientAuth() {
return engine.getWantClientAuth();
}
@Override
public final boolean getNeedClientAuth() {
return engine.getNeedClientAuth();
}
@Override
public final void setNeedClientAuth(boolean need) {
engine.setNeedClientAuth(need);
}
@Override
public final void setWantClientAuth(boolean want) {
engine.setWantClientAuth(want);
}
@Override
@SuppressWarnings("UnsynchronizedOverridesSynchronized")
public final void close() throws IOException {
// TODO: Close SSL sockets using a background thread so they close gracefully.
if (stateLock == null) {
// close() has been called before we've initialized the socket, so just
// return.
return;
}
int previousState;
synchronized (stateLock) {
previousState = state;
if (state == STATE_CLOSED) {
// close() has already been called, so do nothing and return.
return;
}
state = STATE_CLOSED;
stateLock.notifyAll();
}
try {
// Close the engine.
engine.closeInbound();
engine.closeOutbound();
// Closing the outbound direction of a connected engine will trigger a TLS close
// notify, which we should try and send.
// If we don't, then closeOutbound won't be able to free resources because there are
// bytes queued for transmission so drain the queue those and call closeOutbound a
// second time.
if (previousState >= STATE_HANDSHAKE_STARTED) {
drainOutgoingQueue();
engine.closeOutbound();
}
} finally {
// In case of an exception thrown while closing the engine, we still need to close the
// underlying socket and release any resources the input stream is holding.
try {
super.close();
} finally {
if (in != null) {
in.release();
}
}
}
}
@Override
public void setHandshakeTimeout(int handshakeTimeoutMilliseconds) throws SocketException {
// Not supported but ignored rather than throwing for compatibility: b/146041327
}
@Override
final void setApplicationProtocols(String[] protocols) {
engine.setApplicationProtocols(protocols);
}
@Override
final String[] getApplicationProtocols() {
return engine.getApplicationProtocols();
}
@Override
public final String getApplicationProtocol() {
return engine.getApplicationProtocol();
}
@Override
public final String getHandshakeApplicationProtocol() {
return engine.getHandshakeApplicationProtocol();
}
@Override
public final void setApplicationProtocolSelector(ApplicationProtocolSelector selector) {
setApplicationProtocolSelector(
selector == null ? null : new ApplicationProtocolSelectorAdapter(this, selector));
}
@Override
final void setApplicationProtocolSelector(ApplicationProtocolSelectorAdapter selector) {
engine.setApplicationProtocolSelector(selector);
}
void setBufferAllocator(BufferAllocator bufferAllocator) {
engine.setBufferAllocator(bufferAllocator);
this.bufferAllocator = bufferAllocator;
}
private void onHandshakeFinished() {
boolean notify = false;
synchronized (stateLock) {
if (state != STATE_CLOSED) {
if (state == STATE_HANDSHAKE_STARTED) {
state = STATE_READY_HANDSHAKE_CUT_THROUGH;
} else if (state == STATE_HANDSHAKE_COMPLETED) {
state = STATE_READY;
}
// Unblock threads that are waiting for our state to transition
// into STATE_READY or STATE_READY_HANDSHAKE_CUT_THROUGH.
stateLock.notifyAll();
notify = true;
}
}
if (notify) {
notifyHandshakeCompletedListeners();
}
}
/**
* Waits for the handshake to complete.
*/
private void waitForHandshake() throws IOException {
startHandshake();
synchronized (stateLock) {
while (state != STATE_READY && state != STATE_READY_HANDSHAKE_CUT_THROUGH
&& state != STATE_CLOSED) {
try {
stateLock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Interrupted waiting for handshake", e);
}
}
if (state == STATE_CLOSED) {
throw new SocketException("Socket is closed");
}
}
}
private void drainOutgoingQueue() {
try {
while (engine.pendingOutboundEncryptedBytes() > 0) {
out.writeInternal(EMPTY_BUFFER);
// Always flush handshake frames immediately.
out.flushInternal();
}
} catch (IOException e) {
// Ignore
}
}
private OutputStream getUnderlyingOutputStream() throws IOException {
return super.getOutputStream();
}
private InputStream getUnderlyingInputStream() throws IOException {
return super.getInputStream();
}
@Override
public final String chooseServerAlias(X509KeyManager keyManager, String keyType) {
return keyManager.chooseServerAlias(keyType, null, this);
}
@Override
public final String chooseClientAlias(
X509KeyManager keyManager, X500Principal[] issuers, String[] keyTypes) {
return keyManager.chooseClientAlias(keyTypes, issuers, this);
}
/**
* Wrap bytes written to the underlying socket.
*/
private final class SSLOutputStream extends OutputStream {
private final Object writeLock = new Object();
private final ByteBuffer target;
private final int targetArrayOffset;
private OutputStream socketOutputStream;
SSLOutputStream() {
target = ByteBuffer.allocate(engine.getSession().getPacketBufferSize());
targetArrayOffset = target.arrayOffset();
}
@Override
public void close() throws IOException {
ConscryptEngineSocket.this.close();
}
@Override
public void write(int b) throws IOException {
startHandshake();
synchronized (writeLock) {
write(new byte[] {(byte) b});
}
}
@Override
public void write(byte[] b) throws IOException {
startHandshake();
synchronized (writeLock) {
writeInternal(ByteBuffer.wrap(b));
}
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
startHandshake();
synchronized (writeLock) {
writeInternal(ByteBuffer.wrap(b, off, len));
}
}
private void writeInternal(ByteBuffer buffer) throws IOException {
Platform.blockGuardOnNetwork();
checkOpen();
init();
// Need to loop through at least once to enable handshaking where no application
// bytes are processed.
int len = buffer.remaining();
SSLEngineResult engineResult;
do {
target.clear();
engineResult = engine.wrap(buffer, target);
if (engineResult.getStatus() != OK && engineResult.getStatus() != CLOSED) {
throw new SSLException("Unexpected engine result " + engineResult.getStatus());
}
if (target.position() != engineResult.bytesProduced()) {
throw new SSLException("Engine bytesProduced " + engineResult.bytesProduced()
+ " does not match bytes written " + target.position());
}
len -= engineResult.bytesConsumed();
if (len != buffer.remaining()) {
throw new SSLException("Engine did not read the correct number of bytes");
}
if (engineResult.getStatus() == CLOSED && engineResult.bytesProduced() == 0) {
if (len > 0) {
throw new SocketException("Socket closed");
}
break;
}
target.flip();
// Write the data to the socket.
writeToSocket();
} while (len > 0);
}
@Override
public void flush() throws IOException {
startHandshake();
synchronized (writeLock) {
flushInternal();
}
}
private void flushInternal() throws IOException {
checkOpen();
init();
socketOutputStream.flush();
}
private void init() throws IOException {
if (socketOutputStream == null) {
socketOutputStream = getUnderlyingOutputStream();
}
}
private void writeToSocket() throws IOException {
// Write the data to the socket.
socketOutputStream.write(target.array(), targetArrayOffset, target.limit());
}
}
/**
* Unwrap bytes read from the underlying socket.
*/
private final class SSLInputStream extends InputStream {
private final Object readLock = new Object();
private final byte[] singleByte = new byte[1];
private final ByteBuffer fromEngine;
private final ByteBuffer fromSocket;
private final int fromSocketArrayOffset;
private final AllocatedBuffer allocatedBuffer;
private InputStream socketInputStream;
SSLInputStream() {
if (bufferAllocator != null) {
allocatedBuffer = bufferAllocator.allocateDirectBuffer(
engine.getSession().getApplicationBufferSize());
fromEngine = allocatedBuffer.nioBuffer();
} else {
allocatedBuffer = null;
fromEngine = ByteBuffer.allocateDirect(engine.getSession().getApplicationBufferSize());
}
// Initially fromEngine.remaining() == 0.
fromEngine.flip();
fromSocket = ByteBuffer.allocate(engine.getSession().getPacketBufferSize());
fromSocketArrayOffset = fromSocket.arrayOffset();
}
@Override
public void close() throws IOException {
ConscryptEngineSocket.this.close();
}
void release() {
synchronized (readLock) {
if (allocatedBuffer != null) {
allocatedBuffer.release();
}
}
}
@Override
public int read() throws IOException {
startHandshake();
synchronized (readLock) {
// Handle returning of -1 if EOF is reached.
int count = read(singleByte, 0, 1);
if (count == -1) {
// Handle EOF.
return -1;
}
if (count != 1) {
throw new SSLException("read incorrect number of bytes " + count);
}
return singleByte[0] & 0xff;
}
}
@Override
public int read(byte[] b) throws IOException {
startHandshake();
synchronized (readLock) {
return read(b, 0, b.length);
}
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
startHandshake();
synchronized (readLock) {
return readUntilDataAvailable(b, off, len);
}
}
@Override
public int available() throws IOException {
startHandshake();
synchronized (readLock) {
init();
return fromEngine.remaining();
}
}
private boolean isHandshaking(HandshakeStatus status) {
switch(status) {
case NEED_TASK:
case NEED_WRAP:
case NEED_UNWRAP:
return true;
default:
return false;
}
}
private int readUntilDataAvailable(byte[] b, int off, int len) throws IOException {
int count;
do {
count = processDataFromSocket(b, off, len);
} while (count == 0);
return count;
}
// Returns any decrypted data from the engine. If no data is currently present in the
// engine's output buffer, reads from the input socket until the engine has processed
// at least one TLS record, then returns any data in the output buffer or 0 if no
// data is available. This is used both during handshaking (in which case, the records
// will produce no data and this method will return 0) and by the InputStream read()
// methods that expect records to produce application data.
private int processDataFromSocket(byte[] b, int off, int len) throws IOException {
Platform.blockGuardOnNetwork();
checkOpen();
// Make sure the input stream has been created.
init();
for (;;) {
// Serve any remaining data from the engine first.
if (fromEngine.remaining() > 0) {
int readFromEngine = Math.min(fromEngine.remaining(), len);
fromEngine.get(b, off, readFromEngine);
return readFromEngine;
}
// Try to unwrap any data already in the socket buffer.
boolean needMoreDataFromSocket = true;
// Unwrap the unencrypted bytes into the engine buffer.
fromSocket.flip();
fromEngine.clear();
boolean engineHandshaking = isHandshaking(engine.getHandshakeStatus());
SSLEngineResult engineResult = engine.unwrap(fromSocket, fromEngine);
// Shift any remaining data to the beginning of the buffer so that
// we can accommodate the next full packet. After this is called,
// limit will be restored to capacity and position will point just
// past the end of the data.
fromSocket.compact();
fromEngine.flip();
switch (engineResult.getStatus()) {
case BUFFER_UNDERFLOW: {
if (engineResult.bytesProduced() == 0) {
// Need to read more data from the socket.
break;
}
// Also serve the data that was produced.
needMoreDataFromSocket = false;
break;
}
case OK: {
// We processed the entire packet successfully...
if (!engineHandshaking && isHandshaking(engineResult.getHandshakeStatus())
&& isHandshakeFinished()) {
// The received packet is the beginning of a renegotiation handshake.
// Perform another handshake.
renegotiate();
return 0;
}
needMoreDataFromSocket = false;
break;
}
case CLOSED: {
// EOF
return -1;
}
default: {
// Anything else is an error.
throw new SSLException(
"Unexpected engine result " + engineResult.getStatus());
}
}
if (!needMoreDataFromSocket && engineResult.bytesProduced() == 0) {
// Read successfully, but produced no data. Possibly part of a
// handshake.
return 0;
}
// Read more data from the socket.
if (needMoreDataFromSocket && readFromSocket() == -1) {
// Failed to read the next encrypted packet before reaching EOF.
return -1;
}
// Continue the loop and return the data from the engine buffer.
}
}
private boolean isHandshakeFinished() {
synchronized (stateLock) {
return state >= STATE_READY_HANDSHAKE_CUT_THROUGH;
}
}
/**
* Processes a renegotiation received from the remote peer.
*/
private void renegotiate() throws IOException {
synchronized (handshakeLock) {
doHandshake();
}
}
private void init() throws IOException {
if (socketInputStream == null) {
socketInputStream = getUnderlyingInputStream();
}
}
private int readFromSocket() throws IOException {
try {
// Read directly to the underlying array and increment the buffer position if
// appropriate.
int pos = fromSocket.position();
int lim = fromSocket.limit();
int read = socketInputStream.read(
fromSocket.array(), fromSocketArrayOffset + pos, lim - pos);
if (read > 0) {
fromSocket.position(pos + read);
}
return read;
} catch (EOFException e) {
return -1;
}
}
}
}