Created
January 31, 2020 05:18
-
-
Save sangmoon/d24f7b915cc13a7d88a194e59cb11a26 to your computer and use it in GitHub Desktop.
Converted TlsInspector to java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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