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 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 newSsl() { return bssl.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_ = new bssl.UniquePtr(); 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 { //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 = new bssl.UniquePtr(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.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.get(); final SysCallResult 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(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 protocols = new ArrayList(); 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(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 file_event_ = new std::unique_ptr(); private bssl.UniquePtr ssl_ = new bssl.UniquePtr(); 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; }