From ca49b7c840038f1e79f0244b61906364bd98b798 Mon Sep 17 00:00:00 2001 From: Kristofer Berggren Date: Sat, 15 Jun 2024 10:11:02 +0800 Subject: [PATCH] add support for server name indication --- README.md | 5 +++++ src/imap.cpp | 22 ++++++++++++++++++++-- src/imap.h | 4 +++- src/imapmanager.cpp | 3 ++- src/imapmanager.h | 1 + src/main.cpp | 3 +++ src/nmail.1 | 2 +- src/util.cpp | 19 +++++++++++++++++++ src/util.h | 1 + src/version.cpp | 2 +- 10 files changed, 56 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 413874c..f8f9bec 100644 --- a/README.md +++ b/README.md @@ -289,6 +289,7 @@ Full example of a config file `~/.nmail/main.conf`: smtp_host=smtp.example.com smtp_port=587 smtp_user= + sni_enabled=1 text_to_html_cmd= trash=Trash user=example@example.com @@ -522,6 +523,10 @@ case the email account has different username and password for sending emails for sending emails). If not specified, the configured `user` field will be used. +### sni_enabled + +Controls whether to enable Server Name Indication (SNI) during TLS handshaking. + ### spell_cmd This field specifies a custom command to use for spell checking composed diff --git a/src/imap.cpp b/src/imap.cpp index b3dfe58..38027c4 100644 --- a/src/imap.cpp +++ b/src/imap.cpp @@ -1,6 +1,6 @@ // imap.cpp // -// Copyright (c) 2019-2023 Kristofer Berggren +// Copyright (c) 2019-2024 Kristofer Berggren // All rights reserved. // // nmail is distributed under the MIT license, see LICENSE for details. @@ -27,6 +27,7 @@ Imap::Imap(const std::string& p_User, const std::string& p_Pass, const std::stri const uint16_t p_Port, const int64_t p_Timeout, const bool p_CacheEncrypt, const bool p_CacheIndexEncrypt, const std::set& p_FoldersExclude, + const bool p_SniEnabled, const std::function& p_StatusHandler) : m_User(p_User) , m_Pass(p_Pass) @@ -36,6 +37,7 @@ Imap::Imap(const std::string& p_User, const std::string& p_Pass, const std::stri , m_CacheEncrypt(p_CacheEncrypt) , m_CacheIndexEncrypt(p_CacheIndexEncrypt) , m_FoldersExclude(p_FoldersExclude) + , m_SniEnabled(p_SniEnabled) { if (Log::GetTraceEnabled()) { @@ -90,6 +92,15 @@ void Imap::CleanupImap() } } +static void SslContextCallback(struct mailstream_ssl_context* p_SslContext, void* p_Data) +{ + if (p_Data != nullptr) + { + char* host = (char*)p_Data; + LOG_IF_IMAP_ERR(mailstream_ssl_set_server_name(p_SslContext, host)); + } +} + bool Imap::Login() { LOG_DEBUG_FUNC(STR()); @@ -105,7 +116,14 @@ bool Imap::Login() int rv = 0; if (isSSL) { - rv = LOG_IF_IMAP_ERR(mailimap_ssl_connect(m_Imap, m_Host.c_str(), m_Port)); + char* sni = (m_SniEnabled && !Util::IsIpAddress(m_Host)) + ? strdup(m_Host.c_str()) + : nullptr; + rv = LOG_IF_IMAP_ERR(mailimap_ssl_connect_with_callback(m_Imap, m_Host.c_str(), m_Port, SslContextCallback, sni)); + if (sni != nullptr) + { + free(sni); + } } else if (isStartTLS) { diff --git a/src/imap.h b/src/imap.h index d8d2aa8..ce20e82 100644 --- a/src/imap.h +++ b/src/imap.h @@ -1,6 +1,6 @@ // imap.h // -// Copyright (c) 2019-2023 Kristofer Berggren +// Copyright (c) 2019-2024 Kristofer Berggren // All rights reserved. // // nmail is distributed under the MIT license, see LICENSE for details. @@ -48,6 +48,7 @@ class Imap const uint16_t p_Port, const int64_t p_Timeout, const bool p_CacheEncrypt, const bool p_CacheIndexEncrypt, const std::set& p_FoldersExclude, + const bool p_SniEnabled, const std::function& p_StatusHandler); virtual ~Imap(); @@ -109,6 +110,7 @@ class Imap bool m_CacheEncrypt = false; bool m_CacheIndexEncrypt = false; std::set m_FoldersExclude; + bool m_SniEnabled = false; std::mutex m_ImapMutex; struct mailimap* m_Imap = NULL; diff --git a/src/imapmanager.cpp b/src/imapmanager.cpp index a94ed01..71599cf 100644 --- a/src/imapmanager.cpp +++ b/src/imapmanager.cpp @@ -20,6 +20,7 @@ ImapManager::ImapManager(const std::string& p_User, const std::string& p_Pass, const bool p_CacheIndexEncrypt, const uint32_t p_IdleTimeout, const std::set& p_FoldersExclude, + const bool p_SniEnabled, const std::function& p_ResponseHandler, const std::function& p_FoldersExclude, + const bool p_SniEnabled, const std::function& p_ResponseHandler, const std::function& p_ResultHandler, const std::function& p_StatusHandler, diff --git a/src/main.cpp b/src/main.cpp index 52363b2..cf38f13 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -202,6 +202,7 @@ int main(int argc, char* argv[]) { "file_picker_cmd", "" }, { "downloads_dir", "" }, { "idle_timeout", "29" }, + { "sni_enabled", "1" }, }; const std::string mainConfigPath(Util::GetApplicationDir() + std::string("main.conf")); std::shared_ptr mainConfig = std::make_shared(mainConfigPath, defaultMainConfig); @@ -283,6 +284,7 @@ int main(int argc, char* argv[]) Util::SetFilePickerCmd(mainConfig->Get("file_picker_cmd")); Util::SetDownloadsDir(mainConfig->Get("downloads_dir")); const bool isCoredumpEnabled = (mainConfig->Get("coredump_enabled") == "1"); + const bool sniEnabled = (mainConfig->Get("sni_enabled") == "1"); // Set logging verbosity level based on config, if not specified with command line arguments if (Log::GetVerboseLevel() == Log::INFO_LEVEL) @@ -394,6 +396,7 @@ int main(int argc, char* argv[]) cacheEncrypt, cacheIndexEncrypt, idleTimeout, foldersExclude, + sniEnabled, std::bind(&Ui::ResponseHandler, std::ref(ui), std::placeholders::_1, std::placeholders::_2), std::bind(&Ui::ResultHandler, std::ref(ui), std::placeholders::_1, diff --git a/src/nmail.1 b/src/nmail.1 index 7992a9d..c1aa79e 100644 --- a/src/nmail.1 +++ b/src/nmail.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man. -.TH NMAIL "1" "June 2024" "nmail v4.66" "User Commands" +.TH NMAIL "1" "June 2024" "nmail v4.67" "User Commands" .SH NAME nmail \- ncurses mail .SH SYNOPSIS diff --git a/src/util.cpp b/src/util.cpp index a4a0727..135b51e 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -22,11 +22,14 @@ #include #endif #include +#include #include #include #include #include +#include +#include #ifdef __APPLE__ #include @@ -2271,3 +2274,19 @@ std::string Util::ExtractString(const std::string& p_Str, const std::string& p_P return ""; } + +bool Util::IsIpAddress(const std::string& p_Str) +{ + struct addrinfo addrhints; + memset(&addrhints, 0, sizeof(struct addrinfo)); + addrhints.ai_family = AF_UNSPEC; + addrhints.ai_flags = AI_NUMERICHOST; + struct addrinfo* addr = nullptr; + bool rv = (getaddrinfo(p_Str.c_str(), NULL, &addrhints, &addr) == 0); + if (rv && (addr != nullptr)) + { + freeaddrinfo(addr); + } + + return rv; +} diff --git a/src/util.h b/src/util.h index 7aae102..0a777f8 100644 --- a/src/util.h +++ b/src/util.h @@ -293,6 +293,7 @@ class Util static std::string GetCompiler(); static std::string GetOsArch(); static std::string ExtractString(const std::string& p_Str, const std::string& p_Prefix, const std::string& p_Suffix); + static bool IsIpAddress(const std::string& p_Str); template static inline void Unused(const T& p_Arg) diff --git a/src/version.cpp b/src/version.cpp index 9931967..9933272 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -7,7 +7,7 @@ #include "version.h" -#define NMAIL_VERSION "4.66" +#define NMAIL_VERSION "4.67" std::string Version::GetBuildOs() {