Skip to content

Instantly share code, notes, and snippets.

@sangmoon
Created January 31, 2020 05:18
Show Gist options
  • Save sangmoon/d24f7b915cc13a7d88a194e59cb11a26 to your computer and use it in GitHub Desktop.
Save sangmoon/d24f7b915cc13a7d88a194e59cb11a26 to your computer and use it in GitHub Desktop.
Converted TlsInspector to java
public class Config
{
public Config(Stats.Scope scope)
{
this(scope, TLS_MAX_CLIENT_HELLO);
}
//C++ TO JAVA CONVERTER NOTE: Java does not allow default values for parameters. Overloaded methods are inserted above:
//ORIGINAL LINE: Config(Stats::Scope& scope, uint max_client_hello_size = TLS_MAX_CLIENT_HELLO) : stats_({ALL_TLS_INSPECTOR_STATS((scope).counter(Envoy::statPrefixJoin("tls_inspector.", FINISH_STAT_DECL_)}), ssl_ctx_(SSL_CTX_new(TLS_with_buffers_method())), max_client_hello_size_(max_client_hello_size)
public Config(Stats.Scope scope, int max_client_hello_size)
{
this.stats_ = new Envoy.Extensions.ListenerFilters.TlsInspector.TlsInspectorStats();
if (max_client_hello_size_ > TLS_MAX_CLIENT_HELLO)
{
throw new EnvoyException(fmt.format("max_client_hello_size of {} is greater than maximum of {}.", max_client_hello_size_, size_t(TLS_MAX_CLIENT_HELLO)));
}
SSL_CTX_set_options(ssl_ctx_.get(), SSL_OP_NO_TICKET);
SSL_CTX_set_session_cache_mode(ssl_ctx_.get(), SSL_SESS_CACHE_OFF);
SSL_CTX_set_select_certificate_cb(ssl_ctx_.get(), (SSL_CLIENT_HELLO client_hello) ->
{
byte data;
int len;
if (SSL_early_callback_ctx_extension_get(client_hello, TLSEXT_TYPE_application_layer_protocol_negotiation, data, len))
{
Filter filter = (Filter)SSL_get_app_data(client_hello.ssl);
filter.onALPN(data, len);
}
return ssl_select_cert_success;
});
SSL_CTX_set_tlsext_servername_callback(ssl_ctx_.get(), (SSL ssl, tangible.RefObject<Integer> out_alert, Object UnnamedParameter) ->
{
Filter filter = (Filter)SSL_get_app_data(ssl);
filter.onServername(absl.NullSafeStringView(SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name)));
// Return an error to stop the handshake; we have what we wanted already.
out_alert = SSL_AD_USER_CANCELLED;
return SSL_TLSEXT_ERR_ALERT_FATAL;
});
}
//C++ TO JAVA CONVERTER WARNING: 'const' methods are not available in Java:
//ORIGINAL LINE: const TlsInspectorStats& stats() const
public final TlsInspectorStats stats()
{
return stats_;
}
public final bssl.UniquePtr<SSL> newSsl()
{
return bssl.<SSL>UniquePtr({SSL_new(ssl_ctx_.get())});
}
//C++ TO JAVA CONVERTER WARNING: 'const' methods are not available in Java:
//ORIGINAL LINE: uint maxClientHelloSize() const
public final int maxClientHelloSize()
{
return max_client_hello_size_;
}
public static final int TLS_MAX_CLIENT_HELLO = 64 * 1024;
private TlsInspectorStats stats_ = new TlsInspectorStats();
private bssl.UniquePtr<SSL_CTX> ssl_ctx_ = new bssl.UniquePtr<SSL_CTX>();
private final int max_client_hello_size_;
}
/**
* TLS inspector listener filter.
*/
//C++ TO JAVA CONVERTER TODO TASK: Multiple inheritance is not available in Java:
public class Filter extends Network.ListenerFilter, Logger.Loggable<Logger.Id.filter>
{
//C++ TO JAVA CONVERTER TODO TASK: The implementation of the following method could not be found:
// Filter(ConfigSharedPtr config);
// Network::ListenerFilter
@Override
public Network.FilterStatus onAccept(Network.ListenerFilterCallbacks cb)
{
do
{
if (((spdlog.level.level_enum)Envoy.Logger.Logger.debug >= __log_do_not_use_read_comment().level()))
{
//C++ TO JAVA CONVERTER TODO TASK: There is no direct equivalent in Java to the following C++ macro:
__log_do_not_use_read_comment().debug("[" __FILE__ ":" "__LINE__" "] " __VA_ARGS__);
}
} while (0 != 0);
Network.ConnectionSocket socket = cb.socket();
(...) _ASSERT_SELECTOR(__VA_ARGS__, _ASSERT_VERBOSE, _ASSERT_ORIGINAL)(__VA_ARGS__)(file_event_ == null);
cb_ = cb;
ParseState parse_state = onRead();
switch (parse_state)
{
case Error:
// As per discussion in https://github.com/envoyproxy/envoy/issues/7864
// we don't add new enum in FilterStatus so we have to signal the caller
// the new condition.
cb.socket().close();
return Network.FilterStatus.StopIteration;
case Done:
return Network.FilterStatus.Continue;
case Continue:
// do nothing but create the event
//C++ TO JAVA CONVERTER TODO TASK: Only lambda expressions having all locals passed by reference can be converted to Java:
//ORIGINAL LINE: file_event_ = cb.dispatcher().createFileEvent(socket.ioHandle().fd(), [this](uint events)
file_event_ = cb.dispatcher().createFileEvent(socket.ioHandle().fd(), (int events) ->
{
if ((events & Event.FileReadyType.Closed) != 0)
{
config_.stats().connection_closed_.inc();
done(false);
return;
}
(...) _ASSERT_SELECTOR(__VA_ARGS__, _ASSERT_VERBOSE, _ASSERT_ORIGINAL)(__VA_ARGS__)(events == Event.FileReadyType.Read);
ParseState parse_state = onRead();
switch (parse_state)
{
case Error:
done(false);
break;
case Done:
done(true);
break;
case Continue:
// do nothing but wait for the next event
break;
}
}, Event.FileTriggerType.Edge, Event.FileReadyType.Read | Event.FileReadyType.Closed);
return Network.FilterStatus.StopIteration;
}
do
{
do
{
if (((spdlog.level.level_enum)Envoy.Logger.Logger.critical >= Envoy.Logger.Registry.getLog(Envoy.Logger.Id.assert_Renamed).level()))
{
//C++ TO JAVA CONVERTER TODO TASK: There is no direct equivalent in Java to the following C++ macro:
Envoy.Logger.Registry.getLog(Envoy.Logger.Id.assert_Renamed).critical("[" __FILE__ ":" "__LINE__" "] " __VA_ARGS__);
}
} while (0 != 0);
abort();
} while (false);
}
private ParseState parseClientHello(Object data, int len)
{
// Ownership is passed to ssl_ in SSL_set_bio()
bssl.UniquePtr<BIO> bio = new bssl.UniquePtr<BIO>(BIO_new_mem_buf(data, len));
// Make the mem-BIO return that there is more data
// available beyond it's end
BIO_set_mem_eof_return(bio.get(), -1);
SSL_set_bio(ssl_.get(), bio.get(), bio.get());
bio.release();
int ret = SSL_do_handshake(ssl_.get());
// This should never succeed because an error is always returned from the SNI callback.
(...) _ASSERT_SELECTOR(__VA_ARGS__, _ASSERT_VERBOSE, _ASSERT_ORIGINAL)(__VA_ARGS__)(ret <= 0);
switch (SSL_get_error(ssl_.get(), ret))
{
case SSL_ERROR_WANT_READ:
if (read_ == config_.maxClientHelloSize())
{
// We've hit the specified size limit. This is an unreasonably large ClientHello;
// indicate failure.
config_.stats().client_hello_too_large_.inc();
return ParseState.Error;
}
return ParseState.Continue;
case SSL_ERROR_SSL:
if (clienthello_success_)
{
config_.stats().tls_found_.inc();
if (alpn_found_)
{
config_.stats().alpn_found_.inc();
}
else
{
config_.stats().alpn_not_found_.inc();
}
cb_.socket().setDetectedTransportProtocol(ConstSingleton<TransportProtocolNameValues>.get().Tls);
}
else
{
config_.stats().tls_not_found_.inc();
}
return ParseState.Done;
default:
return ParseState.Error;
}
}
private ParseState onRead()
{
// This receive code is somewhat complicated, because it must be done as a MSG_PEEK because
// there is no way for a listener-filter to pass payload data to the ConnectionImpl and filters
// that get created later.
//
// The file_event_ in this class gets events every time new data is available on the socket,
// even if previous data has not been read, which is always the case due to MSG_PEEK. When
// the TlsInspector completes and passes the socket along, a new FileEvent is created for the
// socket, so that new event is immediately signaled as readable because it is new and the socket
// is readable, even though no new events have occurred.
//
// TODO(ggreenway): write an integration test to ensure the events work as expected on all
// platforms.
//C++ TO JAVA CONVERTER TODO TASK: There is no equivalent to implicit typing in Java unless the Java 10 inferred typing option is selected:
auto os_syscalls = GlobalMembers.ThreadSafeSingleton<OsSysCallsImpl>.get();
final SysCallResult<ptrdiff_t> result = os_syscalls.recv(cb_.socket().ioHandle().fd(), buf_, config_.maxClientHelloSize(), MSG_PEEK);
do
{
if (((spdlog.level.level_enum)Envoy.Logger.Logger.trace >= __log_do_not_use_read_comment().level()))
{
//C++ TO JAVA CONVERTER TODO TASK: There is no direct equivalent in Java to the following C++ macro:
__log_do_not_use_read_comment().trace("[" __FILE__ ":" "__LINE__" "] " __VA_ARGS__);
}
} while (0 != 0);
if (result.rc_ == -1 && result.errno_ == EAGAIN)
{
return ParseState.Continue;
}
else if (result.rc_ < 0)
{
config_.stats().read_error_.inc();
return ParseState.Error;
}
// Because we're doing a MSG_PEEK, data we've seen before gets returned every time, so
// skip over what we've already processed.
if ((long)result.rc_ > read_)
{
//C++ TO JAVA CONVERTER TODO TASK: Java does not have an equivalent to pointers to value types:
//ORIGINAL LINE: const byte* data = buf_ + read_;
byte data = buf_ + read_;
final int len = result.rc_ - read_;
read_ = result.rc_;
return parseClientHello(data, len);
}
return ParseState.Continue;
}
private void done(boolean success)
{
do
{
if (((spdlog.level.level_enum)Envoy.Logger.Logger.trace >= __log_do_not_use_read_comment().level()))
{
//C++ TO JAVA CONVERTER TODO TASK: There is no direct equivalent in Java to the following C++ macro:
__log_do_not_use_read_comment().trace("[" __FILE__ ":" "__LINE__" "] " __VA_ARGS__);
}
} while (0 != 0);
file_event_.reset();
cb_.continueFilterChain(success);
}
//C++ TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java:
//ORIGINAL LINE: void onALPN(const unsigned char* data, unsigned int len);
private void onALPN(byte data, int len)
{
CBS wire = new CBS();
CBS list = new CBS();
//C++ TO JAVA CONVERTER TODO TASK: There is no equivalent to 'reinterpret_cast' in Java:
CBS_init(wire, reinterpret_cast<const GlobalMembers.byte>(data), (int)len);
if (!CBS_get_u16_length_prefixed(wire, list) || CBS_len(wire) != 0 || CBS_len(list) < 2)
{
// Don't produce errors, let the real TLS stack do it.
return;
}
CBS name = new CBS();
ArrayList<absl.string_view> protocols = new ArrayList<absl.string_view>();
while (CBS_len(list) > 0)
{
if (!CBS_get_u8_length_prefixed(list, name) || CBS_len(name) == 0)
{
// Don't produce errors, let the real TLS stack do it.
return;
}
//C++ TO JAVA CONVERTER TODO TASK: There is no equivalent to 'reinterpret_cast' in Java:
protocols.emplace_back(reinterpret_cast<const byte>(CBS_data(name)), CBS_len(name));
}
cb_.socket().setRequestedApplicationProtocols(protocols);
alpn_found_ = true;
}
private void onServername(absl.string_view name)
{
if (!name.empty())
{
config_.stats().sni_found_.inc();
//C++ TO JAVA CONVERTER TODO TASK: The following line was determined to contain a copy constructor call - this should be verified and a copy constructor should be created:
//ORIGINAL LINE: cb_->socket().setRequestedServerName(name);
cb_.socket().setRequestedServerName(new absl.string_view(name));
do
{
if (((spdlog.level.level_enum)Envoy.Logger.Logger.debug >= __log_do_not_use_read_comment().level()))
{
//C++ TO JAVA CONVERTER TODO TASK: There is no direct equivalent in Java to the following C++ macro:
__log_do_not_use_read_comment().debug("[" __FILE__ ":" "__LINE__" "] " __VA_ARGS__);
}
} while (0 != 0);
}
else
{
config_.stats().sni_not_found_.inc();
}
clienthello_success_ = true;
}
private ConfigSharedPtr config_ = new ConfigSharedPtr();
private Network.ListenerFilterCallbacks cb_;
private std::unique_ptr<FileEvent> file_event_ = new std::unique_ptr<FileEvent>();
private bssl.UniquePtr<SSL> ssl_ = new bssl.UniquePtr<SSL>();
private long read_ = 0;
private boolean alpn_found_ = false;
private boolean clienthello_success_ = false;
private static thread_local[] byte buf_ = tangible.Arrays.initializeWithDefaultthread_localInstances(Config.TLS_MAX_CLIENT_HELLO);
// Allows callbacks on the SSL_CTX to set fields in this class.
//C++ TO JAVA CONVERTER TODO TASK: Java has no concept of a 'friend' class:
// friend class Config;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment