[balsa] Initial commit of libclient
- From: Peter Bloomfield <peterb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [balsa] Initial commit of libclient
- Date: Wed, 8 Feb 2017 03:26:28 +0000 (UTC)
commit c69dd2a5f8463de0597972a398e307ad2b0ac781
Author: Albrecht Dreß <albrecht dress arcor de>
Date: Tue Feb 7 22:17:50 2017 -0500
Initial commit of libclient
libnetclient/Makefile.am | 19 +
libnetclient/README | 103 ++
libnetclient/libnetclient.dox | 265 +++++
libnetclient/net-client-smtp.c | 668 +++++++++++
libnetclient/net-client-smtp.h | 260 +++++
libnetclient/net-client-utils.c | 87 ++
libnetclient/net-client-utils.h | 75 ++
libnetclient/net-client.c | 518 +++++++++
libnetclient/net-client.h | 305 +++++
libnetclient/test/Makefile.am | 31 +
libnetclient/test/ca_cert.pem | 46 +
libnetclient/test/cert.pem | 53 +
libnetclient/test/cert_u.pem | 51 +
libnetclient/test/inetsim.conf | 1932 ++++++++++++++++++++++++++++++++
libnetclient/test/start-test-env.sh | 20 +
libnetclient/test/start-test-env.sh.in | 20 +
libnetclient/test/tests.c | 470 ++++++++
libnetclient/test/valgrind.supp | 17 +
18 files changed, 4940 insertions(+), 0 deletions(-)
---
diff --git a/libnetclient/Makefile.am b/libnetclient/Makefile.am
new file mode 100644
index 0000000..3562e41
--- /dev/null
+++ b/libnetclient/Makefile.am
@@ -0,0 +1,19 @@
+# $Id$
+
+noinst_LIBRARIES = libnetclient.a
+libnetclient_a_SOURCES = \
+ net-client.c \
+ net-client.h \
+ net-client-smtp.c \
+ net-client-smtp.h \
+ net-client-utils.c \
+ net-client-utils.h
+CLEANFILES = doxygen.log
+SUBDIRS = test
+AM_CFLAGS = $(LIBNETCLIENT_CFLAGS)
+
+clean-local:
+ -rm -rf html
+
+doc:
+ @DOXYGEN@ libnetclient.dox
\ No newline at end of file
diff --git a/libnetclient/README b/libnetclient/README
new file mode 100644
index 0000000..f5d4d23
--- /dev/null
+++ b/libnetclient/README
@@ -0,0 +1,103 @@
+Written by Albrecht Dreß <albrecht dress arcor de>
+Copyright (C) Albrecht Dreß 2017
+
+This library is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published bythe Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this library. If not, see <http://www.gnu.org/licenses/>.
+
+
+Purpose
+=======
+
+This library provides an implementation of CRLF-terminated line-based client
+protocols built on top of GIO. It provides a base module, containing the
+line-based IO methods, and on top of that a SMTP (RFC5321) client class.
+
+
+Coding Style
+============
+
+The implementation tries to follow the MISRA C:2012 (MISRA C3) and SEI CERT
+Secure Coding standards for safer code. For further details about these
+standards, see
+<https://www.misra.org.uk/Publications/tabid/57/Default.aspx> and
+<https://www.securecoding.cert.org/confluence/display/c/SEI+CERT+C+Coding+Standard>.
+
+
+Requirements
+============
+
+Apart from GLib/GIO (at least version 2.40.0) it requires GnuTLS.
+
+
+API Documentation
+=================
+
+Doxygen is required to create a HTML API documentation. Install doxygen from
+<http://www.stack.nl/~dimitri/doxygen/download.html>. Then run in this folder
+
+ make doc
+
+and open html/index.html.
+
+
+Testing
+=======
+
+In the folder test, a test application for running unit tests, a check for
+memory leaks and a coverage analysis is available. The following packages
+are required for running them:
+
+- the Sput Unit Testing Framework for C/C++, available from
+ <http://www.use-strict.de/sput-unit-testing/>
+- ncat, available from <https://nmap.org/ncat/>
+- screen, available from <https://www.gnu.org/software/screen/>
+- sudo, available from <https://www.sudo.ws/>
+- Valgrind, avilable from <http://valgrind.org/>
+- LCOV and Genhtml, available from
+ <http://ltp.sourceforge.net/coverage/lcov.php>
+- gnutls-serv, which is a part of the GnuTLS package, available from
+ <https://www.gnutls.org/>
+- INetSim Internet Services Simulation Suite, available from
+ <http://www.inetsim.org/>
+
+Note that most of these requirements are typically available pre-packaged
+for your favorite distribution.
+
+For running the tests, open two terminal windows, and cd to the test folder
+of this package.
+
+In the first terminal, call
+
+ ./start-test-env.sh
+
+which will launch several dummy servers required for testing. Note that
+INetSim requires root permissions and is thus called via sudo.
+
+Then, in the second terminal, call
+
+ make tests
+
+to build the test application, and run it in Valgrind. Note that the test
+application will report many CRITICAL glib errors. This is normal, as the
+behaviour of the library's parameter checks is verified.
+
+The test produces the following output:
+- stdout/stderr: unit test results, which shall report zero failed
+- tests.vg: the output of the Valgrind memory check
+- gcov/index.html: results of the coverage analysis
+
+Finally, just terminate the test servers in the first window. Note that
+INetSim will dump further information in its output files (typically in
+/var/log/inetsim). You should verify that INetSim recorded the requested
+operations properly.
+
+ -oOo-
diff --git a/libnetclient/libnetclient.dox b/libnetclient/libnetclient.dox
new file mode 100644
index 0000000..699e013
--- /dev/null
+++ b/libnetclient/libnetclient.dox
@@ -0,0 +1,265 @@
+DOXYFILE_ENCODING = UTF-8
+PROJECT_NAME = "libnetclient"
+PROJECT_NUMBER = "0.1.0"
+PROJECT_BRIEF =
+PROJECT_LOGO =
+OUTPUT_DIRECTORY = .
+CREATE_SUBDIRS = NO
+ALLOW_UNICODE_NAMES = NO
+OUTPUT_LANGUAGE = English
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ABBREVIATE_BRIEF = "The $name class" "The $name widget" "The $name file" is provides specifies contains
represents a an the
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = NO
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH =
+STRIP_FROM_INC_PATH =
+SHORT_NAMES = NO
+JAVADOC_AUTOBRIEF = NO
+QT_AUTOBRIEF = NO
+MULTILINE_CPP_IS_BRIEF = NO
+INHERIT_DOCS = YES
+SEPARATE_MEMBER_PAGES = NO
+TAB_SIZE = 4
+TCL_SUBST =
+OPTIMIZE_OUTPUT_FOR_C = YES
+OPTIMIZE_OUTPUT_JAVA = NO
+OPTIMIZE_FOR_FORTRAN = NO
+OPTIMIZE_OUTPUT_VHDL = NO
+EXTENSION_MAPPING =
+MARKDOWN_SUPPORT = YES
+AUTOLINK_SUPPORT = YES
+BUILTIN_STL_SUPPORT = NO
+CPP_CLI_SUPPORT = NO
+SIP_SUPPORT = NO
+IDL_PROPERTY_SUPPORT = YES
+DISTRIBUTE_GROUP_DOC = NO
+GROUP_NESTED_COMPOUNDS = NO
+SUBGROUPING = YES
+INLINE_GROUPED_CLASSES = NO
+INLINE_SIMPLE_STRUCTS = NO
+TYPEDEF_HIDES_STRUCT = NO
+LOOKUP_CACHE_SIZE = 0
+EXTRACT_ALL = YES
+EXTRACT_PRIVATE = YES
+EXTRACT_PACKAGE = NO
+EXTRACT_STATIC = YES
+EXTRACT_LOCAL_CLASSES = YES
+EXTRACT_LOCAL_METHODS = NO
+EXTRACT_ANON_NSPACES = NO
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+HIDE_IN_BODY_DOCS = NO
+INTERNAL_DOCS = NO
+CASE_SENSE_NAMES = NO
+HIDE_SCOPE_NAMES = NO
+HIDE_COMPOUND_REFERENCE = NO
+SHOW_INCLUDE_FILES = YES
+SHOW_GROUPED_MEMB_INC = NO
+FORCE_LOCAL_INCLUDES = NO
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+SORT_BRIEF_DOCS = NO
+SORT_MEMBERS_CTORS_1ST = NO
+SORT_GROUP_NAMES = NO
+SORT_BY_SCOPE_NAME = NO
+STRICT_PROTO_MATCHING = NO
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST = YES
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+SHOW_USED_FILES = YES
+SHOW_FILES = YES
+SHOW_NAMESPACES = YES
+FILE_VERSION_FILTER =
+LAYOUT_FILE =
+CITE_BIB_FILES =
+QUIET = NO
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_NO_PARAMDOC = NO
+WARN_AS_ERROR = NO
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE = doxygen.log
+INPUT = ./
+INPUT_ENCODING = UTF-8
+FILE_PATTERNS = *.h
+RECURSIVE = NO
+EXCLUDE =
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS =
+EXCLUDE_SYMBOLS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS = *
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_PATTERNS =
+FILTER_SOURCE_FILES = NO
+FILTER_SOURCE_PATTERNS =
+USE_MDFILE_AS_MAINPAGE =
+SOURCE_BROWSER = NO
+INLINE_SOURCES = NO
+STRIP_CODE_COMMENTS = YES
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION = NO
+REFERENCES_LINK_SOURCE = YES
+SOURCE_TOOLTIPS = YES
+USE_HTAGS = NO
+VERBATIM_HEADERS = YES
+CLANG_ASSISTED_PARSING = NO
+CLANG_OPTIONS =
+ALPHABETICAL_INDEX = YES
+COLS_IN_ALPHA_INDEX = 5
+IGNORE_PREFIX =
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_EXTRA_STYLESHEET =
+HTML_EXTRA_FILES =
+HTML_COLORSTYLE_HUE = 220
+HTML_COLORSTYLE_SAT = 100
+HTML_COLORSTYLE_GAMMA = 80
+HTML_TIMESTAMP = NO
+HTML_DYNAMIC_SECTIONS = NO
+HTML_INDEX_NUM_ENTRIES = 100
+GENERATE_DOCSET = NO
+DOCSET_FEEDNAME = "Doxygen generated docs"
+DOCSET_BUNDLE_ID = org.doxygen.Project
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+DOCSET_PUBLISHER_NAME = Publisher
+GENERATE_HTMLHELP = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+CHM_INDEX_ENCODING =
+BINARY_TOC = NO
+TOC_EXPAND = NO
+GENERATE_QHP = NO
+QCH_FILE =
+QHP_NAMESPACE = org.doxygen.Project
+QHP_VIRTUAL_FOLDER = doc
+QHP_CUST_FILTER_NAME =
+QHP_CUST_FILTER_ATTRS =
+QHP_SECT_FILTER_ATTRS =
+QHG_LOCATION =
+GENERATE_ECLIPSEHELP = NO
+ECLIPSE_DOC_ID = org.doxygen.Project
+DISABLE_INDEX = NO
+GENERATE_TREEVIEW = YES
+ENUM_VALUES_PER_LINE = 4
+TREEVIEW_WIDTH = 250
+EXT_LINKS_IN_WINDOW = NO
+FORMULA_FONTSIZE = 10
+FORMULA_TRANSPARENT = YES
+USE_MATHJAX = NO
+MATHJAX_FORMAT = HTML-CSS
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+MATHJAX_EXTENSIONS =
+MATHJAX_CODEFILE =
+SEARCHENGINE = YES
+SERVER_BASED_SEARCH = NO
+EXTERNAL_SEARCH = NO
+SEARCHENGINE_URL =
+SEARCHDATA_FILE = searchdata.xml
+EXTERNAL_SEARCH_ID =
+EXTRA_SEARCH_MAPPINGS =
+GENERATE_LATEX = NO
+LATEX_OUTPUT = latex
+LATEX_CMD_NAME = latex
+MAKEINDEX_CMD_NAME = makeindex
+COMPACT_LATEX = NO
+PAPER_TYPE = a4
+EXTRA_PACKAGES =
+LATEX_HEADER =
+LATEX_FOOTER =
+LATEX_EXTRA_STYLESHEET =
+LATEX_EXTRA_FILES =
+PDF_HYPERLINKS = YES
+USE_PDFLATEX = YES
+LATEX_BATCHMODE = NO
+LATEX_HIDE_INDICES = NO
+LATEX_SOURCE_CODE = NO
+LATEX_BIB_STYLE = plain
+LATEX_TIMESTAMP = NO
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+RTF_SOURCE_CODE = NO
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_SUBDIR =
+MAN_LINKS = NO
+GENERATE_XML = NO
+XML_OUTPUT = xml
+XML_PROGRAMLISTING = YES
+GENERATE_DOCBOOK = NO
+DOCBOOK_OUTPUT = docbook
+DOCBOOK_PROGRAMLISTING = NO
+GENERATE_AUTOGEN_DEF = NO
+GENERATE_PERLMOD = NO
+PERLMOD_LATEX = NO
+PERLMOD_PRETTY = YES
+PERLMOD_MAKEVAR_PREFIX =
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = YES
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED = G_GNUC_MALLOC= G_GNUC_PRINTF(x,y)=
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = NO
+TAGFILES =
+GENERATE_TAGFILE =
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+EXTERNAL_PAGES = YES
+PERL_PATH = /usr/bin/perl
+CLASS_DIAGRAMS = NO
+MSCGEN_PATH =
+DIA_PATH =
+HIDE_UNDOC_RELATIONS = NO
+HAVE_DOT = YES
+DOT_NUM_THREADS = 0
+DOT_FONTNAME = Helvetica
+DOT_FONTSIZE = 10
+DOT_FONTPATH =
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+GROUP_GRAPHS = YES
+UML_LOOK = YES
+UML_LIMIT_NUM_FIELDS = 10
+TEMPLATE_RELATIONS = NO
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+CALL_GRAPH = YES
+CALLER_GRAPH = YES
+GRAPHICAL_HIERARCHY = YES
+DIRECTORY_GRAPH = YES
+DOT_IMAGE_FORMAT = png
+INTERACTIVE_SVG = NO
+DOT_PATH =
+DOTFILE_DIRS =
+MSCFILE_DIRS =
+DIAFILE_DIRS =
+PLANTUML_JAR_PATH =
+PLANTUML_INCLUDE_PATH =
+DOT_GRAPH_MAX_NODES = 50
+MAX_DOT_GRAPH_DEPTH = 0
+DOT_TRANSPARENT = NO
+DOT_MULTI_TARGETS = NO
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
diff --git a/libnetclient/net-client-smtp.c b/libnetclient/net-client-smtp.c
new file mode 100644
index 0000000..74c7858
--- /dev/null
+++ b/libnetclient/net-client-smtp.c
@@ -0,0 +1,668 @@
+/* NetClient - simple line-based network client library
+ *
+ * Copyright (C) Albrecht Dreß <mailto:albrecht dress arcor de> 2017
+ *
+ * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License
+ * as published by the Free Software Foundation; either version 3 of the License, or (at your option) any
later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with this library. If not,
see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <glib/gi18n.h>
+#include "net-client-utils.h"
+#include "net-client-smtp.h"
+
+
+struct _NetClientSmtpPrivate {
+ NetClientCryptMode crypt_mode;
+ guint auth_allowed[2]; /** 0: encrypted, 1: unencrypted */
+ guint auth_supported;
+ gboolean can_dsn;
+ gboolean can_starttls;
+};
+
+
+struct _NetClientSmtpMessage {
+ gchar *sender;
+ GList *recipients;
+ gchar *dsn_envid;
+ gboolean dsn_ret_full;
+ gboolean have_dsn_rcpt;
+ NetClientSmtpSendCb data_callback;
+ gpointer user_data;
+};
+
+
+typedef struct {
+ gchar *rfc5321_addr;
+ NetClientSmtpDsnMode dsn_mode;
+} smtp_rcpt_t;
+
+
+#define MAX_SMTP_LINE_LEN 512U
+#define SMTP_DATA_BUF_SIZE 8192U
+
+
+G_DEFINE_TYPE(NetClientSmtp, net_client_smtp, NET_CLIENT_TYPE)
+
+
+static void net_client_smtp_finalise(GObject *object);
+static gboolean net_client_smtp_ehlo(NetClientSmtp *client, GError **error);
+static gboolean net_client_smtp_starttls(NetClientSmtp *client, GError **error);
+static gboolean net_client_smtp_execute(NetClientSmtp *client, const gchar *request_fmt, gchar **last_reply,
GError **error, ...)
+ G_GNUC_PRINTF(2, 5);
+static gboolean net_client_smtp_auth(NetClientSmtp *client, const gchar *user, const gchar *passwd, GError
**error);
+static gboolean net_client_smtp_auth_plain(NetClientSmtp *client, const gchar* user, const gchar* passwd,
GError** error);
+static gboolean net_client_smtp_auth_login(NetClientSmtp *client, const gchar* user, const gchar* passwd,
GError** error);
+static gboolean net_client_smtp_auth_cram(NetClientSmtp *client, GChecksumType chksum_type, const gchar
*user, const gchar *passwd,
+ GError **error);
+static gboolean net_client_smtp_read_reply(NetClientSmtp *client, gint expect_code, gchar **last_reply,
GError **error);
+static gboolean net_client_smtp_eval_rescode(gint res_code, const gchar *reply, GError **error);
+static gchar *net_client_smtp_dsn_to_string(const NetClientSmtp *client, NetClientSmtpDsnMode dsn_mode);
+static void smtp_rcpt_free(smtp_rcpt_t *rcpt);
+
+
+NetClientSmtp *
+net_client_smtp_new(const gchar *host, guint16 port, NetClientCryptMode crypt_mode)
+{
+ NetClientSmtp *client;
+
+ g_return_val_if_fail(host != NULL, NULL);
+
+ client = NET_CLIENT_SMTP(g_object_new(NET_CLIENT_SMTP_TYPE, NULL));
+ if (client != NULL) {
+ if (!net_client_configure(NET_CLIENT(client), host, port, MAX_SMTP_LINE_LEN, NULL)) {
+ g_object_unref(G_OBJECT(client));
+ client = NULL;
+ } else {
+ client->priv->crypt_mode = crypt_mode;
+ }
+ }
+
+ return client;
+}
+
+
+gboolean
+net_client_smtp_allow_auth(NetClientSmtp *client, gboolean encrypted, guint allow_auth)
+{
+ /* paranoia check */
+ g_return_val_if_fail(NET_IS_CLIENT_SMTP(client), FALSE);
+ if (encrypted) {
+ client->priv->auth_allowed[0] = allow_auth;
+ } else {
+ client->priv->auth_allowed[1] = allow_auth;
+ }
+ return TRUE;
+}
+
+
+gboolean
+net_client_smtp_connect(NetClientSmtp *client, gchar **greeting, GError **error)
+{
+ gboolean result;
+
+ /* paranoia checks */
+ g_return_val_if_fail(NET_IS_CLIENT_SMTP(client), FALSE);
+
+ /* establish connection, and immediately switch to TLS if required */
+ result = net_client_connect(NET_CLIENT(client), error);
+ if (result && (client->priv->crypt_mode == NET_CLIENT_CRYPT_ENCRYPTED)) {
+ result = net_client_start_tls(NET_CLIENT(client), error);
+ }
+
+ /* get the greeting */
+ if (result) {
+ result = net_client_smtp_read_reply(client, 220, greeting, error);
+ }
+
+ /* send EHLO and read the capabilities of the server */
+ if (result) {
+ result = net_client_smtp_ehlo(client, error);
+ }
+
+ /* perform STARTTLS if required, and send EHLO again */
+ if (result &&
+ ((client->priv->crypt_mode == NET_CLIENT_CRYPT_STARTTLS) || (client->priv->crypt_mode ==
NET_CLIENT_CRYPT_STARTTLS_OPT))) {
+ if (!client->priv->can_starttls) {
+ if (client->priv->crypt_mode == NET_CLIENT_CRYPT_STARTTLS) {
+ g_set_error(error, NET_CLIENT_SMTP_ERROR_QUARK, (gint)
NET_CLIENT_ERROR_SMTP_NO_STARTTLS,
+ _("remote server does not support STARTTLS"));
+ result = FALSE;
+ }
+ } else {
+ result = net_client_smtp_starttls(client, error);
+ if (result) {
+ result = net_client_smtp_ehlo(client, error);
+ }
+ }
+ }
+
+ /* authenticate if we were successful so far */
+ if (result) {
+ gchar **auth_data;
+
+ auth_data = NULL;
+ g_debug("emit 'auth' signal for client %p", client);
+ g_signal_emit_by_name(client, "auth", &auth_data);
+ if ((auth_data != NULL) && (auth_data[0] != NULL) && (auth_data[1] != NULL)) {
+ result = net_client_smtp_auth(client, auth_data[0], auth_data[1], error);
+ memset(auth_data[0], 0, strlen(auth_data[0]));
+ memset(auth_data[1], 0, strlen(auth_data[1]));
+ }
+ g_strfreev(auth_data);
+ }
+
+ return result;
+}
+
+
+gboolean
+net_client_smtp_can_dsn(NetClientSmtp *client)
+{
+ return NET_IS_CLIENT_SMTP(client) ? client->priv->can_dsn : FALSE;
+}
+
+
+gboolean
+net_client_smtp_send_msg(NetClientSmtp *client, const NetClientSmtpMessage *message, GError **error)
+{
+ gboolean result;
+ const GList *rcpt;
+
+ /* paranoia checks */
+ g_return_val_if_fail(NET_IS_CLIENT_SMTP(client) && (message != NULL) && (message->sender != NULL) &&
+ (message->recipients != NULL) && (message->data_callback != NULL), FALSE);
+
+ /* set the RFC 5321 sender and recipient(s) */
+ if (message->have_dsn_rcpt) {
+ if (message->dsn_envid != NULL) {
+ result = net_client_smtp_execute(client, "MAIL FROM:<%s> RET=%s ENVID=%s", NULL,
error, message->sender,
+
(message->dsn_ret_full) ? "FULL" : "HDRS", message->dsn_envid);
+ } else {
+ result = net_client_smtp_execute(client, "MAIL FROM:<%s> RET=%s", NULL, error,
message->sender,
+
(message->dsn_ret_full) ? "FULL" : "HDRS");
+ }
+ } else {
+ result = net_client_smtp_execute(client, "MAIL FROM:<%s>", NULL, error, message->sender);
+ }
+ rcpt = message->recipients;
+ while (result && (rcpt != NULL)) {
+ const smtp_rcpt_t *this_rcpt = (const smtp_rcpt_t *) rcpt->data; /*lint !e9079 !e9087
(MISRA C:2012 Rules 11.3, 11.5) */
+ gchar *dsn_opts;
+
+ /* create the RFC 3461 DSN string */
+ dsn_opts = net_client_smtp_dsn_to_string(client, this_rcpt->dsn_mode);
+ result = net_client_smtp_execute(client, "RCPT TO:<%s>%s", NULL, error,
this_rcpt->rfc5321_addr, dsn_opts);
+ g_free(dsn_opts);
+ rcpt = rcpt->next;
+ }
+
+ /* initialise sending the message data */
+ if (result) {
+ result = net_client_smtp_execute(client, "DATA", NULL, error);
+ }
+
+ /* call the data callback until all data has been transmitted or an error occurs */
+ if (result) {
+ gchar buffer[SMTP_DATA_BUF_SIZE];
+ gssize count;
+ gchar last_char = '\0';
+
+ do {
+ count = message->data_callback(buffer, SMTP_DATA_BUF_SIZE, message->user_data, error);
+ if (count < 0) {
+ result = FALSE;
+ } else if (count > 0) {
+ result = net_client_write_buffer(NET_CLIENT(client), buffer, (gsize) count,
error);
+ last_char = buffer[count - 1];
+ } else {
+ /* write termination */
+ if (last_char == '\n') {
+ result = net_client_write_buffer(NET_CLIENT(client), ".\r\n", 3U,
error);
+ } else {
+ result = net_client_write_buffer(NET_CLIENT(client), "\r\n.\r\n", 5U,
error);
+ }
+ }
+ } while (result && (count > 0));
+ }
+
+ if (result) {
+ result = net_client_smtp_read_reply(client, -1, NULL, error);
+ }
+
+ return result;
+}
+
+
+NetClientSmtpMessage *
+net_client_smtp_msg_new(NetClientSmtpSendCb data_callback, gpointer user_data)
+{
+ NetClientSmtpMessage *smtp_msg;
+
+ g_return_val_if_fail(data_callback != NULL, NULL);
+
+ smtp_msg = g_new0(NetClientSmtpMessage, 1U);
+ smtp_msg->data_callback = data_callback;
+ smtp_msg->user_data = user_data;
+ return smtp_msg;
+}
+
+
+gboolean
+net_client_smtp_msg_set_dsn_opts(NetClientSmtpMessage *smtp_msg, const gchar *envid, gboolean ret_full)
+{
+ g_return_val_if_fail(smtp_msg != NULL, FALSE);
+
+ g_free(smtp_msg->dsn_envid);
+ if (envid != NULL) {
+ smtp_msg->dsn_envid = g_strdup(envid);
+ } else {
+ smtp_msg->dsn_envid = NULL;
+ }
+ smtp_msg->dsn_ret_full = ret_full;
+ return TRUE;
+}
+
+
+gboolean
+net_client_smtp_msg_set_sender(NetClientSmtpMessage *smtp_msg, const gchar *rfc5321_sender)
+{
+ g_return_val_if_fail((smtp_msg != NULL) && (rfc5321_sender != NULL), FALSE);
+
+ g_free(smtp_msg->sender);
+ smtp_msg->sender = g_strdup(rfc5321_sender);
+ return TRUE;
+}
+
+
+gboolean
+net_client_smtp_msg_add_recipient(NetClientSmtpMessage *smtp_msg, const gchar *rfc5321_rcpt,
NetClientSmtpDsnMode dsn_mode)
+{
+ smtp_rcpt_t *new_rcpt;
+
+ g_return_val_if_fail((smtp_msg != NULL) && (rfc5321_rcpt != NULL), FALSE);
+ new_rcpt = g_new0(smtp_rcpt_t, 1U);
+ new_rcpt->rfc5321_addr = g_strdup(rfc5321_rcpt);
+ new_rcpt->dsn_mode = dsn_mode;
+ smtp_msg->recipients = g_list_append(smtp_msg->recipients, new_rcpt);
+ if (dsn_mode != NET_CLIENT_SMTP_DSN_NEVER) {
+ smtp_msg->have_dsn_rcpt = TRUE;
+ }
+ return TRUE;
+}
+
+
+void
+net_client_smtp_msg_free(NetClientSmtpMessage *smtp_msg)
+{
+ if (smtp_msg != NULL) {
+ g_free(smtp_msg->sender);
+ g_free(smtp_msg->dsn_envid);
+ /*lint -e{9074} -e{9087} accept safe (and required) pointer conversion */
+ g_list_free_full(smtp_msg->recipients, (GDestroyNotify) smtp_rcpt_free);
+ g_free(smtp_msg);
+ }
+}
+
+
+/* == local functions
=========================================================================================================== */
+
+static void
+net_client_smtp_class_init(NetClientSmtpClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+
+ gobject_class->finalize = net_client_smtp_finalise;
+}
+
+
+static void
+net_client_smtp_init(NetClientSmtp *self)
+{
+ self->priv = g_new0(NetClientSmtpPrivate, 1U);
+ self->priv->auth_allowed[0] = NET_CLIENT_SMTP_AUTH_ALL;
+ self->priv->auth_allowed[1] = NET_CLIENT_SMTP_AUTH_SAFE;
+}
+
+
+static void
+net_client_smtp_finalise(GObject *object)
+{
+ const NetClientSmtp *client = NET_CLIENT_SMTP(object);
+ const GObjectClass *parent_class = G_OBJECT_CLASS(net_client_smtp_parent_class);
+
+ /* send the 'QUIT' command - no need to evaluate the reply or check for errors */
+ (void) net_client_execute(NET_CLIENT(client), NULL, "QUIT", NULL);
+
+ g_free(client->priv);
+ (*parent_class->finalize)(object);
+}
+
+
+static gboolean
+net_client_smtp_starttls(NetClientSmtp *client, GError **error)
+{
+ gboolean result;
+
+ g_return_val_if_fail(NET_IS_CLIENT_SMTP(client), FALSE);
+
+ result = net_client_smtp_execute(client, "STARTTLS", NULL, error);
+ if (result) {
+ result = net_client_start_tls(NET_CLIENT(client), error);
+ }
+
+ return result;
+}
+
+
+static gboolean
+net_client_smtp_auth(NetClientSmtp *client, const gchar *user, const gchar *passwd, GError **error)
+{
+ gboolean result;
+ guint auth_mask;
+
+ g_return_val_if_fail(NET_IS_CLIENT_SMTP(client), FALSE);
+
+ if (net_client_is_encrypted(NET_CLIENT(client))) {
+ auth_mask = client->priv->auth_allowed[0] & client->priv->auth_supported;
+ } else {
+ auth_mask = client->priv->auth_allowed[1] & client->priv->auth_supported;
+ }
+
+ if ((auth_mask & NET_CLIENT_SMTP_AUTH_CRAM_SHA1) != 0U) {
+ result = net_client_smtp_auth_cram(client, G_CHECKSUM_SHA1, user, passwd, error);
+ } else if ((auth_mask & NET_CLIENT_SMTP_AUTH_CRAM_MD5) != 0U) {
+ result = net_client_smtp_auth_cram(client, G_CHECKSUM_MD5, user, passwd, error);
+ } else if ((auth_mask & NET_CLIENT_SMTP_AUTH_PLAIN) != 0U) {
+ result = net_client_smtp_auth_plain(client, user, passwd, error);
+ } else if ((auth_mask & NET_CLIENT_SMTP_AUTH_LOGIN) != 0U) {
+ result = net_client_smtp_auth_login(client, user, passwd, error);
+ } else {
+ g_set_error(error, NET_CLIENT_SMTP_ERROR_QUARK, (gint) NET_CLIENT_ERROR_SMTP_NO_AUTH,
+ _("no suitable authentication mechanism"));
+ result = FALSE;
+ }
+
+ return result;
+}
+
+
+static gboolean
+net_client_smtp_auth_plain(NetClientSmtp *client, const gchar *user, const gchar *passwd, GError **error)
+{
+ gboolean result ;
+ gchar *base64_buf;
+
+ base64_buf = net_client_auth_plain_calc(user, passwd);
+ if (base64_buf != NULL) {
+ result = net_client_smtp_execute(client, "AUTH PLAIN %s", NULL, error, base64_buf);
+ memset(base64_buf, 0, strlen(base64_buf));
+ g_free(base64_buf);
+ } else {
+ result = FALSE;
+ }
+
+ return result;
+}
+
+
+static gboolean
+net_client_smtp_auth_login(NetClientSmtp *client, const gchar *user, const gchar *passwd, GError **error)
+{
+ gboolean result;
+ gchar *base64_buf;
+
+ g_return_val_if_fail((user != NULL) && (passwd != NULL), FALSE);
+
+ base64_buf = g_base64_encode((const guchar *) user, strlen(user));
+ result = net_client_smtp_execute(client, "AUTH LOGIN %s", NULL, error, base64_buf);
+ memset(base64_buf, 0, strlen(base64_buf));
+ g_free(base64_buf);
+ if (result) {
+ base64_buf = g_base64_encode((const guchar *) passwd, strlen(passwd));
+ result = net_client_smtp_execute(client, "%s", NULL, error, base64_buf);
+ memset(base64_buf, 0, strlen(base64_buf));
+ g_free(base64_buf);
+ }
+
+ return result;
+}
+
+
+static gboolean
+net_client_smtp_auth_cram(NetClientSmtp *client, GChecksumType chksum_type, const gchar *user, const gchar
*passwd, GError **error)
+{
+ gboolean result;
+ gchar *challenge = NULL;
+
+ g_return_val_if_fail((user != NULL) && (passwd != NULL), FALSE);
+
+ result = net_client_smtp_execute(client, "AUTH CRAM-%s", &challenge, error,
net_client_chksum_to_str(chksum_type));
+ if (result) {
+ gchar *auth_buf;
+
+ auth_buf = net_client_cram_calc(challenge, chksum_type, user, passwd);
+ if (auth_buf != NULL) {
+ result = net_client_smtp_execute(client, "%s", NULL, error, auth_buf);
+ memset(auth_buf, 0, strlen(auth_buf));
+ g_free(auth_buf);
+ } else {
+ result = FALSE;
+ }
+ }
+ g_free(challenge);
+
+ return result;
+}
+
+
+static gboolean
+net_client_smtp_execute(NetClientSmtp *client, const gchar *request_fmt, gchar **last_reply, GError **error,
...)
+{
+ va_list args;
+ gboolean result;
+
+ va_start(args, error); /*lint !e413 a NULL error argument is irrelevant here */
+ result = net_client_vwrite_line(NET_CLIENT(client), request_fmt, args, error);
+ va_end(args);
+
+ if (result) {
+ result = net_client_smtp_read_reply(client, -1, last_reply, error);
+ }
+
+ return result;
+}
+
+
+static gboolean
+net_client_smtp_ehlo(NetClientSmtp *client, GError **error)
+{
+ gboolean result;
+ gboolean done;
+
+ result = net_client_write_line(NET_CLIENT(client), "EHLO %s", error, g_get_host_name());
+
+ /* clear all capability flags */
+ client->priv->auth_supported = 0U;
+ client->priv->can_dsn = FALSE;
+ client->priv->can_starttls = FALSE;
+
+ /* evaluate the response */
+ done = FALSE;
+ while (result && !done) {
+ gchar *reply;
+
+ result = net_client_read_line(NET_CLIENT(client), &reply, error);
+ if (result) {
+ gint reply_code;
+ gchar *endptr;
+
+ reply_code = strtol(reply, &endptr, 10);
+ if (reply_code != 250) {
+ g_set_error(error, NET_CLIENT_SMTP_ERROR_QUARK, (gint)
NET_CLIENT_ERROR_SMTP_PROTOCOL,
+ _("bad server reply: %s"), reply);
+ result = FALSE;
+ } else {
+ if (strcmp(&endptr[1], "DSN") == 0) {
+ client->priv->can_dsn = TRUE;
+ } else if (strcmp(&endptr[1], "STARTTLS") == 0) {
+ client->priv->can_starttls = TRUE;
+ } else if ((strncmp(&endptr[1], "AUTH ", 5U) == 0) || (strncmp(&endptr[1],
"AUTH=", 5U) == 0)) {
+ gchar **auth;
+ guint n;
+
+ auth = g_strsplit(&endptr[6], " ", -1);
+ for (n = 0U; auth[n] != NULL; n++) {
+ if (strcmp(auth[n], "PLAIN") == 0) {
+ client->priv->auth_supported |=
NET_CLIENT_SMTP_AUTH_PLAIN;
+ } else if (strcmp(auth[n], "LOGIN") == 0) {
+ client->priv->auth_supported |=
NET_CLIENT_SMTP_AUTH_LOGIN;
+ } else if (strcmp(auth[n], "CRAM-MD5") == 0) {
+ client->priv->auth_supported |=
NET_CLIENT_SMTP_AUTH_CRAM_MD5;
+ } else if (strcmp(auth[n], "CRAM-SHA1") == 0) {
+ client->priv->auth_supported |=
NET_CLIENT_SMTP_AUTH_CRAM_SHA1;
+ } else {
+ /* other auth methods are ignored for the time being
*/
+ }
+ }
+ g_strfreev(auth);
+ } else {
+ /* ignored */
+ }
+
+ if (*endptr == ' ') {
+ done = TRUE;
+ }
+ }
+
+ g_free(reply);
+ }
+ }
+
+ return result;
+}
+
+
+/* Note: according to RFC 5321, sect. 4.2, \em any reply may be multiline. */
+static gboolean
+net_client_smtp_read_reply(NetClientSmtp *client, gint expect_code, gchar **last_reply, GError **error)
+{
+ gint rescode;
+ gboolean done;
+ gboolean result;
+
+ done = FALSE;
+ rescode = expect_code;
+ do {
+ gchar *reply;
+
+ result = net_client_read_line(NET_CLIENT(client), &reply, error);
+ if (result) {
+ gint this_rescode;
+ gchar *endptr;
+
+ this_rescode = strtol(reply, &endptr, 10);
+ if (rescode == -1) {
+ rescode = this_rescode;
+ result = net_client_smtp_eval_rescode(rescode, reply, error);
+ } else if (rescode != this_rescode) {
+ g_set_error(error, NET_CLIENT_SMTP_ERROR_QUARK, (gint)
NET_CLIENT_ERROR_SMTP_PROTOCOL,
+ _("bad server reply: %s"), reply);
+ result = FALSE;
+ } else {
+ /* nothing to do (see MISRA C:2012, Rule 15.7) */
+ }
+ if (reply[3] == ' ') {
+ done = TRUE;
+ if (last_reply != NULL) {
+ *last_reply = g_strdup(&reply[4]);
+ }
+ }
+
+ g_free(reply);
+ }
+ } while (result && !done);
+
+ return result;
+}
+
+
+static gboolean
+net_client_smtp_eval_rescode(gint res_code, const gchar *reply, GError **error)
+{
+ gboolean result;
+
+ switch (res_code / 100) {
+ case 2:
+ case 3:
+ result = TRUE;
+ break;
+ case 4:
+ g_set_error(error, NET_CLIENT_SMTP_ERROR_QUARK, (gint) NET_CLIENT_ERROR_SMTP_TRANSIENT,
+ _("transient error %d: %s"), res_code, reply);
+ result = FALSE;
+ break;
+ case 5:
+ g_set_error(error, NET_CLIENT_SMTP_ERROR_QUARK, (gint) NET_CLIENT_ERROR_SMTP_PERMANENT,
+ _("permanent error %d: %s"), res_code, reply);
+ result = FALSE;
+ break;
+ default:
+ g_set_error(error, NET_CLIENT_SMTP_ERROR_QUARK, (gint) NET_CLIENT_ERROR_SMTP_PROTOCOL,
+ _("bad server reply: %s"), reply);
+ result = FALSE;
+ break;
+ }
+
+ return result;
+}
+
+
+static gchar *
+net_client_smtp_dsn_to_string(const NetClientSmtp *client, NetClientSmtpDsnMode dsn_mode)
+{
+ gchar *result;
+
+ /* create the RFC 3461 DSN string */
+ if (client->priv->can_dsn && (dsn_mode != NET_CLIENT_SMTP_DSN_NEVER)) {
+ GString *dsn_buf;
+ gsize start_len;
+
+ dsn_buf = g_string_new(" NOTIFY=");
+ start_len = dsn_buf->len;
+ if ((dsn_mode & NET_CLIENT_SMTP_DSN_DELAY) == NET_CLIENT_SMTP_DSN_DELAY) {
+ dsn_buf = g_string_append(dsn_buf, "DELAY");
+ }
+ if ((dsn_mode & NET_CLIENT_SMTP_DSN_FAILURE) == NET_CLIENT_SMTP_DSN_FAILURE) {
+ if (start_len != dsn_buf->len) {
+ dsn_buf = g_string_append_c(dsn_buf, ',');
+ }
+ dsn_buf = g_string_append(dsn_buf, "FAILURE");
+ }
+ if ((dsn_mode & NET_CLIENT_SMTP_DSN_SUCCESS) == NET_CLIENT_SMTP_DSN_SUCCESS) {
+ if (start_len != dsn_buf->len) {
+ dsn_buf = g_string_append_c(dsn_buf, ',');
+ }
+ dsn_buf = g_string_append(dsn_buf, "SUCCESS");
+ }
+ result = g_string_free(dsn_buf, FALSE);
+ } else {
+ result = g_strdup("");
+ }
+
+ return result;
+}
+
+
+static void
+smtp_rcpt_free(smtp_rcpt_t *rcpt)
+{
+ g_free(rcpt->rfc5321_addr);
+ g_free(rcpt);
+}
diff --git a/libnetclient/net-client-smtp.h b/libnetclient/net-client-smtp.h
new file mode 100644
index 0000000..b098d83
--- /dev/null
+++ b/libnetclient/net-client-smtp.h
@@ -0,0 +1,260 @@
+/* NetClient - simple line-based network client library
+ *
+ * Copyright (C) Albrecht Dreß <mailto:albrecht dress arcor de> 2017
+ *
+ * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License
+ * as published by the Free Software Foundation; either version 3 of the License, or (at your option) any
later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with this library. If not,
see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NET_CLIENT_SMTP_H_
+#define NET_CLIENT_SMTP_H_
+
+
+#include "net-client.h"
+
+
+G_BEGIN_DECLS
+
+
+#define NET_CLIENT_SMTP_TYPE (net_client_smtp_get_type())
+#define NET_CLIENT_SMTP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
NET_CLIENT_SMTP_TYPE, NetClientSmtp))
+#define NET_IS_CLIENT_SMTP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),
NET_CLIENT_SMTP_TYPE))
+#define NET_CLIENT_SMTP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NET_CLIENT_SMTP_TYPE,
NetClientSmtpClass))
+#define NET_IS_CLIENT_SMTP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),
NET_CLIENT_SMTP_TYPE))
+#define NET_CLIENT_SMTP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NET_CLIENT_SMTP_TYPE,
NetClientSmtpClass))
+
+#define NET_CLIENT_SMTP_ERROR_QUARK (g_quark_from_static_string("net-client-smtp"))
+
+
+typedef struct _NetClientSmtp NetClientSmtp;
+typedef struct _NetClientSmtpClass NetClientSmtpClass;
+typedef struct _NetClientSmtpPrivate NetClientSmtpPrivate;
+typedef struct _NetClientSmtpMessage NetClientSmtpMessage;
+typedef enum _NetClientSmtpDsnMode NetClientSmtpDsnMode;
+
+
+/** @brief SMTP-specific error codes */
+enum _NetClientSmtpError {
+ NET_CLIENT_ERROR_SMTP_PROTOCOL = 1, /**< A bad server reply has been received. */
+ NET_CLIENT_ERROR_SMTP_TRANSIENT, /**< The server replied with a transient error code
(code 4yz). */
+ NET_CLIENT_ERROR_SMTP_PERMANENT, /**< The server replied with a permanent error code
(code 5yz). */
+ NET_CLIENT_ERROR_SMTP_NO_AUTH, /**< The server offers no implemented authentication
mechanism. */
+ NET_CLIENT_ERROR_SMTP_NO_STARTTLS /**< The server does not support STARTTLS. */
+};
+
+
+/** @name SMTP authentication methods
+ * @{
+ */
+/** RFC 4616 "PLAIN" authentication method. */
+#define NET_CLIENT_SMTP_AUTH_PLAIN 0x01U
+/** "LOGIN" authentication method. */
+#define NET_CLIENT_SMTP_AUTH_LOGIN 0x02U
+/** RFC 2195 "CRAM-MD5" authentication method. */
+#define NET_CLIENT_SMTP_AUTH_CRAM_MD5 0x04U
+/** RFC xxxx "CRAM-SHA1" authentication method. */
+#define NET_CLIENT_SMTP_AUTH_CRAM_SHA1 0x08U
+/** Mask of all safe authentication methods, i.e. all methods which do not send the cleartext password. */
+#define NET_CLIENT_SMTP_AUTH_SAFE (NET_CLIENT_SMTP_AUTH_CRAM_MD5 +
NET_CLIENT_SMTP_AUTH_CRAM_SHA1)
+/** Mask of all authentication methods. */
+#define NET_CLIENT_SMTP_AUTH_ALL \
+ (NET_CLIENT_SMTP_AUTH_PLAIN + NET_CLIENT_SMTP_AUTH_LOGIN + NET_CLIENT_SMTP_AUTH_CRAM_MD5 +
NET_CLIENT_SMTP_AUTH_CRAM_SHA1)
+/** @} */
+
+
+struct _NetClientSmtp {
+ NetClient parent;
+ NetClientSmtpPrivate *priv;
+};
+
+
+struct _NetClientSmtpClass {
+ NetClientClass parent;
+};
+
+
+/** @brief Delivery Status Notification mode
+ *
+ * See <a href="https://tools.ietf.org/html/rfc3461">RFC 3461</a> for a description of Delivery Status
Notifications (DSNs). The
+ * DSN mode is the logical OR of these options.
+ */
+enum _NetClientSmtpDsnMode {
+ NET_CLIENT_SMTP_DSN_NEVER = 0, /**< Never request a DSN (do not combine with other
options). */
+ NET_CLIENT_SMTP_DSN_SUCCESS = 1, /**< Request a DSN on successful delivery. */
+ NET_CLIENT_SMTP_DSN_FAILURE = 2, /**< Request a DSN on delivery failure. */
+ NET_CLIENT_SMTP_DSN_DELAY = 4 /**< Request a DSN if delivery of a message has been
delayed. */
+};
+
+
+/** @brief SMTP Message Transmission Callback Function
+ *
+ * The user-provided callback function to send a message to the remote SMTP server:
+ * - @em buffer - shall be filled with the next chunk of data
+ * - @em count - maximum number of bytes which may be written to @em buffer
+ * - @em user_data - user data pointer (_NetClientSmtpMessage::user_data)
+ * - @em error - shall be filled with error information if an error occurs in the callback
+ * - return value: a value > 0 indicating the number of bytes written to @em buffer, or 0 to indicate that
all data has been
+ * transferred, or a value < 0 to indicate an error in the callback function.
+ *
+ * @note The callback function is responsible for properly formatting the message body according to
+ * <a href="https://tools.ietf.org/html/rfc5321">RFC 5321</a>, <a
href="https://tools.ietf.org/html/rfc5322">RFC 5322</a> and
+ * further relevant standards, e.g. by using <a href="http://spruce.sourceforge.net/gmime/">GMime</a>.
+ */
+typedef gssize (*NetClientSmtpSendCb)(gchar *buffer, gsize count, gpointer user_data, GError **error);
+
+
+GType net_client_smtp_get_type(void)
+ G_GNUC_CONST;
+
+
+/** @brief Create a new SMTP network client
+ *
+ * @param host host name or IP address to connect
+ * @param port port number to connect
+ * @param crypt_mode encryption mode
+ * @return the net SMTP network client object
+ */
+NetClientSmtp *net_client_smtp_new(const gchar *host, guint16 port, NetClientCryptMode crypt_mode);
+
+
+/** @brief Set allowed SMTP AUTH methods
+ *
+ * @param client SMTP network client object
+ * @param encrypted set allowed methods for encrypted or unencrypted connections
+ * @param allow_auth mask of allowed authentication methods
+ * @return TRUE on success or FALSE on error
+ *
+ * Set the allowed authentication methods for the passed connection. The default is @ref
NET_CLIENT_SMTP_AUTH_ALL for encrypted and
+ * @ref NET_CLIENT_SMTP_AUTH_SAFE for unencrypted connections, respectively.
+ *
+ * @note Call this function @em before calling net_client_smtp_connect().
+ */
+gboolean net_client_smtp_allow_auth(NetClientSmtp *client, gboolean encrypted, guint allow_auth);
+
+
+/** @brief Connect a SMTP network client
+ *
+ * @param client SMTP network client object
+ * @param greeting filled with the greeting of the SMTP server on success, may be NULL to ignore
+ * @param error filled with error information if the connection fails
+ * @return TRUE on success or FALSE if the connection failed
+ *
+ * Connect the remote SMTP server, initialise the encryption if requested, and emit the @ref auth signal to
request authentication
+ * information. Simply ignore the signal for an unauthenticated connection. In order to shut down a
successfully established
+ * connection, just call <tt>g_object_unref()</tt> on the SMTP network client object.
+ *
+ * @note The caller must free the returned greeting when it is not needed any more.
+ */
+gboolean net_client_smtp_connect(NetClientSmtp *client, gchar **greeting, GError **error);
+
+
+/** @brief Check if the SMTP network client supports Delivery Status Notifications
+ *
+ * @param client connected SMTP network client object
+ * @return TRUE is DSN's are supported, FALSE if not
+ *
+ * Return if the connected SMTP server announced support for Delivery Status Notifications (DSNs) according
to
+ * <a href="https://tools.ietf.org/html/rfc3461">RFC 3461</a>.
+ */
+gboolean net_client_smtp_can_dsn(NetClientSmtp *client);
+
+
+/** @brief Send a message to a SMTP network client
+ *
+ * @param client connected SMTP network client object
+ * @param message message data
+ * @param error filled with error information if the connection fails
+ * @return TRUE on success or FALSE if sending the message failed
+ *
+ * Send the passed SMTP message to the connected SMTP server.
+ */
+gboolean net_client_smtp_send_msg(NetClientSmtp *client, const NetClientSmtpMessage *message, GError
**error);
+
+
+/** @brief Create a SMTP message
+ *
+ * @param data_callback callback function called to send the message data
+ * @param user_data additional user data passed to the callback function
+ * @return a newly create SMTP message
+ *
+ * Create a message suitable for transmission by calling net_client_smtp_send_msg(). At least one sender
and at least one recipient
+ * must be added by calling net_client_smtp_msg_set_sender() and net_client_smtp_msg_add_recipient(),
respectively. When the SMTP
+ * message is not needed any more, call net_client_smtp_msg_free() to free it.
+ */
+NetClientSmtpMessage *net_client_smtp_msg_new(NetClientSmtpSendCb data_callback, gpointer user_data)
+ G_GNUC_MALLOC;
+
+
+/** @brief Set the sender of a SMTP message
+ *
+ * @param smtp_msg SMTP message returned by net_client_smtp_msg_new()
+ * @param rfc5321_sender RFC 5321-compliant sender address
+ * @return TRUE on success or FALSE on error
+ *
+ * Set the sender address ("MAIL FROM" reverse-path, see <a href="https://tools.ietf.org/html/rfc5321">RFC
5321</a>) of the SMTP
+ * message.
+ */
+gboolean net_client_smtp_msg_set_sender(NetClientSmtpMessage *smtp_msg, const gchar *rfc5321_sender);
+
+
+/** @brief Set options for Delivery Status Notifications
+ *
+ * @param smtp_msg SMTP message returned by net_client_smtp_msg_new()
+ * @param envid ENVID parameter to the ESMTP MAIL command, may be NULL
+ * @param ret_full return the full message on failure instead of the headers only
+ * @return TRUE on success or FALSE on error
+ *
+ * Set the @em ENVID and @em RET parameters for Delivery Status Notifications (DSN's). The default is to
omit the ENVID and to
+ * request headers only.
+ */
+gboolean net_client_smtp_msg_set_dsn_opts(NetClientSmtpMessage *smtp_msg, const gchar *envid, gboolean
ret_full);
+
+
+/** @brief Add a recipient to a SMTP message
+ *
+ * @param smtp_msg SMTP message returned by net_client_smtp_msg_new()
+ * @param rfc5321_rcpt RFC 5321-compliant recipient address
+ * @param dsn_mode Delivery Status Notification mode for the recipient
+ * @return TRUE on success or FALSE on error
+ *
+ * Add a recipient address ("RCPT TO" forward-path, see <a href="https://tools.ietf.org/html/rfc5321">RFC
5321</a>) to the SMTP
+ * message.
+ */
+gboolean net_client_smtp_msg_add_recipient(NetClientSmtpMessage *smtp_msg, const gchar *rfc5321_rcpt,
+ NetClientSmtpDsnMode
dsn_mode);
+
+
+/** @brief Free a SMTP message
+ *
+ * @param smtp_msg SMTP message returned by net_client_smtp_msg_new()
+ *
+ * Free all resources of the passed SMTP message.
+ */
+void net_client_smtp_msg_free(NetClientSmtpMessage *smtp_msg);
+
+
+/** @file
+ *
+ * This module implements a SMTP client class conforming with <a
href="https://tools.ietf.org/html/rfc5321">RFC 5321</a>.
+ *
+ * The following additional features are supported:
+ * - Authentication according to <a href="https://tools.ietf.org/html/rfc4954">RFC 4954</a>, using the
methods
+ * - CRAM-MD5 according to <a href="https://tools.ietf.org/html/rfc2195">RFC 2195</a>
+ * - CRAM-SHA1 according to <a href="https://tools.ietf.org/html/rfc_TBD">TBD</a>
+ * - PLAIN according to <a href="https://tools.ietf.org/html/rfc4616">RFC 4616</a>
+ * - LOGIN
+ * - STARTTLS encryption according to <a href="https://tools.ietf.org/html/rfc3207">RFC 3207</a>
+ * - Delivery Status Notifications (DSNs) according to <a href="https://tools.ietf.org/html/rfc3461">RFC
3461</a>
+ */
+
+
+G_END_DECLS
+
+
+#endif /* NET_CLIENT_SMTP_H_ */
diff --git a/libnetclient/net-client-utils.c b/libnetclient/net-client-utils.c
new file mode 100644
index 0000000..79d1677
--- /dev/null
+++ b/libnetclient/net-client-utils.c
@@ -0,0 +1,87 @@
+/* NetClient - simple line-based network client library
+ *
+ * Copyright (C) Albrecht Dreß <mailto:albrecht dress arcor de> 2017
+ *
+ * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License
+ * as published by the Free Software Foundation; either version 3 of the License, or (at your option) any
later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with this library. If not,
see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include "net-client-utils.h"
+
+gchar *
+net_client_cram_calc(const gchar *base64_challenge, GChecksumType chksum_type, const gchar *user, const
gchar *passwd)
+{
+ guchar *chal_plain;
+ gsize plain_len;
+ gchar *digest;
+ gchar *auth_buf;
+ gchar *base64_buf;
+
+ g_return_val_if_fail((base64_challenge != NULL) && (user != NULL) && (passwd != NULL), NULL);
+
+ chal_plain = g_base64_decode(base64_challenge, &plain_len);
+ digest = g_compute_hmac_for_data(chksum_type, (const guchar *) passwd, strlen(passwd), chal_plain,
plain_len);
+ memset(chal_plain, 0, plain_len);
+ g_free(chal_plain);
+
+ auth_buf = g_strdup_printf("%s %s", user, digest);
+ memset(digest, 0, strlen(digest));
+ g_free(digest);
+
+ base64_buf = g_base64_encode((const guchar *) auth_buf, strlen(auth_buf));
+ memset(auth_buf, 0, strlen(auth_buf));
+ g_free(auth_buf);
+
+ return base64_buf;
+}
+
+
+const gchar *
+net_client_chksum_to_str(GChecksumType chksum_type)
+{
+ /*lint -e{904} -e{9077} -e{9090} (MISRA C:2012 Rules 15.5, 16.1, 16.3) */
+ switch (chksum_type) {
+ case G_CHECKSUM_MD5:
+ return "MD5";
+ case G_CHECKSUM_SHA1:
+ return "SHA1";
+ case G_CHECKSUM_SHA256:
+ return "SHA256";
+ case G_CHECKSUM_SHA512:
+ return "SHA512";
+ default:
+ return "_UNKNOWN_";
+ }
+}
+
+
+gchar *
+net_client_auth_plain_calc(const gchar *user, const gchar *passwd)
+{
+ gchar *base64_buf;
+ gchar *plain_buf;
+ size_t user_len;
+ size_t passwd_len;
+
+ g_return_val_if_fail((user != NULL) && (passwd != NULL), NULL);
+
+ user_len = strlen(user);
+ passwd_len = strlen(passwd);
+ plain_buf = g_malloc0((2U * user_len) + passwd_len + 3U); /*lint !e9079 (MISRA C:2012
Rule 11.5) */
+ strcpy(plain_buf, user);
+ strcpy(&plain_buf[user_len + 1U], user);
+ strcpy(&plain_buf[(2U * user_len) + 2U], passwd);
+ base64_buf = g_base64_encode((const guchar *) plain_buf, (2U * user_len) + passwd_len + 2U);
+ memset(plain_buf, 0, (2U * user_len) + passwd_len + 2U);
+ g_free(plain_buf);
+
+ return base64_buf;
+}
diff --git a/libnetclient/net-client-utils.h b/libnetclient/net-client-utils.h
new file mode 100644
index 0000000..c3065bb
--- /dev/null
+++ b/libnetclient/net-client-utils.h
@@ -0,0 +1,75 @@
+/* NetClient - simple line-based network client library
+ *
+ * Copyright (C) Albrecht Dreß <mailto:albrecht dress arcor de> 2017
+ *
+ * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License
+ * as published by the Free Software Foundation; either version 3 of the License, or (at your option) any
later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with this library. If not,
see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NET_CLIENT_UTILS_H_
+#define NET_CLIENT_UTILS_H_
+
+
+#include <gio/gio.h>
+
+
+G_BEGIN_DECLS
+
+
+/** @brief Calculate a CRAM authentication string
+ *
+ * @param base64_challenge base64-encoded challenge sent by the server
+ * @param chksum_type checksum type
+ * @param user user name
+ * @param passwd password
+ * @return a newly allocated string containing the base64-encoded authentication
+ *
+ * This helper function calculates the the base64-encoded authentication string from the server challenge,
the user name and the
+ * password according to the standards <a href="https://tools.ietf.org/html/rfc2104">RFC 2104 (MD5,
SHA1)</a> and
+ * <a href="https://tools.ietf.org/html/rfc4868">RFC 4868 (SHA256, SHA512)</a>. The caller shall free the
returned string when it
+ * is not needed any more.
+ *
+ * \sa <a href="https://tools.ietf.org/html/rfc2195">RFC 2195</a>.
+ */
+gchar *net_client_cram_calc(const gchar *base64_challenge, GChecksumType chksum_type, const gchar *user,
const gchar *passwd)
+ G_GNUC_MALLOC;
+
+
+/** @brief Get the checksum type as string
+ *
+ * @param chksum_type checksum type
+ * @return a string representation of the checksum type
+ */
+const gchar *net_client_chksum_to_str(GChecksumType chksum_type);
+
+
+/** @brief Calculate a SASL AUTH PLAIN authentication string
+ *
+ * @param user user name
+ * @param passwd password
+ * @return a newly allocated string containing the base64-encoded authentication
+ *
+ * This helper function calculates the the base64-encoded SASL AUTH PLAIN authentication string from the
user name and the password
+ * according to the <a href="https://tools.ietf.org/html/rfc4616">RFC 4616</a>. The caller shall free the
returned string when it
+ * is not needed any more.
+ */
+gchar *net_client_auth_plain_calc(const gchar *user, const gchar *passwd)
+ G_GNUC_MALLOC;
+
+
+/** @file
+ *
+ * This module implements authentication-related helper functions for the network client library.
+ */
+
+
+G_END_DECLS
+
+
+#endif /* NET_CLIENT_UTILS_H_ */
diff --git a/libnetclient/net-client.c b/libnetclient/net-client.c
new file mode 100644
index 0000000..1250769
--- /dev/null
+++ b/libnetclient/net-client.c
@@ -0,0 +1,518 @@
+/* NetClient - simple line-based network client library
+ *
+ * Copyright (C) Albrecht Dreß <mailto:albrecht dress arcor de> 2017
+ *
+ * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License
+ * as published by the Free Software Foundation; either version 3 of the License, or (at your option) any
later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with this library. If not,
see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <glib/gi18n.h>
+#include <gnutls/x509.h>
+#include "net-client.h"
+
+
+#define LINE_BUF_LEN 1024U
+
+
+struct _NetClientPrivate {
+ gchar *host_and_port;
+ guint16 default_port;
+ gsize max_line_len;
+
+ GSocketClient *sock;
+ GSocketConnection *plain_conn;
+ GIOStream *tls_conn;
+ GDataInputStream *istream;
+ GOutputStream *ostream;
+ GTlsCertificate *certificate;
+};
+
+
+static guint signals[3];
+
+
+G_DEFINE_TYPE(NetClient, net_client, G_TYPE_OBJECT)
+
+
+static void net_client_finalise(GObject *object);
+static gboolean cert_accept_cb(GTlsConnection *conn, GTlsCertificate *peer_cert, GTlsCertificateFlags
errors, gpointer user_data);
+
+
+NetClient *
+net_client_new(const gchar *host_and_port, guint16 default_port, gsize max_line_len)
+{
+ NetClient *client;
+
+ g_return_val_if_fail((host_and_port != NULL) && (max_line_len > 0U), NULL);
+
+ client = NET_CLIENT(g_object_new(NET_CLIENT_TYPE, NULL));
+
+ if (client->priv->sock == NULL) {
+ g_object_unref(G_OBJECT(client));
+ } else {
+ client->priv->host_and_port = g_strdup(host_and_port);
+ client->priv->default_port = default_port;
+ client->priv->max_line_len = max_line_len;
+ }
+
+ return client;
+}
+
+
+gboolean
+net_client_configure(NetClient *client, const gchar *host_and_port, guint16 default_port, gsize
max_line_len, GError **error)
+{
+ NetClientPrivate *priv;
+ gboolean result;
+
+ g_return_val_if_fail(NET_IS_CLIENT(client) && (host_and_port != NULL) && (max_line_len > 0U), FALSE);
+
+ priv = client->priv;
+ if (priv->plain_conn != NULL) {
+ g_set_error(error, NET_CLIENT_ERROR_QUARK, (gint) NET_CLIENT_ERROR_CONNECTED, _("network
client is already connected"));
+ result = FALSE;
+ } else {
+ g_free(priv->host_and_port);
+ priv->host_and_port = g_strdup(host_and_port);
+ priv->default_port = default_port;
+ priv->max_line_len = max_line_len;
+ result = TRUE;
+ }
+ return result;
+}
+
+
+const gchar *
+net_client_get_host(NetClient *client)
+{
+ const gchar *result;
+
+ if (NET_IS_CLIENT(client)) {
+ result = client->priv->host_and_port;
+ } else {
+ result = NULL;
+ }
+ return result;
+}
+
+
+gboolean
+net_client_connect(NetClient *client, GError **error)
+{
+ gboolean result = FALSE;
+ NetClientPrivate *priv;
+
+ g_return_val_if_fail(NET_IS_CLIENT(client), FALSE);
+
+ priv = client->priv;
+ if (priv->plain_conn != NULL) {
+ g_set_error(error, NET_CLIENT_ERROR_QUARK, (gint) NET_CLIENT_ERROR_CONNECTED, _("network
client is already connected"));
+ } else {
+ priv->plain_conn = g_socket_client_connect_to_host(priv->sock, priv->host_and_port,
priv->default_port, NULL, error);
+ if (priv->plain_conn != NULL) {
+ priv->istream =
g_data_input_stream_new(g_io_stream_get_input_stream(G_IO_STREAM(priv->plain_conn)));
+ g_data_input_stream_set_newline_type(priv->istream, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
+ priv->ostream = g_io_stream_get_output_stream(G_IO_STREAM(priv->plain_conn));
+ result = TRUE;
+ }
+ }
+
+ return result;
+}
+
+
+gboolean
+net_client_is_encrypted(NetClient *client)
+{
+ gboolean result;
+
+ if (NET_IS_CLIENT(client) && (client->priv->plain_conn != NULL) && (client->priv->tls_conn != NULL)) {
+ result = TRUE;
+ } else {
+ result = FALSE;
+ }
+ return result;
+}
+
+
+gboolean
+net_client_read_line(NetClient *client, gchar **recv_line, GError **error)
+{
+ gboolean result = FALSE;
+
+ g_return_val_if_fail(NET_IS_CLIENT(client), FALSE);
+
+ if (client->priv->istream == NULL) {
+ g_set_error(error, NET_CLIENT_ERROR_QUARK, (gint) NET_CLIENT_ERROR_NOT_CONNECTED, _("network
client is not connected"));
+ } else {
+ gchar *line_buf;
+ gsize length;
+ GError *read_err = NULL;
+
+ line_buf = g_data_input_stream_read_line(client->priv->istream, &length, NULL, &read_err);
+ if (line_buf != NULL) {
+ /* check that the protocol-specific maximum line length is not exceeded */
+ if (length > client->priv->max_line_len) {
+ g_set_error(error, NET_CLIENT_ERROR_QUARK, (gint)
NET_CLIENT_ERROR_LINE_TOO_LONG,
+ _("reply length %lu exceeds the maximum allowed length %lu"), length,
client->priv->max_line_len);
+ g_free(line_buf);
+ } else {
+ g_debug("R '%s'", line_buf);
+ result = TRUE;
+ if (recv_line != NULL) {
+ *recv_line = line_buf;
+ } else {
+ g_free(line_buf);
+ }
+ }
+ } else {
+ if (read_err != NULL) {
+ g_propagate_error(error, read_err);
+ } else {
+ g_set_error(error, NET_CLIENT_ERROR_QUARK, (gint)
NET_CLIENT_ERROR_CONNECTION_LOST, _("connection lost"));
+ }
+ }
+ }
+
+ return result;
+}
+
+
+gboolean
+net_client_write_buffer(NetClient *client, const gchar *buffer, gsize count, GError **error)
+{
+ gboolean result;
+
+ g_return_val_if_fail(NET_IS_CLIENT(client) && (buffer != NULL) && (count > 0UL), FALSE);
+
+ if (client->priv->ostream == NULL) {
+ g_set_error(error, NET_CLIENT_ERROR_QUARK, (gint) NET_CLIENT_ERROR_NOT_CONNECTED, _("network
client is not connected"));
+ result = FALSE;
+ } else {
+ gsize bytes_written;
+
+ if ((count > 2U) && (buffer[count - 1U] == '\n')) {
+ g_debug("W '%.*s'", (int) count - 2, buffer);
+ } else {
+ g_debug("W '%.*s'", (int) count, buffer);
+ }
+ result = g_output_stream_write_all(client->priv->ostream, buffer, count, &bytes_written,
NULL, error);
+ if (result) {
+ result = g_output_stream_flush(client->priv->ostream, NULL, error);
+ }
+ }
+
+ return result;
+}
+
+
+gboolean
+net_client_vwrite_line(NetClient *client, const gchar *format, va_list args, GError **error)
+{
+ gboolean result;
+ gchar buffer[LINE_BUF_LEN];
+ gint buf_len;
+
+ g_return_val_if_fail(NET_IS_CLIENT(client) && (format != NULL), FALSE);
+
+ buf_len = g_vsnprintf(buffer, client->priv->max_line_len - 2U, format, args);
+ if ((buf_len < 0) || ((gsize) buf_len > (client->priv->max_line_len - 2U))) {
+ g_set_error(error, NET_CLIENT_ERROR_QUARK, (gint) NET_CLIENT_ERROR_LINE_TOO_LONG, _("line too
long"));
+ result = FALSE;
+ } else {
+ buffer[buf_len] = '\r';
+ buffer[buf_len + 1] = '\n';
+ result = net_client_write_buffer(client, buffer, (gsize) buf_len + 2U, error);
+ }
+
+ return result;
+}
+
+
+gboolean
+net_client_write_line(NetClient *client, const gchar *format, GError **error, ...)
+{
+ va_list args;
+ gboolean result;
+
+ va_start(args, error);
+ result = net_client_vwrite_line(client, format, args, error);
+ va_end(args);
+
+ return result;
+}
+
+
+gboolean
+net_client_execute(NetClient *client, gchar **response, const gchar *request_fmt, GError **error, ...)
+{
+ va_list args;
+ gboolean result;
+
+ va_start(args, error);
+ result = net_client_vwrite_line(client, request_fmt, args, error);
+ va_end(args);
+ if (result) {
+ result = net_client_read_line(client, response, error);
+ }
+
+ return result;
+}
+
+
+/* Note: I have no idea how I can load a PEM with an encrypted key into a GTlsCertificate using
g_tls_certificate_new_from_file()
+ * or g_tls_certificate_new_from_pem(), as this will always fail with a GnuTLS error "ASN1 parser: Error in
DER parsing". There
+ * /might/ be a solution with GTlsInteraction and GTlsPassword, though. Please enlighten me if you know
more...
+ *
+ * This function uses "raw" GnuTLS calls and will emit the cert-pass signal if necessary.
+ *
+ * See also <https://mail.gnome.org/archives/gtk-list/2016-May/msg00027.html>.
+ */
+gboolean
+net_client_set_cert_from_pem(NetClient *client, const gchar *pem_data, GError **error)
+{
+ gboolean result = FALSE;
+ gnutls_x509_crt_t cert;
+ int res;
+
+ g_return_val_if_fail(NET_IS_CLIENT(client) && (pem_data != NULL), FALSE);
+
+ /* always free any existing certificate */
+ if (client->priv->certificate != NULL) {
+ g_object_unref(G_OBJECT(client->priv->certificate));
+ client->priv->certificate = NULL;
+ }
+
+ /* load the certificate */
+ res = gnutls_x509_crt_init(&cert);
+ if (res != GNUTLS_E_SUCCESS) {
+ g_set_error(error, NET_CLIENT_ERROR_QUARK, (gint) NET_CLIENT_ERROR_GNUTLS, _("error
initializing certificate: %s"),
+ gnutls_strerror(res));
+ } else {
+ gnutls_datum_t data;
+
+ /*lint -e9005 cast'ing away the const is safe as gnutls treas data as const */
+ data.data = (unsigned char *) pem_data;
+ data.size = strlen(pem_data);
+ res = gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM);
+ if (res != GNUTLS_E_SUCCESS) {
+ g_set_error(error, NET_CLIENT_ERROR_QUARK, (gint) NET_CLIENT_ERROR_GNUTLS, _("error
loading certificate: %s"),
+ gnutls_strerror(res));
+ } else {
+ gnutls_x509_privkey_t key;
+
+ /* try to load the key, emit cert-pass signal if gnutls says the key is encrypted */
+ res = gnutls_x509_privkey_init(&key);
+ if (res != GNUTLS_E_SUCCESS) {
+ g_set_error(error, NET_CLIENT_ERROR_QUARK, (gint) NET_CLIENT_ERROR_GNUTLS,
_("error initializing key: %s"),
+ gnutls_strerror(res));
+ } else {
+ res = gnutls_x509_privkey_import2(key, &data, GNUTLS_X509_FMT_PEM, NULL, 0);
+ if (res == GNUTLS_E_DECRYPTION_FAILED) {
+ size_t der_size;
+ guint8 *der_data;
+
+ /* determine cert buffer size requirements */
+ der_size = 0U;
+ (void) gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, NULL,
&der_size);
+ der_data = g_malloc(der_size); /*lint !e9079 (MISRA C:2012
Rule 11.5) */
+
+ res = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, der_data,
&der_size);
+ if (res == GNUTLS_E_SUCCESS) {
+ GByteArray *cert_der;
+ gchar *key_pass = NULL;
+
+ cert_der = g_byte_array_new_take(der_data, der_size);
+ g_debug("emit 'cert-pass' signal for client %p", client);
+ g_signal_emit(client, signals[2], 0, cert_der, &key_pass);
+ g_byte_array_unref(cert_der);
+ if (key_pass != NULL) {
+ res = gnutls_x509_privkey_import2(key, &data,
GNUTLS_X509_FMT_PEM, key_pass, 0);
+ memset(key_pass, 0, strlen(key_pass));
+ g_free(key_pass);
+ }
+ }
+ }
+
+ /* on success, set the certificate using the unencrypted key */
+ if (res == GNUTLS_E_SUCCESS) {
+ gchar *pem_buf;
+ size_t key_buf_size = 0;
+ size_t crt_buf_size = 0;
+
+ /* determine buffer size requirements */
+ (void) gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, NULL,
&key_buf_size);
+ (void) gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, NULL,
&crt_buf_size);
+ pem_buf = g_malloc(key_buf_size + crt_buf_size);
/*lint !e9079 (MISRA C:2012 Rule 11.5) */
+ res = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, pem_buf,
&key_buf_size);
+ if (res == GNUTLS_E_SUCCESS) {
+ res = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM,
&pem_buf[key_buf_size], &crt_buf_size);
+ }
+
+ if (res == GNUTLS_E_SUCCESS) {
+ client->priv->certificate =
g_tls_certificate_new_from_pem(pem_buf, -1, error);
+ if (client->priv->certificate != NULL) {
+ result = TRUE;
+ }
+ }
+ g_free(pem_buf);
+ }
+ gnutls_x509_privkey_deinit(key);
+
+ if (res != GNUTLS_E_SUCCESS) {
+ g_set_error(error, NET_CLIENT_ERROR_QUARK, (gint)
NET_CLIENT_ERROR_GNUTLS, _("error loading key: %s"),
+ gnutls_strerror(res));
+ }
+ }
+ }
+ gnutls_x509_crt_deinit(cert);
+ }
+
+ return result;
+}
+
+
+gboolean
+net_client_set_cert_from_file(NetClient *client, const gchar *pem_path, GError **error)
+{
+ gboolean result;
+ GFile *pem_file;
+ gchar *pem_buf;
+
+ g_return_val_if_fail(pem_path != NULL, FALSE);
+
+ pem_file = g_file_new_for_path(pem_path);
+ result = g_file_load_contents(pem_file, NULL, &pem_buf, NULL, NULL, error);
+ if (result) {
+ result = net_client_set_cert_from_pem(client, pem_buf, error);
+ g_free(pem_buf);
+ }
+ g_object_unref(G_OBJECT(pem_file));
+ return result;
+}
+
+
+gboolean
+net_client_start_tls(NetClient *client, GError **error)
+{
+ gboolean result = FALSE;
+
+ g_return_val_if_fail(NET_IS_CLIENT(client), FALSE);
+
+ if (client->priv->plain_conn == NULL) {
+ g_set_error(error, NET_CLIENT_ERROR_QUARK, (gint) NET_CLIENT_ERROR_NOT_CONNECTED, _("not
connected"));
+ } else if (client->priv->tls_conn != NULL) {
+ g_set_error(error, NET_CLIENT_ERROR_QUARK, (gint) NET_CLIENT_ERROR_TLS_ACTIVE, _("connection
is already encrypted"));
+ } else {
+ client->priv->tls_conn = g_tls_client_connection_new(G_IO_STREAM(client->priv->plain_conn),
NULL, error);
+ if (client->priv->tls_conn != NULL) {
+ if (client->priv->certificate != NULL) {
+ g_tls_connection_set_certificate(G_TLS_CONNECTION(client->priv->tls_conn),
client->priv->certificate);
+ }
+ (void) g_signal_connect(G_OBJECT(client->priv->tls_conn), "accept-certificate",
G_CALLBACK(cert_accept_cb), client);
+ result = g_tls_connection_handshake(G_TLS_CONNECTION(client->priv->tls_conn), NULL,
error);
+ if (result) {
+
g_filter_input_stream_set_close_base_stream(G_FILTER_INPUT_STREAM(client->priv->istream), FALSE);
+ g_object_unref(G_OBJECT(client->priv->istream)); /* unref the
plain connection's stream */
+ client->priv->istream =
g_data_input_stream_new(g_io_stream_get_input_stream(G_IO_STREAM(client->priv->tls_conn)));
+ g_data_input_stream_set_newline_type(client->priv->istream,
G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
+ client->priv->ostream =
g_io_stream_get_output_stream(G_IO_STREAM(client->priv->tls_conn));
+ } else {
+ g_object_unref(G_OBJECT(client->priv->tls_conn));
+ client->priv->tls_conn = NULL;
+ }
+ }
+ }
+
+ return result;
+}
+
+
+gboolean
+net_client_set_timeout(NetClient *client, guint timeout_secs)
+{
+ g_return_val_if_fail(NET_IS_CLIENT(client), FALSE);
+
+ g_socket_client_set_timeout(client->priv->sock, timeout_secs);
+ return TRUE;
+}
+
+
+/* == local functions
=========================================================================================================== */
+
+static void
+net_client_class_init(NetClientClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+
+ g_type_class_add_private(klass, sizeof(NetClientPrivate));
+ gobject_class->finalize = net_client_finalise;
+ signals[0] = g_signal_new("cert-check", NET_CLIENT_TYPE, G_SIGNAL_RUN_LAST, 0U, NULL, NULL, NULL,
G_TYPE_BOOLEAN, 2U,
+ G_TYPE_TLS_CERTIFICATE, G_TYPE_TLS_CERTIFICATE_FLAGS);
+ signals[1] = g_signal_new("auth", NET_CLIENT_TYPE, G_SIGNAL_RUN_LAST, 0U, NULL, NULL, NULL,
G_TYPE_STRV, 0U);
+ signals[2] = g_signal_new("cert-pass", NET_CLIENT_TYPE, G_SIGNAL_RUN_LAST, 0U, NULL, NULL, NULL,
G_TYPE_STRING, 1U,
+ G_TYPE_BYTE_ARRAY);
+}
+
+
+static void
+net_client_init(NetClient *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NET_CLIENT_TYPE, NetClientPrivate);
+ self->priv->sock = g_socket_client_new();
+ if (self->priv->sock != NULL) {
+ g_socket_client_set_timeout(self->priv->sock, 180U);
+ }
+}
+
+
+static void
+net_client_finalise(GObject *object)
+{
+ const NetClient *client = NET_CLIENT(object);
+ const GObjectClass *parent_class = G_OBJECT_CLASS(net_client_parent_class);
+
+ /* note: we must unref the GDataInputStream, but *not* the GOutputStream! */
+ if (client->priv->istream != NULL) {
+ g_object_unref(G_OBJECT(client->priv->istream));
+ client->priv->istream = NULL;
+ }
+ if (client->priv->tls_conn != NULL) {
+ g_object_unref(G_OBJECT(client->priv->tls_conn));
+ client->priv->tls_conn = NULL;
+ }
+ if (client->priv->plain_conn != NULL) {
+ g_object_unref(G_OBJECT(client->priv->plain_conn));
+ client->priv->plain_conn = NULL;
+ }
+ if (client->priv->sock != NULL) {
+ g_object_unref(G_OBJECT(client->priv->sock));
+ client->priv->sock = NULL;
+ }
+ if (client->priv->certificate != NULL) {
+ g_object_unref(G_OBJECT(client->priv->certificate));
+ client->priv->certificate = NULL;
+ }
+ g_free(client->priv->host_and_port);
+ (*parent_class->finalize)(object);
+}
+
+
+static gboolean
+cert_accept_cb(G_GNUC_UNUSED GTlsConnection *conn, GTlsCertificate *peer_cert, GTlsCertificateFlags errors,
gpointer user_data)
+{
+ NetClient *client = NET_CLIENT(user_data);
+ gboolean result;
+
+ g_debug("emit 'cert-check' signal for client %p", client);
+ g_signal_emit(client, signals[0], 0, peer_cert, errors, &result);
+ return result;
+}
diff --git a/libnetclient/net-client.h b/libnetclient/net-client.h
new file mode 100644
index 0000000..41db07d
--- /dev/null
+++ b/libnetclient/net-client.h
@@ -0,0 +1,305 @@
+/* NetClient - simple line-based network client library
+ *
+ * Copyright (C) Albrecht Dreß <mailto:albrecht dress arcor de> 2017
+ *
+ * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License
+ * as published by the Free Software Foundation; either version 3 of the License, or (at your option) any
later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with this library. If not,
see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NET_CLIENT_H_
+#define NET_CLIENT_H_
+
+
+#include <stdarg.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+
+G_BEGIN_DECLS
+
+
+#define NET_CLIENT_TYPE (net_client_get_type())
+#define NET_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),
NET_CLIENT_TYPE, NetClient))
+#define NET_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),
NET_CLIENT_TYPE))
+#define NET_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),
NET_CLIENT_TYPE, NetClientClass))
+#define NET_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NET_CLIENT_TYPE))
+#define NET_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NET_CLIENT_TYPE,
NetClientClass))
+
+#define NET_CLIENT_ERROR_QUARK (g_quark_from_static_string("net-client"))
+
+
+typedef struct _NetClient NetClient;
+typedef struct _NetClientClass NetClientClass;
+typedef struct _NetClientPrivate NetClientPrivate;
+typedef enum _NetClientError NetClientError;
+typedef enum _NetClientCryptMode NetClientCryptMode;
+
+
+struct _NetClient {
+ GObject parent;
+ NetClientPrivate *priv;
+};
+
+
+struct _NetClientClass {
+ GObjectClass parent;
+};
+
+
+/** @brief Encryption mode */
+enum _NetClientCryptMode {
+ NET_CLIENT_CRYPT_ENCRYPTED = 1, /**< TLS encryption @em before starting the protocol
required (e.g. SMTPS). */
+ NET_CLIENT_CRYPT_STARTTLS, /**< Protocol-specific STARTTLS encryption
required. */
+ NET_CLIENT_CRYPT_STARTTLS_OPT, /**< Optional protocol-specific STARTTLS encryption,
proceed unencrypted on fail. */
+ NET_CLIENT_CRYPT_NONE /**< Unencrypted connection. */
+};
+
+
+/** @brief Error codes */
+enum _NetClientError {
+ NET_CLIENT_ERROR_CONNECTED = 1, /**< The client is already connected. */
+ NET_CLIENT_ERROR_NOT_CONNECTED, /**< The client is not connected. */
+ NET_CLIENT_ERROR_CONNECTION_LOST, /**< The connection is lost. */
+ NET_CLIENT_ERROR_TLS_ACTIVE, /**< TLS is already active for the connection. */
+ NET_CLIENT_ERROR_LINE_TOO_LONG, /**< The line is too long. */
+ NET_CLIENT_ERROR_GNUTLS /**< A GnuTLS error occurred. */
+};
+
+
+GType net_client_get_type(void)
+ G_GNUC_CONST;
+
+
+/** @brief Create a new network client
+ *
+ * @param host_and_port remote host and port or service, separated by a colon, which shall be connected
+ * @param default_port default remote port if host_and_port does not contain a port
+ * @param max_line_len maximum line length supported by the underlying protocol
+ * @return the net network client object
+ *
+ * Create a new network client object with the passed parameters. Call <tt>g_object_unref()</tt> on it to
shut down the connection
+ * and to free all resources of it.
+ */
+NetClient *net_client_new(const gchar *host_and_port, guint16 default_port, gsize max_line_len);
+
+
+/** @brief Configure a network client
+ *
+ * @param client network client
+ * @param host_and_port remote host and port or service, separated by a colon, which shall be connected
+ * @param default_port default remote port if host_and_port does not contain a port
+ * @param max_line_len maximum line length supported by the underlying protocol
+ * @param error filled with error information on error
+ * @return TRUE is the connection was successful, FALSE on error
+ *
+ * Set the remote host and port and the maximum line length to the passed parameters, replacing the previous
values set by calling
+ * net_client_new().
+ */
+gboolean net_client_configure(NetClient *client, const gchar *host_and_port, guint16 default_port, gsize
max_line_len,
+ GError **error);
+
+
+/** @brief Get the target host of a network client
+ *
+ * @param client network client
+ * @return the currently set host on success, or NULL on error
+ *
+ * @note The function returns the value of @em host_and_port set by net_client_new() or
net_client_configure().
+ */
+const gchar *net_client_get_host(NetClient *client);
+
+
+/** @brief Connect a network client
+ *
+ * @param client network client
+ * @param error filled with error information on error
+ * @return TRUE is the connection was successful, FALSE on error
+ *
+ * Try to connect the remote host and TCP port.
+ */
+gboolean net_client_connect(NetClient *client, GError **error);
+
+
+/** @brief Check if a network client is connected
+ *
+ * @param client network client
+ * @return TRUE if the passed network client is connected, FALSE if not
+ */
+gboolean net_client_is_encrypted(NetClient *client);
+
+
+/** @brief Load a certificate and private key from PEM data
+ *
+ * @param client network client
+ * @param pem_data NUL-terminated PEM data buffer
+ * @param error filled with error information on error
+ * @return TRUE is the certificate and private key were loaded, FALSE on error
+ *
+ * Load a client certificate and private key form the passed PEM data. If the private key is encrypted, the
signal @ref cert-pass
+ * is emitted.
+ *
+ * Use this function (or net_client_set_cert_from_file()) if the remote server requires a client certificate.
+ */
+gboolean net_client_set_cert_from_pem(NetClient *client, const gchar *pem_data, GError **error);
+
+
+/** @brief Load a certificate and private key from a PEM file
+ *
+ * @param client network client
+ * @param pem_path path name of the file containing the PEM certificate and private key
+ * @param error filled with error information on error
+ * @return TRUE is the certificate and private key were loaded, FALSE on error
+ *
+ * Load a client certificate and private key form the passed PEM file. If the private key is encrypted, the
signal @ref cert-pass
+ * is emitted.
+ *
+ * Use this function (or net_client_set_cert_from_pem()) if the remote server requires a client certificate.
+ */
+gboolean net_client_set_cert_from_file(NetClient *client, const gchar *pem_path, GError **error);
+
+
+/** @brief Start encryption
+ *
+ * @param client network client
+ * @param error filled with error information on error
+ * @return TRUE if the connection is now TLS encrypted, FALSE on error
+ *
+ * Try to negotiate TLS encryption. If the remote server presents an untrusted certificate, the signal @ref
cert-check is emitted.
+ */
+gboolean net_client_start_tls(NetClient *client, GError **error);
+
+
+/** @brief Read a CRLF-terminated line from a network client
+ *
+ * @param client network client
+ * @param recv_line filled with the response buffer on success, may be NULL to discard the line read
+ * @param error filled with error information on error
+ * @return TRUE is the read operation was successful, FALSE on error
+ *
+ * Read a CRLF-terminated line from the remote server and return it in the passed buffer. The terminating
CRLF is always stripped.
+ *
+ * @note The caller must free the returned buffer when it is not needed any more.
+ */
+gboolean net_client_read_line(NetClient *client, gchar **recv_line, GError **error);
+
+
+/** @brief Write data to a network client
+ *
+ * @param client network client
+ * @param buffer data buffer
+ * @param count number of bytes in the data buffer
+ * @param error filled with error information on error
+ * @return TRUE is the send operation was successful, FALSE on error
+ *
+ * Send the complete data buffer to the remote server. The caller must ensure that the allowed line length
is not exceeded, and
+ * that the lines are CRLF-terminated.
+ */
+gboolean net_client_write_buffer(NetClient *client, const gchar *buffer, gsize count, GError **error);
+
+
+/** @brief Write a line to a network client
+ *
+ * @param client network client
+ * @param format printf-like format string
+ * @param args argument list for the format
+ * @param error filled with error information on error
+ * @return TRUE is the send operation was successful, FALSE on error
+ *
+ * Format a line according to the passed arguments, append CRLF, and send it to the remote server.
+ */
+gboolean net_client_vwrite_line(NetClient *client, const gchar *format, va_list args, GError **error);
+
+
+/** @brief Write a line with a variable argument list to a network client
+ *
+ * @param client network client
+ * @param format printf-like format string
+ * @param error filled with error information on error
+ * @param ... additional arguments according to the format string
+ * @return TRUE is the send operation was successful, FALSE on error
+ *
+ * Format a line according to the passed arguments, append CRLF, and send it to the remote server.
+ */
+gboolean net_client_write_line(NetClient *client, const gchar *format, GError **error, ...)
+ G_GNUC_PRINTF(2, 4);
+
+
+/** @brief Execute a command-response sequence with a network client
+ *
+ * @param client network client
+ * @param response filled with the response buffer on success, may be NULL to discard the response
+ * @param request_fmt printf-like request format string
+ * @param error filled with error information on error
+ * @param ... additional arguments according to the format string
+ * @return TRUE is the operation was successful, FALSE on error
+ *
+ * This convenience function calls net_client_vwrite_line() to write a one-line command to the remote
server, and then
+ * net_client_read_line() to read the response. The terminating CRLF of the response is always stripped.
+ *
+ * @note The caller must free the returned buffer when it is not needed any more.
+ */
+gboolean net_client_execute(NetClient *client, gchar **response, const gchar *request_fmt, GError **error,
...)
+ G_GNUC_PRINTF(3, 5);
+
+
+/** @brief Set the connection time-out
+ *
+ * @param client network client
+ * @param timeout_secs time-out in seconds
+ * @return TRUE on success, FALSE on error
+ *
+ * @note The default timeout is 180 seconds (3 minutes).
+ */
+gboolean net_client_set_timeout(NetClient *client, guint timeout_secs);
+
+
+/**
+ * @mainpage
+ *
+ * This library provides an implementation of CRLF-terminated line-based client protocols built on top of
GIO. It provides a base
+ * module (see file net-client.h), containing the line-based IO methods, and on top of that a SMTP (RFC5321)
client class (see
+ * file net-client-smtp.h). The file net-client-utils.h contains some helper functions for authentication.
+ *
+ * \author Written by Albrecht Dreß mailto:albrecht dress arcor de
+ * \copyright Copyright © Albrecht Dreß 2017<br/>
+ * This library is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as
+ * published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later
version.<br/>
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.<br/>
+ * You should have received a copy of the GNU General Public License along with this library. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * @file
+ *
+ * This module implements the base class for a simple network client communication on the basis of
CRLF-terminated lines.
+ *
+ * @section signals Signals
+ *
+ * The following signals are implemented:
+ *
+ * - @anchor cert-pass cert-pass
+ * @code gchar *cert_pass(NetClient *client, GByteArray *peer_cert_der, gpointer user_data) @endcode The
client certificate used
+ * for the connection has a password-protected key. The certificate in DER format is passed to the signal
handler, and shall
+ * return a newly allocated string containing the password. The string is wiped and freed when it is not
needed any more.
+ * - @anchor cert-check cert-check
+ * @code gboolean check_cert(NetClient *client, GTlsCertificate *peer_cert, GTlsCertificateFlags errors,
gpointer user_data)
+ * @endcode The server certificate is not trusted. The received certificate and the errors which occurred
during the check are
+ * passed to the signal handler. The handler shall return TRUE to accept the certificate, or FALSE to
reject it.
+ * - @anchor auth auth
+ * @code gchar **get_auth(NetClient *client, gpointer user_data) @endcode Authentication is required by
the remote server. The
+ * signal handler shall return a NULL-terminated array of strings, containing the user name in the first
and the password in the
+ * second element. The strings are wiped and freed when they are not needed any more. Return NULL if no
authentication is
+ * required.
+ */
+
+
+G_END_DECLS
+
+
+#endif /* NET_CLIENT_H_ */
diff --git a/libnetclient/test/Makefile.am b/libnetclient/test/Makefile.am
new file mode 100644
index 0000000..be9caf7
--- /dev/null
+++ b/libnetclient/test/Makefile.am
@@ -0,0 +1,31 @@
+# $Id$
+
+# Note: the following hack is needed so lcov recognises the paths of the sources...
+libsrcdir = $(shell echo $(abs_srcdir) | sed -e 's;/test$$;;')
+test_src = $(libsrcdir)/net-client.c $(libsrcdir)/net-client-smtp.c $(libsrcdir)/net-client-utils.c
+
+EXTRA_DIST = \
+ tests.c \
+ inetsim.conf \
+ ca_cert.pem \
+ cert.pem \
+ cert_u.pem \
+ valgrind.supp
+
+TESTFLAGS = -DNCAT="\"@NCAT@\"" -DSED="\"@SED@\"" -fprofile-arcs -ftest-coverage -g -Wno-error
+
+LCOVFLGS = --rc lcov_branch_coverage=1
+GENHTMLFLGS = --function-coverage --branch-coverage --num-spaces 4
+VALGRFLAGS = --tool=memcheck --log-file=$@.vg --suppressions=valgrind.supp --leak-check=full
--child-silent-after-fork=yes
+
+CLEANFILES = *.gcda *.gcno *.covi *.vg tests
+
+clean-local:
+ -rm -rf gcov
+
+tests: tests.c
+ $(CC) $(LIBNETCLIENT_CFLAGS) $(CPPFLAGS) $(TESTFLAGS) -I. -I.. $< $(test_src) -o $@
$(LIBNETCLIENT_LIBS)
+ $(VALGRIND) $(VALGRFLAGS) ./$@
+ $(LCOV) $(LCOVFLGS) -c -b $(libsrcdir) -d $(abs_srcdir) --no-external -o $@.covi
+ $(LCOV) $(LCOVFLGS) -r $@.covi $< -o $@.covi
+ $(GENHTML) $(GENHTMLFLGS) -o gcov $@.covi
diff --git a/libnetclient/test/ca_cert.pem b/libnetclient/test/ca_cert.pem
new file mode 100644
index 0000000..bf8945b
--- /dev/null
+++ b/libnetclient/test/ca_cert.pem
@@ -0,0 +1,46 @@
+-----BEGIN CERTIFICATE-----
+MIIDxjCCAq6gAwIBAgIBATANBgkqhkiG9w0BAQUFADB0MRMwEQYKCZImiZPyLGQB
+GRYDb3JnMRYwFAYKCZImiZPyLGQBGRYGc2ltcGxlMRMwEQYDVQQKDApTaW1wbGUg
+SW5jMRcwFQYDVQQLDA5TaW1wbGUgUm9vdCBDQTEXMBUGA1UEAwwOU2ltcGxlIFJv
+b3QgQ0EwHhcNMTYwNTI0MTYxNjU4WhcNMjYwNTI0MTYxNjU4WjB0MRMwEQYKCZIm
+iZPyLGQBGRYDb3JnMRYwFAYKCZImiZPyLGQBGRYGc2ltcGxlMRMwEQYDVQQKDApT
+aW1wbGUgSW5jMRcwFQYDVQQLDA5TaW1wbGUgUm9vdCBDQTEXMBUGA1UEAwwOU2lt
+cGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPn/T4
+w9Ggb9aXhZ5stchfb+gZAnLkYKSZZC/+SryGwToK2HouWCwMUx8Jgg9DeuIjjVEx
+gnuTveVWWWL7QWdIWOrcDYN8XDjS/Rx0brhmDY5q19/CuNPDWMPb5/8SboOmEM4b
+D/S0gsBUI8N7yJ6qYzvtAB5GDBy8LcK+RBp197R5aFlqIUDy5neH2NoA6XU5NcfS
+cHpqs/oJkg0zVksrZ279mGEHJTxtpPVlm7/z153DgxB1XDWUGIbkmzzl8FPrLGWo
+o1gUFs3TfxnYuFl4r2NhwYeVEUcuBYFhJGVbeisV47ynFUV8gHqxaH03DfghaR+6
+WGe4MBmYpM3NTi6jAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
+BTADAQH/MB0GA1UdDgQWBBQ+k/H1yqCqwMnESs4nzdqhHI2IkDAfBgNVHSMEGDAW
+gBQ+k/H1yqCqwMnESs4nzdqhHI2IkDANBgkqhkiG9w0BAQUFAAOCAQEAswaE/9XS
+uVbWyY6CsMry1dfTCzEaTe3odrVe2GM3nU4nowAUqJfqR4JAYjqJim37997n6jbZ
+lK168uFwI/mj4QCY72KixbaSpl7I54MUNXtBtb1/kTwXDeCMFO0Iw1f60Kr8QOeM
+d+aktreCqE+Gss3nmJJ6TT7b/X1UHywlBcO0ZX4y0aDlVWgMqOHxAYF4+Ayxq4El
+MH3+aDItPqnYp1WXfIn/8cYKhdbKjPgZWXVtIzmPHPrAbLQT3DxvPEv4MIwmWx24
+/c4mAvM8TStB+m2/tYZfRflPZ133Nw5zbKHaP1OJgqErIAZCUCHU8tVVBnc/uFHm
+sS+pZL8j9zrzhg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDzzCCAregAwIBAgIBAjANBgkqhkiG9w0BAQUFADB0MRMwEQYKCZImiZPyLGQB
+GRYDb3JnMRYwFAYKCZImiZPyLGQBGRYGc2ltcGxlMRMwEQYDVQQKDApTaW1wbGUg
+SW5jMRcwFQYDVQQLDA5TaW1wbGUgUm9vdCBDQTEXMBUGA1UEAwwOU2ltcGxlIFJv
+b3QgQ0EwHhcNMTYwNTI0MTYxOTAyWhcNMjYwNTI0MTYxOTAyWjB6MRMwEQYKCZIm
+iZPyLGQBGRYDb3JnMRYwFAYKCZImiZPyLGQBGRYGc2ltcGxlMRMwEQYDVQQKDApT
+aW1wbGUgSW5jMRowGAYDVQQLDBFTaW1wbGUgU2lnbmluZyBDQTEaMBgGA1UEAwwR
+U2ltcGxlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQDQXD9AyJJwmx7Ng/Jo6dJy8xLJnWMWi8MmHOhqig5GPuOPff/iNRnhdU6xdr2j
+v5BCdmHbosnOxPq3RJ9r9wyQv6PukCkPvD/2bePQV+ObqegTR5VlNVBhPoWLda5Y
+hMnLTX/mVjC0duz2x+UcS/hd3Yd/QdVdFMJ0LJvJ96SplyfOAowTmw4jii/dtkl+
+hTGp8xl9+wyi1c/ZFKhq3efgC5ozh5oyMMJ6iziaxecq/Rehk4KWDxlPQ5usFU6n
+ls1lBuBszJfNR1XGTCzlvBR/TWNu1WiZmpXgizIoSehOgc/27G4EvkEa4PlEp6I6
+r5CQrDebSlzJSkyhFnkMZQT/AgMBAAGjZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNV
+HRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTuAfbK98FLKXj/yBvsD5owPDo5ijAf
+BgNVHSMEGDAWgBQ+k/H1yqCqwMnESs4nzdqhHI2IkDANBgkqhkiG9w0BAQUFAAOC
+AQEAZYWaIHK+09tQN7IHJZatoeE/06QM/pNZBe5/er3C95zOgis7jCOC+Au4srLf
++oi8y+i4WPqRQAJKOOIf7nLMQfGqEV1UVfEOiP40I5WmNQDk3E5jFqTRZC42mf1v
+u2XL3FkjSj+LQyLCz80JdYFUn+I6lyvNSkAC2f5IA+aleX628LtyiuLF0T3Vtjul
+95vgaGd8m3Zr3/rGUeTMhMHPEGvTjTcXyOC1Sc85PhChZtGDehhafosJU8vXmVeH
+dCk3xxx2sjBvWxwTM1pdCJ0HzP7vveLhB22b2aPu8dVAiGJVKtWrnVj1whQeC3qr
+IjagpJLKKVbRnz1plUtXsO6YdA==
+-----END CERTIFICATE-----
diff --git a/libnetclient/test/cert.pem b/libnetclient/test/cert.pem
new file mode 100644
index 0000000..a3b1ee6
--- /dev/null
+++ b/libnetclient/test/cert.pem
@@ -0,0 +1,53 @@
+-----BEGIN CERTIFICATE-----
+MIID6TCCAtGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB6MRMwEQYKCZImiZPyLGQB
+GRYDb3JnMRYwFAYKCZImiZPyLGQBGRYGc2ltcGxlMRMwEQYDVQQKDApTaW1wbGUg
+SW5jMRowGAYDVQQLDBFTaW1wbGUgU2lnbmluZyBDQTEaMBgGA1UEAwwRU2ltcGxl
+IFNpZ25pbmcgQ0EwHhcNMTYwNTI0MTYyOTE0WhcNMTgwNTI0MTYyOTE0WjBbMRMw
+EQYKCZImiZPyLGQBGRYDb3JnMRYwFAYKCZImiZPyLGQBGRYGc2ltcGxlMRMwEQYD
+VQQKDApTaW1wbGUgSW5jMRcwFQYDVQQDDA53d3cuc2ltcGxlLm9yZzCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBALrxG+kSRCK8qlBNYvEQIEKGe75SC4GB
+s46Sr3lUgpX6S8ynAHGh6Zs4lTVQIul3bFbJVD0pmDD2aDncaUhvdXjGk59To1Ol
+rg1Tx8ZudV8EzE4oU5W/mx4Xw0/F6PAcXvD5cFeA2nEMfY8iryyl+1ZwY8yc/OzV
+cN+LovnUWX2UTGAbQ0+F9u/fPfxl+WmdcwsbR8UHAXmYuyoOz8gy65ffKC4kmJvm
+hgER0TMZd0dKvLQaK5aAdYF9w2ViU0tbd56MjZq18Bq80QZZYMaS/7FAg1LcYEoV
+g3cCD4HoLmkRY3wgWJYPxHUN9NOGhrM3bfXHUwaGAWD68DvZAzJL+OcCAwEAAaOB
+mDCBlTAOBgNVHQ8BAf8EBAMCBaAwCQYDVR0TBAIwADAdBgNVHSUEFjAUBggrBgEF
+BQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFF/0rJNIaKTJMgC0gqAM2M7S0EYMMB8G
+A1UdIwQYMBaAFO4B9sr3wUspeP/IG+wPmjA8OjmKMBkGA1UdEQQSMBCCDnd3dy5z
+aW1wbGUub3JnMA0GCSqGSIb3DQEBBQUAA4IBAQCoUYUfMa312MR6CDpoXAdmijXj
+gJhSQyrtJwN0BuyrTIE3wFvFGCRwrGhFZGfzgo+7J1uFZ2Np2sBFl+hiEfzlS2GO
+Ft4s52hNt8WAF6roWGMatLwQXkZtbkpZ2uz8SeUHYskKCv32Qwxtodm6tC1ihyKo
+i6RLVpIML/AvYYe6RrEQ8xSg0GtC28FXQ7dTd41AQ8AVw+/udTYBSa170Pmd5Cr4
+IZIOa/k00VHgcl4TvPuIwJVyNBX5IR4WyRhKHJV1BKXa3jTDJVokvpefIopcLArj
+UPfERJi6R14hKxkfcT7+4tYqt8LWpk0E1ebS7CvEQe3zwVeR10fCKfC4KRNm
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,AAB517FD5312490C
+
+Z4IvcUkeV2vcvF8AR/jt6EDi4MiMKQXNAswQV1BqJMfotGJxQR/XSnrnh0EYadkz
+wRmOhASu6gnVHv0diC37kE1F9DShrqMeia8pXP52hYje/368qTz73/Y70lVIKujz
+G4Ns/1kB2n9CDAi7Kk/hOSnAzpMNirtkabAnpR2VxgyH44DzwMgxavWCGibvIq8Q
+eyqd7sB6lOsP4/gaGILRCGjsYIgtC1xq6rcRRl7Mk07hxjroTy1rtDjOMzJ/RtYX
+FNDqlk0KJeuH/wJCaJ1ys2lA4QSKLs0P/kAh/nmwuMnOFyz46wMEIOE0JEAlJfB/
+yNv93QAOZiyZ71hWtq2hkI6c8znS49rG+ULiYOEDuhU5s54SEHeA1Oz2vtmu5Wz0
+DBzIlCGKNy+v9BOWkbnpAgA/S1cLNGX2Cf5f1ReRYiaHCz6LCaYXzDbV3x8FvU/s
+lKAuuq2nfq2jDHrbSw4Nni8SVsN6vjUzQieQwux0J3XPYMsxwQwL8Z01bZaeom0L
+odQwE64vGtQWsvmhA20dBYthurVzNK9FKZY1oKd7y2vdplKRUg/24J2/HwlZB/ru
+CN4Yrsky5b+q37OafP7ycSCesR+bQWrfLMyUMyUU/9JqH7eIoEh5+LfSZMqSEDAt
+ERi2UxHepSN/oZK8ECAzbUmcSwKJ/oHI5fGnbaN4hsxbcgwoW33+y4ZGFpmaLXj1
+5P2jY1GEfU6ql9oWrRgSAwizfI1riyK+N3x4ecCRf2dO6rMKrJk7AbA3KnQbT76r
+jZOmbGPpWA+7d8RAclr8cAe5LegETzuzA42VanvFhzMoo5opYcaYsPqlXtrj5fbX
+f02l7raps/r1eoJD7esT6leBlQZjAsd2YiGqio1rQmMK5T7kLK2CjLZTGAeQRDJF
+ZiOlZmqC70n5rBFzCUGKw2Hofns7qKpj5+Je9SBbuNkpyahiRRRUlTrQi9sXuEjL
+cd6XX9jURumyv/3XITeauDh4jUtboi4BrBHbztfJIVrXLJ24bsj0OLgnwGHe8vQR
+MUWFRyYGDNzh16hdDPSni1Z6aqwuxiX7FYXs9ZYuUXIl7Klq9wVDLPOHao9xWAWk
+yeMeNETj8bqQmnfO3XgOdH7uOZdZAMFtySKF0Quh/77SvCsqiXFAapwEdZFvnycO
+VmLiQ7eXG4CZZE9QaDfbpY4JcYA3Fyk9OhACErEAyFyEPUTD4B9wXJ1mQNfaWJQD
+Nv6WpCQawCZLPy4Hohe+fCG4ZLZOrTJjXSSNhWPXLgymNbdsWLdcS/tLKfx4nTAY
+XU9ShGTiDE6J0DY2vNmsNK9Gl1b69oiWUc1DkaDc5VYjRpkqhqd2jzeJ3rS0SHLE
+ccejoQA7DPZZ7LqKs6v79STay05CSGpIsC0sa+8JemmaOSRgC8a73dNu6eZ8gywL
+uUvmSEmYXILJhYoObvlxv2mnjwXUpU+svFskj2QxbjVPOC23YNsbZKNWtCRx+cbJ
+RpGye05q7XmjmIeFO3SdnvQmaKpetIEEULihD9hy25OVOH3VbfgBoHdphhCr5Kom
+LXGbxmYKL/Br4bTVkqczLXTBrEAllhEgCkrFfOJX73SUItW53I/p/Jdu7Xm6+f6m
+-----END RSA PRIVATE KEY-----
diff --git a/libnetclient/test/cert_u.pem b/libnetclient/test/cert_u.pem
new file mode 100644
index 0000000..f5ffd65
--- /dev/null
+++ b/libnetclient/test/cert_u.pem
@@ -0,0 +1,51 @@
+-----BEGIN CERTIFICATE-----
+MIID6TCCAtGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB6MRMwEQYKCZImiZPyLGQB
+GRYDb3JnMRYwFAYKCZImiZPyLGQBGRYGc2ltcGxlMRMwEQYDVQQKDApTaW1wbGUg
+SW5jMRowGAYDVQQLDBFTaW1wbGUgU2lnbmluZyBDQTEaMBgGA1UEAwwRU2ltcGxl
+IFNpZ25pbmcgQ0EwHhcNMTYwNTI0MTYyOTE0WhcNMTgwNTI0MTYyOTE0WjBbMRMw
+EQYKCZImiZPyLGQBGRYDb3JnMRYwFAYKCZImiZPyLGQBGRYGc2ltcGxlMRMwEQYD
+VQQKDApTaW1wbGUgSW5jMRcwFQYDVQQDDA53d3cuc2ltcGxlLm9yZzCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBALrxG+kSRCK8qlBNYvEQIEKGe75SC4GB
+s46Sr3lUgpX6S8ynAHGh6Zs4lTVQIul3bFbJVD0pmDD2aDncaUhvdXjGk59To1Ol
+rg1Tx8ZudV8EzE4oU5W/mx4Xw0/F6PAcXvD5cFeA2nEMfY8iryyl+1ZwY8yc/OzV
+cN+LovnUWX2UTGAbQ0+F9u/fPfxl+WmdcwsbR8UHAXmYuyoOz8gy65ffKC4kmJvm
+hgER0TMZd0dKvLQaK5aAdYF9w2ViU0tbd56MjZq18Bq80QZZYMaS/7FAg1LcYEoV
+g3cCD4HoLmkRY3wgWJYPxHUN9NOGhrM3bfXHUwaGAWD68DvZAzJL+OcCAwEAAaOB
+mDCBlTAOBgNVHQ8BAf8EBAMCBaAwCQYDVR0TBAIwADAdBgNVHSUEFjAUBggrBgEF
+BQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFF/0rJNIaKTJMgC0gqAM2M7S0EYMMB8G
+A1UdIwQYMBaAFO4B9sr3wUspeP/IG+wPmjA8OjmKMBkGA1UdEQQSMBCCDnd3dy5z
+aW1wbGUub3JnMA0GCSqGSIb3DQEBBQUAA4IBAQCoUYUfMa312MR6CDpoXAdmijXj
+gJhSQyrtJwN0BuyrTIE3wFvFGCRwrGhFZGfzgo+7J1uFZ2Np2sBFl+hiEfzlS2GO
+Ft4s52hNt8WAF6roWGMatLwQXkZtbkpZ2uz8SeUHYskKCv32Qwxtodm6tC1ihyKo
+i6RLVpIML/AvYYe6RrEQ8xSg0GtC28FXQ7dTd41AQ8AVw+/udTYBSa170Pmd5Cr4
+IZIOa/k00VHgcl4TvPuIwJVyNBX5IR4WyRhKHJV1BKXa3jTDJVokvpefIopcLArj
+UPfERJi6R14hKxkfcT7+4tYqt8LWpk0E1ebS7CvEQe3zwVeR10fCKfC4KRNm
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC68RvpEkQivKpQ
+TWLxECBChnu+UguBgbOOkq95VIKV+kvMpwBxoembOJU1UCLpd2xWyVQ9KZgw9mg5
+3GlIb3V4xpOfU6NTpa4NU8fGbnVfBMxOKFOVv5seF8NPxejwHF7w+XBXgNpxDH2P
+Iq8spftWcGPMnPzs1XDfi6L51Fl9lExgG0NPhfbv3z38ZflpnXMLG0fFBwF5mLsq
+Ds/IMuuX3yguJJib5oYBEdEzGXdHSry0GiuWgHWBfcNlYlNLW3eejI2atfAavNEG
+WWDGkv+xQINS3GBKFYN3Ag+B6C5pEWN8IFiWD8R1DfTThoazN231x1MGhgFg+vA7
+2QMyS/jnAgMBAAECggEBAKDmGwas4RYg2loJcUpYfdukUmdJyHtr/faNjBhaw5aA
+erMnXftvx1gBCHN9iL73ObksyaNm5IXn32dFrRxaSnpsN+zfGOlK5Za08HJ66eyh
+fZMbpG10H/dzrng/uDDJynGPz8CQt/KrNHp4+Nrt9Xi9fThEOnFBeEf/sINk4K1y
++M6lBgMVc7hjI9P6NH92yj8is5X2cBTDx521jnAOwdiNqVeu/ndddYisM2NBhfRK
+oV/lnSZABVLykJM+eKCkcviCjcnL9AkcbXMPKPjh5LbB0NOSi/uKtQfvBEcFDsfx
+Q3H0OWXJwMUMrqWuzsAt63gAKExi3O26T3v43gqPbFECgYEA4fiGrgTM6CR+8BbM
+7SFmhBURVCC54iQ6LGhBzC2BEpQazQ1T2jJyuXAm881Lmc2MkR7mzeiQlDrqjrQv
+1Ao5EEedfiR4IF3oUyDb0UPkT8GF7kholvjCXQgWMADrSln3hnKndlsAI7/y4LYR
+ZKxSSAoS8bv0MyrOU6PuRzDqXSkCgYEA08jTj2ewjbscbSS9tj+RSmbpA1GxQP6D
+ZPukfDAc7seZO24c1tYRFE3+qB5qMzB/sOWveoIc8Ii5LTa0VofMMEiuRKoaedVq
+z8NixkjmRqQ/YQKmJaEUjaXw0tPr/sENnxmRKINTdPGyTQewaX+Xi//wTeaM89ZR
+E76UDfoRV48CgYEAzi0IFTbkDttdxonHKIVVGCZwzSWe9KrBOUtW3YiXP/BKE9WZ
+MHjfHDssDz69P/O/0Zk0VaNgZx7qbJITIZDCSAxPsoxr3nxQQai1Z9ZeNjcIkEUY
+yMbn8CI0vE/aXth/c/iguuiE1BmK7WSxsf1YhcpLRqyLPzRLVR05h1MmQyECgYAz
++P3KHIUcUwXH1xNjbTgnjRezw9F/BLaUCpk0DM6c3ojBJ+WV51SrqfRLp6MiSs2b
+SNKGevXFJZhj+x/IeGzokH2Lxj7XFUOwFt/fjzZLsbUIBpFlfKFBna563pz0mMXR
+/IE98vSBJ2s9Mhzd1v0G01lSlLiUgkzFTqigwXSOCwKBgQCGSDpRZxuL7xfO0++S
+jJxBdGCX/20vVJ3jdvzO2BtmMr87gIgHNA38TG1u5TWcYgvtqzS8kZcKsKJiLkLR
+8qlPYpyCjD+4sOm2Yg7LgH/O0MbEm1RMTpZQ/XVk98afFoXqM9O17EyYL4GIDbVO
+4+Pa9RSnfdHcOTPl4DjTkePpZQ==
+-----END PRIVATE KEY-----
diff --git a/libnetclient/test/inetsim.conf b/libnetclient/test/inetsim.conf
new file mode 100644
index 0000000..d3ab91b
--- /dev/null
+++ b/libnetclient/test/inetsim.conf
@@ -0,0 +1,1932 @@
+#############################################################
+#
+# INetSim configuration file
+#
+#############################################################
+
+
+#############################################################
+# Main configuration
+#############################################################
+
+#########################################
+# start_service
+#
+# The services to start
+#
+# Syntax: start_service <service name>
+#
+# Default: none
+#
+# Available service names are:
+# dns, http, smtp, pop3, tftp, ftp, ntp, time_tcp,
+# time_udp, daytime_tcp, daytime_udp, echo_tcp,
+# echo_udp, discard_tcp, discard_udp, quotd_tcp,
+# quotd_udp, chargen_tcp, chargen_udp, finger,
+# ident, syslog, dummy_tcp, dummy_udp, smtps, pop3s,
+# ftps, irc, https
+#
+# start_service dns
+# start_service http
+# start_service https
+start_service smtp
+start_service smtps
+start_service pop3
+start_service pop3s
+# start_service ftp
+# start_service ftps
+# start_service tftp
+# start_service irc
+# start_service ntp
+# start_service finger
+# start_service ident
+# start_service syslog
+# start_service time_tcp
+# start_service time_udp
+# start_service daytime_tcp
+# start_service daytime_udp
+# start_service echo_tcp
+# start_service echo_udp
+# start_service discard_tcp
+# start_service discard_udp
+# start_service quotd_tcp
+# start_service quotd_udp
+# start_service chargen_tcp
+# start_service chargen_udp
+# start_service dummy_tcp
+# start_service dummy_udp
+
+
+#########################################
+# service_bind_address
+#
+# IP address to bind services to
+#
+# Syntax: service_bind_address <IP address>
+#
+# Default: 127.0.0.1
+#
+#service_bind_address 10.10.10.1
+
+
+#########################################
+# service_run_as_user
+#
+# User to run services
+#
+# Syntax: service_run_as_user <username>
+#
+# Default: inetsim
+#
+#service_run_as_user nobody
+
+
+#########################################
+# service_max_childs
+#
+# Maximum number of child processes (parallel connections)
+# for each service
+#
+# Syntax: service_max_childs [1..30]
+#
+# Default: 10
+#
+#service_max_childs 15
+
+
+#########################################
+# service_timeout
+#
+# If a client does not send any data for the number of seconds
+# given here, the corresponding connection will be closed.
+#
+# Syntax: service_timeout [1..600]
+#
+# Default: 120
+#
+#service_timeout 60
+
+
+#########################################
+# create_reports
+#
+# Create report with a summary of connections
+# for the session on shutdown
+#
+# Syntax: create_reports [yes|no]
+#
+# Default: yes
+#
+#create_reports no
+
+
+#########################################
+# report_language
+#
+# Set language for reports
+# Note: Currently only languages 'en' and 'de' are supported
+#
+# Syntax: report_language <language>
+#
+# Default: en
+#
+#report_language de
+
+
+#############################################################
+# Faketime
+#############################################################
+
+#########################################
+# faketime_init_delta
+#
+# Initial number of seconds (positive or negative)
+# relative to current date/time for fake time used by all services
+#
+# Syntax: faketime_init_delta <number of seconds>
+#
+# Default: 0 (use current date/time)
+#
+#faketime_init_delta 1000
+
+
+#########################################
+# faketime_auto_delay
+#
+# Number of seconds to wait before incrementing fake time
+# by value specified with 'faketime_auto_increment'.
+# Setting to '0' disables this option.
+#
+# Syntax: faketime_auto_delay [0..86400]
+#
+# Default: 0 (disabled)
+#
+#faketime_auto_delay 1000
+
+
+#########################################
+# faketime_auto_increment
+#
+# Number of seconds by which fake time is incremented at
+# regular intervals specified by 'faketime_auto_delay'.
+# This option only takes effect if 'faketime_auto_delay'
+# is enabled (not set to '0').
+#
+# Syntax: faketime_auto_increment [-31536000..31536000]
+#
+# Default: 3600
+#
+#faketime_auto_increment 86400
+
+
+#############################################################
+# Service DNS
+#############################################################
+
+#########################################
+# dns_bind_port
+#
+# Port number to bind DNS service to
+#
+# Syntax: dns_bind_port <port number>
+#
+# Default: 53
+#
+#dns_bind_port 53
+
+
+#########################################
+# dns_default_ip
+#
+# Default IP address to return with DNS replies
+#
+# Syntax: dns_default_ip <IP address>
+#
+# Default: 127.0.0.1
+#
+#dns_default_ip 10.10.10.1
+
+
+#########################################
+# dns_default_hostname
+#
+# Default hostname to return with DNS replies
+#
+# Syntax: dns_default_hostname <hostname>
+#
+# Default: www
+#
+#dns_default_hostname somehost
+
+
+#########################################
+# dns_default_domainname
+#
+# Default domain name to return with DNS replies
+#
+# Syntax: dns_default_domainname <domain name>
+#
+# Default: inetsim.org
+#
+#dns_default_domainname some.domain
+
+
+#########################################
+# dns_static
+#
+# Static mappings for DNS
+#
+# Syntax: dns_static <fqdn hostname> <IP address>
+#
+# Default: none
+#
+#dns_static www.foo.com 10.10.10.10
+#dns_static ns1.foo.com 10.70.50.30
+#dns_static ftp.bar.net 10.10.20.30
+
+
+#########################################
+# dns_version
+#
+# DNS version
+#
+# Syntax: dns_version <version>
+#
+# Default: "INetSim DNS Server"
+#
+#dns_version "9.2.4"
+
+
+#############################################################
+# Service HTTP
+#############################################################
+
+#########################################
+# http_bind_port
+#
+# Port number to bind HTTP service to
+#
+# Syntax: http_bind_port <port number>
+#
+# Default: 80
+#
+#http_bind_port 80
+
+
+#########################################
+# http_version
+#
+# Version string to return in HTTP replies
+#
+# Syntax: http_version <string>
+#
+# Default: "INetSim HTTP server"
+#
+#http_version "Microsoft-IIS/4.0"
+
+
+#########################################
+# http_fakemode
+#
+# Turn HTTP fake mode on or off
+#
+# Syntax: http_fakemode [yes|no]
+#
+# Default: yes
+#
+#http_fakemode no
+
+
+#########################################
+# http_fakefile
+#
+# Fake files returned in fake mode based on the file extension
+# in the HTTP request.
+# The fake files must be placed in <data-dir>/http/fakefiles
+#
+# Syntax: http_fakefile <extension> <filename> <mime-type>
+#
+# Default: none
+#
+http_fakefile txt sample.txt text/plain
+http_fakefile htm sample.html text/html
+http_fakefile html sample.html text/html
+http_fakefile php sample.html text/html
+http_fakefile gif sample.gif image/gif
+http_fakefile jpg sample.jpg image/jpeg
+http_fakefile jpeg sample.jpg image/jpeg
+http_fakefile png sample.png image/png
+http_fakefile bmp sample.bmp image/x-ms-bmp
+http_fakefile ico favicon.ico image/x-icon
+http_fakefile exe sample_gui.exe x-msdos-program
+http_fakefile com sample_gui.exe x-msdos-program
+
+
+#########################################
+# http_default_fakefile
+#
+# The default fake file returned in fake mode if the file extension
+# in the HTTP request does not match any of the extensions
+# defined above.
+#
+# The default fake file must be placed in <data-dir>/http/fakefiles
+#
+# Syntax: http_default_fakefile <filename> <mime-type>
+#
+# Default: none
+#
+http_default_fakefile sample.html text/html
+
+
+#########################################
+# http_static_fakefile
+#
+# Fake files returned in fake mode based on static path.
+# The fake files must be placed in <data-dir>/http/fakefiles
+#
+# Syntax: http_static_fakefile <path> <filename> <mime-type>
+#
+# Default: none
+#
+#http_static_fakefile /path/ sample_gui.exe x-msdos-program
+#http_static_fakefile /path/to/file.exe sample_gui.exe x-msdos-program
+
+
+#############################################################
+# Service HTTPS
+#############################################################
+
+#########################################
+# https_bind_port
+#
+# Port number to bind HTTPS service to
+#
+# Syntax: https_bind_port <port number>
+#
+# Default: 443
+#
+#https_bind_port 443
+
+
+#########################################
+# https_version
+#
+# Version string to return in HTTPS replies
+#
+# Syntax: https_version <string>
+#
+# Default: "INetSim HTTPs server"
+#
+#https_version "Microsoft-IIS/4.0"
+
+
+#########################################
+# https_fakemode
+#
+# Turn HTTPS fake mode on or off
+#
+# Syntax: https_fakemode [yes|no]
+#
+# Default: yes
+#
+#https_fakemode no
+
+
+#########################################
+# https_fakefile
+#
+# Fake files returned in fake mode based on the file extension
+# in the HTTPS request.
+# The fake files must be placed in <data-dir>/http/fakefiles
+#
+# Syntax: https_fakefile <extension> <filename> <mime-type>
+#
+# Default: none
+#
+https_fakefile txt sample.txt text/plain
+https_fakefile htm sample.html text/html
+https_fakefile html sample.html text/html
+https_fakefile php sample.html text/html
+https_fakefile gif sample.gif image/gif
+https_fakefile jpg sample.jpg image/jpeg
+https_fakefile jpeg sample.jpg image/jpeg
+https_fakefile png sample.png image/png
+https_fakefile bmp sample.bmp image/x-ms-bmp
+https_fakefile ico favicon.ico image/x-icon
+https_fakefile exe sample_gui.exe x-msdos-program
+https_fakefile com sample_gui.exe x-msdos-program
+
+
+#########################################
+# https_default_fakefile
+#
+# The default fake file returned in fake mode if the file extension
+# in the HTTPS request does not match any of the extensions
+# defined above.
+#
+# The default fake file must be placed in <data-dir>/http/fakefiles
+#
+# Syntax: https_default_fakefile <filename> <mime-type>
+#
+# Default: none
+#
+https_default_fakefile sample.html text/html
+
+
+#########################################
+# https_static_fakefile
+#
+# Fake files returned in fake mode based on static path.
+# The fake files must be placed in <data-dir>/http/fakefiles
+#
+# Syntax: https_static_fakefile <path> <filename> <mime-type>
+#
+# Default: none
+#
+#https_static_fakefile /path/ sample_gui.exe x-msdos-program
+#https_static_fakefile /path/to/file.exe sample_gui.exe x-msdos-program
+
+
+#########################################
+# https_ssl_keyfile
+#
+# Name of the SSL private key PEM file.
+# The key MUST NOT be encrypted!
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Syntax: https_ssl_keyfile <filename>
+#
+# Default: default_key.pem
+#
+#https_ssl_keyfile https_key.pem
+
+
+#########################################
+# https_ssl_certfile
+#
+# Name of the SSL certificate file.
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Syntax: https_ssl_certfile <filename>
+#
+# Default: default_cert.pem
+#
+#https_ssl_certfile https_cert.pem
+
+
+#########################################
+# https_ssl_dhfile
+#
+# Name of the Diffie-Hellman parameter PEM file.
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Syntax: https_ssl_dhfile <filename>
+#
+# Default: none
+#
+#https_ssl_dhfile https_dh1024.pem
+
+
+#############################################################
+# Service SMTP
+#############################################################
+
+#########################################
+# smtp_bind_port
+#
+# Port number to bind SMTP service to
+#
+# Syntax: smtp_bind_port <port number>
+#
+# Default: 25
+#
+smtp_bind_port 65025
+
+
+#########################################
+# smtp_fqdn_hostname
+#
+# The FQDN hostname used for SMTP
+#
+# Syntax: smtp_fqdn_hostname <string>
+#
+# Default: mail.inetsim.org
+#
+#smtp_fqdn_hostname foo.bar.org
+
+
+#########################################
+# smtp_banner
+#
+# The banner string used in SMTP greeting message
+#
+# Syntax: smtp_banner <string>
+#
+# Default: "INetSim Mail Service ready."
+#
+#smtp_banner "SMTP Mailer ready."
+
+
+#########################################
+# smtp_helo_required
+#
+# Client has to send HELO/EHLO before any other command
+#
+# Syntax: smtp_helo_required [yes|no]
+#
+# Default: no
+#
+smtp_helo_required yes
+
+
+#########################################
+# smtp_extended_smtp
+#
+# Turn support for extended smtp (ESMTP) on or off
+#
+# Syntax: smtp_extended_smtp [yes|no]
+#
+# Default: yes
+#
+#smtp_extended_smtp no
+
+
+#########################################
+# smtp_service_extension
+#
+# SMTP service extensions offered to client.
+# For more information, see
+# <http://www.iana.org/assignments/mail-parameters>
+#
+# Syntax: smtp_service_extension <extension [parameter(s)]>
+#
+# Supported extensions and parameters:
+# VRFY
+# EXPN
+# HELP
+# 8BITMIME
+# SIZE # one optional parameter
+# ENHANCEDSTATUSCODES
+# AUTH # one or more of [PLAIN LOGIN ANONYMOUS CRAM-MD5 CRAM-SHA1]
+# DSN
+# SEND
+# SAML
+# SOML
+# TURN
+# ETRN
+# ATRN
+# VERP
+# MTRK
+# CHUNKING
+# STARTTLS
+# DELIVERBY # one optional parameter
+# SUBMITTER
+# CHECKPOINT
+# BINARYMIME
+# NO-SOLICITING # one optional parameter
+# FUTURERELEASE # two required parameters
+#
+# Default: none
+#
+smtp_service_extension VRFY
+smtp_service_extension EXPN
+smtp_service_extension HELP
+smtp_service_extension 8BITMIME
+smtp_service_extension SIZE 102400000
+smtp_service_extension ENHANCEDSTATUSCODES
+smtp_service_extension AUTH PLAIN LOGIN CRAM-MD5 CRAM-SHA1
+smtp_service_extension DSN
+smtp_service_extension ETRN
+smtp_service_extension STARTTLS
+#
+
+
+#########################################
+# smtp_auth_reversibleonly
+#
+# Only offer authentication mechanisms which allow reversing
+# the authentication information sent by a client
+# to clear text username/password.
+# This option only takes effect if 'smtp_extended_smtp' is
+# enabled and 'smtp_service_extension AUTH' is configured.
+#
+# Syntax: smtp_auth_reversibleonly [yes|no]
+#
+# Default: no
+#
+#smtp_auth_reversibleonly yes
+
+
+#########################################
+# smtp_auth_required
+#
+# Force the client to authenticate.
+# This option only takes effect if 'smtp_extended_smtp' is
+# enabled and 'smtp_service_extension AUTH' is configured.
+#
+# Syntax: smtp_auth_required [yes|no]
+#
+# Default: no
+#
+smtp_auth_required yes
+
+
+#########################################
+# smtp_ssl_keyfile
+#
+# Name of the SSL private key PEM file.
+# The key MUST NOT be encrypted!
+#
+# This option only takes effect if 'smtp_extended_smtp' is
+# enabled and 'smtp_service_extension STARTTLS' is configured.
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Note: If no key file is specified, the extension STARTTLS
+# will be disabled.
+#
+# Syntax: smtp_ssl_keyfile <filename>
+#
+# Default: default_key.pem
+#
+#smtp_ssl_keyfile default_key.pem
+
+
+#########################################
+# smtp_ssl_certfile
+#
+# Name of the SSL certificate PEM file.
+#
+# This option only takes effect if 'smtp_extended_smtp' is
+# enabled and 'smtp_service_extension STARTTLS' is configured.
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Note: If no cert file is specified, the extension STARTTLS
+# will be disabled.
+#
+# Syntax: smtp_ssl_certfile <filename>
+#
+# Default: default_cert.pem
+#
+#smtp_ssl_certfile default_cert.pem
+
+
+#########################################
+# smtp_ssl_dhfile
+#
+# Name of the Diffie-Hellman parameter PEM file.
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Syntax: smtp_ssl_dhfile <filename>
+#
+# Default: none
+#
+#smtp_ssl_dhfile smtp_dh1024.pem
+
+
+
+#############################################################
+# Service SMTPS
+#############################################################
+
+#########################################
+# smtps_bind_port
+#
+# Port number to bind SMTPS service to
+#
+# Syntax: smtps_bind_port <port number>
+#
+# Default: 465
+#
+smtps_bind_port 65465
+
+
+#########################################
+# smtps_fqdn_hostname
+#
+# The FQDN hostname used for SMTPS
+#
+# Syntax: smtps_fqdn_hostname <string>
+#
+# Default: mail.inetsim.org
+#
+#smtps_fqdn_hostname foo.bar.org
+
+
+#########################################
+# smtps_banner
+#
+# The banner string used in SMTPS greeting message
+#
+# Syntax: smtps_banner <string>
+#
+# Default: "INetSim Mail Service ready."
+#
+#smtps_banner "SMTPS Mailer ready."
+
+
+#########################################
+# smtps_helo_required
+#
+# Client has to send HELO/EHLO before any other command
+#
+# Syntax: smtps_helo_required [yes|no]
+#
+# Default: no
+#
+smtps_helo_required yes
+
+
+#########################################
+# smtps_extended_smtp
+#
+# Turn support for extended smtp (ESMTP) on or off
+#
+# Syntax: smtps_extended_smtp [yes|no]
+#
+# Default: yes
+#
+#smtps_extended_smtp no
+
+
+#########################################
+# smtps_service_extension
+#
+# SMTP service extensions offered to client.
+# For more information, see
+# <http://www.iana.org/assignments/mail-parameters>
+#
+# Syntax: smtp_service_extension <extension [parameter(s)]>
+#
+# Supported extensions and parameters:
+# VRFY
+# EXPN
+# HELP
+# 8BITMIME
+# SIZE # one optional parameter
+# ENHANCEDSTATUSCODES
+# AUTH # one or more of [PLAIN LOGIN ANONYMOUS CRAM-MD5 CRAM-SHA1]
+# DSN
+# SEND
+# SAML
+# SOML
+# TURN
+# ETRN
+# ATRN
+# VERP
+# MTRK
+# CHUNKING
+# DELIVERBY # one optional parameter
+# SUBMITTER
+# CHECKPOINT
+# BINARYMIME
+# NO-SOLICITING # one optional parameter
+# FUTURERELEASE # two required parameters
+#
+# Default: none
+#
+smtps_service_extension VRFY
+smtps_service_extension EXPN
+smtps_service_extension HELP
+smtps_service_extension 8BITMIME
+smtps_service_extension SIZE 102400000
+smtps_service_extension ENHANCEDSTATUSCODES
+smtps_service_extension AUTH PLAIN LOGIN CRAM-MD5 CRAM-SHA1
+smtps_service_extension DSN
+smtps_service_extension ETRN
+#
+
+
+#########################################
+# smtps_auth_reversibleonly
+#
+# Only offer authentication mechanisms which allow reversing
+# the authentication information sent by a client
+# to clear text username/password.
+# This option only takes effect if 'smtps_extended_smtp' is
+# enabled and 'smtps_service_extension AUTH' is configured.
+#
+# Syntax: smtps_auth_reversibleonly [yes|no]
+#
+# Default: no
+#
+#smtps_auth_reversibleonly yes
+
+
+#########################################
+# smtps_auth_required
+#
+# Force the client to authenticate.
+# This option only takes effect if 'smtps_extended_smtp' is
+# enabled and 'smtp_service_extension AUTH' is configured.
+#
+# Syntax: smtps_auth_required [yes|no]
+#
+# Default: no
+#
+smtps_auth_required yes
+
+
+#########################################
+# smtps_ssl_keyfile
+#
+# Name of the SSL private key PEM file.
+# The key MUST NOT be encrypted!
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Syntax: smtps_ssl_keyfile <filename>
+#
+# Default: default_key.pem
+#
+#smtps_ssl_keyfile default_key.pem
+
+
+#########################################
+# smtps_ssl_certfile
+#
+# Name of the SSL certificate PEM file.
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Syntax: smtps_ssl_certfile <filename>
+#
+# Default: default_cert.pem
+#
+#smtps_ssl_certfile default_cert.pem
+
+
+#########################################
+# smtps_ssl_dhfile
+#
+# Name of the Diffie-Hellman parameter PEM file.
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Syntax: smtps_ssl_dhfile <filename>
+#
+# Default: none
+#
+#smtps_ssl_dhfile smtps_dh1024.pem
+
+
+#############################################################
+# Service POP3
+#############################################################
+
+#########################################
+# pop3_bind_port
+#
+# Port number to bind POP3 service to
+#
+# Syntax: pop3_bind_port <port number>
+#
+# Default: 110
+#
+#pop3_bind_port 110
+
+
+#########################################
+# pop3_banner
+#
+# The banner string used in POP3 greeting message
+#
+# Syntax: pop3_banner <string>
+#
+# Default: "INetSim POP3 Server ready"
+#
+#pop3_banner "POP3 Server ready"
+
+
+#########################################
+# pop3_hostname
+#
+# The hostname used in POP3 greeting message
+#
+# Syntax: pop3_hostname <string>
+#
+# Default: pop3host
+#
+#pop3_hostname pop3server
+
+
+#########################################
+# pop3_mbox_maxmails
+#
+# Maximum number of e-mails to select from supplied mbox files
+# for creation of random POP3 mailbox
+#
+# Syntax: pop3_mbox_maxmails <number>
+#
+# Default: 10
+#
+#pop3_mbox_maxmails 20
+
+
+#########################################
+# pop3_mbox_reread
+#
+# Re-read supplied mbox files if POP3 service was inactive
+# for <number> seconds
+#
+# Syntax: pop3_mbox_reread <number>
+#
+# Default: 180
+#
+#pop3_mbox_reread 300
+
+
+#########################################
+# pop3_mbox_rebuild
+#
+# Rebuild random POP3 mailbox if POP3 service was inactive
+# for <number> seconds
+#
+# Syntax: pop3_mbox_rebuild <number>
+#
+# Default: 60
+#
+#pop3_mbox_rebuild 120
+
+
+#########################################
+# pop3_enable_apop
+#
+# Turn APOP on or off
+#
+# Syntax: pop3_enable_apop [yes|no]
+#
+# Default: yes
+#
+#pop3_enable_apop no
+
+
+#########################################
+# pop3_auth_reversibleonly
+#
+# Only offer authentication mechanisms which allow reversing
+# the authentication information sent by a client
+# to clear text username/password
+#
+# Syntax: pop3_auth_reversibleonly [yes|no]
+#
+# Default: no
+#
+#pop3_auth_reversibleonly yes
+
+
+#########################################
+# pop3_enable_capabilities
+#
+# Turn support for pop3 capabilities on or off
+#
+# Syntax: pop3_enable_capabilities [yes|no]
+#
+# Default: yes
+#
+#pop3_enable_capabilities no
+
+
+#########################################
+# pop3_capability
+#
+# POP3 capabilities offered to client.
+# For more information, see
+# <http://www.iana.org/assignments/pop3-extension-mechanism>
+#
+# Syntax: pop3_capability <capability [parameter(s)]>
+#
+# Supported capabilities and parameters:
+# TOP
+# USER
+# UIDL
+# SASL # one or more of [PLAIN LOGIN ANONYMOUS CRAM-MD5 CRAM-SHA1]
+# RESP-CODES
+# EXPIRE # one required parameter and one optional parameter
+# LOGIN-DELAY # one required parameter and one optional parameter
+# IMPLEMENTATION # one required parameter
+# AUTH-RESP-CODE
+# STLS
+#
+# Default: none
+#
+pop3_capability TOP
+pop3_capability USER
+pop3_capability SASL PLAIN LOGIN ANONYMOUS CRAM-MD5 CRAM-SHA1
+pop3_capability UIDL
+pop3_capability IMPLEMENTATION "INetSim POP3 server"
+pop3_capability STLS
+#
+
+
+#########################################
+# pop3_ssl_keyfile
+#
+# Name of the SSL private key PEM file.
+# The key MUST NOT be encrypted!
+#
+# This option only takes effect if 'pop3_enable_capabilities' is
+# true and 'pop3_capability STLS' is configured.
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Note: If no key file is specified, capability STLS will be disabled.
+#
+# Syntax: pop3_ssl_keyfile <filename>
+#
+# Default: default_key.pem
+#
+#pop3_ssl_keyfile pop3_key.pem
+
+
+#########################################
+# pop3_ssl_certfile
+#
+# Name of the SSL certificate PEM file.
+#
+# This option only takes effect if 'pop3_enable_capabilities' is
+# true and 'pop3_capability STLS' is configured.
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Note: If no cert file is specified, capability STLS will be disabled.
+#
+# Syntax: pop3_ssl_certfile <filename>
+#
+# Default: default_cert.pem
+#
+#pop3_ssl_certfile pop3_cert.pem
+
+
+#########################################
+# pop3_ssl_dhfile
+#
+# Name of the Diffie-Hellman parameter PEM file.
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Syntax: pop3_ssl_dhfile <filename>
+#
+# Default: none
+#
+#pop3_ssl_dhfile pop3_dh1024.pem
+
+
+#############################################################
+# Service POP3S
+#############################################################
+
+#########################################
+# pop3s_bind_port
+#
+# Port number to bind POP3S service to
+#
+# Syntax: pop3s_bind_port <port number>
+#
+# Default: 995
+#
+#pop3s_bind_port 995
+
+
+#########################################
+# pop3s_banner
+#
+# The banner string used in POP3 greeting message
+#
+# Syntax: pop3s_banner <string>
+#
+# Default: "INetSim POP3 Server ready"
+#
+#pop3s_banner "POP3 Server ready"
+
+
+#########################################
+# pop3s_hostname
+#
+# The hostname used in POP3 greeting message
+#
+# Syntax: pop3s_hostname <string>
+#
+# Default: pop3host
+#
+#pop3s_hostname pop3server
+
+
+#########################################
+# pop3s_mbox_maxmails
+#
+# Maximum number of e-mails to select from supplied mbox files
+# for creation of random POP3 mailbox
+#
+# Syntax: pop3s_mbox_maxmails <number>
+#
+# Default: 10
+#
+#pop3s_mbox_maxmails 20
+
+
+#########################################
+# pop3s_mbox_reread
+#
+# Re-read supplied mbox files if POP3S service was inactive
+# for <number> seconds
+#
+# Syntax: pop3s_mbox_reread <number>
+#
+# Default: 180
+#
+#pop3s_mbox_reread 300
+
+
+#########################################
+# pop3s_mbox_rebuild
+#
+# Rebuild random POP3 mailbox if POP3S service was inactive
+# for <number> seconds
+#
+# Syntax: pop3s_mbox_rebuild <number>
+#
+# Default: 60
+#
+#pop3s_mbox_rebuild 120
+
+
+#########################################
+# pop3s_enable_apop
+#
+# Turn APOP on or off
+#
+# Syntax: pop3s_enable_apop [yes|no]
+#
+# Default: yes
+#
+#pop3s_enable_apop no
+
+
+#########################################
+# pop3s_auth_reversibleonly
+#
+# Only offer authentication mechanisms which allow reversing
+# the authentication information sent by a client
+# to clear text username/password
+#
+# Syntax: pop3s_auth_reversibleonly [yes|no]
+#
+# Default: no
+#
+#pop3s_auth_reversibleonly yes
+
+
+#########################################
+# pop3s_enable_capabilities
+#
+# Turn support for pop3 capabilities on or off
+#
+# Syntax: pop3s_enable_capabilities [yes|no]
+#
+# Default: yes
+#
+#pop3s_enable_capabilities no
+
+
+#########################################
+# pop3s_capability
+#
+# POP3 capabilities offered to client.
+# For more information, see
+# <http://www.iana.org/assignments/pop3-extension-mechanism>
+#
+# Syntax: pop3s_capability <capability [parameter(s)]>
+#
+# Supported capabilities and parameters:
+# TOP
+# USER
+# UIDL
+# SASL # one or more of [PLAIN LOGIN ANONYMOUS CRAM-MD5 CRAM-SHA1]
+# RESP-CODES
+# EXPIRE # one required parameter and one optional parameter
+# LOGIN-DELAY # one required parameter and one optional parameter
+# IMPLEMENTATION # one required parameter
+# AUTH-RESP-CODE
+#
+# Default: none
+#
+pop3s_capability TOP
+pop3s_capability USER
+pop3s_capability SASL PLAIN LOGIN ANONYMOUS CRAM-MD5 CRAM-SHA1
+pop3s_capability UIDL
+pop3s_capability IMPLEMENTATION "INetSim POP3s server"
+#
+
+
+#########################################
+# pop3s_ssl_keyfile
+#
+# Name of the SSL private key PEM file.
+# The key MUST NOT be encrypted!
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Syntax: pop3s_ssl_keyfile <filename>
+#
+# Default: default_key.pem
+#
+#pop3s_ssl_keyfile pop3s_key.pem
+
+
+#########################################
+# pop3s_ssl_certfile
+#
+# Name of the SSL certificate PEM file.
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Syntax: pop3s_ssl_certfile <filename>
+#
+# Default: default_cert.pem
+#
+#pop3s_ssl_certfile pop3s_cert.pem
+
+
+#########################################
+# pop3s_ssl_dhfile
+#
+# Name of the Diffie-Hellman parameter PEM file.
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Syntax: pop3s_ssl_dhfile <filename>
+#
+# Default: none
+#
+#pop3s_ssl_dhfile pop3s_dh1024.pem
+
+
+#############################################################
+# Service TFTP
+#############################################################
+
+#########################################
+# tftp_bind_port
+#
+# Port number to bind TFTP service to
+#
+# Syntax: tftp_bind_port <port number>
+#
+# Default: 69
+#
+#tftp_bind_port 69
+
+
+#########################################
+# tftp_allow_overwrite
+#
+# Allow overwriting of existing files
+#
+# Syntax: tftp_allow_overwrite [yes|no]
+#
+# Default: no
+#
+#tftp_allow_overwrite yes
+
+
+#########################################
+# tftp_enable_options
+#
+# Turn support for tftp options on or off
+#
+# Syntax: tftp_enable_options [yes|no]
+#
+# Default: yes
+#
+#tftp_enable_options no
+
+
+#########################################
+# tftp_option
+#
+# TFTP extensions offered to client.
+# For more information, see RFC 2347
+#
+# Syntax: tftp_option <option [parameter(s)]>
+#
+# Supported extensions and parameters:
+# BLKSIZE # two optional parameters
+# TIMEOUT # two optional parameters
+# TSIZE # one optional parameter
+#
+# Default: none
+#
+tftp_option BLKSIZE 512 65464
+tftp_option TIMEOUT 5 60
+tftp_option TSIZE 10485760
+#
+
+
+#############################################################
+# Service FTP
+#############################################################
+
+#########################################
+# ftp_bind_port
+#
+# Port number to bind FTP service to
+#
+# Syntax: ftp_bind_port <port number>
+#
+# Default: 21
+#
+#ftp_bind_port 21
+
+
+#########################################
+# ftp_version
+#
+# Version string to return in replies to the STAT command
+#
+# Syntax: ftp_version <string>
+#
+# Default: "INetSim FTP Server"
+#
+#ftp_version "vsFTPd 2.0.4 - secure, fast, stable"
+
+
+#########################################
+# ftp_banner
+#
+# The banner string used in FTP greeting message
+#
+# Syntax: ftp_banner <string>
+#
+# Default: "INetSim FTP Service ready."
+#
+#ftp_banner "FTP Server ready"
+
+
+#########################################
+# ftp_recursive_delete
+#
+# Allow recursive deletion of directories,
+# even if they are not empty
+#
+# Syntax: ftp_recursive_delete [yes|no]
+#
+# Default: no
+#
+#ftp_recursive_delete yes
+
+
+#############################################################
+# Service FTPS
+#############################################################
+
+#########################################
+# ftps_bind_port
+#
+# Port number to bind FTP service to
+#
+# Syntax: ftp_bind_port <port number>
+#
+# Default: 990
+#
+#ftps_bind_port 990
+
+
+#########################################
+# ftps_version
+#
+# Version string to return in replies to the STAT command
+#
+# Syntax: ftps_version <string>
+#
+# Default: "INetSim FTPs Server"
+#
+#ftps_version "vsFTPd 2.0.4 - secure, fast, stable"
+
+
+#########################################
+# ftps_banner
+#
+# The banner string used in FTP greeting message
+#
+# Syntax: ftps_banner <string>
+#
+# Default: "INetSim FTP Service ready."
+#
+#ftps_banner "FTP Server ready"
+
+
+#########################################
+# ftps_recursive_delete
+#
+# Allow recursive deletion of directories,
+# even if they are not empty
+#
+# Syntax: ftps_recursive_delete [yes|no]
+#
+# Default: no
+#
+#ftps_recursive_delete yes
+
+
+#########################################
+# ftps_ssl_keyfile
+#
+# Name of the SSL private key PEM file.
+# The key MUST NOT be encrypted!
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Syntax: ftps_ssl_keyfile <filename>
+#
+# Default: default_key.pem
+#
+#ftps_ssl_keyfile ftps_key.pem
+
+
+#########################################
+# ftps_ssl_certfile
+#
+# Name of the SSL certificate PEM file.
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Syntax: ftps_ssl_certfile <filename>
+#
+# Default: default_cert.pem
+#
+#ftps_ssl_certfile ftps_cert.pem
+
+
+#########################################
+# ftps_ssl_dhfile
+#
+# Name of the Diffie-Hellman parameter PEM file.
+#
+# The file must be placed in <data-dir>/certs/
+#
+# Syntax: ftps_ssl_dhfile <filename>
+#
+# Default: none
+#
+#ftps_ssl_dhfile ftps_dh1024.pem
+
+
+#############################################################
+# Service NTP
+#############################################################
+
+#########################################
+# ntp_bind_port
+#
+# Port number to bind NTP service to
+#
+# Syntax: ntp_bind_port <port number>
+#
+# Default: 123
+#
+#ntp_bind_port 123
+
+
+#########################################
+# ntp_server_ip
+#
+# The IP address to return in NTP replies
+#
+# Syntax: ntp_server_ip <IP address>
+#
+# Default: 127.0.0.1
+#
+#ntp_server_ip 10.15.20.30
+
+
+#########################################
+# ntp_strict_checks
+#
+# Turn strict checks for client packets on or off
+#
+# Syntax: ntp_strict_checks [yes|no]
+#
+# Default: yes
+#
+#ntp_strict_checks no
+
+
+#############################################################
+# Service IRC
+#############################################################
+
+#########################################
+# irc_bind_port
+#
+# Port number to bind IRC service to
+#
+# Syntax: irc_bind_port <port number>
+#
+# Default: 6667
+#
+#irc_bind_port 6667
+
+
+#########################################
+# irc_fqdn_hostname
+#
+# The FQDN hostname used for IRC
+#
+# Syntax: irc_fqdn_hostname <string>
+#
+# Default: irc.inetsim.org
+#
+#irc_fqdn_hostname foo.bar.org
+
+
+#########################################
+# irc_version
+#
+# Version string to return
+#
+# Syntax: irc_version <string>
+#
+# Default: "INetSim IRC Server"
+#
+#irc_version "Unreal3.2.7"
+
+
+#############################################################
+# Service Time
+#############################################################
+
+#########################################
+# time_bind_port
+#
+# Port number to bind time service to
+#
+# Syntax: time_bind_port <port number>
+#
+# Default: 37
+#
+#time_bind_port 37
+
+
+#############################################################
+# Service Daytime
+#############################################################
+
+#########################################
+# daytime_bind_port
+#
+# Port number to bind daytime service to
+#
+# Syntax: daytime_bind_port <port number>
+#
+# Default: 13
+#
+#daytime_bind_port 13
+
+
+#############################################################
+# Service Echo
+#############################################################
+
+#########################################
+# echo_bind_port
+#
+# Port number to bind echo service to
+#
+# Syntax: echo_bind_port <port number>
+#
+# Default: 7
+#
+#echo_bind_port 7
+
+
+#############################################################
+# Service Discard
+#############################################################
+
+#########################################
+# discard_bind_port
+#
+# Port number to bind discard service to
+#
+# Syntax: discard_bind_port <port number>
+#
+# Default: 9
+#
+#discard_bind_port 9
+
+
+#############################################################
+# Service Quotd
+#############################################################
+
+#########################################
+# quotd_bind_port
+#
+# Port number to bind quotd service to
+#
+# Syntax: quotd_bind_port <port number>
+#
+# Default: 17
+#
+#quotd_bind_port 17
+
+
+#############################################################
+# Service Chargen
+#############################################################
+
+#########################################
+# chargen_bind_port
+#
+# Port number to bind chargen service to
+#
+# Syntax: chargen_bind_port <port number>
+#
+# Default: 19
+#
+#chargen_bind_port 19
+
+
+#############################################################
+# Service Finger
+#############################################################
+
+#########################################
+# finger_bind_port
+#
+# Port number to bind finger service to
+#
+# Syntax: finger_bind_port <port number>
+#
+# Default: 79
+#
+#finger_bind_port 79
+
+
+#############################################################
+# Service Ident
+#############################################################
+
+#########################################
+# ident_bind_port
+#
+# Port number to bind ident service to
+#
+# Syntax: ident_bind_port <port number>
+#
+# Default: 113
+#
+#ident_bind_port 113
+
+
+#############################################################
+# Service Syslog
+#############################################################
+
+#########################################
+# syslog_bind_port
+#
+# Port number to bind syslog service to
+#
+# Syntax: syslog_bind_port <port number>
+#
+# Default: 514
+#
+#syslog_bind_port 514
+
+
+#########################################
+# syslog_trim_maxlength
+#
+# Chop syslog messages at 1024 bytes.
+#
+# Syntax: syslog_trim_maxlength [yes|no]
+#
+# Default: no
+#
+#syslog_trim_maxlength yes
+
+
+#########################################
+# syslog_accept_invalid
+#
+# Accept invalid syslog messages.
+#
+# Syntax: syslog_accept_invalid [yes|no]
+#
+# Default: no
+#
+#syslog_accept_invalid yes
+
+
+#############################################################
+# Service Dummy
+#############################################################
+
+#########################################
+# dummy_bind_port
+#
+# Port number to bind dummy service to
+#
+# Syntax: dummy_bind_port <port number>
+#
+# Default: 1
+#
+#dummy_bind_port 1
+
+
+#########################################
+# dummy_banner
+#
+# Banner string sent to client if no data has been
+# received for 'dummy_banner_wait' seconds since
+# the client has established the connection.
+# If set to an empty string (""), only CRLF will be sent.
+# This option only takes effect if 'dummy_banner_wait'
+# is not set to '0'.
+#
+# Syntax: dummy_banner <string>
+#
+# Default: "220 ESMTP FTP +OK POP3 200 OK"
+#
+#dummy_banner ""
+
+
+#########################################
+# dummy_banner_wait
+#
+# Number of seconds to wait for client sending any data
+# after establishing a new connection.
+# If no data has been received within this amount of time,
+# 'dummy_banner' will be sent to the client.
+# Setting to '0' disables sending of a banner string.
+#
+# Syntax: dummy_banner_wait [0..600]
+#
+# Default: 5
+#
+#dummy_banner_wait 3
+
+
+#############################################################
+# Redirect
+#############################################################
+
+#########################################
+# redirect_enabled
+#
+# Turn connection redirection on or off.
+#
+# Syntax: redirect_enabled [yes|no]
+#
+# Default: no
+#
+#redirect_enabled yes
+
+
+#########################################
+# redirect_unknown_services
+#
+# Redirect connection attempts to unbound ports
+# to dummy service
+#
+# Syntax: redirect_unknown_services [yes|no]
+#
+# Default: yes
+#
+#redirect_unknown_services no
+
+
+#########################################
+# redirect_external_address
+#
+# IP address used as source address if INetSim
+# acts as a router for redirecting packets to
+# external networks.
+# This option only takes effect if static rules
+# for redirecting packets to external networks
+# are defined (see 'redirect_static_rule' below).
+#
+# Syntax: redirect_external_address <IP address>
+#
+# Default: none
+#
+redirect_external_address 10.10.10.1
+
+
+#########################################
+# redirect_static_rule
+#
+# Static mappings for connection redirection.
+# Note: Currently only protocols tcp, udp and icmp are supported.
+#
+# Syntax: redirect_static_rule tcp|udp <IP address:port> <IP address:port>
+# redirect_static_rule tcp|udp <IP address:> <IP address:>
+# redirect_static_rule tcp|udp <:port> <IP address:>
+# redirect_static_rule tcp|udp <:port> <:port>
+# redirect_static_rule icmp <IP address:icmp-type> <IP address>
+# redirect_static_rule icmp <IP address:> <IP address>
+# redirect_static_rule icmp <:icmp-type> <IP address>
+#
+# Default: none
+#
+# Examples:
+#
+# WWW caching service
+#redirect_static_rule tcp :8080 :80
+#
+# Submission [RFC4409]
+#redirect_static_rule tcp :587 :25
+#
+# Echo-Request [RFC792]
+#redirect_static_rule icmp 10.10.10.20:echo-request 10.1.0.25
+#
+# Redirection based on IP address and/or port:
+#redirect_static_rule tcp 10.10.10.55:88 10.10.10.1:80
+#redirect_static_rule tcp :99 192.168.1.1:25
+#redirect_static_rule tcp 10.10.10.20: 172.16.1.2:
+
+
+#########################################
+# redirect_change_ttl
+#
+# Change the time-to-live header field to a random value
+# in outgoing IP packets.
+#
+# Syntax: redirect_change_ttl [yes|no]
+#
+# Default: no
+#
+#redirect_change_ttl yes
+
+
+#########################################
+# redirect_exclude_port
+#
+# Connections to <service_bind_address> on this port
+# are not redirected
+#
+# Syntax: redirect_exclude_port <protocol:port>
+#
+# Default: none
+#
+redirect_exclude_port tcp:22
+#redirect_exclude_port udp:111
+
+
+#########################################
+# redirect_ignore_bootp
+#
+# If set to 'yes', BOOTP (DHCP) broadcasts will not be redirected
+# (UDP packets with source address 0.0.0.0, port 68 and
+# destination address 255.255.255.255, port 67 or vice versa)
+#
+# Syntax: redirect_ignore_bootp [yes|no]
+#
+# Default: no
+#
+#redirect_ignore_bootp yes
+
+
+#########################################
+# redirect_ignore_netbios
+#
+# If set to 'yes', NetBIOS broadcasts will not be redirected
+# (UDP packets with source/destination port 137/138
+# and destination address x.x.x.255 on the local network)
+#
+# Syntax: redirect_ignore_netbios [yes|no]
+#
+# Default: no
+#
+#redirect_ignore_netbios yes
+
+
+#########################################
+# redirect_icmp_timestamp
+#
+# If set to 'ms', ICMP Timestamp requests will be answered
+# with number of milliseconds since midnight UTC according
+# to faketime.
+# If set to 'sec', ICMP Timestamp requests will be answered
+# with number of seconds since epoch (high order bit of the
+# timestamp will be set to indicate non-standard value).
+# Setting to 'no' disables manipulation of ICMP Timestamp
+# requests.
+#
+# Syntax: redirect_icmp_timestamp [ms|sec|no]
+#
+# Default: ms
+#
+#redirect_icmp_timestamp sec
+
+
+#############################################################
+# End of INetSim configuration file
+#############################################################
diff --git a/libnetclient/test/start-test-env.sh b/libnetclient/test/start-test-env.sh
new file mode 100755
index 0000000..c79d331
--- /dev/null
+++ b/libnetclient/test/start-test-env.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+# $Id$
+
+echo "starting test environment:"
+
+echo "GnuTLS server w/o client checking @ port 65001 as s_server1..."
+ -d -m -S s_server1 \
+ -a --x509keyfile=cert_u.pem --x509certfile=cert_u.pem --echo -p 65001
+echo "GnuTLS server w/ client checking @ port 65002 as s_server2..."
+ -d -m -S s_server2 \
+ -r --verify-client-cert --x509keyfile=cert_u.pem --x509certfile=cert_u.pem \
+ --x509cafile=ca_cert.pem --echo -p 65002
+ -ls
+
+echo "inetsim (as root)..."
+ --config inetsim.conf
+
+echo "shut down GnuTLS servers..."
+ -S s_server1 -X quit
+ -S s_server2 -X quit
diff --git a/libnetclient/test/start-test-env.sh.in b/libnetclient/test/start-test-env.sh.in
new file mode 100644
index 0000000..5479612
--- /dev/null
+++ b/libnetclient/test/start-test-env.sh.in
@@ -0,0 +1,20 @@
+#!/bin/sh
+# $Id$
+
+echo "starting test environment:"
+
+echo "GnuTLS server w/o client checking @ port 65001 as s_server1..."
+@SCREEN@ -d -m -S s_server1 \
+ @GTLSSRV@ -a --x509keyfile=cert_u.pem --x509certfile=cert_u.pem --echo -p 65001
+echo "GnuTLS server w/ client checking @ port 65002 as s_server2..."
+@SCREEN@ -d -m -S s_server2 \
+ @GTLSSRV@ -r --verify-client-cert --x509keyfile=cert_u.pem --x509certfile=cert_u.pem \
+ --x509cafile=ca_cert.pem --echo -p 65002
+@SCREEN@ -ls
+
+echo "inetsim (as root)..."
+@SUDO@ @INETSIM@ --config inetsim.conf
+
+echo "shut down GnuTLS servers..."
+@SCREEN@ -S s_server1 -X quit
+@SCREEN@ -S s_server2 -X quit
diff --git a/libnetclient/test/tests.c b/libnetclient/test/tests.c
new file mode 100644
index 0000000..d636a0b
--- /dev/null
+++ b/libnetclient/test/tests.c
@@ -0,0 +1,470 @@
+/*
+ * tests.c
+ *
+ * Created on: 07.01.2017
+ * Author: albrecht
+ */
+
+#include <sys/types.h>
+#include <signal.h>
+#include <string.h>
+#include <sput.h>
+#include "net-client.h"
+#include "net-client-smtp.h"
+#include "net-client-utils.h"
+
+
+static void test_basic(void);
+static void test_basic_crypt(void);
+static void test_smtp(void);
+static void test_utils(void);
+
+
+int
+main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv)
+{
+ sput_start_testing();
+
+ sput_enter_suite("test basic (plain)");
+ sput_run_test(test_basic);
+
+ sput_enter_suite("test basic (encrypted)");
+ sput_run_test(test_basic_crypt);
+
+ sput_enter_suite("test SMTP");
+ sput_run_test(test_smtp);
+
+ //sput_enter_suite("test POP3");
+ //sput_run_test(test_pop3);
+
+ sput_enter_suite("test utility functions");
+ sput_run_test(test_utils);
+
+ sput_finish_testing();
+
+ return sput_get_return_value();
+}
+
+
+static void
+test_basic(void)
+{
+ static gchar *nc_args[] = { NCAT, "-l", "65000", "--exec", SED " -u -e s/x/ThisIsLong/g", NULL };
+ NetClient *basic;
+ GPid child;
+ GError *error = NULL;
+ gboolean op_res;
+ gchar *read_res;
+
+ sput_fail_unless(net_client_new(NULL, 65000, 42) == NULL, "missing host");
+ sput_fail_unless(net_client_new("localhost", 65000, 0) == NULL, "zero max line length");
+
+ sput_fail_unless((basic = net_client_new("localhost", 65000, 42)) != NULL, "localhost; port 65000");
+ sput_fail_unless(net_client_get_host(NULL) == NULL, "get host w/o client");
+ sput_fail_unless(strcmp(net_client_get_host(basic), "localhost") == 0, "read host ok");
+ sput_fail_unless(net_client_connect(basic, NULL) == FALSE, "connect failed");
+ g_object_unref(basic);
+
+ sput_fail_unless((basic = net_client_new("www.google.com", 80, 1)) != NULL, "www.google.com:80; port
0");
+ sput_fail_unless(net_client_configure(NULL, "localhost", 65000, 42, NULL) == FALSE, "configure w/o
client");
+ sput_fail_unless(net_client_configure(basic, NULL, 65000, 42, NULL) == FALSE, "configure w/o host");
+ sput_fail_unless(net_client_configure(basic, "localhost", 65000, 0, NULL) == FALSE, "configure w/
zero max line length");
+ sput_fail_unless(net_client_configure(basic, "localhost", 65000, 42, NULL) == TRUE, "configure
localhost:65000 ok");
+
+ sput_fail_unless(net_client_set_timeout(NULL, 3) == FALSE, "set timeout w/o client");
+ sput_fail_unless(net_client_set_timeout(basic, 10) == TRUE, "set timeout");
+
+ op_res = net_client_write_line(basic, "Hi There", &error);
+ sput_fail_unless((op_res == FALSE) && (error->code == NET_CLIENT_ERROR_NOT_CONNECTED), "write w/o
connection");
+ g_clear_error(&error);
+ op_res = net_client_read_line(basic, NULL, &error);
+ sput_fail_unless((op_res == FALSE) && (error->code == NET_CLIENT_ERROR_NOT_CONNECTED), "read w/o
connection");
+ g_clear_error(&error);
+
+ op_res =
+ g_spawn_async(NULL, nc_args, NULL, G_SPAWN_STDOUT_TO_DEV_NULL + G_SPAWN_STDERR_TO_DEV_NULL,
NULL, NULL, &child, &error);
+ if (!op_res) {
+ g_error("launching %s failed: %s", nc_args[0], error->message);
+ g_assert_not_reached();
+ }
+ sleep(1);
+
+ sput_fail_unless(net_client_connect(basic, NULL) == TRUE, "connect succeeded");
+ op_res = net_client_connect(basic, &error);
+ sput_fail_unless((op_res == FALSE) && (error->code == NET_CLIENT_ERROR_CONNECTED), "cannot connect
already connected");
+ g_clear_error(&error);
+ op_res = net_client_configure(basic, "localhost", 65000, 42, &error);
+ sput_fail_unless((op_res == FALSE) && (error->code == NET_CLIENT_ERROR_CONNECTED), "cannot configure
already connected");
+ g_clear_error(&error);
+
+ sput_fail_unless(net_client_write_buffer(NULL, "xxx", 3U, NULL) == FALSE, "write buffer w/o client");
+ sput_fail_unless(net_client_write_buffer(basic, NULL, 3U, NULL) == FALSE, "write buffer w/o buffer");
+ sput_fail_unless(net_client_write_buffer(basic, "xxx", 0U, NULL) == FALSE, "write buffer w/o count");
+
+ sput_fail_unless(net_client_write_line(NULL, "%100s", NULL, "x") == FALSE, "write line w/o client");
+ sput_fail_unless(net_client_write_line(basic, NULL, NULL) == FALSE, "write line w/o format string");
+ op_res = net_client_write_line(basic, "%100s", &error, "x");
+ sput_fail_unless((op_res == FALSE) && (error->code == NET_CLIENT_ERROR_LINE_TOO_LONG), "write w/ line
too long");
+ g_clear_error(&error);
+ sput_fail_unless(net_client_write_line(basic, "%s", NULL, "x") == TRUE, "write ok");
+
+ sput_fail_unless(net_client_read_line(NULL, NULL, NULL) == FALSE, "read w/o client");
+ sput_fail_unless(net_client_read_line(basic, NULL, NULL) == TRUE, "read, data discarded");
+ op_res = net_client_read_line(basic, NULL, &error);
+ sput_fail_unless((op_res == FALSE) && (error->code == G_IO_ERROR_TIMED_OUT), "read timeout");
+ g_message("%s %d %s", g_quark_to_string(error->domain), error->code, error->message);
+ g_clear_error(&error);
+
+ sput_fail_unless(net_client_execute(NULL, NULL, "Hi There", NULL) == FALSE, "execute w/o client");
+ sput_fail_unless(net_client_execute(basic, NULL, NULL, NULL) == FALSE, "execute w/o format string");
+ sput_fail_unless(net_client_execute(basic, NULL, "%100s", NULL, "x") == FALSE, "execute w/ xmit line
too long");
+ op_res = net_client_execute(basic, &read_res, "Hi There", NULL);
+ sput_fail_unless((op_res == TRUE) && (strcmp("Hi There", read_res) == 0), "execute ok");
+ g_free(read_res);
+
+ sput_fail_unless(net_client_write_buffer(basic, "H", 1U, NULL) == TRUE, "write buffer part 1");
+ sput_fail_unless(net_client_write_buffer(basic, "i Y", 3U, NULL) == TRUE, "write buffer part 2");
+ sput_fail_unless(net_client_write_buffer(basic, "ou\r\n", 4U, NULL) == TRUE, "write buffer part 3");
+ op_res = net_client_read_line(basic, &read_res, NULL);
+ sput_fail_unless((op_res == TRUE) && (strcmp("Hi You", read_res) == 0), "read back ok");
+ g_free(read_res);
+
+ op_res = net_client_execute(basic, &read_res, "xxxxxxxxxx", &error);
+ sput_fail_unless((op_res == FALSE) && (error->code == NET_CLIENT_ERROR_LINE_TOO_LONG), "read line too
long");
+ g_clear_error(&error);
+
+ kill(child, SIGTERM);
+
+ op_res = net_client_read_line(basic, NULL, &error);
+ sput_fail_unless((op_res == FALSE) && (error->code = NET_CLIENT_ERROR_CONNECTION_LOST), "read line,
client lost");
+ g_clear_error(&error);
+
+ g_object_unref(basic);
+}
+
+
+static gboolean
+check_cert(NetClient *client, GTlsCertificate *peer_cert, GTlsCertificateFlags errors, gpointer user_data)
+{
+ gchar *hash;
+ GByteArray *cert_der = NULL;
+
+ g_object_get(G_OBJECT(peer_cert), "certificate", &cert_der, NULL);
+ hash = g_compute_checksum_for_data(G_CHECKSUM_SHA256, cert_der->data, cert_der->len);
+ g_byte_array_unref(cert_der);
+ g_message("%s(%p, %p, %x, %p) -> fp(sha256) = %s", __func__, client, peer_cert, errors, user_data,
hash);
+ g_free(hash);
+ return TRUE;
+}
+
+
+static gchar *
+get_cert_passwd(NetClient *client, const GByteArray *cert_der, gpointer user_data)
+{
+ g_message("%s(%p, %p, %p)", __func__, client, cert_der, user_data);
+ return g_strdup("test-server");
+}
+
+
+static void
+test_basic_crypt(void)
+{
+ NetClient *basic;
+ gboolean op_res;
+ GError *error = NULL;
+
+ /* tests without client cert check */
+ sput_fail_unless((basic = net_client_new("localhost", 65001, 42)) != NULL, "localhost; port 65001");
+ sput_fail_unless(net_client_is_encrypted(NULL) == FALSE, "NULL client is unencrypted");
+ sput_fail_unless(net_client_is_encrypted(basic) == FALSE, "unconnected client is unencrypted");
+ sput_fail_unless(net_client_start_tls(NULL, NULL) == FALSE, "start tls: no client");
+ op_res = net_client_start_tls(basic, &error);
+ sput_fail_unless((op_res == FALSE) && (error->code == NET_CLIENT_ERROR_NOT_CONNECTED), "start tls:
not connected");
+ g_clear_error(&error);
+ sput_fail_unless(net_client_connect(basic, NULL) == TRUE, "connect ok");
+ sput_fail_unless(net_client_is_encrypted(basic) == FALSE, "still unencrypted");
+ op_res = net_client_start_tls(basic, &error);
+ sput_fail_unless((op_res == FALSE) && (error != NULL), "start tls: bad server cert");
+ g_clear_error(&error);
+ g_object_unref(basic);
+
+ sput_fail_unless((basic = net_client_new("localhost", 65001, 42)) != NULL, "localhost; port 65001");
+ g_signal_connect(G_OBJECT(basic), "cert-check", G_CALLBACK(check_cert), NULL);
+ sput_fail_unless(net_client_connect(basic, NULL) == TRUE, "connect ok");
+ sput_fail_unless(net_client_start_tls(basic, NULL) == TRUE, "start tls: success");
+ sput_fail_unless(net_client_is_encrypted(basic) == TRUE, "is encrypted");
+ op_res = net_client_start_tls(basic, &error);
+ sput_fail_unless((op_res == FALSE) && (error->code == NET_CLIENT_ERROR_TLS_ACTIVE), "start tls:
already started");
+ g_clear_error(&error);
+ g_object_unref(basic);
+
+ /* tests with client cert check */
+ sput_fail_unless((basic = net_client_new("localhost", 65002, 42)) != NULL, "localhost; port 65002");
+ g_signal_connect(G_OBJECT(basic), "cert-check", G_CALLBACK(check_cert), NULL);
+ sput_fail_unless(net_client_connect(basic, NULL) == TRUE, "connect ok");
+ sput_fail_unless(net_client_start_tls(basic, NULL) == FALSE, "start tls: fails");
+ sput_fail_unless(net_client_is_encrypted(basic) == FALSE, "not encrypted");
+ g_object_unref(basic);
+
+ sput_fail_unless((basic = net_client_new("localhost", 65002, 42)) != NULL, "localhost; port 65002");
+ g_signal_connect(G_OBJECT(basic), "cert-check", G_CALLBACK(check_cert), NULL);
+ sput_fail_unless(net_client_set_cert_from_file(NULL, "cert_u.pem", NULL) == FALSE, "load cert file
w/o client");
+ sput_fail_unless(net_client_set_cert_from_file(basic, NULL, NULL) == FALSE, "load cert file w/o
file");
+ sput_fail_unless(net_client_set_cert_from_file(basic, "no_such_file.crt", NULL) == FALSE, "load cert
file, file missing");
+ sput_fail_unless(net_client_set_cert_from_pem(NULL, "This is no cert", NULL) == FALSE, "load cert
buffer w/o client");
+ sput_fail_unless(net_client_set_cert_from_pem(basic, NULL, NULL) == FALSE, "load cert buffer w/o
buffer");
+ op_res = net_client_set_cert_from_pem(basic, "This is no cert", &error);
+ sput_fail_unless((op_res == FALSE) && (error->code == NET_CLIENT_ERROR_GNUTLS), "load cert buffer w/
broken pem data");
+ g_clear_error(&error);
+ sput_fail_unless(net_client_set_cert_from_file(basic, "cert_u.pem", NULL) == TRUE, "load cert file w/
plain key ok");
+ sput_fail_unless(net_client_connect(basic, NULL) == TRUE, "connect ok");
+ sput_fail_unless(net_client_start_tls(basic, NULL) == TRUE, "start tls: ok");
+ sput_fail_unless(net_client_is_encrypted(basic) == TRUE, "encrypted");
+ g_object_unref(basic);
+
+ sput_fail_unless((basic = net_client_new("localhost", 65002, 42)) != NULL, "localhost; port 65002");
+ g_signal_connect(G_OBJECT(basic), "cert-check", G_CALLBACK(check_cert), NULL);
+ sput_fail_unless(net_client_set_cert_from_file(basic, "cert_u.pem", NULL) == TRUE, "load cert file w/
plain key ok");
+ sput_fail_unless(net_client_set_cert_from_file(basic, "cert.pem", NULL) == FALSE, "load cert file w/
crypt key fails");
+ g_signal_connect(G_OBJECT(basic), "cert-pass", G_CALLBACK(get_cert_passwd), NULL);
+ sput_fail_unless(net_client_set_cert_from_file(basic, "cert.pem", NULL) == TRUE, "load cert file w/
crypt key ok");
+ sput_fail_unless(net_client_connect(basic, NULL) == TRUE, "connect ok");
+ sput_fail_unless(net_client_start_tls(basic, NULL) == TRUE, "start tls: ok");
+ sput_fail_unless(net_client_is_encrypted(basic) == TRUE, "encrypted");
+ g_object_unref(basic);
+}
+
+
+typedef struct {
+ gchar *msg_text;
+ gchar *read_ptr;
+} msg_data_t;
+
+
+static gssize
+msg_data_cb(gchar *buffer, gsize count, gpointer user_data, GError **error)
+{
+ msg_data_t *msg_data = (msg_data_t *) user_data;
+ size_t msg_len;
+ gssize result;
+
+ g_message("%s(%p, %lu, %p, %p)", __func__, buffer, count, user_data, error);
+ msg_len = strlen(msg_data->read_ptr);
+ if (msg_len > 0) {
+ if (msg_len > count) {
+ result = count;
+ } else {
+ result = msg_len;
+ }
+ memcpy(buffer, msg_data->read_ptr, result);
+ msg_data->read_ptr = &msg_data->read_ptr[result];
+ } else {
+ result = 0;
+ }
+ g_message("%s: return %ld", __func__, result);
+ return result;
+}
+
+
+#define MSG_TEXT \
+ "From: \"Sender Name\" <me here com>\r\n" \
+ "To: \"Recipient Name\" <you there com>\r\n" \
+ "Cc: \"Other Recipient\" <other there com>\r\n" \
+ "Subject: This is just a test message\r\n" \
+ "Date: Fri, 13 Jan 2017 21:27:11 +0100\r\n" \
+ "Message-ID: <20170113212711 19833 here com>\r\n" \
+ "\r\n" \
+ "This is the body of the test message.\r\n"
+
+
+static gchar **
+get_auth(NetClient *client, gpointer user_data)
+{
+ gchar ** result;
+
+ g_message("%s(%p, %p)", __func__, client, user_data);
+ result = g_new0(gchar *, 3U);
+ result[0] = g_strdup("john.doe");
+ result[1] = g_strdup("@ C0mplex P@sswd");
+ return result;
+}
+
+
+static void
+test_smtp(void)
+{
+ msg_data_t msg_buf;
+ NetClientSmtp *smtp;
+ NetClientSmtpMessage *msg;
+ GError *error = NULL;
+ gboolean op_res;
+ gchar *read_res;
+
+ // message creation
+ msg_buf.msg_text = msg_buf.read_ptr = MSG_TEXT;
+ sput_fail_unless(net_client_smtp_msg_new(NULL, NULL) == NULL, "create msg: no callback");
+ sput_fail_unless((msg = net_client_smtp_msg_new(msg_data_cb, &msg_buf)) != NULL, "create msg: ok");
+
+ sput_fail_unless(net_client_smtp_msg_set_sender(NULL, "some sender com") == FALSE, "set sender, no
message");
+ sput_fail_unless(net_client_smtp_msg_set_sender(msg, NULL) == FALSE, "set sender, no address");
+
+ sput_fail_unless(net_client_smtp_msg_add_recipient(NULL, "you there com", NET_CLIENT_SMTP_DSN_NEVER)
== FALSE,
+ "add recipient, no message");
+ sput_fail_unless(net_client_smtp_msg_add_recipient(msg, NULL, NET_CLIENT_SMTP_DSN_NEVER) == FALSE,
+ "add recipient, no address");
+
+ sput_fail_unless(net_client_smtp_msg_set_dsn_opts(NULL, NULL, FALSE) == FALSE, "set dsn opts, no
message");
+ sput_fail_unless(net_client_smtp_msg_set_dsn_opts(msg, NULL, FALSE) == TRUE, "set dsn opts ok");
+
+
+ // smtp stuff - test various failures
+ sput_fail_unless(net_client_smtp_new(NULL, 0, NET_CLIENT_CRYPT_NONE) == NULL, "missing host");
+
+ sput_fail_unless((smtp = net_client_smtp_new("localhost", 65000, NET_CLIENT_CRYPT_NONE)) != NULL,
"localhost; port 65000");
+ sput_fail_unless(net_client_smtp_connect(smtp, NULL, NULL) == FALSE, "no server");
+ g_object_unref(smtp);
+
+ sput_fail_unless((smtp = net_client_smtp_new("localhost", 65025, NET_CLIENT_CRYPT_NONE)) != NULL,
"localhost:65025");
+ op_res = net_client_smtp_connect(smtp, &read_res, NULL);
+ sput_fail_unless((op_res == TRUE) && (strcmp(read_res, "mail.inetsim.org INetSim Mail Service
ready.") == 0),
+ "connect: success");
+ g_free(read_res);
+ sput_fail_unless(net_client_is_encrypted(NET_CLIENT(smtp)) == FALSE, "not encrypted");
+ op_res = net_client_smtp_connect(smtp, NULL, &error);
+ sput_fail_unless((op_res == FALSE) && (error->code == NET_CLIENT_ERROR_CONNECTED), "cannot
reconnect");
+ g_clear_error(&error);
+ sput_fail_unless(net_client_smtp_can_dsn(NULL) == FALSE, "NULL client: no dsn");
+ sput_fail_unless(net_client_smtp_can_dsn(smtp) == TRUE, "inetsim: can dsn");
+
+ sput_fail_unless(net_client_smtp_send_msg(NULL, msg, NULL) == FALSE, "send msg, NULL client");
+ sput_fail_unless(net_client_smtp_send_msg(smtp, NULL, NULL) == FALSE, "send msg, NULL message");
+ sput_fail_unless(net_client_smtp_send_msg(smtp, msg, NULL) == FALSE, "send msg: error, no sender");
+ sput_fail_unless(net_client_smtp_msg_set_sender(msg, "some sender com") == TRUE, "set sender ok");
+ sput_fail_unless(net_client_smtp_msg_set_sender(msg, "me here com") == TRUE, "replace sender ok");
+ sput_fail_unless(net_client_smtp_send_msg(smtp, msg, NULL) == FALSE, "send msg: error, no recipient");
+ sput_fail_unless(net_client_smtp_msg_add_recipient(msg, "you there com", NET_CLIENT_SMTP_DSN_NEVER)
== TRUE,
+ "add recipient ok (no dsn)");
+ op_res = net_client_smtp_send_msg(smtp, msg, &error);
+ sput_fail_unless((op_res == FALSE) && (error->code == NET_CLIENT_ERROR_SMTP_PERMANENT), "send failed:
not authenticated");
+ g_clear_error(&error);
+ g_object_unref(smtp);
+
+ sput_fail_unless((smtp = net_client_smtp_new("localhost", 65025, NET_CLIENT_CRYPT_STARTTLS)) != NULL,
+ "localhost:65025, starttls");
+ op_res = net_client_smtp_connect(smtp, NULL, &error);
+ sput_fail_unless((op_res == FALSE) && (error != NULL), "connect: fails (untrusted cert)");
+ g_clear_error(&error);
+ g_object_unref(smtp);
+
+ sput_fail_unless((smtp = net_client_smtp_new("localhost", 65025, NET_CLIENT_CRYPT_NONE)) != NULL,
"localhost:65025");
+ sput_fail_unless(net_client_smtp_allow_auth(smtp, FALSE, 0U) == TRUE, "force no auth mech available");
+ g_signal_connect(G_OBJECT(smtp), "auth", G_CALLBACK(get_auth), smtp);
+ op_res = net_client_smtp_connect(smtp, NULL, &error);
+ sput_fail_unless((op_res == FALSE) && (error->code == NET_CLIENT_ERROR_SMTP_NO_AUTH), "connect:
fails");
+ g_clear_error(&error);
+ g_object_unref(smtp);
+
+ // unencrypted, PLAIN auth
+ sput_fail_unless((smtp = net_client_smtp_new("localhost", 65025, NET_CLIENT_CRYPT_NONE)) != NULL,
"localhost:65025");
+ sput_fail_unless(net_client_smtp_allow_auth(NULL, FALSE, NET_CLIENT_SMTP_AUTH_PLAIN) == FALSE, "set
auth meths, no client");
+ sput_fail_unless(net_client_smtp_allow_auth(smtp, FALSE, NET_CLIENT_SMTP_AUTH_PLAIN) == TRUE, "force
auth meth PLAIN");
+ g_signal_connect(G_OBJECT(smtp), "auth", G_CALLBACK(get_auth), smtp);
+ sput_fail_unless(net_client_smtp_connect(smtp, NULL, NULL) == TRUE, "connect: success");
+ sput_fail_unless(net_client_smtp_send_msg(smtp, msg, NULL) == TRUE, "send msg: success");
+ g_object_unref(smtp);
+
+ // STARTTLS required, LOGIN auth
+ sput_fail_unless(net_client_smtp_msg_add_recipient(msg, "other1 there com",
NET_CLIENT_SMTP_DSN_SUCCESS) == TRUE,
+ "add recipient ok (dsn)");
+ sput_fail_unless((smtp = net_client_smtp_new("localhost", 65025, NET_CLIENT_CRYPT_STARTTLS)) != NULL,
+ "localhost:65025, starttls");
+ sput_fail_unless(net_client_smtp_allow_auth(smtp, TRUE, NET_CLIENT_SMTP_AUTH_LOGIN) == TRUE, "force
auth meth LOGIN");
+ g_signal_connect(G_OBJECT(smtp), "cert-check", G_CALLBACK(check_cert), NULL);
+ g_signal_connect(G_OBJECT(smtp), "auth", G_CALLBACK(get_auth), smtp);
+ sput_fail_unless(net_client_smtp_connect(smtp, NULL, NULL) == TRUE, "connect: success");
+ sput_fail_unless(net_client_smtp_send_msg(smtp, msg, NULL) == TRUE, "send msg: success");
+ g_object_unref(smtp);
+
+ // STARTTLS optional, CRAM-MD5 auth
+ sput_fail_unless(net_client_smtp_msg_add_recipient(msg, "other2 there com",
NET_CLIENT_SMTP_DSN_FAILURE) == TRUE,
+ "add recipient ok (dsn)");
+ sput_fail_unless((smtp = net_client_smtp_new("localhost", 65025, NET_CLIENT_CRYPT_STARTTLS_OPT)) !=
NULL,
+ "localhost:65025, starttls");
+ sput_fail_unless(net_client_smtp_allow_auth(smtp, TRUE, NET_CLIENT_SMTP_AUTH_CRAM_MD5) == TRUE,
"force auth meth CRAM-MD5");
+ g_signal_connect(G_OBJECT(smtp), "cert-check", G_CALLBACK(check_cert), NULL);
+ g_signal_connect(G_OBJECT(smtp), "auth", G_CALLBACK(get_auth), smtp);
+ sput_fail_unless(net_client_smtp_connect(smtp, NULL, NULL) == TRUE, "connect: success");
+ sput_fail_unless(net_client_smtp_msg_set_dsn_opts(msg, "20170113212711 19833 here com", FALSE) ==
TRUE, "dsn: envid, headers");
+ sput_fail_unless(net_client_smtp_send_msg(smtp, msg, NULL) == TRUE, "send msg: success");
+ g_object_unref(smtp);
+
+ // SSL, CRAM-SHA1 auth
+ sput_fail_unless(net_client_smtp_msg_add_recipient(msg, "other3 there com",
NET_CLIENT_SMTP_DSN_DELAY) == TRUE,
+ "add recipient ok (dsn)");
+ sput_fail_unless((smtp = net_client_smtp_new("localhost", 65465, NET_CLIENT_CRYPT_ENCRYPTED)) !=
NULL, "localhost:65025, ssl");
+ sput_fail_unless(net_client_smtp_allow_auth(smtp, TRUE, NET_CLIENT_SMTP_AUTH_CRAM_SHA1) == TRUE,
"force auth meth CRAM-SHA1");
+ g_signal_connect(G_OBJECT(smtp), "cert-check", G_CALLBACK(check_cert), NULL);
+ g_signal_connect(G_OBJECT(smtp), "auth", G_CALLBACK(get_auth), smtp);
+ sput_fail_unless(net_client_smtp_connect(smtp, NULL, NULL) == TRUE, "connect: success");
+ sput_fail_unless(net_client_smtp_msg_set_dsn_opts(msg, NULL, TRUE) == TRUE, "dsn: no envid, message");
+ sput_fail_unless(net_client_smtp_send_msg(smtp, msg, NULL) == TRUE, "send msg: success");
+ g_object_unref(smtp);
+
+ // SSL, auto select auth
+ sput_fail_unless(net_client_smtp_msg_add_recipient(msg, "other4 there com",
+ NET_CLIENT_SMTP_DSN_SUCCESS + NET_CLIENT_SMTP_DSN_FAILURE + NET_CLIENT_SMTP_DSN_DELAY) ==
TRUE, "add recipient ok (dsn)");
+ sput_fail_unless((smtp = net_client_smtp_new("localhost", 65465, NET_CLIENT_CRYPT_ENCRYPTED)) !=
NULL, "localhost:65025, ssl");
+ g_signal_connect(G_OBJECT(smtp), "cert-check", G_CALLBACK(check_cert), NULL);
+ g_signal_connect(G_OBJECT(smtp), "auth", G_CALLBACK(get_auth), smtp);
+ sput_fail_unless(net_client_smtp_connect(smtp, NULL, NULL) == TRUE, "connect: success");
+ sput_fail_unless(net_client_smtp_msg_set_dsn_opts(msg, "20170113212711 19833 here com", TRUE) == TRUE,
+ "dsn: envid, message");
+ sput_fail_unless(net_client_smtp_send_msg(smtp, msg, NULL) == TRUE, "send msg: success");
+ g_object_unref(smtp);
+
+ net_client_smtp_msg_free(NULL);
+ net_client_smtp_msg_free(msg);
+}
+
+
+static void
+test_utils(void)
+{
+ gchar *authstr;
+
+ sput_fail_unless(strcmp(net_client_chksum_to_str(G_CHECKSUM_MD5), "MD5") == 0, "checksum string for
MD5");
+ sput_fail_unless(strcmp(net_client_chksum_to_str(G_CHECKSUM_SHA1), "SHA1") == 0, "checksum string for
SHA1");
+ sput_fail_unless(strcmp(net_client_chksum_to_str(G_CHECKSUM_SHA256), "SHA256") == 0, "checksum string
for SHA256");
+ sput_fail_unless(strcmp(net_client_chksum_to_str(G_CHECKSUM_SHA512), "SHA512") == 0, "checksum string
for SHA512");
+ sput_fail_unless(strcmp(net_client_chksum_to_str((GChecksumType) -1), "_UNKNOWN_") == 0, "checksum
string for unknown");
+
+ /* note: test the md5 example from rfc2195, sect. 2; other hashes calculated from
+ * http://www.freeformatter.com/hmac-generator.html */
+#define CRAM_MD5_CHAL "PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+"
+#define CRAM_MD5_USER "tim"
+#define CRAM_MD5_PASS "tanstaaftanstaaf"
+#define CRAM_MD5_RES "dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw"
+#define CRAM_SHA1_RES "dGltIDhjYjEwZWQwYThiNzY0YWIwZTA1MTI1ZGQ2ZWFhMjk1ZjE5YjU5NDM="
+#define CRAM_SHA256_RES
"dGltIGYwMDExYmJmN2MxNjE4ZWY5MGUzZjE4MTQ4ZTVlZGE0ZTE1NjMxYzljMzhlYmVmMDYyYmY3MTQzZmY3MmU5NDQ="
+ sput_fail_unless(net_client_cram_calc(NULL, G_CHECKSUM_MD5, CRAM_MD5_USER, CRAM_MD5_PASS) == NULL,
"cram-md5: no challenge");
+ sput_fail_unless(net_client_cram_calc(CRAM_MD5_CHAL, G_CHECKSUM_MD5, NULL, CRAM_MD5_PASS) == NULL,
"cram-md5: no user");
+ sput_fail_unless(net_client_cram_calc(CRAM_MD5_CHAL, G_CHECKSUM_MD5, CRAM_MD5_USER, NULL) == NULL,
"cram-md5: no password");
+ authstr = net_client_cram_calc(CRAM_MD5_CHAL, G_CHECKSUM_MD5, CRAM_MD5_USER, CRAM_MD5_PASS);
+ sput_fail_unless(strcmp(authstr, CRAM_MD5_RES) == 0, "cram-md5: auth string ok");
+ g_free(authstr);
+
+ authstr = net_client_cram_calc(CRAM_MD5_CHAL, G_CHECKSUM_SHA1, CRAM_MD5_USER, CRAM_MD5_PASS);
+ sput_fail_unless(strcmp(authstr, CRAM_SHA1_RES) == 0, "cram-sha1: auth string ok");
+ g_free(authstr);
+
+ authstr = net_client_cram_calc(CRAM_MD5_CHAL, G_CHECKSUM_SHA256, CRAM_MD5_USER, CRAM_MD5_PASS);
+ sput_fail_unless(strcmp(authstr, CRAM_SHA256_RES) == 0, "cram-sha256: auth string ok");
+ g_free(authstr);
+
+#define AUTH_PLAIN_RES "dGltAHRpbQB0YW5zdGFhZnRhbnN0YWFm"
+ sput_fail_unless(net_client_auth_plain_calc(NULL, CRAM_MD5_PASS) == NULL, "auth plain: no user");
+ sput_fail_unless(net_client_auth_plain_calc(CRAM_MD5_USER, NULL) == NULL, "auth plain: no password");
+ authstr = net_client_auth_plain_calc(CRAM_MD5_USER, CRAM_MD5_PASS);
+ sput_fail_unless(strcmp(authstr, AUTH_PLAIN_RES) == 0, "auth plain: auth string ok");
+ g_free(authstr);
+}
diff --git a/libnetclient/test/valgrind.supp b/libnetclient/test/valgrind.supp
new file mode 100644
index 0000000..b8ba298
--- /dev/null
+++ b/libnetclient/test/valgrind.supp
@@ -0,0 +1,17 @@
+# Valgrind suppressions
+{
+ g_type_register_fundamental
+ Memcheck:Leak
+ match-leak-kinds:all
+ ...
+ fun:g_type_register_fundamental
+}
+
+{
+ g_type_register_static
+ Memcheck:Leak
+ match-leak-kinds:all
+ ...
+ fun:g_type_register_static
+}
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]