Skip to content

Commit b4ad38d

Browse files
committed
Revert "Merge pull request #1493 from shapeblue/nio-fix"
This reverts commit 7ce0e10, reversing changes made to 29ba71f. This was reverted because it seemed to be related to an issue when doing a DeployDC, causing an `addHost` error.
1 parent f175ad1 commit b4ad38d

File tree

7 files changed

+270
-406
lines changed

7 files changed

+270
-406
lines changed

engine/orchestration/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -499,19 +499,16 @@ public SocketChannel connectToPeer(final String peerName, final SocketChannel pr
499499
SocketChannel ch1 = null;
500500
try {
501501
ch1 = SocketChannel.open(new InetSocketAddress(addr, Port.value()));
502-
ch1.configureBlocking(false);
502+
ch1.configureBlocking(true); // make sure we are working at blocking mode
503503
ch1.socket().setKeepAlive(true);
504504
ch1.socket().setSoTimeout(60 * 1000);
505505
try {
506506
final SSLContext sslContext = Link.initSSLContext(true);
507507
sslEngine = sslContext.createSSLEngine(ip, Port.value());
508508
sslEngine.setUseClientMode(true);
509509
sslEngine.setEnabledProtocols(SSLUtils.getSupportedProtocols(sslEngine.getEnabledProtocols()));
510-
sslEngine.beginHandshake();
511-
if (!Link.doHandshake(ch1, sslEngine, true)) {
512-
ch1.close();
513-
throw new IOException("SSL handshake failed!");
514-
}
510+
511+
Link.doHandshake(ch1, sslEngine, true);
515512
s_logger.info("SSL: Handshake done");
516513
} catch (final Exception e) {
517514
ch1.close();

utils/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@
208208
<excludes>
209209
<exclude>com/cloud/utils/testcase/*TestCase*</exclude>
210210
<exclude>com/cloud/utils/db/*Test*</exclude>
211+
<exclude>com/cloud/utils/testcase/NioTest.java</exclude>
211212
</excludes>
212213
</configuration>
213214
</plugin>

utils/src/main/java/com/cloud/utils/nio/Link.java

Lines changed: 114 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -19,33 +19,37 @@
1919

2020
package com.cloud.utils.nio;
2121

22-
import com.cloud.utils.PropertiesUtil;
23-
import com.cloud.utils.db.DbProperties;
24-
import org.apache.cloudstack.utils.security.SSLUtils;
25-
import org.apache.log4j.Logger;
26-
27-
import javax.net.ssl.KeyManagerFactory;
28-
import javax.net.ssl.SSLContext;
29-
import javax.net.ssl.SSLEngine;
30-
import javax.net.ssl.SSLEngineResult;
31-
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
32-
import javax.net.ssl.SSLException;
33-
import javax.net.ssl.SSLSession;
34-
import javax.net.ssl.TrustManager;
35-
import javax.net.ssl.TrustManagerFactory;
3622
import java.io.File;
3723
import java.io.FileInputStream;
3824
import java.io.IOException;
3925
import java.io.InputStream;
4026
import java.net.InetSocketAddress;
27+
import java.net.SocketTimeoutException;
4128
import java.nio.ByteBuffer;
29+
import java.nio.channels.Channels;
4230
import java.nio.channels.ClosedChannelException;
31+
import java.nio.channels.ReadableByteChannel;
4332
import java.nio.channels.SelectionKey;
4433
import java.nio.channels.SocketChannel;
4534
import java.security.GeneralSecurityException;
4635
import java.security.KeyStore;
4736
import java.util.concurrent.ConcurrentLinkedQueue;
4837

38+
import javax.net.ssl.KeyManagerFactory;
39+
import javax.net.ssl.SSLContext;
40+
import javax.net.ssl.SSLEngine;
41+
import javax.net.ssl.SSLEngineResult;
42+
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
43+
import javax.net.ssl.SSLSession;
44+
import javax.net.ssl.TrustManager;
45+
import javax.net.ssl.TrustManagerFactory;
46+
47+
import org.apache.cloudstack.utils.security.SSLUtils;
48+
import org.apache.log4j.Logger;
49+
50+
import com.cloud.utils.PropertiesUtil;
51+
import com.cloud.utils.db.DbProperties;
52+
4953
/**
5054
*/
5155
public class Link {
@@ -449,185 +453,115 @@ public static SSLContext initSSLContext(boolean isClient) throws GeneralSecurity
449453
return sslContext;
450454
}
451455

452-
public static ByteBuffer enlargeBuffer(ByteBuffer buffer, final int sessionProposedCapacity) {
453-
if (buffer == null || sessionProposedCapacity < 0) {
454-
return buffer;
455-
}
456-
if (sessionProposedCapacity > buffer.capacity()) {
457-
buffer = ByteBuffer.allocate(sessionProposedCapacity);
458-
} else {
459-
buffer = ByteBuffer.allocate(buffer.capacity() * 2);
456+
public static void doHandshake(SocketChannel ch, SSLEngine sslEngine, boolean isClient) throws IOException {
457+
if (s_logger.isTraceEnabled()) {
458+
s_logger.trace("SSL: begin Handshake, isClient: " + isClient);
460459
}
461-
return buffer;
462-
}
463460

464-
public static ByteBuffer handleBufferUnderflow(final SSLEngine engine, ByteBuffer buffer) {
465-
if (engine == null || buffer == null) {
466-
return buffer;
467-
}
468-
if (buffer.position() < buffer.limit()) {
469-
return buffer;
461+
SSLEngineResult engResult;
462+
SSLSession sslSession = sslEngine.getSession();
463+
HandshakeStatus hsStatus;
464+
ByteBuffer in_pkgBuf = ByteBuffer.allocate(sslSession.getPacketBufferSize() + 40);
465+
ByteBuffer in_appBuf = ByteBuffer.allocate(sslSession.getApplicationBufferSize() + 40);
466+
ByteBuffer out_pkgBuf = ByteBuffer.allocate(sslSession.getPacketBufferSize() + 40);
467+
ByteBuffer out_appBuf = ByteBuffer.allocate(sslSession.getApplicationBufferSize() + 40);
468+
int count;
469+
ch.socket().setSoTimeout(60 * 1000);
470+
InputStream inStream = ch.socket().getInputStream();
471+
// Use readCh to make sure the timeout on reading is working
472+
ReadableByteChannel readCh = Channels.newChannel(inStream);
473+
474+
if (isClient) {
475+
hsStatus = SSLEngineResult.HandshakeStatus.NEED_WRAP;
476+
} else {
477+
hsStatus = SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
470478
}
471-
ByteBuffer replaceBuffer = enlargeBuffer(buffer, engine.getSession().getPacketBufferSize());
472-
buffer.flip();
473-
replaceBuffer.put(buffer);
474-
return replaceBuffer;
475-
}
476479

477-
private static boolean doHandshakeUnwrap(final SocketChannel socketChannel, final SSLEngine sslEngine,
478-
ByteBuffer peerAppData, ByteBuffer peerNetData, final int appBufferSize) throws IOException {
479-
if (socketChannel == null || sslEngine == null || peerAppData == null || peerNetData == null || appBufferSize < 0) {
480-
return false;
481-
}
482-
if (socketChannel.read(peerNetData) < 0) {
483-
if (sslEngine.isInboundDone() && sslEngine.isOutboundDone()) {
484-
return false;
485-
}
486-
try {
487-
sslEngine.closeInbound();
488-
} catch (SSLException e) {
489-
s_logger.warn("This SSL engine was forced to close inbound due to end of stream.");
480+
while (hsStatus != SSLEngineResult.HandshakeStatus.FINISHED) {
481+
if (s_logger.isTraceEnabled()) {
482+
s_logger.trace("SSL: Handshake status " + hsStatus);
490483
}
491-
sslEngine.closeOutbound();
492-
// After closeOutbound the engine will be set to WRAP state,
493-
// in order to try to send a close message to the client.
494-
return true;
495-
}
496-
peerNetData.flip();
497-
SSLEngineResult result = null;
498-
try {
499-
result = sslEngine.unwrap(peerNetData, peerAppData);
500-
peerNetData.compact();
501-
} catch (SSLException sslException) {
502-
s_logger.error("SSL error occurred while processing unwrap data: " + sslException.getMessage());
503-
sslEngine.closeOutbound();
504-
return true;
505-
}
506-
switch (result.getStatus()) {
507-
case OK:
508-
break;
509-
case BUFFER_OVERFLOW:
510-
// Will occur when peerAppData's capacity is smaller than the data derived from peerNetData's unwrap.
511-
peerAppData = enlargeBuffer(peerAppData, appBufferSize);
512-
break;
513-
case BUFFER_UNDERFLOW:
514-
// Will occur either when no data was read from the peer or when the peerNetData buffer
515-
// was too small to hold all peer's data.
516-
peerNetData = handleBufferUnderflow(sslEngine, peerNetData);
517-
break;
518-
case CLOSED:
519-
if (sslEngine.isOutboundDone()) {
520-
return false;
521-
} else {
522-
sslEngine.closeOutbound();
523-
break;
524-
}
525-
default:
526-
throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
527-
}
528-
return true;
529-
}
530-
531-
private static boolean doHandshakeWrap(final SocketChannel socketChannel, final SSLEngine sslEngine,
532-
ByteBuffer myAppData, ByteBuffer myNetData, ByteBuffer peerNetData,
533-
final int netBufferSize) throws IOException {
534-
if (socketChannel == null || sslEngine == null || myNetData == null || peerNetData == null
535-
|| myAppData == null || netBufferSize < 0) {
536-
return false;
537-
}
538-
myNetData.clear();
539-
SSLEngineResult result = null;
540-
try {
541-
result = sslEngine.wrap(myAppData, myNetData);
542-
} catch (SSLException sslException) {
543-
s_logger.error("SSL error occurred while processing wrap data: " + sslException.getMessage());
544-
sslEngine.closeOutbound();
545-
return true;
546-
}
547-
switch (result.getStatus()) {
548-
case OK :
549-
myNetData.flip();
550-
while (myNetData.hasRemaining()) {
551-
socketChannel.write(myNetData);
484+
engResult = null;
485+
if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
486+
out_pkgBuf.clear();
487+
out_appBuf.clear();
488+
out_appBuf.put("Hello".getBytes());
489+
engResult = sslEngine.wrap(out_appBuf, out_pkgBuf);
490+
out_pkgBuf.flip();
491+
int remain = out_pkgBuf.limit();
492+
while (remain != 0) {
493+
remain -= ch.write(out_pkgBuf);
494+
if (remain < 0) {
495+
throw new IOException("Too much bytes sent?");
496+
}
552497
}
553-
break;
554-
case BUFFER_OVERFLOW:
555-
// Will occur if there is not enough space in myNetData buffer to write all the data
556-
// that would be generated by the method wrap. Since myNetData is set to session's packet
557-
// size we should not get to this point because SSLEngine is supposed to produce messages
558-
// smaller or equal to that, but a general handling would be the following:
559-
myNetData = enlargeBuffer(myNetData, netBufferSize);
560-
break;
561-
case BUFFER_UNDERFLOW:
562-
throw new SSLException("Buffer underflow occurred after a wrap. We should not reach here.");
563-
case CLOSED:
564-
try {
565-
myNetData.flip();
566-
while (myNetData.hasRemaining()) {
567-
socketChannel.write(myNetData);
498+
} else if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
499+
in_appBuf.clear();
500+
// One packet may contained multiply operation
501+
if (in_pkgBuf.position() == 0 || !in_pkgBuf.hasRemaining()) {
502+
in_pkgBuf.clear();
503+
count = 0;
504+
try {
505+
count = readCh.read(in_pkgBuf);
506+
} catch (SocketTimeoutException ex) {
507+
if (s_logger.isTraceEnabled()) {
508+
s_logger.trace("Handshake reading time out! Cut the connection");
509+
}
510+
count = -1;
511+
}
512+
if (count == -1) {
513+
throw new IOException("Connection closed with -1 on reading size.");
568514
}
569-
// At this point the handshake status will probably be NEED_UNWRAP
570-
// so we make sure that peerNetData is clear to read.
571-
peerNetData.clear();
572-
} catch (Exception e) {
573-
s_logger.error("Failed to send server's CLOSE message due to socket channel's failure.");
515+
in_pkgBuf.flip();
574516
}
575-
break;
576-
default:
577-
throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
578-
}
579-
return true;
580-
}
581-
582-
public static boolean doHandshake(final SocketChannel socketChannel, final SSLEngine sslEngine, final boolean isClient) throws IOException {
583-
if (socketChannel == null || sslEngine == null) {
584-
return false;
585-
}
586-
final int appBufferSize = sslEngine.getSession().getApplicationBufferSize();
587-
final int netBufferSize = sslEngine.getSession().getPacketBufferSize();
588-
ByteBuffer myAppData = ByteBuffer.allocate(appBufferSize);
589-
ByteBuffer peerAppData = ByteBuffer.allocate(appBufferSize);
590-
ByteBuffer myNetData = ByteBuffer.allocate(netBufferSize);
591-
ByteBuffer peerNetData = ByteBuffer.allocate(netBufferSize);
592-
593-
final long startTimeMills = System.currentTimeMillis();
594-
595-
HandshakeStatus handshakeStatus = sslEngine.getHandshakeStatus();
596-
while (handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED
597-
&& handshakeStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
598-
final long timeTaken = System.currentTimeMillis() - startTimeMills;
599-
if (timeTaken > 60000L) {
600-
s_logger.warn("SSL Handshake has taken more than 60s to connect to: " + socketChannel.getRemoteAddress() +
601-
". Please investigate this connection.");
602-
return false;
603-
}
604-
switch (handshakeStatus) {
605-
case NEED_UNWRAP:
606-
if (!doHandshakeUnwrap(socketChannel, sslEngine, peerAppData, peerNetData, appBufferSize)) {
607-
return false;
517+
engResult = sslEngine.unwrap(in_pkgBuf, in_appBuf);
518+
ByteBuffer tmp_pkgBuf = ByteBuffer.allocate(sslSession.getPacketBufferSize() + 40);
519+
int loop_count = 0;
520+
while (engResult.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
521+
// The client is too slow? Cut it and let it reconnect
522+
if (loop_count > 10) {
523+
throw new IOException("Too many times in SSL BUFFER_UNDERFLOW, disconnect guest.");
608524
}
609-
break;
610-
case NEED_WRAP:
611-
if (!doHandshakeWrap(socketChannel, sslEngine, myAppData, myNetData, peerNetData, netBufferSize)) {
612-
return false;
525+
// We need more packets to complete this operation
526+
if (s_logger.isTraceEnabled()) {
527+
s_logger.trace("SSL: Buffer underflowed, getting more packets");
613528
}
614-
break;
615-
case NEED_TASK:
616-
Runnable task;
617-
while ((task = sslEngine.getDelegatedTask()) != null) {
618-
new Thread(task).run();
529+
tmp_pkgBuf.clear();
530+
count = ch.read(tmp_pkgBuf);
531+
if (count == -1) {
532+
throw new IOException("Connection closed with -1 on reading size.");
619533
}
620-
break;
621-
case FINISHED:
622-
break;
623-
case NOT_HANDSHAKING:
624-
break;
625-
default:
626-
throw new IllegalStateException("Invalid SSL status: " + handshakeStatus);
534+
tmp_pkgBuf.flip();
535+
536+
in_pkgBuf.mark();
537+
in_pkgBuf.position(in_pkgBuf.limit());
538+
in_pkgBuf.limit(in_pkgBuf.limit() + tmp_pkgBuf.limit());
539+
in_pkgBuf.put(tmp_pkgBuf);
540+
in_pkgBuf.reset();
541+
542+
in_appBuf.clear();
543+
engResult = sslEngine.unwrap(in_pkgBuf, in_appBuf);
544+
loop_count++;
545+
}
546+
} else if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) {
547+
Runnable run;
548+
while ((run = sslEngine.getDelegatedTask()) != null) {
549+
if (s_logger.isTraceEnabled()) {
550+
s_logger.trace("SSL: Running delegated task!");
551+
}
552+
run.run();
553+
}
554+
} else if (hsStatus == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
555+
throw new IOException("NOT a handshaking!");
556+
}
557+
if (engResult != null && engResult.getStatus() != SSLEngineResult.Status.OK) {
558+
throw new IOException("Fail to handshake! " + engResult.getStatus());
627559
}
628-
handshakeStatus = sslEngine.getHandshakeStatus();
560+
if (engResult != null)
561+
hsStatus = engResult.getHandshakeStatus();
562+
else
563+
hsStatus = sslEngine.getHandshakeStatus();
629564
}
630-
return true;
631565
}
632566

633567
}

0 commit comments

Comments
 (0)