Issue #6728 - QUIC and HTTP/3

- refactor resource scopes and memory segments usage
This commit is contained in:
Ludovic Orban 2021-11-09 09:53:31 +01:00 committed by Simone Bordet
parent d2e76b077b
commit d0af388ab4
1 changed files with 177 additions and 146 deletions

View File

@ -74,16 +74,16 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
// Source Connection ID // Source Connection ID
MemorySegment scid = MemorySegment.allocateNative(QUICHE_MAX_CONN_ID_LEN, scope); MemorySegment scid = MemorySegment.allocateNative(QUICHE_MAX_CONN_ID_LEN, scope);
MemorySegment scid_len = MemorySegment.allocateNative(CLinker.C_LONG, scope); MemorySegment scid_len = MemorySegment.allocateNative(CLinker.C_LONG, scope);
scid_len.asByteBuffer().order(ByteOrder.nativeOrder()).putLong(scid.byteSize()); putLong(scid_len, scid.byteSize());
// Destination Connection ID // Destination Connection ID
MemorySegment dcid = MemorySegment.allocateNative(QUICHE_MAX_CONN_ID_LEN, scope); MemorySegment dcid = MemorySegment.allocateNative(QUICHE_MAX_CONN_ID_LEN, scope);
MemorySegment dcid_len = MemorySegment.allocateNative(CLinker.C_LONG, scope); MemorySegment dcid_len = MemorySegment.allocateNative(CLinker.C_LONG, scope);
dcid_len.asByteBuffer().order(ByteOrder.nativeOrder()).putLong(dcid.byteSize()); putLong(dcid_len, dcid.byteSize());
MemorySegment token = MemorySegment.allocateNative(QuicheConnection.TokenMinter.MAX_TOKEN_LENGTH, scope); MemorySegment token = MemorySegment.allocateNative(QuicheConnection.TokenMinter.MAX_TOKEN_LENGTH, scope);
MemorySegment token_len = MemorySegment.allocateNative(CLinker.C_LONG, scope); MemorySegment token_len = MemorySegment.allocateNative(CLinker.C_LONG, scope);
token_len.asByteBuffer().order(ByteOrder.nativeOrder()).putLong(token.byteSize()); putLong(token_len, token.byteSize());
LOG.debug("getting header info (fromPacket)..."); LOG.debug("getting header info (fromPacket)...");
int rc; int rc;
@ -103,7 +103,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
// If the ByteBuffer is heap-allocated, it must be copied to native memory. // If the ByteBuffer is heap-allocated, it must be copied to native memory.
MemorySegment packetReadSegment = MemorySegment.allocateNative(packet.remaining(), scope); MemorySegment packetReadSegment = MemorySegment.allocateNative(packet.remaining(), scope);
int prevPosition = packet.position(); int prevPosition = packet.position();
packetReadSegment.asByteBuffer().order(ByteOrder.nativeOrder()).put(packet); packetReadSegment.asByteBuffer().put(packet);
packet.position(prevPosition); packet.position(prevPosition);
rc = quiche_h.quiche_header_info(packetReadSegment.address(), packet.remaining(), QUICHE_MAX_CONN_ID_LEN, rc = quiche_h.quiche_header_info(packetReadSegment.address(), packet.remaining(), QUICHE_MAX_CONN_ID_LEN,
version.address(), type.address(), version.address(), type.address(),
@ -118,8 +118,8 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
return null; return null;
} }
byte[] bytes = new byte[(int)dcid_len.asByteBuffer().order(ByteOrder.nativeOrder()).getLong()]; byte[] bytes = new byte[(int)getLong(dcid_len)];
dcid.asByteBuffer().order(ByteOrder.nativeOrder()).get(bytes); dcid.asByteBuffer().get(bytes);
return bytes; return bytes;
} }
} }
@ -133,16 +133,28 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
{ {
if (connectionIdLength > QUICHE_MAX_CONN_ID_LEN) if (connectionIdLength > QUICHE_MAX_CONN_ID_LEN)
throw new IOException("Connection ID length is too large: " + connectionIdLength + " > " + QUICHE_MAX_CONN_ID_LEN); throw new IOException("Connection ID length is too large: " + connectionIdLength + " > " + QUICHE_MAX_CONN_ID_LEN);
ResourceScope scope = ResourceScope.newSharedScope();
byte[] scidBytes = new byte[connectionIdLength];
SECURE_RANDOM.nextBytes(scidBytes);
MemorySegment scid = MemorySegment.allocateNative(scidBytes.length, scope);
scid.asByteBuffer().order(ByteOrder.nativeOrder()).put(scidBytes);
MemoryAddress libQuicheConfig = buildConfig(quicheConfig, scope);
MemorySegment s = sockaddr.convert(peer, scope); ResourceScope scope = ResourceScope.newSharedScope();
MemoryAddress quicheConn = quiche_h.quiche_connect(CLinker.toCString(peer.getHostName(), scope), scid, scid.byteSize(), s, s.byteSize(), libQuicheConfig); boolean keepScope = false;
return new ForeignIncubatorQuicheConnection(quicheConn, libQuicheConfig, scope); try
{
byte[] scidBytes = new byte[connectionIdLength];
SECURE_RANDOM.nextBytes(scidBytes);
MemorySegment scid = MemorySegment.allocateNative(scidBytes.length, scope);
scid.asByteBuffer().put(scidBytes);
MemoryAddress libQuicheConfig = buildConfig(quicheConfig, scope);
MemorySegment s = sockaddr.convert(peer, scope);
MemoryAddress quicheConn = quiche_h.quiche_connect(CLinker.toCString(peer.getHostName(), scope), scid, scid.byteSize(), s, s.byteSize(), libQuicheConfig);
ForeignIncubatorQuicheConnection connection = new ForeignIncubatorQuicheConnection(quicheConn, libQuicheConfig, scope);
keepScope = true;
return connection;
}
finally
{
if (!keepScope)
scope.close();
}
} }
private static MemoryAddress buildConfig(QuicheConfig config, ResourceScope scope) throws IOException private static MemoryAddress buildConfig(QuicheConfig config, ResourceScope scope) throws IOException
@ -175,7 +187,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
} }
byte[] bytes = baos.toByteArray(); byte[] bytes = baos.toByteArray();
MemorySegment segment = MemorySegment.allocateNative(bytes.length, scope); MemorySegment segment = MemorySegment.allocateNative(bytes.length, scope);
segment.asByteBuffer().order(ByteOrder.nativeOrder()).put(bytes); segment.asByteBuffer().put(bytes);
quiche_h.quiche_config_set_application_protos(quicheConfig, segment.address(), segment.byteSize()); quiche_h.quiche_config_set_application_protos(quicheConfig, segment.address(), segment.byteSize());
} }
@ -233,7 +245,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
// If the ByteBuffer is heap-allocated, it must be copied to native memory. // If the ByteBuffer is heap-allocated, it must be copied to native memory.
packetReadSegment = MemorySegment.allocateNative(packetRead.remaining(), scope); packetReadSegment = MemorySegment.allocateNative(packetRead.remaining(), scope);
int prevPosition = packetRead.position(); int prevPosition = packetRead.position();
packetReadSegment.asByteBuffer().order(ByteOrder.nativeOrder()).put(packetRead); packetReadSegment.asByteBuffer().put(packetRead);
packetRead.position(prevPosition); packetRead.position(prevPosition);
} }
@ -243,16 +255,16 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
// Source Connection ID // Source Connection ID
MemorySegment scid = MemorySegment.allocateNative(QUICHE_MAX_CONN_ID_LEN, scope); MemorySegment scid = MemorySegment.allocateNative(QUICHE_MAX_CONN_ID_LEN, scope);
MemorySegment scid_len = MemorySegment.allocateNative(CLinker.C_LONG, scope); MemorySegment scid_len = MemorySegment.allocateNative(CLinker.C_LONG, scope);
scid_len.asByteBuffer().order(ByteOrder.nativeOrder()).putLong(scid.byteSize()); putLong(scid_len, scid.byteSize());
// Destination Connection ID // Destination Connection ID
MemorySegment dcid = MemorySegment.allocateNative(QUICHE_MAX_CONN_ID_LEN, scope); MemorySegment dcid = MemorySegment.allocateNative(QUICHE_MAX_CONN_ID_LEN, scope);
MemorySegment dcid_len = MemorySegment.allocateNative(CLinker.C_LONG, scope); MemorySegment dcid_len = MemorySegment.allocateNative(CLinker.C_LONG, scope);
dcid_len.asByteBuffer().order(ByteOrder.nativeOrder()).putLong(dcid.byteSize()); putLong(dcid_len, dcid.byteSize());
MemorySegment token = MemorySegment.allocateNative(TokenMinter.MAX_TOKEN_LENGTH, scope); MemorySegment token = MemorySegment.allocateNative(TokenMinter.MAX_TOKEN_LENGTH, scope);
MemorySegment token_len = MemorySegment.allocateNative(CLinker.C_LONG, scope); MemorySegment token_len = MemorySegment.allocateNative(CLinker.C_LONG, scope);
token_len.asByteBuffer().order(ByteOrder.nativeOrder()).putLong(token.byteSize()); putLong(token_len, token.byteSize());
LOG.debug("getting header info (negotiate)..."); LOG.debug("getting header info (negotiate)...");
int rc = quiche_h.quiche_header_info(packetReadSegment.address(), packetRead.remaining(), QUICHE_MAX_CONN_ID_LEN, int rc = quiche_h.quiche_header_info(packetReadSegment.address(), packetRead.remaining(), QUICHE_MAX_CONN_ID_LEN,
@ -264,13 +276,13 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
throw new IOException("failed to parse header: " + quiche_error.errToString(rc)); throw new IOException("failed to parse header: " + quiche_error.errToString(rc));
packetRead.position(packetRead.limit()); packetRead.position(packetRead.limit());
LOG.debug("version: {}", version.asByteBuffer().order(ByteOrder.nativeOrder()).getInt()); LOG.debug("version: {}", getInt(version));
LOG.debug("type: {}", type.asByteBuffer().order(ByteOrder.nativeOrder()).get()); LOG.debug("type: {}", getByte(type));
LOG.debug("scid len: {}", scid_len.asByteBuffer().order(ByteOrder.nativeOrder()).getLong()); LOG.debug("scid len: {}", getLong(scid_len));
LOG.debug("dcid len: {}", dcid_len.asByteBuffer().order(ByteOrder.nativeOrder()).getLong()); LOG.debug("dcid len: {}", getLong(dcid_len));
LOG.debug("token len: {}", token_len.asByteBuffer().order(ByteOrder.nativeOrder()).getLong()); LOG.debug("token len: {}", getLong(token_len));
if (quiche_h.quiche_version_is_supported(version.asByteBuffer().order(ByteOrder.nativeOrder()).getInt()) == C_FALSE) if (quiche_h.quiche_version_is_supported(getInt(version)) == C_FALSE)
{ {
LOG.debug("version negotiation"); LOG.debug("version negotiation");
@ -286,29 +298,29 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
packetToSendSegment = MemorySegment.allocateNative(packetToSend.remaining(), scope); packetToSendSegment = MemorySegment.allocateNative(packetToSend.remaining(), scope);
} }
long generated = quiche_h.quiche_negotiate_version(scid.address(), scid_len.asByteBuffer().order(ByteOrder.nativeOrder()).getLong(), dcid.address(), dcid_len.asByteBuffer().order(ByteOrder.nativeOrder()).getLong(), packetToSendSegment.address(), packetToSend.remaining()); long generated = quiche_h.quiche_negotiate_version(scid.address(), getLong(scid_len), dcid.address(), getLong(dcid_len), packetToSendSegment.address(), packetToSend.remaining());
if (generated < 0) if (generated < 0)
throw new IOException("failed to create vneg packet : " + quiche_error.errToString(generated)); throw new IOException("failed to create vneg packet : " + quiche_error.errToString(generated));
if (!packetToSend.isDirect()) if (!packetToSend.isDirect())
packetToSend.put(packetToSendSegment.asByteBuffer().order(ByteOrder.nativeOrder()).limit((int)generated)); packetToSend.put(packetToSendSegment.asByteBuffer().limit((int)generated));
else else
packetToSend.position((int)(packetToSend.position() + generated)); packetToSend.position((int)(packetToSend.position() + generated));
return true; return true;
} }
if (token_len.asByteBuffer().order(ByteOrder.nativeOrder()).getLong() == 0L) if (getLong(token_len) == 0L)
{ {
LOG.debug("stateless retry"); LOG.debug("stateless retry");
byte[] dcidBytes = new byte[(int)dcid_len.asByteBuffer().order(ByteOrder.nativeOrder()).getLong()]; byte[] dcidBytes = new byte[(int)getLong(dcid_len)];
dcid.asByteBuffer().order(ByteOrder.nativeOrder()).get(dcidBytes); dcid.asByteBuffer().get(dcidBytes);
byte[] tokenBytes = tokenMinter.mint(dcidBytes, dcidBytes.length); byte[] tokenBytes = tokenMinter.mint(dcidBytes, dcidBytes.length);
token.asByteBuffer().order(ByteOrder.nativeOrder()).put(tokenBytes); token.asByteBuffer().put(tokenBytes);
byte[] newCid = new byte[QUICHE_MAX_CONN_ID_LEN]; byte[] newCid = new byte[QUICHE_MAX_CONN_ID_LEN];
SECURE_RANDOM.nextBytes(newCid); SECURE_RANDOM.nextBytes(newCid);
MemorySegment newCidSegment = MemorySegment.allocateNative(newCid.length, scope); MemorySegment newCidSegment = MemorySegment.allocateNative(newCid.length, scope);
newCidSegment.asByteBuffer().order(ByteOrder.nativeOrder()).put(newCid); newCidSegment.asByteBuffer().put(newCid);
MemorySegment packetToSendSegment; MemorySegment packetToSendSegment;
if (packetToSend.isDirect()) if (packetToSend.isDirect())
@ -322,17 +334,17 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
packetToSendSegment = MemorySegment.allocateNative(packetToSend.remaining(), scope); packetToSendSegment = MemorySegment.allocateNative(packetToSend.remaining(), scope);
} }
long generated = quiche_h.quiche_retry(scid.address(), scid_len.asByteBuffer().order(ByteOrder.nativeOrder()).getLong(), long generated = quiche_h.quiche_retry(scid.address(), getLong(scid_len),
dcid.address(), dcid_len.asByteBuffer().order(ByteOrder.nativeOrder()).getLong(), dcid.address(), getLong(dcid_len),
newCidSegment.address(), newCid.length, newCidSegment.address(), newCid.length,
token.address(), tokenBytes.length, token.address(), tokenBytes.length,
version.asByteBuffer().order(ByteOrder.nativeOrder()).getInt(), getInt(version),
packetToSendSegment.address(), packetToSendSegment.byteSize() packetToSendSegment.address(), packetToSendSegment.byteSize()
); );
if (generated < 0) if (generated < 0)
throw new IOException("failed to create retry packet: " + quiche_error.errToString(generated)); throw new IOException("failed to create retry packet: " + quiche_error.errToString(generated));
if (!packetToSend.isDirect()) if (!packetToSend.isDirect())
packetToSend.put(packetToSendSegment.asByteBuffer().order(ByteOrder.nativeOrder()).limit((int)generated)); packetToSend.put(packetToSendSegment.asByteBuffer().limit((int)generated));
else else
packetToSend.position((int)(packetToSend.position() + generated)); packetToSend.position((int)(packetToSend.position() + generated));
return true; return true;
@ -344,116 +356,115 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
public static ForeignIncubatorQuicheConnection tryAccept(QuicheConfig quicheConfig, TokenValidator tokenValidator, ByteBuffer packetRead, SocketAddress peer) throws IOException public static ForeignIncubatorQuicheConnection tryAccept(QuicheConfig quicheConfig, TokenValidator tokenValidator, ByteBuffer packetRead, SocketAddress peer) throws IOException
{ {
boolean keepScope = false;
ResourceScope scope = ResourceScope.newSharedScope(); ResourceScope scope = ResourceScope.newSharedScope();
try
MemorySegment type = MemorySegment.allocateNative(CLinker.C_CHAR, scope);
MemorySegment version = MemorySegment.allocateNative(CLinker.C_INT, scope);
// Source Connection ID
MemorySegment scid = MemorySegment.allocateNative(QUICHE_MAX_CONN_ID_LEN, scope);
MemorySegment scid_len = MemorySegment.allocateNative(CLinker.C_LONG, scope);
scid_len.asByteBuffer().order(ByteOrder.nativeOrder()).putLong(scid.byteSize());
// Destination Connection ID
MemorySegment dcid = MemorySegment.allocateNative(QUICHE_MAX_CONN_ID_LEN, scope);
MemorySegment dcid_len = MemorySegment.allocateNative(CLinker.C_LONG, scope);
dcid_len.asByteBuffer().order(ByteOrder.nativeOrder()).putLong(dcid.byteSize());
MemorySegment token = MemorySegment.allocateNative(TokenMinter.MAX_TOKEN_LENGTH, scope);
MemorySegment token_len = MemorySegment.allocateNative(CLinker.C_LONG, scope);
token_len.asByteBuffer().order(ByteOrder.nativeOrder()).putLong(token.byteSize());
LOG.debug("getting header info (tryAccept)...");
int rc;
if (packetRead.isDirect())
{ {
// If the ByteBuffer is direct, it can be used without any copy. MemorySegment type = MemorySegment.allocateNative(CLinker.C_CHAR, scope);
MemorySegment packetReadSegment = MemorySegment.ofByteBuffer(packetRead); MemorySegment version = MemorySegment.allocateNative(CLinker.C_INT, scope);
rc = quiche_h.quiche_header_info(packetReadSegment.address(), packetRead.remaining(), QUICHE_MAX_CONN_ID_LEN,
version.address(), type.address(), // Source Connection ID
scid.address(), scid_len.address(), MemorySegment scid = MemorySegment.allocateNative(QUICHE_MAX_CONN_ID_LEN, scope);
dcid.address(), dcid_len.address(), MemorySegment scid_len = MemorySegment.allocateNative(CLinker.C_LONG, scope);
token.address(), token_len.address()); putLong(scid_len, scid.byteSize());
}
else // Destination Connection ID
{ MemorySegment dcid = MemorySegment.allocateNative(QUICHE_MAX_CONN_ID_LEN, scope);
// If the ByteBuffer is heap-allocated, it must be copied to native memory. MemorySegment dcid_len = MemorySegment.allocateNative(CLinker.C_LONG, scope);
try (ResourceScope segmentScope = ResourceScope.newConfinedScope()) putLong(dcid_len, dcid.byteSize());
MemorySegment token = MemorySegment.allocateNative(TokenMinter.MAX_TOKEN_LENGTH, scope);
MemorySegment token_len = MemorySegment.allocateNative(CLinker.C_LONG, scope);
putLong(token_len, token.byteSize());
LOG.debug("getting header info (tryAccept)...");
int rc;
if (packetRead.isDirect())
{ {
MemorySegment packetReadSegment = MemorySegment.allocateNative(packetRead.remaining(), segmentScope); // If the ByteBuffer is direct, it can be used without any copy.
int prevPosition = packetRead.position(); MemorySegment packetReadSegment = MemorySegment.ofByteBuffer(packetRead);
packetReadSegment.asByteBuffer().order(ByteOrder.nativeOrder()).put(packetRead);
packetRead.position(prevPosition);
rc = quiche_h.quiche_header_info(packetReadSegment.address(), packetRead.remaining(), QUICHE_MAX_CONN_ID_LEN, rc = quiche_h.quiche_header_info(packetReadSegment.address(), packetRead.remaining(), QUICHE_MAX_CONN_ID_LEN,
version.address(), type.address(), version.address(), type.address(),
scid.address(), scid_len.address(), scid.address(), scid_len.address(),
dcid.address(), dcid_len.address(), dcid.address(), dcid_len.address(),
token.address(), token_len.address()); token.address(), token_len.address());
} }
else
{
// If the ByteBuffer is heap-allocated, it must be copied to native memory.
try (ResourceScope segmentScope = ResourceScope.newConfinedScope())
{
MemorySegment packetReadSegment = MemorySegment.allocateNative(packetRead.remaining(), segmentScope);
int prevPosition = packetRead.position();
packetReadSegment.asByteBuffer().put(packetRead);
packetRead.position(prevPosition);
rc = quiche_h.quiche_header_info(packetReadSegment.address(), packetRead.remaining(), QUICHE_MAX_CONN_ID_LEN,
version.address(), type.address(),
scid.address(), scid_len.address(),
dcid.address(), dcid_len.address(),
token.address(), token_len.address());
}
}
if (rc < 0)
throw new IOException("failed to parse header: " + quiche_error.errToString(rc));
LOG.debug("version: {}", getInt(version));
LOG.debug("type: {}", getByte(type));
LOG.debug("scid len: {}", getLong(scid_len));
LOG.debug("dcid len: {}", getLong(dcid_len));
LOG.debug("token len: {}", getLong(token_len));
if (quiche_h.quiche_version_is_supported(getInt(version)) == C_FALSE)
{
LOG.debug("need version negotiation");
return null;
}
int tokenLen = (int)getLong(token_len);
if (tokenLen == 0)
{
LOG.debug("need stateless retry");
return null;
}
LOG.debug("token validation...");
// Original Destination Connection ID
byte[] tokenBytes = new byte[(int)token.byteSize()];
token.asByteBuffer().get(tokenBytes, 0, tokenLen);
byte[] odcidBytes = tokenValidator.validate(tokenBytes, tokenLen);
if (odcidBytes == null)
throw new TokenValidationException("invalid address validation token");
LOG.debug("validated token");
MemorySegment odcid = MemorySegment.allocateNative(odcidBytes.length, scope);
odcid.asByteBuffer().put(odcidBytes);
LOG.debug("connection creation...");
MemoryAddress libQuicheConfig = buildConfig(quicheConfig, scope);
MemorySegment s = sockaddr.convert(peer, scope);
MemoryAddress quicheConn = quiche_h.quiche_accept(dcid.address(), getLong(dcid_len), odcid.address(), odcid.byteSize(), s.address(), s.byteSize(), libQuicheConfig);
if (quicheConn == null)
{
quiche_h.quiche_config_free(libQuicheConfig);
throw new IOException("failed to create connection");
}
LOG.debug("connection created");
ForeignIncubatorQuicheConnection quicheConnection = new ForeignIncubatorQuicheConnection(quicheConn, libQuicheConfig, scope);
LOG.debug("accepted, immediately receiving the same packet - remaining in buffer: {}", packetRead.remaining());
while (packetRead.hasRemaining())
{
quicheConnection.feedCipherBytes(packetRead, peer);
}
keepScope = true;
return quicheConnection;
} }
if (rc < 0) finally
{ {
scope.close(); if (!keepScope)
throw new IOException("failed to parse header: " + quiche_error.errToString(rc)); scope.close();
} }
LOG.debug("version: {}", version.asByteBuffer().order(ByteOrder.nativeOrder()).getInt());
LOG.debug("type: {}", type.asByteBuffer().order(ByteOrder.nativeOrder()).get());
LOG.debug("scid len: {}", scid_len.asByteBuffer().order(ByteOrder.nativeOrder()).getLong());
LOG.debug("dcid len: {}", dcid_len.asByteBuffer().order(ByteOrder.nativeOrder()).getLong());
LOG.debug("token len: {}", token_len.asByteBuffer().order(ByteOrder.nativeOrder()).getLong());
if (quiche_h.quiche_version_is_supported(version.asByteBuffer().order(ByteOrder.nativeOrder()).getInt()) == C_FALSE)
{
LOG.debug("need version negotiation");
scope.close();
return null;
}
int tokenLen = (int)token_len.asByteBuffer().order(ByteOrder.nativeOrder()).getLong();
if (tokenLen == 0)
{
LOG.debug("need stateless retry");
scope.close();
return null;
}
LOG.debug("token validation...");
// Original Destination Connection ID
byte[] tokenBytes = new byte[(int)token.byteSize()];
token.asByteBuffer().order(ByteOrder.nativeOrder()).get(tokenBytes, 0, tokenLen);
byte[] odcidBytes = tokenValidator.validate(tokenBytes, tokenLen);
if (odcidBytes == null)
{
scope.close();
throw new TokenValidationException("invalid address validation token");
}
LOG.debug("validated token");
MemorySegment odcid = MemorySegment.allocateNative(odcidBytes.length, scope);
odcid.asByteBuffer().order(ByteOrder.nativeOrder()).put(odcidBytes);
LOG.debug("connection creation...");
MemoryAddress libQuicheConfig = buildConfig(quicheConfig, scope);
MemorySegment s = sockaddr.convert(peer, scope);
MemoryAddress quicheConn = quiche_h.quiche_accept(dcid.address(), dcid_len.asByteBuffer().order(ByteOrder.nativeOrder()).getLong(), odcid.address(), odcid.byteSize(), s.address(), s.byteSize(), libQuicheConfig);
if (quicheConn == null)
{
quiche_h.quiche_config_free(libQuicheConfig);
scope.close();
throw new IOException("failed to create connection");
}
LOG.debug("connection created");
ForeignIncubatorQuicheConnection quicheConnection = new ForeignIncubatorQuicheConnection(quicheConn, libQuicheConfig, scope);
LOG.debug("accepted, immediately receiving the same packet - remaining in buffer: {}", packetRead.remaining());
while (packetRead.hasRemaining())
{
quicheConnection.feedCipherBytes(packetRead, peer);
}
return quicheConnection;
} }
@Override @Override
@ -476,7 +487,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
MemorySegment streamIdSegment = MemorySegment.allocateNative(CLinker.C_LONG, scope); MemorySegment streamIdSegment = MemorySegment.allocateNative(CLinker.C_LONG, scope);
while (quiche_h.quiche_stream_iter_next(quiche_stream_iter, streamIdSegment.address()) != C_FALSE) while (quiche_h.quiche_stream_iter_next(quiche_stream_iter, streamIdSegment.address()) != C_FALSE)
{ {
long streamId = streamIdSegment.asByteBuffer().order(ByteOrder.nativeOrder()).getLong(); long streamId = getLong(streamIdSegment);
result.add(streamId); result.add(streamId);
} }
} }
@ -509,7 +520,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
// If the ByteBuffer is heap-allocated, it must be copied to native memory. // If the ByteBuffer is heap-allocated, it must be copied to native memory.
MemorySegment bufferSegment = MemorySegment.allocateNative(buffer.remaining(), scope); MemorySegment bufferSegment = MemorySegment.allocateNative(buffer.remaining(), scope);
int prevPosition = buffer.position(); int prevPosition = buffer.position();
bufferSegment.asByteBuffer().order(ByteOrder.nativeOrder()).put(buffer); bufferSegment.asByteBuffer().put(buffer);
buffer.position(prevPosition); buffer.position(prevPosition);
received = quiche_h.quiche_conn_recv(quicheConn, bufferSegment.address(), buffer.remaining(), recvInfo.address()); received = quiche_h.quiche_conn_recv(quicheConn, bufferSegment.address(), buffer.remaining(), recvInfo.address());
} }
@ -544,7 +555,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
{ {
MemorySegment bufferSegment = MemorySegment.allocateNative(buffer.remaining(), scope); MemorySegment bufferSegment = MemorySegment.allocateNative(buffer.remaining(), scope);
written = quiche_h.quiche_conn_send(quicheConn, bufferSegment.address(), buffer.remaining(), sendInfo.address()); written = quiche_h.quiche_conn_send(quicheConn, bufferSegment.address(), buffer.remaining(), sendInfo.address());
buffer.put(bufferSegment.asByteBuffer().order(ByteOrder.nativeOrder()).slice().limit((int)written)); buffer.put(bufferSegment.asByteBuffer().slice().limit((int)written));
buffer.position(prevPosition); buffer.position(prevPosition);
} }
} }
@ -614,12 +625,12 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
MemorySegment outLenSegment = MemorySegment.allocateNative(CLinker.C_LONG, scope); MemorySegment outLenSegment = MemorySegment.allocateNative(CLinker.C_LONG, scope);
quiche_h.quiche_conn_application_proto(quicheConn, outSegment.address(), outLenSegment.address()); quiche_h.quiche_conn_application_proto(quicheConn, outSegment.address(), outLenSegment.address());
long outLen = outLenSegment.asByteBuffer().order(ByteOrder.nativeOrder()).getLong(); long outLen = getLong(outLenSegment);
if (outLen == 0L) if (outLen == 0L)
return null; return null;
byte[] out = new byte[(int)outLen]; byte[] out = new byte[(int)outLen];
// dereference outSegment pointer // dereference outSegment pointer
MemoryAddress memoryAddress = MemoryAddress.ofLong(outSegment.asByteBuffer().order(ByteOrder.nativeOrder()).getLong()); MemoryAddress memoryAddress = MemoryAddress.ofLong(getLong(outSegment));
memoryAddress.asSegment(outLen, ResourceScope.globalScope()).asByteBuffer().get(out); memoryAddress.asSegment(outLen, ResourceScope.globalScope()).asByteBuffer().get(out);
return new String(out, StandardCharsets.UTF_8); return new String(out, StandardCharsets.UTF_8);
@ -649,7 +660,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
{ {
byte[] reasonBytes = reason.getBytes(StandardCharsets.UTF_8); byte[] reasonBytes = reason.getBytes(StandardCharsets.UTF_8);
MemorySegment reasonSegment = MemorySegment.allocateNative(reasonBytes.length, scope); MemorySegment reasonSegment = MemorySegment.allocateNative(reasonBytes.length, scope);
reasonSegment.asByteBuffer().order(ByteOrder.nativeOrder()).put(reasonBytes); reasonSegment.asByteBuffer().put(reasonBytes);
int length = reasonBytes.length; int length = reasonBytes.length;
MemoryAddress reasonAddress = reasonSegment.address(); MemoryAddress reasonAddress = reasonSegment.address();
rc = quiche_h.quiche_conn_close(quicheConn, C_TRUE, error, reasonAddress, length); rc = quiche_h.quiche_conn_close(quicheConn, C_TRUE, error, reasonAddress, length);
@ -783,7 +794,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
{ {
MemorySegment bufferSegment = MemorySegment.allocateNative(buffer.remaining(), scope); MemorySegment bufferSegment = MemorySegment.allocateNative(buffer.remaining(), scope);
int prevPosition = buffer.position(); int prevPosition = buffer.position();
bufferSegment.asByteBuffer().order(ByteOrder.nativeOrder()).put(buffer); bufferSegment.asByteBuffer().put(buffer);
buffer.position(prevPosition); buffer.position(prevPosition);
written = quiche_h.quiche_conn_stream_send(quicheConn, streamId, bufferSegment.address(), buffer.remaining(), last ? C_TRUE : C_FALSE); written = quiche_h.quiche_conn_stream_send(quicheConn, streamId, bufferSegment.address(), buffer.remaining(), last ? C_TRUE : C_FALSE);
} }
@ -826,7 +837,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
read = quiche_h.quiche_conn_stream_recv(quicheConn, streamId, bufferSegment.address(), buffer.remaining(), fin.address()); read = quiche_h.quiche_conn_stream_recv(quicheConn, streamId, bufferSegment.address(), buffer.remaining(), fin.address());
int prevPosition = buffer.position(); int prevPosition = buffer.position();
buffer.put(bufferSegment.asByteBuffer().order(ByteOrder.nativeOrder()).limit((int)read)); buffer.put(bufferSegment.asByteBuffer().limit((int)read));
buffer.position(prevPosition); buffer.position(prevPosition);
} }
} }
@ -866,8 +877,8 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
MemorySegment reasonLength = MemorySegment.allocateNative(CLinker.C_LONG, scope); MemorySegment reasonLength = MemorySegment.allocateNative(CLinker.C_LONG, scope);
if (quiche_h.quiche_conn_peer_error(quicheConn, app.address(), error.address(), reason.address(), reasonLength.address()) != C_FALSE) if (quiche_h.quiche_conn_peer_error(quicheConn, app.address(), error.address(), reason.address(), reasonLength.address()) != C_FALSE)
{ {
long errorValue = error.asByteBuffer().order(ByteOrder.nativeOrder()).getLong(); long errorValue = getLong(error);
long reasonLengthValue = reasonLength.asByteBuffer().order(ByteOrder.nativeOrder()).getLong(); long reasonLengthValue = getLong(reasonLength);
String reasonValue; String reasonValue;
if (reasonLengthValue == 0L) if (reasonLengthValue == 0L)
@ -878,7 +889,7 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
{ {
byte[] reasonBytes = new byte[(int)reasonLengthValue]; byte[] reasonBytes = new byte[(int)reasonLengthValue];
// dereference reason pointer // dereference reason pointer
MemoryAddress memoryAddress = MemoryAddress.ofLong(reason.asByteBuffer().order(ByteOrder.nativeOrder()).getLong()); MemoryAddress memoryAddress = MemoryAddress.ofLong(getLong(reason));
memoryAddress.asSegment(reasonLengthValue, ResourceScope.globalScope()).asByteBuffer().get(reasonBytes); memoryAddress.asSegment(reasonLengthValue, ResourceScope.globalScope()).asByteBuffer().get(reasonBytes);
reasonValue = new String(reasonBytes, StandardCharsets.UTF_8); reasonValue = new String(reasonBytes, StandardCharsets.UTF_8);
} }
@ -889,4 +900,24 @@ public class ForeignIncubatorQuicheConnection extends QuicheConnection
} }
} }
} }
private static void putLong(MemorySegment memorySegment, long value)
{
memorySegment.asByteBuffer().order(ByteOrder.nativeOrder()).putLong(value);
}
private static long getLong(MemorySegment memorySegment)
{
return memorySegment.asByteBuffer().order(ByteOrder.nativeOrder()).getLong();
}
private static int getInt(MemorySegment memorySegment)
{
return memorySegment.asByteBuffer().order(ByteOrder.nativeOrder()).getInt();
}
private static byte getByte(MemorySegment memorySegment)
{
return memorySegment.asByteBuffer().get();
}
} }