private String unwrapBytes( ByteBuffer[] dsts, int offset, int length, ByteBuffer src, boolean readHandshakeMsg) { // define unwrapped data to be written to dsts String wrapped = new String(Buffers.take(src), StandardCharsets.ISO_8859_1); int wrappedEndIndex = wrapped.length(); int wrappedLeftOver = -1; while (wrappedEndIndex > 0 && !unwrapMap.containsKey(wrapped.substring(0, wrappedEndIndex))) { wrappedLeftOver = wrappedEndIndex--; } // undo the reading of data that won't be used now if (wrappedLeftOver != -1 && wrappedEndIndex > 0) { src.position(src.position() - (wrapped.length() - wrappedEndIndex)); wrapped = wrapped.substring(0, wrappedEndIndex); } else { int msgIndex; if ((msgIndex = wrapped.indexOf(HANDSHAKE_MSG)) != -1) { if (msgIndex == 0) { src.position(src.position() - (wrapped.length() - HANDSHAKE_MSG.length())); wrapped = wrapped.substring(0, HANDSHAKE_MSG.length()); } else { src.position(src.position() - (wrapped.length() - msgIndex)); wrapped = wrapped.substring(0, msgIndex); } } if ((msgIndex = wrapped.indexOf(CLOSE_MSG)) != -1) { if (msgIndex == 0) { src.position(src.position() - (wrapped.length() - CLOSE_MSG.length())); wrapped = wrapped.substring(0, CLOSE_MSG.length()); } else { src.position(src.position() - (wrapped.length() - msgIndex)); wrapped = wrapped.substring(0, msgIndex); } } } String unwrapped = unwrapMap.containsKey(wrapped) ? unwrapMap.get(wrapped) : wrapped; if (unwrapped.equals(HANDSHAKE_MSG) && !readHandshakeMsg) { src.position(src.position() - wrapped.length()); return ""; } if (!unwrapped.equals(CLOSE_MSG) && !unwrapped.equals(HANDSHAKE_MSG)) { if (CLOSE_MSG.startsWith(unwrapped) || HANDSHAKE_MSG.startsWith(unwrapped)) { src.position(0); return null; } // check if there is enough space to write unwrapped data, if not, do not write if (Buffers.remaining(dsts, offset, length) < unwrapped.length()) { src.position(src.position() - wrapped.length()); return null; } // copy data to dsts Buffers.copy( dsts, offset, length, ByteBuffer.wrap(unwrapped.getBytes(StandardCharsets.ISO_8859_1))); } return unwrapped; }
private Frame(FrameCallBack callback, ByteBuffer[] data, int offs, int len) { this.callback = callback; this.data = data; this.offs = offs; this.len = len; this.remaining = Buffers.remaining(data, offs, len); }
public int wrapBytes(ByteBuffer dst, String src) { String wrapped = wrapMap.containsKey(src) ? wrapMap.get(src) : src; if (dst.remaining() < wrapped.length()) { return 0; } Buffers.putModifiedUtf8(dst, wrapped); return src.length(); }
private SSLEngineResult wrapMessage(ByteBuffer dst, String msg, Status okayStatus) { String wrappedMessage = wrapMap.containsKey(msg) ? wrapMap.get(msg) : msg; if (dst.remaining() < wrappedMessage.length()) { return new SSLEngineResult(Status.BUFFER_OVERFLOW, getHandshakeStatus(), 0, 0); } Buffers.putModifiedUtf8(dst, wrappedMessage); return new SSLEngineResult(okayStatus, getHandshakeStatus(), 0, wrappedMessage.length()); }
protected static byte[] toArray(ByteBuffer... payload) { if (payload.length == 1) { ByteBuffer buf = payload[0]; if (buf.hasArray() && buf.arrayOffset() == 0 && buf.position() == 0) { return buf.array(); } } int size = (int) Buffers.remaining(payload); byte[] data = new byte[size]; for (ByteBuffer buf : payload) { buf.get(data); } return data; }
public int wrapBytes(ByteBuffer dst, ByteBuffer[] srcs, int offset, int length) { int totalLength = 0; int srcsLength = offset + length; for (int i = offset; i < srcsLength && dst.hasRemaining(); i++) { StringBuilder unwrappedBuilder = new StringBuilder(); Buffers.readModifiedUtf8Line(srcs[i], unwrappedBuilder); int wrappedLength = wrapBytes(dst, unwrappedBuilder.toString()); if (wrappedLength == 0 && unwrappedBuilder.length() > 0) { srcs[i].position(srcs[i].position() - unwrappedBuilder.length()); break; } totalLength += wrappedLength; } return totalLength; }
protected static ByteBuffer toBuffer(ByteBuffer... payload) { if (payload.length == 1) { return payload[0]; } int size = (int) Buffers.remaining(payload); if (size == 0) { return Buffers.EMPTY_BYTE_BUFFER; } ByteBuffer buffer = ByteBuffer.allocate(size); for (ByteBuffer buf : payload) { buffer.put(buf); } buffer.flip(); return buffer; }
public long write(final ByteBuffer[] srcs, final int offset, final int length) throws IOException { if (length == 0) { return 0L; } else if (length == 1) { return write(srcs[offset]); } long val = state; final long remaining = val & MASK_COUNT; if (allAreSet(val, FLAG_CLOSE_REQUESTED)) { throw new ClosedChannelException(); } long toWrite = Buffers.remaining(srcs, offset, length); if (toWrite > remaining) { throw new FixedLengthOverflowException(); } long res = 0L; try { return res = next.write(srcs, offset, length); } finally { exitWrite(val, res); } }
public void handleEvent(final ConnectedMessageChannel channel) { final Pooled<ByteBuffer> pooledReceiveBuffer = connection.allocate(); try { final ByteBuffer receiveBuffer = pooledReceiveBuffer.getResource(); int res = 0; try { res = channel.receive(receiveBuffer); } catch (IOException e) { connection.handleException(e); return; } if (res == -1) { connection.handleException(client.abruptClose(connection)); return; } if (res == 0) { return; } receiveBuffer.flip(); boolean starttls = false; final Set<String> saslMechs = new LinkedHashSet<String>(); final byte msgType = receiveBuffer.get(); switch (msgType) { case Protocol.CONNECTION_ALIVE: { client.trace("Client received connection alive"); return; } case Protocol.CONNECTION_CLOSE: { client.trace("Client received connection close request"); connection.handleIncomingCloseRequest(); return; } case Protocol.CAPABILITIES: { client.trace("Client received capabilities response"); while (receiveBuffer.hasRemaining()) { final byte type = receiveBuffer.get(); final int len = receiveBuffer.get() & 0xff; final ByteBuffer data = Buffers.slice(receiveBuffer, len); switch (type) { case Protocol.CAP_VERSION: { final byte version = data.get(); client.tracef( "Client received capability: version %d", Integer.valueOf(version & 0xff)); // We only support version zero, so knowing the other side's version is not // useful presently break; } case Protocol.CAP_SASL_MECH: { final String mechName = Buffers.getModifiedUtf8(data); client.tracef("Client received capability: SASL mechanism %s", mechName); if (!failedMechs.contains(mechName) && !disallowedMechs.contains(mechName) && (allowedMechs == null || allowedMechs.contains(mechName))) { client.tracef("SASL mechanism %s added to allowed set", mechName); saslMechs.add(mechName); } break; } case Protocol.CAP_STARTTLS: { client.trace("Client received capability: STARTTLS"); starttls = true; break; } default: { client.tracef( "Client received unknown capability %02x", Integer.valueOf(type & 0xff)); // unknown, skip it for forward compatibility. break; } } } if (starttls) { // only initiate starttls if not forbidden by config if (optionMap.get(Options.SSL_STARTTLS, true)) { // Prepare the request message body final Pooled<ByteBuffer> pooledSendBuffer = connection.allocate(); final ByteBuffer sendBuffer = pooledSendBuffer.getResource(); sendBuffer.put(Protocol.STARTTLS); sendBuffer.flip(); connection.setReadListener(new StartTls(serverName)); connection.send(pooledSendBuffer); // all set return; } } if (saslMechs.isEmpty()) { connection.handleException( new SaslException("No more authentication mechanisms to try")); return; } // OK now send our authentication request final OptionMap optionMap = connection.getOptionMap(); final String userName = optionMap.get(RemotingOptions.AUTHORIZE_ID); final Map<String, ?> propertyMap = SaslUtils.createPropertyMap( optionMap, Channels.getOption(channel, Options.SECURE, false)); final SaslClient saslClient; try { saslClient = AccessController.doPrivileged( new PrivilegedExceptionAction<SaslClient>() { public SaslClient run() throws SaslException { return Sasl.createSaslClient( saslMechs.toArray(new String[saslMechs.size()]), userName, "remote", serverName, propertyMap, callbackHandler); } }, accessControlContext); } catch (PrivilegedActionException e) { final SaslException se = (SaslException) e.getCause(); connection.handleException(se); return; } final String mechanismName = saslClient.getMechanismName(); client.tracef("Client initiating authentication using mechanism %s", mechanismName); // Prepare the request message body final Pooled<ByteBuffer> pooledSendBuffer = connection.allocate(); final ByteBuffer sendBuffer = pooledSendBuffer.getResource(); sendBuffer.put(Protocol.AUTH_REQUEST); Buffers.putModifiedUtf8(sendBuffer, mechanismName); sendBuffer.flip(); connection.send(pooledSendBuffer); connection.setReadListener(new Authentication(saslClient, serverName)); return; } default: { client.unknownProtocolId(msgType); connection.handleException(client.invalidMessage(connection)); return; } } } catch (BufferUnderflowException e) { connection.handleException(client.invalidMessage(connection)); return; } catch (BufferOverflowException e) { connection.handleException(client.invalidMessage(connection)); return; } finally { pooledReceiveBuffer.free(); } }
public void handleEvent(final ConnectedMessageChannel channel) { final Pooled<ByteBuffer> pooledReceiveBuffer = connection.allocate(); try { final ByteBuffer receiveBuffer = pooledReceiveBuffer.getResource(); int res = 0; try { res = channel.receive(receiveBuffer); } catch (IOException e) { connection.handleException(e); return; } if (res == -1) { connection.handleException(client.abruptClose(connection)); return; } if (res == 0) { return; } client.tracef("Received %s", receiveBuffer); receiveBuffer.flip(); String serverName = channel.getPeerAddress(InetSocketAddress.class).getHostName(); final byte msgType = receiveBuffer.get(); switch (msgType) { case Protocol.CONNECTION_ALIVE: { client.trace("Client received connection alive"); return; } case Protocol.CONNECTION_CLOSE: { client.trace("Client received connection close request"); connection.handleIncomingCloseRequest(); return; } case Protocol.GREETING: { client.trace("Client received greeting"); while (receiveBuffer.hasRemaining()) { final byte type = receiveBuffer.get(); final int len = receiveBuffer.get() & 0xff; final ByteBuffer data = Buffers.slice(receiveBuffer, len); switch (type) { case Protocol.GRT_SERVER_NAME: { serverName = Buffers.getModifiedUtf8(data); client.tracef("Client received server name: %s", serverName); break; } default: { client.tracef( "Client received unknown greeting message %02x", Integer.valueOf(type & 0xff)); // unknown, skip it for forward compatibility. break; } } } sendCapRequest(serverName); return; } default: { client.unknownProtocolId(msgType); connection.handleException(client.invalidMessage(connection)); return; } } } catch (BufferUnderflowException e) { connection.handleException(client.invalidMessage(connection)); return; } catch (BufferOverflowException e) { connection.handleException(client.invalidMessage(connection)); return; } finally { pooledReceiveBuffer.free(); } }
public void handleEvent(final ConnectedMessageChannel channel) { int res; SaslWrapper saslWrapper = connection.getSaslWrapper(); try { Pooled<ByteBuffer> pooled = connection.allocate(); ByteBuffer buffer = pooled.getResource(); try { for (; ; ) try { res = channel.receive(buffer); if (res == -1) { log.trace("Received connection end-of-stream"); try { channel.shutdownReads(); } finally { handler.handleConnectionClose(); } return; } else if (res == 0) { log.trace("No message ready; returning"); return; } buffer.flip(); if (saslWrapper != null) { final ByteBuffer source = buffer.duplicate(); buffer.clear(); saslWrapper.unwrap(buffer, source); buffer.flip(); } final byte protoId = buffer.get(); try { switch (protoId) { case Protocol.CONNECTION_ALIVE: { log.trace("Received connection alive"); connection.sendAliveResponse(); return; } case Protocol.CONNECTION_ALIVE_ACK: { log.trace("Received connection alive ack"); return; } case Protocol.CONNECTION_CLOSE: { log.trace("Received connection close request"); handler.receiveCloseRequest(); return; } case Protocol.CHANNEL_OPEN_REQUEST: { log.trace("Received channel open request"); int channelId = buffer.getInt() ^ 0x80000000; int inboundWindow = Integer.MAX_VALUE; int inboundMessages = 0xffff; int outboundWindow = Integer.MAX_VALUE; int outboundMessages = 0xffff; long inboundMessageSize = Long.MAX_VALUE; long outboundMessageSize = Long.MAX_VALUE; // parse out request int b; String serviceType = null; OUT: for (; ; ) { b = buffer.get() & 0xff; switch (b) { case Protocol.O_END: break OUT; case Protocol.O_SERVICE_NAME: { serviceType = ProtocolUtils.readString(buffer); break; } case Protocol.O_MAX_INBOUND_MSG_WINDOW_SIZE: { outboundWindow = Math.min(outboundWindow, ProtocolUtils.readInt(buffer)); break; } case Protocol.O_MAX_INBOUND_MSG_COUNT: { outboundMessages = Math.min(outboundMessages, ProtocolUtils.readUnsignedShort(buffer)); break; } case Protocol.O_MAX_OUTBOUND_MSG_WINDOW_SIZE: { inboundWindow = Math.min(inboundWindow, ProtocolUtils.readInt(buffer)); break; } case Protocol.O_MAX_OUTBOUND_MSG_COUNT: { inboundMessages = Math.min(inboundMessages, ProtocolUtils.readUnsignedShort(buffer)); break; } case Protocol.O_MAX_INBOUND_MSG_SIZE: { outboundMessageSize = Math.min(outboundMessageSize, ProtocolUtils.readLong(buffer)); break; } case Protocol.O_MAX_OUTBOUND_MSG_SIZE: { inboundMessageSize = Math.min(inboundMessageSize, ProtocolUtils.readLong(buffer)); break; } default: { Buffers.skip(buffer, buffer.get() & 0xff); break; } } } if ((channelId & 0x80000000) != 0) { // invalid channel ID, original should have had MSB=1 and thus the complement // should be MSB=0 refuseService(channelId, "Invalid channel ID"); break; } if (serviceType == null) { // invalid service reply refuseService(channelId, "Missing service name"); break; } final RegisteredService registeredService = handler.getConnectionContext().getRegisteredService(serviceType); if (registeredService == null) { refuseService(channelId, "Unknown service name"); break; } final OptionMap serviceOptionMap = registeredService.getOptionMap(); outboundWindow = Math.min( outboundWindow, serviceOptionMap.get( RemotingOptions.TRANSMIT_WINDOW_SIZE, Protocol.DEFAULT_WINDOW_SIZE)); outboundMessages = Math.min( outboundMessages, serviceOptionMap.get( RemotingOptions.MAX_OUTBOUND_MESSAGES, Protocol.DEFAULT_MESSAGE_COUNT)); inboundWindow = Math.min( inboundWindow, serviceOptionMap.get( RemotingOptions.RECEIVE_WINDOW_SIZE, Protocol.DEFAULT_WINDOW_SIZE)); inboundMessages = Math.min( inboundMessages, serviceOptionMap.get( RemotingOptions.MAX_INBOUND_MESSAGES, Protocol.DEFAULT_MESSAGE_COUNT)); outboundMessageSize = Math.min( outboundMessageSize, serviceOptionMap.get( RemotingOptions.MAX_OUTBOUND_MESSAGE_SIZE, Long.MAX_VALUE)); inboundMessageSize = Math.min( inboundMessageSize, serviceOptionMap.get( RemotingOptions.MAX_INBOUND_MESSAGE_SIZE, Long.MAX_VALUE)); final OpenListener openListener = registeredService.getOpenListener(); if (!handler.handleInboundChannelOpen()) { // refuse refuseService(channelId, "Channel refused"); break; } boolean ok1 = false; try { // construct the channel RemoteConnectionChannel connectionChannel = new RemoteConnectionChannel( handler, connection, channelId, outboundWindow, inboundWindow, outboundMessages, inboundMessages, outboundMessageSize, inboundMessageSize); RemoteConnectionChannel existing = handler.addChannel(connectionChannel); if (existing != null) { log.tracef("Encountered open request for duplicate %s", existing); // the channel already exists, which means the remote side "forgot" about it // or we somehow missed the close message. // the only safe thing to do is to terminate the existing channel. try { refuseService(channelId, "Duplicate ID"); } finally { existing.handleRemoteClose(); } break; } // construct reply Pooled<ByteBuffer> pooledReply = connection.allocate(); boolean ok2 = false; try { ByteBuffer replyBuffer = pooledReply.getResource(); replyBuffer.clear(); replyBuffer.put(Protocol.CHANNEL_OPEN_ACK); replyBuffer.putInt(channelId); ProtocolUtils.writeInt( replyBuffer, Protocol.O_MAX_INBOUND_MSG_WINDOW_SIZE, inboundWindow); ProtocolUtils.writeShort( replyBuffer, Protocol.O_MAX_INBOUND_MSG_COUNT, inboundMessages); if (inboundMessageSize != Long.MAX_VALUE) { ProtocolUtils.writeLong( replyBuffer, Protocol.O_MAX_INBOUND_MSG_SIZE, inboundMessageSize); } ProtocolUtils.writeInt( replyBuffer, Protocol.O_MAX_OUTBOUND_MSG_WINDOW_SIZE, outboundWindow); ProtocolUtils.writeShort( replyBuffer, Protocol.O_MAX_OUTBOUND_MSG_COUNT, outboundMessages); if (outboundMessageSize != Long.MAX_VALUE) { ProtocolUtils.writeLong( replyBuffer, Protocol.O_MAX_OUTBOUND_MSG_SIZE, outboundMessageSize); } replyBuffer.put((byte) 0); replyBuffer.flip(); ok2 = true; // send takes ownership of the buffer connection.send(pooledReply); } finally { if (!ok2) pooledReply.free(); } ok1 = true; // Call the service open listener connection .getExecutor() .execute(SpiUtils.getServiceOpenTask(connectionChannel, openListener)); break; } finally { // the inbound channel wasn't open so don't leak the ref count if (!ok1) handler.handleInboundChannelClosed(); } } case Protocol.MESSAGE_DATA: { log.trace("Received message data"); int channelId = buffer.getInt() ^ 0x80000000; RemoteConnectionChannel connectionChannel = handler.getChannel(channelId); if (connectionChannel == null) { // ignore the data log.tracef("Ignoring message data for expired channel"); break; } connectionChannel.handleMessageData(pooled); // need a new buffer now pooled = connection.allocate(); buffer = pooled.getResource(); break; } case Protocol.MESSAGE_WINDOW_OPEN: { log.trace("Received message window open"); int channelId = buffer.getInt() ^ 0x80000000; RemoteConnectionChannel connectionChannel = handler.getChannel(channelId); if (connectionChannel == null) { // ignore log.tracef("Ignoring window open for expired channel"); break; } connectionChannel.handleWindowOpen(pooled); break; } case Protocol.MESSAGE_CLOSE: { log.trace("Received message async close"); int channelId = buffer.getInt() ^ 0x80000000; RemoteConnectionChannel connectionChannel = handler.getChannel(channelId); if (connectionChannel == null) { break; } connectionChannel.handleAsyncClose(pooled); break; } case Protocol.CHANNEL_CLOSED: { log.trace("Received channel closed"); int channelId = buffer.getInt() ^ 0x80000000; RemoteConnectionChannel connectionChannel = handler.getChannel(channelId); if (connectionChannel == null) { break; } connectionChannel.handleRemoteClose(); break; } case Protocol.CHANNEL_SHUTDOWN_WRITE: { log.trace("Received channel shutdown write"); int channelId = buffer.getInt() ^ 0x80000000; RemoteConnectionChannel connectionChannel = handler.getChannel(channelId); if (connectionChannel == null) { break; } connectionChannel.handleIncomingWriteShutdown(); break; } case Protocol.CHANNEL_OPEN_ACK: { log.trace("Received channel open ack"); int channelId = buffer.getInt() ^ 0x80000000; if ((channelId & 0x80000000) == 0) { // invalid break; } PendingChannel pendingChannel = handler.removePendingChannel(channelId); if (pendingChannel == null) { // invalid break; } int outboundWindow = pendingChannel.getOutboundWindowSize(); int inboundWindow = pendingChannel.getInboundWindowSize(); int outboundMessageCount = pendingChannel.getOutboundMessageCount(); int inboundMessageCount = pendingChannel.getInboundMessageCount(); long outboundMessageSize = pendingChannel.getOutboundMessageSize(); long inboundMessageSize = pendingChannel.getInboundMessageSize(); OUT: for (; ; ) { switch (buffer.get() & 0xff) { case Protocol.O_MAX_INBOUND_MSG_WINDOW_SIZE: { outboundWindow = Math.min(outboundWindow, ProtocolUtils.readInt(buffer)); break; } case Protocol.O_MAX_INBOUND_MSG_COUNT: { outboundMessageCount = Math.min( outboundMessageCount, ProtocolUtils.readUnsignedShort(buffer)); break; } case Protocol.O_MAX_OUTBOUND_MSG_WINDOW_SIZE: { inboundWindow = Math.min(inboundWindow, ProtocolUtils.readInt(buffer)); break; } case Protocol.O_MAX_OUTBOUND_MSG_COUNT: { inboundMessageCount = Math.min( inboundMessageCount, ProtocolUtils.readUnsignedShort(buffer)); break; } case Protocol.O_MAX_INBOUND_MSG_SIZE: { outboundMessageSize = Math.min(outboundMessageSize, ProtocolUtils.readLong(buffer)); break; } case Protocol.O_MAX_OUTBOUND_MSG_SIZE: { inboundMessageSize = Math.min(inboundMessageSize, ProtocolUtils.readLong(buffer)); break; } case Protocol.O_END: { break OUT; } default: { // ignore unknown parameter Buffers.skip(buffer, buffer.get() & 0xff); break; } } } RemoteConnectionChannel newChannel = new RemoteConnectionChannel( handler, connection, channelId, outboundWindow, inboundWindow, outboundMessageCount, inboundMessageCount, outboundMessageSize, inboundMessageSize); handler.putChannel(newChannel); pendingChannel.getResult().setResult(newChannel); break; } case Protocol.SERVICE_ERROR: { log.trace("Received service error"); int channelId = buffer.getInt() ^ 0x80000000; PendingChannel pendingChannel = handler.removePendingChannel(channelId); if (pendingChannel == null) { // invalid break; } String reason = new String(Buffers.take(buffer), Protocol.UTF_8); pendingChannel.getResult().setException(new IOException(reason)); break; } default: { log.unknownProtocolId(protoId); break; } } } catch (BufferUnderflowException e) { log.bufferUnderflow(protoId); } } catch (BufferUnderflowException e) { log.bufferUnderflowRaw(); } finally { buffer.clear(); } } finally { pooled.free(); } } catch (IOException e) { connection.handleException(e); handler.handleConnectionClose(); } }
/** * Queues a frame for sending. * * @param callback * @param data */ protected void queueFrame(FrameCallBack callback, ByteBuffer... data) { queuedData += Buffers.remaining(data); bufferCount += data.length; frameQueue.add(new Frame(callback, data, 0, data.length)); }
public void handleEvent(final ConnectedMessageChannel channel) { final Pooled<ByteBuffer> pooledBuffer = connection.allocate(); boolean free = true; try { final ByteBuffer buffer = pooledBuffer.getResource(); synchronized (connection.getLock()) { final int res; try { res = channel.receive(buffer); } catch (IOException e) { connection.handleException(e); saslDispose(saslClient); return; } if (res == 0) { return; } if (res == -1) { connection.handleException(client.abruptClose(connection)); saslDispose(saslClient); return; } } buffer.flip(); final byte msgType = buffer.get(); switch (msgType) { case Protocol.CONNECTION_ALIVE: { client.trace("Client received connection alive"); connection.sendAliveResponse(); return; } case Protocol.CONNECTION_ALIVE_ACK: { client.trace("Client received connection alive ack"); return; } case Protocol.CONNECTION_CLOSE: { client.trace("Client received connection close request"); connection.handlePreAuthCloseRequest(); saslDispose(saslClient); return; } case Protocol.AUTH_CHALLENGE: { client.trace("Client received authentication challenge"); channel.suspendReads(); connection .getExecutor() .execute( () -> { try { final boolean clientComplete = saslClient.isComplete(); if (clientComplete) { connection.handleException( new SaslException("Received extra auth message after completion")); return; } final byte[] response; final byte[] challenge = Buffers.take(buffer, buffer.remaining()); try { response = saslClient.evaluateChallenge(challenge); } catch (Throwable e) { final String mechanismName = saslClient.getMechanismName(); client.debugf( "Client authentication failed for mechanism %s: %s", mechanismName, e); failedMechs.put(mechanismName, e.toString()); saslDispose(saslClient); sendCapRequest(serverName); return; } client.trace("Client sending authentication response"); final Pooled<ByteBuffer> pooled = connection.allocate(); boolean ok = false; try { final ByteBuffer sendBuffer = pooled.getResource(); sendBuffer.put(Protocol.AUTH_RESPONSE); sendBuffer.put(response); sendBuffer.flip(); connection.send(pooled); ok = true; channel.resumeReads(); } finally { if (!ok) pooled.free(); } return; } finally { pooledBuffer.free(); } }); free = false; return; } case Protocol.AUTH_COMPLETE: { client.trace("Client received authentication complete"); channel.suspendReads(); connection .getExecutor() .execute( () -> { try { final boolean clientComplete = saslClient.isComplete(); final byte[] challenge = Buffers.take(buffer, buffer.remaining()); if (!clientComplete) try { final byte[] response = saslClient.evaluateChallenge(challenge); if (response != null && response.length > 0) { connection.handleException( new SaslException( "Received extra auth message after completion")); saslDispose(saslClient); return; } if (!saslClient.isComplete()) { connection.handleException( new SaslException( "Client not complete after processing auth complete message")); saslDispose(saslClient); return; } } catch (Throwable e) { final String mechanismName = saslClient.getMechanismName(); client.debugf( "Client authentication failed for mechanism %s: %s", mechanismName, e); failedMechs.put(mechanismName, e.toString()); saslDispose(saslClient); sendCapRequest(serverName); return; } final Object qop = saslClient.getNegotiatedProperty(Sasl.QOP); if ("auth-int".equals(qop) || "auth-conf".equals(qop)) { connection.setSaslWrapper(SaslWrapper.create(saslClient)); } // auth complete. final ConnectionHandlerFactory connectionHandlerFactory = connectionContext -> { // this happens immediately. final RemoteConnectionHandler connectionHandler = new RemoteConnectionHandler( connectionContext, connection, maxInboundChannels, maxOutboundChannels, remoteEndpointName, behavior); connection.setReadListener( new RemoteReadListener(connectionHandler, connection), false); connection .getRemoteConnectionProvider() .addConnectionHandler(connectionHandler); return connectionHandler; }; connection.getResult().setResult(connectionHandlerFactory); channel.resumeReads(); return; } finally { pooledBuffer.free(); } }); free = false; return; } case Protocol.AUTH_REJECTED: { final String mechanismName = saslClient.getMechanismName(); client.debugf( "Client received authentication rejected for mechanism %s", mechanismName); failedMechs.put(mechanismName, "Server rejected authentication"); saslDispose(saslClient); sendCapRequest(serverName); return; } default: { client.unknownProtocolId(msgType); connection.handleException(client.invalidMessage(connection)); saslDispose(saslClient); return; } } } finally { if (free) pooledBuffer.free(); } }
public void handleEvent(final ConnectedMessageChannel channel) { final Pooled<ByteBuffer> pooledReceiveBuffer = connection.allocate(); try { final ByteBuffer receiveBuffer = pooledReceiveBuffer.getResource(); synchronized (connection.getLock()) { int res; try { res = channel.receive(receiveBuffer); } catch (IOException e) { connection.handleException(e); return; } if (res == -1) { connection.handleException(client.abruptClose(connection)); return; } if (res == 0) { return; } } receiveBuffer.flip(); boolean starttls = false; final Set<String> serverSaslMechs = new LinkedHashSet<String>(); final byte msgType = receiveBuffer.get(); switch (msgType) { case Protocol.CONNECTION_ALIVE: { client.trace("Client received connection alive"); connection.sendAliveResponse(); return; } case Protocol.CONNECTION_ALIVE_ACK: { client.trace("Client received connection alive ack"); return; } case Protocol.CONNECTION_CLOSE: { client.trace("Client received connection close request"); connection.handlePreAuthCloseRequest(); return; } case Protocol.CAPABILITIES: { client.trace("Client received capabilities response"); String remoteEndpointName = null; int version = Protocol.VERSION; int behavior = Protocol.BH_FAULTY_MSG_SIZE; boolean useDefaultChannels = true; int channelsIn = 40; int channelsOut = 40; while (receiveBuffer.hasRemaining()) { final byte type = receiveBuffer.get(); final int len = receiveBuffer.get() & 0xff; final ByteBuffer data = Buffers.slice(receiveBuffer, len); switch (type) { case Protocol.CAP_VERSION: { version = data.get() & 0xff; client.tracef( "Client received capability: version %d", Integer.valueOf(version & 0xff)); break; } case Protocol.CAP_SASL_MECH: { final String mechName = Buffers.getModifiedUtf8(data); client.tracef("Client received capability: SASL mechanism %s", mechName); if (!failedMechs.containsKey(mechName) && !disallowedMechs.contains(mechName) && (allowedMechs == null || allowedMechs.contains(mechName))) { client.tracef("SASL mechanism %s added to allowed set", mechName); serverSaslMechs.add(mechName); } break; } case Protocol.CAP_STARTTLS: { client.trace("Client received capability: STARTTLS"); starttls = true; break; } case Protocol.CAP_ENDPOINT_NAME: { remoteEndpointName = Buffers.getModifiedUtf8(data); client.tracef( "Client received capability: remote endpoint name \"%s\"", remoteEndpointName); break; } case Protocol.CAP_MESSAGE_CLOSE: { behavior |= Protocol.BH_MESSAGE_CLOSE; // remote side must be >= 3.2.11.GA // but, we'll assume it's >= 3.2.14.GA because no AS or EAP release included // 3.2.8.SP1 < x < 3.2.14.GA behavior &= ~Protocol.BH_FAULTY_MSG_SIZE; client.tracef("Client received capability: message close protocol supported"); break; } case Protocol.CAP_VERSION_STRING: { // remote side must be >= 3.2.16.GA behavior &= ~Protocol.BH_FAULTY_MSG_SIZE; final String remoteVersionString = Buffers.getModifiedUtf8(data); client.tracef( "Client received capability: remote version is \"%s\"", remoteVersionString); break; } case Protocol.CAP_CHANNELS_IN: { useDefaultChannels = false; // their channels in is our channels out channelsOut = ProtocolUtils.readIntData(data, len); client.tracef( "Client received capability: remote channels in is \"%d\"", channelsOut); break; } case Protocol.CAP_CHANNELS_OUT: { useDefaultChannels = false; // their channels out is our channels in channelsIn = ProtocolUtils.readIntData(data, len); client.tracef( "Client received capability: remote channels out is \"%d\"", channelsIn); break; } default: { client.tracef( "Client received unknown capability %02x", Integer.valueOf(type & 0xff)); // unknown, skip it for forward compatibility. break; } } } if (useDefaultChannels) { channelsIn = 40; channelsOut = 40; } if (starttls) { // only initiate starttls if not forbidden by config if (optionMap.get(Options.SSL_STARTTLS, true)) { // Prepare the request message body final Pooled<ByteBuffer> pooledSendBuffer = connection.allocate(); boolean ok = false; try { final ByteBuffer sendBuffer = pooledSendBuffer.getResource(); sendBuffer.put(Protocol.STARTTLS); sendBuffer.flip(); connection.setReadListener(new StartTls(remoteServerName), true); connection.send(pooledSendBuffer); ok = true; // all set return; } finally { if (!ok) pooledSendBuffer.free(); } } } if (serverSaslMechs.isEmpty()) { if (failedMechs.isEmpty()) { connection.handleException( new SaslException( "Authentication failed: the server presented no authentication mechanisms")); } else { // At this point we have been attempting to use mechanisms as they have been // presented to us but we have now exhausted the list. connection.handleException(allMechanismsFailed()); } return; } // OK now send our authentication request final AuthenticationContextConfigurationClient configurationClient = AUTH_CONFIGURATION_CLIENT; AuthenticationConfiguration configuration = configurationClient.getAuthenticationConfiguration(uri, authenticationContext); final SaslClient saslClient; try { saslClient = configurationClient.createSaslClient( uri, configuration, saslClientFactory, serverSaslMechs); } catch (SaslException e) { // apparently no more mechanisms can succeed connection.handleException(e); return; } if (saslClient == null) { connection.handleException(allMechanismsFailed()); return; } final String mechanismName = saslClient.getMechanismName(); client.tracef("Client initiating authentication using mechanism %s", mechanismName); connection.getChannel().suspendReads(); final int negotiatedVersion = version; final SaslClient usedSaslClient = saslClient; final Authentication authentication = new Authentication( usedSaslClient, remoteServerName, remoteEndpointName, behavior, channelsIn, channelsOut); connection .getExecutor() .execute( () -> { final byte[] response; try { response = usedSaslClient.hasInitialResponse() ? usedSaslClient.evaluateChallenge(EMPTY_BYTES) : null; } catch (SaslException e) { client.tracef("Client authentication failed: %s", e); saslDispose(usedSaslClient); failedMechs.put(mechanismName, e.toString()); sendCapRequest(remoteServerName); return; } // Prepare the request message body final Pooled<ByteBuffer> pooledSendBuffer = connection.allocate(); boolean ok = false; try { final ByteBuffer sendBuffer = pooledSendBuffer.getResource(); sendBuffer.put(Protocol.AUTH_REQUEST); if (negotiatedVersion < 1) { sendBuffer.put(mechanismName.getBytes(Protocol.UTF_8)); } else { ProtocolUtils.writeString(sendBuffer, mechanismName); if (response != null) { sendBuffer.put(response); } } sendBuffer.flip(); connection.send(pooledSendBuffer); ok = true; connection.setReadListener(authentication, true); return; } finally { if (!ok) pooledSendBuffer.free(); } }); return; } default: { client.unknownProtocolId(msgType); connection.handleException(client.invalidMessage(connection)); return; } } } catch (BufferUnderflowException | BufferOverflowException e) { connection.handleException(client.invalidMessage(connection)); return; } finally { pooledReceiveBuffer.free(); } }