diff --git a/.gitignore b/.gitignore index 5845175..1c67f6c 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,10 @@ debian/tmp debian/libsntl-dev/ debian/libsntl/ debian/source/ +aclocal +coq +*.log +*.crt +lib/libsntllv2.pc +lv2sd +lv2sc diff --git a/Makefile.am b/Makefile.am index 5906b10..706ea15 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,7 +8,13 @@ else EXAMPLES = endif -SUBDIRS = include lib $(EXAMPLES) +if BUILD_TESTS +TESTS = tests +else +TESTS = +endif + +SUBDIRS = include lib man $(EXAMPLES) $(TESTS) libsntldocdir = ${prefix}/doc/libsntl libsntldoc_DATA = \ diff --git a/configure.ac b/configure.ac index 183f7b4..e51f37b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT(libsntl, 0.2.1) +AC_INIT(libsntl, 0.3.0) AC_CONFIG_MACRO_DIR(['/tmp/aclocal']) @@ -10,8 +10,6 @@ AM_INIT_AUTOMAKE([1.11]) AM_SILENT_RULES([yes]) -CC=${CC-/opt/windows_64/bin/x86_64-w64-mingw32-g++.exe} - AC_PROG_CC LT_INIT @@ -31,7 +29,6 @@ AM_CONDITIONAL(BUILD_EXAMPLES, test "x$enable_build_examples" = "xyes") AC_DEFINE([BUILD_WIN32], 1, [build for win32]) - dnl PKG_CHECK_MODULES(LIBUUID, [uuid]) PKG_CHECK_MODULES(OPENSSL, [openssl]) @@ -47,4 +44,6 @@ Makefile lib/libsntl.pc lib/Makefile include/Makefile -examples/Makefile]) +man/Makefile +examples/Makefile +tests/Makefile]) diff --git a/debian/changelog b/debian/changelog index 3df100f..faaba8b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,13 @@ +libsntl (0.3.0) stable; urgency=high + + * New improved architecture + * Performance tricks + * Long messages + * Extended API + * It's incompatible with 0.2.xx and below by protocol + + -- Alexander Vdolainen Thu, 16 Jul 2015 22:50:32 +0200 + libsntl (0.2.1) stable; urgency=low * Initial release (Closes: #nnnn) diff --git a/debian/control b/debian/control index d393f91..594e99b 100644 --- a/debian/control +++ b/debian/control @@ -12,12 +12,12 @@ Package: libsntl-dev Section: libdevel Architecture: any Depends: libsntl (= ${binary:Version}), libsexpr-dev, libssl-dev, libtdata-dev, uuid-dev -Description: Development files for libsntl +Description: Development files for libsntl (sntllv2) Development files for sntl library Package: libsntl Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, libsexpr (>= 1.3.1), libssl1.0.0, libtdata (>= 0.2.2), libuuid1 -Description: Secure Networking Transport Layer implementation library +Description: Secure Networking Transport Layer v2 implementation library Library used to develop secure services diff --git a/debian/emacsen-install.ex b/debian/emacsen-install.ex deleted file mode 100644 index d849956..0000000 --- a/debian/emacsen-install.ex +++ /dev/null @@ -1,47 +0,0 @@ -#! /bin/sh -e -# /usr/lib/emacsen-common/packages/install/libsntl - -# Written by Jim Van Zandt , borrowing heavily -# from the install scripts for gettext by Santiago Vila -# and octave by Dirk Eddelbuettel . - -FLAVOR=$1 -PACKAGE=libsntl - -if [ ${FLAVOR} = emacs ]; then exit 0; fi - -echo install/${PACKAGE}: Handling install for emacsen flavor ${FLAVOR} - -#FLAVORTEST=`echo $FLAVOR | cut -c-6` -#if [ ${FLAVORTEST} = xemacs ] ; then -# SITEFLAG="-no-site-file" -#else -# SITEFLAG="--no-site-file" -#fi -FLAGS="${SITEFLAG} -q -batch -l path.el -f batch-byte-compile" - -ELDIR=/usr/share/emacs/site-lisp/${PACKAGE} -ELCDIR=/usr/share/${FLAVOR}/site-lisp/${PACKAGE} -ELRELDIR=../../../emacs/site-lisp/${PACKAGE} - -# Install-info-altdir does not actually exist. -# Maybe somebody will write it. -if test -x /usr/sbin/install-info-altdir; then - echo install/${PACKAGE}: install Info links for ${FLAVOR} - install-info-altdir --quiet --section "" "" --dirname=${FLAVOR} /usr/share/info/${PACKAGE}.info.gz -fi - -install -m 755 -d ${ELCDIR} -cd ${ELDIR} -FILES=`echo *.el` -cd ${ELCDIR} -ln -sf ${ELRELDIR}/*.el . - -cat << EOF > path.el -(debian-pkg-add-load-path-item ".") -(setq byte-compile-warnings nil) -EOF -${FLAVOR} ${FLAGS} ${FILES} -rm -f path.el - -exit 0 diff --git a/debian/emacsen-remove.ex b/debian/emacsen-remove.ex deleted file mode 100644 index 114d368..0000000 --- a/debian/emacsen-remove.ex +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh -e -# /usr/lib/emacsen-common/packages/remove/libsntl - -FLAVOR=$1 -PACKAGE=libsntl - -if [ ${FLAVOR} != emacs ]; then - if test -x /usr/sbin/install-info-altdir; then - echo remove/${PACKAGE}: removing Info links for ${FLAVOR} - install-info-altdir --quiet --remove --dirname=${FLAVOR} /usr/share/info/libsntl.info.gz - fi - - echo remove/${PACKAGE}: purging byte-compiled files for ${FLAVOR} - rm -rf /usr/share/${FLAVOR}/site-lisp/${PACKAGE} -fi diff --git a/debian/emacsen-startup.ex b/debian/emacsen-startup.ex deleted file mode 100644 index 2cb1f63..0000000 --- a/debian/emacsen-startup.ex +++ /dev/null @@ -1,27 +0,0 @@ -;; -*-emacs-lisp-*- -;; -;; Emacs startup file, e.g. /etc/emacs/site-start.d/50libsntl.el -;; for the Debian libsntl package -;; -;; Originally contributed by Nils Naumann -;; Modified by Dirk Eddelbuettel -;; Adapted for dh-make by Jim Van Zandt - -;; The libsntl package follows the Debian/GNU Linux 'emacsen' policy and -;; byte-compiles its elisp files for each 'emacs flavor' (emacs19, -;; xemacs19, emacs20, xemacs20...). The compiled code is then -;; installed in a subdirectory of the respective site-lisp directory. -;; We have to add this to the load-path: -(let ((package-dir (concat "/usr/share/" - (symbol-name debian-emacs-flavor) - "/site-lisp/libsntl"))) -;; If package-dir does not exist, the libsntl package must have -;; removed but not purged, and we should skip the setup. - (when (file-directory-p package-dir) - (if (fboundp 'debian-pkg-add-load-path-item) - (debian-pkg-add-load-path-item package-dir) - (setq load-path (cons package-dir load-path))) - (autoload 'libsntl-mode "libsntl-mode" - "Major mode for editing libsntl files." t) - (add-to-list 'auto-mode-alist '("\\.libsntl$" . libsntl-mode)))) - diff --git a/debian/files b/debian/files index 164faed..9aff3c4 100644 --- a/debian/files +++ b/debian/files @@ -1,2 +1,2 @@ -libsntl-dev_0.2.1_amd64.deb libdevel extra -libsntl_0.2.1_amd64.deb libs extra +libsntl-dev_0.3.0_amd64.deb libdevel extra +libsntl_0.3.0_amd64.deb libs extra diff --git a/debian/libsntl-dev.dirs b/debian/libsntl-dev.dirs index 4418816..c413e42 100644 --- a/debian/libsntl-dev.dirs +++ b/debian/libsntl-dev.dirs @@ -1,2 +1,4 @@ usr/lib usr/include +usr/share/man +usr/share/man/man3 \ No newline at end of file diff --git a/debian/libsntl-dev.install b/debian/libsntl-dev.install index 6cd8ddd..8a3bca9 100644 --- a/debian/libsntl-dev.install +++ b/debian/libsntl-dev.install @@ -2,3 +2,4 @@ usr/include/* usr/lib/lib*.a usr/lib/lib*.so usr/lib/pkgconfig/* +usr/share/man/man3/* diff --git a/debian/libsntl-dev.manpages b/debian/libsntl-dev.manpages new file mode 100644 index 0000000..a09594c --- /dev/null +++ b/debian/libsntl-dev.manpages @@ -0,0 +1,10 @@ +man/sxmsg_rreply.3 +man/connections_create.3 +man/connections_destroy.3 +man/connections_free.3 +man/connections_init.3 +man/sntl_init.3 +man/connections_setsslserts.3 +man/connections_set_priv.3 +man/connections_get_priv.3 +man/connections_set_ondestroy.3 \ No newline at end of file diff --git a/debian/libsntl.substvars b/debian/libsntl.substvars index 2f9fbbd..fd5aa20 100644 --- a/debian/libsntl.substvars +++ b/debian/libsntl.substvars @@ -1,2 +1,2 @@ -shlibs:Depends=libc6 (>= 2.3.2), libsexpr, libssl1.0.0 (>= 1.0.1), libtdata, libuuid1 (>= 2.20.1) +shlibs:Depends=libc6 (>= 2.2.5), libsexpr, libssl1.0.0 (>= 1.0.1), libtdata, libuuid1 (>= 2.20.1) misc:Depends= diff --git a/debian/manpage.1.ex b/debian/manpage.1.ex deleted file mode 100644 index f41a5f5..0000000 --- a/debian/manpage.1.ex +++ /dev/null @@ -1,56 +0,0 @@ -.\" Hey, EMACS: -*- nroff -*- -.\" (C) Copyright 2014 Alexander Vdolainen , -.\" -.\" First parameter, NAME, should be all caps -.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection -.\" other parameters are allowed: see man(7), man(1) -.TH LIBSNTL SECTION "November 24, 2014" -.\" Please adjust this date whenever revising the manpage. -.\" -.\" Some roff macros, for reference: -.\" .nh disable hyphenation -.\" .hy enable hyphenation -.\" .ad l left justify -.\" .ad b justify to both left and right margins -.\" .nf disable filling -.\" .fi enable filling -.\" .br insert line break -.\" .sp insert n+1 empty lines -.\" for manpage-specific macros, see man(7) -.SH NAME -libsntl \- program to do something -.SH SYNOPSIS -.B libsntl -.RI [ options ] " files" ... -.br -.B bar -.RI [ options ] " files" ... -.SH DESCRIPTION -This manual page documents briefly the -.B libsntl -and -.B bar -commands. -.PP -.\" TeX users may be more comfortable with the \fB\fP and -.\" \fI\fP escape sequences to invode bold face and italics, -.\" respectively. -\fBlibsntl\fP is a program that... -.SH OPTIONS -These programs follow the usual GNU command line syntax, with long -options starting with two dashes (`-'). -A summary of options is included below. -For a complete description, see the Info files. -.TP -.B \-h, \-\-help -Show summary of options. -.TP -.B \-v, \-\-version -Show version of program. -.SH SEE ALSO -.BR bar (1), -.BR baz (1). -.br -The programs are documented fully by -.IR "The Rise and Fall of a Fooish Bar" , -available via the Info system. diff --git a/debian/manpage.sgml.ex b/debian/manpage.sgml.ex deleted file mode 100644 index fa85375..0000000 --- a/debian/manpage.sgml.ex +++ /dev/null @@ -1,154 +0,0 @@ - manpage.1'. You may view - the manual page with: `docbook-to-man manpage.sgml | nroff -man | - less'. A typical entry in a Makefile or Makefile.am is: - -manpage.1: manpage.sgml - docbook-to-man $< > $@ - - - The docbook-to-man binary is found in the docbook-to-man package. - Please remember that if you create the nroff version in one of the - debian/rules file targets (such as build), you will need to include - docbook-to-man in your Build-Depends control field. - - --> - - - FIRSTNAME"> - SURNAME"> - - November 24, 2014"> - - SECTION"> - vdo@daze"> - - LIBSEXPR"> - - - Debian"> - GNU"> - GPL"> -]> - - - -
- &dhemail; -
- - &dhfirstname; - &dhsurname; - - - 2003 - &dhusername; - - &dhdate; -
- - &dhucpackage; - - &dhsection; - - - &dhpackage; - - program to do something - - - - &dhpackage; - - - - - - - - DESCRIPTION - - This manual page documents briefly the - &dhpackage; and bar - commands. - - This manual page was written for the &debian; distribution - because the original program does not have a manual page. - Instead, it has documentation in the &gnu; - Info format; see below. - - &dhpackage; is a program that... - - - - OPTIONS - - These programs follow the usual &gnu; command line syntax, - with long options starting with two dashes (`-'). A summary of - options is included below. For a complete description, see the - Info files. - - - - - - - - Show summary of options. - - - - - - - - Show version of program. - - - - - - SEE ALSO - - bar (1), baz (1). - - The programs are documented fully by The Rise and - Fall of a Fooish Bar available via the - Info system. - - - AUTHOR - - This manual page was written by &dhusername; &dhemail; for - the &debian; system (and may be used by others). Permission is - granted to copy, distribute and/or modify this document under - the terms of the &gnu; General Public License, Version 2 any - later version published by the Free Software Foundation. - - - On Debian systems, the complete text of the GNU General Public - License can be found in /usr/share/common-licenses/GPL. - - - -
- - diff --git a/debian/manpage.xml.ex b/debian/manpage.xml.ex deleted file mode 100644 index 73a275f..0000000 --- a/debian/manpage.xml.ex +++ /dev/null @@ -1,291 +0,0 @@ - -.
will be generated. You may view the -manual page with: nroff -man .
| less'. A typical entry -in a Makefile or Makefile.am is: - -DB2MAN = /usr/share/sgml/docbook/stylesheet/xsl/docbook-xsl/manpages/docbook.xsl -XP = xsltproc -''-nonet -''-param man.charmap.use.subset "0" - -manpage.1: manpage.xml - $(XP) $(DB2MAN) $< - -The xsltproc binary is found in the xsltproc package. The XSL files are in -docbook-xsl. A description of the parameters you can use can be found in the -docbook-xsl-doc-* packages. Please remember that if you create the nroff -version in one of the debian/rules file targets (such as build), you will need -to include xsltproc and docbook-xsl in your Build-Depends control field. -Alternatively use the xmlto command/package. That will also automatically -pull in xsltproc and docbook-xsl. - -Notes for using docbook2x: docbook2x-man does not automatically create the -AUTHOR(S) and COPYRIGHT sections. In this case, please add them manually as - ... . - -To disable the automatic creation of the AUTHOR(S) and COPYRIGHT sections -read /usr/share/doc/docbook-xsl/doc/manpages/authors.html. This file can be -found in the docbook-xsl-doc-html package. - -Validation can be done using: `xmllint -''-noout -''-valid manpage.xml` - -General documentation about man-pages and man-page-formatting: -man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ - ---> - - - - - - - - - - - - - -]> - - - - &dhtitle; - &dhpackage; - - - &dhfirstname; - &dhsurname; - Wrote this manpage for the Debian system. -
- &dhemail; -
-
-
- - 2007 - &dhusername; - - - This manual page was written for the Debian system - (and may be used by others). - Permission is granted to copy, distribute and/or modify this - document under the terms of the GNU General Public License, - Version 2 or (at your option) any later version published by - the Free Software Foundation. - On Debian systems, the complete text of the GNU General Public - License can be found in - /usr/share/common-licenses/GPL. - -
- - &dhucpackage; - &dhsection; - - - &dhpackage; - program to do something - - - - &dhpackage; - - - - - - - - - this - - - - - - - - this - that - - - - - &dhpackage; - - - - - - - - - - - - - - - - - - - DESCRIPTION - This manual page documents briefly the - &dhpackage; and bar - commands. - This manual page was written for the Debian distribution - because the original program does not have a manual page. - Instead, it has documentation in the GNU - info - 1 - format; see below. - &dhpackage; is a program that... - - - OPTIONS - The program follows the usual GNU command line syntax, - with long options starting with two dashes (`-'). A summary of - options is included below. For a complete description, see the - - info - 1 - files. - - - - - - - Does this and that. - - - - - - - Show summary of options. - - - - - - - Show version of program. - - - - - - FILES - - - /etc/foo.conf - - The system-wide configuration file to control the - behaviour of &dhpackage;. See - - foo.conf - 5 - for further details. - - - - ${HOME}/.foo.conf - - The per-user configuration file to control the - behaviour of &dhpackage;. See - - foo.conf - 5 - for further details. - - - - - - ENVIRONMENT - - - FOO_CONF - - If used, the defined file is used as configuration - file (see also ). - - - - - - DIAGNOSTICS - The following diagnostics may be issued - on stderr: - - - Bad configuration file. Exiting. - - The configuration file seems to contain a broken configuration - line. Use the option, to get more info. - - - - - &dhpackage; provides some return codes, that can - be used in scripts: - - Code - Diagnostic - - 0 - Program exited successfully. - - - 1 - The configuration file seems to be broken. - - - - - - BUGS - The program is currently limited to only work - with the foobar library. - The upstreams BTS can be found - at . - - - SEE ALSO - - - bar - 1 - , - baz - 1 - , - foo.conf - 5 - - The programs are documented fully by The Rise and - Fall of a Fooish Bar available via the - info - 1 - system. - -
- diff --git a/debian/menu.ex b/debian/menu.ex deleted file mode 100644 index d14a962..0000000 --- a/debian/menu.ex +++ /dev/null @@ -1,2 +0,0 @@ -?package(libsntl):needs="X11|text|vc|wm" section="Applications/see-menu-manual"\ - title="libsntl" command="/usr/bin/libsntl" diff --git a/debian/shlibs.local.ex b/debian/shlibs.local.ex index 5c6fe79..1746db8 100644 --- a/debian/shlibs.local.ex +++ b/debian/shlibs.local.ex @@ -1 +1 @@ -libsntl 0.2.0 libsntl (>> 0.2.0), libsntl (<< 0.2.99) +libsntl 0.3.0 libsntl (>> 0.3.0), libsntl (<< 0.3.99) diff --git a/include/Makefile.am b/include/Makefile.am index 7af89e1..cafb1ff 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1 +1 @@ -nobase_include_HEADERS = sntl/pth_queue.h sntl/connection.h +nobase_include_HEADERS = sntl/sntllv2.h sntl/errno.h sntl/limits.h diff --git a/include/sntl/connection.h b/include/sntl/connection.h deleted file mode 100644 index f784034..0000000 --- a/include/sntl/connection.h +++ /dev/null @@ -1,312 +0,0 @@ -/* - * File: connection.h - * Author: vdo - * - * Created on September 24, 2014, 2:36 AM - */ - -#ifndef __ESXC_CONNECTION_H_ -#define __ESXC_CONNECTION_H_ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include - -/* error codes */ -#define ESXOREPLYREQ 44 /* protocol require reply with expression, - * or expression return for the request */ -#define ESXOTIMEDOUT 45 /* timedout */ -#define ESXRCBADPROT 46 /* invalid protocol */ -#define ESXNOCONNECT 47 /* connection is lost */ -#define ESXNOCHANSUP 48 -#define ESXRAPIDREPLY 49 - -#define VERIFY_DEPTH 1 /* FIXME: */ - -#define MAX_CONNECTIONS 32768 -#define MAX_CHANNELS 4096 -#define MAX_RPC_LIST 2048 -#define MAX_MULTI 12 -#define MAX_PENDINGMSG 16384 -#define MAX_MSGINDEX ((MAX_PENDINGMSG) * (MAX_MULTI)) - -typedef struct __perm_context_type { - char *login; - char *passwd; - ulong_t certid; - ulong_t uid; - ulong_t gid; - ulong_t *gids; - int n_gids; - int p_attr; - struct in_addr *addr; - void *priv; -} perm_ctx_t; - -#define CXCONN_MASTER (1 << 1) -#define CXCONN_SLAVE (1 << 2) -#define CXCONN_ESTABL (1 << 3) -#define CXCONN_BROKEN (1 << 4) - - -struct __connections_subsys_type; -/* - * älä jätä kommentteja omalla kielellä! yksinkertaisia englanti sijaan! - * i found somebody who write comments and messages in non-english, - * itäs a fucking practice - forget it. - */ -typedef struct __connection_t { - struct __connections_subsys_type *ssys; /* < connections subsystem */ - char *uuid; /** < uuid of the connection */ - idx_allocator_t *idx_ch; /** < index allocation for channels */ - usrtc_t *chnl_tree; /** < search tree of all channels */ - usrtc_t *rpc_list; /** < search tree of possible RPC typed lists */ - SSL_CTX *ctx; /** < SSL context */ - SSL *ssl; /** < SSL connection */ - int ssl_data_index; /** < SSL index for the custom data */ - perm_ctx_t *pctx; /** < higher layer authentification context */ - pthread_t cthread; /** < thread for listening the connection socket */ - pthread_t rmsgthread; /** < thread for message queue (1) */ - pthread_t msgthread; /** < thread for message queue (2) */ - pth_queue_t *mqueue; /** < message queue (2) */ - pth_queue_t *rqueue; /** < message queue (1) */ - pth_dqtpoll_t *tpoll; /** < thread poll for rpc requests */ - pthread_mutex_t oplock; /** < mutex used to sync operations on connection */ - pthread_rwlock_t chnl_lock; /** < rwlock used to sync ops with channels */ - int flags; /** < flags of the connection */ - usrtc_node_t csnode; /** < node to store the connection within list */ -} conn_t; - -struct __connection_rpc_list_type; -struct __message_t; - -#define ESXCHAN_PENDING (1 << 1) -#define ESXCHAN_CLOSURE (1 << 2) - -typedef struct __channel_t { - ulong_t cid; /** < ID of the channel */ - char *uuid; /** < UUID of the channel, used in advanced implementation - * of the complex distributed systems */ - conn_t *connection; /** < pointer to the parent connection */ - idx_allocator_t *idx_msg; /** < index allocation for messages */ - usrtc_t *msgs_tree; /** < search tree of the existing messages */ - struct __message_t *sysmsg; /** < system message used to operate with channel */ - struct __connection_rpc_list_type *rpc_list; /** < rpc functions list */ - pthread_mutex_t oplock; /** < operation ops lock */ - pthread_rwlock_t msglock; /** < rwlock used to operate with messages */ - usrtc_node_t node; /** < node for connection search tree */ - int use_count; /** < use count */ - int flags; /** < flags of the channel */ -} chnl_t; - -typedef struct __sexp_payload_t { - char *cstr; - sexp_t *sx; -} sxpayload_t; - -#define ESX_SYSMSG_SIZE 512 - -#define ESXMSG_SYS (1 << 1) -#define ESXMSG_USR (1 << 2) -#define ESXMSG_PENDING (1 << 3) -#define ESXMSG_NOWAY (1 << 4) -#define ESXMSG_TIMEDOUT (1 << 5) -#define ESXMSG_PULSE (1 << 6) /* obsolete flag */ -#define ESXMSG_NOWAIT (1 << 7) -#define ESXMSG_ISREPLY (1 << 8) -#define ESXMSG_CLOSURE (1 << 9) -#define ESXMSG_RMONRETR (1 << 10) -#define ESXMSG_KILLTHRD (1 << 11) -#define ESXMSG_ISRAPID (1 << 12) - -/** - * \brief Message used in sntl message passing - * - * This structure used to manage a message within a channel - * of the sntl structure stack. - */ -typedef struct __message_t { - chnl_t *pch; /** < channel of the message(if applicable) */ - ulong_t mid; /** < unique ID within connection context */ - char *uuid; /** < UUID of the message, used for special messages */ - usrtc_node_t pendingq_node; /** < node for the pending queue */ - pthread_mutex_t wait; /** < special wait mutex, used for sync */ - void *payload; /** < payload */ - sexp_t *initial_sx; - int opcode; /** < opcode for system and pulse messages */ - int flags; /** < flags of the message (type, state etc ...)*/ - int use_count; /** < use count */ -} sxmsg_t; - -typedef struct __connection_rpc_entry_type { - char *name; - int (*rpcf)(void *, sexp_t *); - usrtc_node_t node; -} cx_rpc_t; - -typedef struct __connection_rpc_list_type { - usrtc_t *rpc_tree; /** < search tree for the rpc lookup */ - char *opt_version; /** < reserved for future implementations */ -} cx_rpc_list_t; - -/** - * \brief Connection subsystem structure. - * - * This structure used for management and control a set of a - * determined connections with the same RPC lists and the same - * mode (server, client). - * - */ -typedef struct __connections_subsys_type { - usrtc_t *connections; - pth_queue_t *ioq; /** < general messages queue */ - pth_queue_t *ioqueue; /** < system messages queue */ - /* system threads */ - pthread_t iog_thread; /** < general io queue */ - pthread_t ios_thread; /** < system io queue */ - pthread_rwlock_t rwlock; - char *rootca, *certpem, *certkey; /* path name to the certificates */ - cx_rpc_list_t *system_rpc; - /* special functions pointers */ - int (*validate_sslpem)(conn_t *); /** < this function used to validate SSL certificate while SSL handshake */ - int (*secure_check)(conn_t *); /** < this function authorize user to login, - * and also should check SSL cert and user, and already made sessions */ - usrtc_t* (*get_rpc_typed_list_tree)(conn_t *); /** < this function is used to set RPC list of the functions */ - int (*set_typed_list_callback)(conn_t *, int, char *); /** < this function is a callback - * during setting up a typed channel */ - void (*on_destroy)(conn_t *); /** < callback on connection destroy */ - void *priv; -} conn_sys_t; - -typedef struct __rpc_typed_list_type { - int type_id; - char *description; - cx_rpc_list_t *rpc_list; - usrtc_node_t lnode; -} rpc_typed_list_t; - -/* General API */ -/* subsystem */ - -extern conn_sys_t *conn_sys; /* an old obsolete method */ -/* old API from 0.1.xx */ -#define connections_subsystem_init() { conn_sys = (conn_sys_t *)malloc(sizeof(conn_sys_t)); connections_init(conn_sys); } - -#define connections_subsystem_setsslserts(a, b, c) connections_setsslserts(conn_sys, a, b, c) - -#define connections_subsystem_setrpclist_function(a) connections_setrpclist_function(conn_sys, a) - -#define connections_set_priv(c, p) (c)->priv = (void *)p - -#define connections_get_priv(c) (c)->priv - -#ifdef __cplusplus -extern "C" { -#endif - - /** call this function before use sntl related functions */ -int sntl_init(void); - -/* new */ -int connections_init(conn_sys_t *ssys); - -int connections_setsslserts(conn_sys_t *ssys, const char *rootca, - const char *certpem, const char *certkey); - -int connections_setrpclist_function(conn_sys_t *ssys, - usrtc_t* (*get_rpc_typed_list_tree)(conn_t *)); - - -#ifdef __cplusplus -} -#endif - -#define connections_subsystem_set_securecheck(c, fuu) (c)->secure_check = fuu -#define connections_subsystem_set_sslvalidator(c, fuu) (c)->validate_sslpem = fuu -#define connections_subsystem_set_rpctlist_call(c, fuu) (c)->set_typed_list_callback = fuu -#define connections_subsystem_set_on_destroy(c, fuu) (c)->on_destroy = fuu - -/* connection - compatibility (old versions) macros */ -#define connection_create(c, s) connection_create_fapi((c), (s), NULL) -#define connection_initiate(c, h, p, s, p1) connection_initiate_m(conn_sys, c, h, p, s, p1) -#define connection_create_fapi(c, s, a) connection_create_fapi_m(conn_sys, c, s, a) -#ifdef __cplusplus -extern "C" { -#endif - -/* new */ -int connection_initiate_m(conn_sys_t *ssys, conn_t *co, const char *host, - int port, const char *SSL_cert, perm_ctx_t *pctx); - -int connection_create_fapi_m(conn_sys_t *ssys, conn_t *co, int sck, - struct in_addr *addr); - -int connection_close(conn_t *co); - -/* channels */ -int channel_open(conn_t *co, chnl_t **ch, int type); - -int channel_close(chnl_t *chnl); - -/* message passing */ -int msg_send(chnl_t *ch, sexp_t *sx, sxmsg_t **msg); - -int msg_send_timed(chnl_t *ch, sexp_t *sx, sxmsg_t **msg, struct timespec *tio); - -int msg_return(sxmsg_t *msg, int opcode); - -int msg_reply(sxmsg_t *msg, sexp_t *sx); - -int msg_reply_timed(sxmsg_t *msg, sexp_t *sx, struct timespec *tio); - -/* reply with S expression without confirmation of delivery and applying */ -int msg_reply_rapid(sxmsg_t *msg, sexp_t *sx); - -/* this is required to clean the message in case if it's a rapid message */ -int msg_rapid_clean(sxmsg_t *msg); - -#ifdef __cplusplus -} -#endif - -/* additional functions */ -#define sntl_msg_get_secctx(m) (m)->pch->connection->pctx - -/* RPC List API */ -#define SNTL_FILTER_INC 0xa -#define SNTL_FILTER_EXC 0xb -#define SNTL_FILTER_END -1 - -#ifdef __cplusplus -extern "C" { -#endif - -int sntl_rpclist_init(usrtc_t *tree); - -int sntl_rpclist_add(usrtc_t *tree, int type, const char *description, - const char *version); - -int sntl_rpclist_add_function(usrtc_t *tree, int type, const char *fu_name, - int (*rpcf)(void *, sexp_t *)); - -int sntl_rpclist_filter(usrtc_t *source, usrtc_t **dest, int flag, int *filter); - -#ifdef __cplusplus -} -#endif - -/* for DEBUG purposes */ -#define __DBGLINE fprintf(stderr, "%s:%d at %s\n", __FILE__, __LINE__, __FUNCTION__) - -#endif /* __ESXC_CONNECTION_H_ */ - diff --git a/include/sntl/errno.h b/include/sntl/errno.h new file mode 100644 index 0000000..e6e0f4c --- /dev/null +++ b/include/sntl/errno.h @@ -0,0 +1,54 @@ +/* + * Secure Network Transport Layer Library v2 implementation. + * (sntllv2) it superseed all versions before due to the: + * - memory consumption + * - new features such as pulse emitting + * - performance optimization + * + * This is a proprietary software. See COPYING for further details. + * + * (c) Askele Group 2013-2015 + * + */ + +#ifndef __SNTL_ERRNO_H__ +#define __SNTL_ERRNO_H__ + +#define __SNTL_EPREFIX 200 + +#define SNE_SUCCESS 200 +#define SNE_FAILED 201 +#define SNE_ENOMEM 202 +#define SNE_BADPROTO 203 +#define SNE_ENORPC 204 +#define SNE_EPERM 205 +#define SNE_TOOLONG 206 +#define SNE_EBUSY 207 +#define SNE_WOULDBLOCK 208 +#define SNE_LINKERROR 209 +#define SNE_NOSUCHMSG 210 +#define SNE_NOSUCHCHAN 211 +#define SNE_ETIMEDOUT 212 +#define SNE_IGNORED 213 +#define SNE_REPLYREQ 214 +#define SNE_RAPIDMSG 215 +#define SNE_ESSL 216 +#define SNE_NOCHANNELS 217 +#define SNE_MCHANNELS 218 +#define SNE_MMESSAGES 219 +#define SNE_LINKBROKEN 220 +#define SNE_INVALINDEX 221 + +/* some old errors for compatibility */ +#define ESXOREPLYREQ SNE_REPLYREQ /* protocol require reply with expression, + * or expression return for the request */ +#define ESXOTIMEDOUT SNE_ETIMEDOUT /* timedout */ +#define ESXRCBADPROT SNE_BADPROTO /* invalid protocol */ +#define ESXNOCONNECT SNE_LINKERROR /* connection is lost */ +#define ESXNOCHANSUP SNE_NOSUCHCHAN +#define ESXRAPIDREPLY SNE_RAPIDMSG + +const char *sntll_errno2cstr(int); + +#endif /* __SNTL_ERRNO_H__ */ + diff --git a/include/sntl/limits.h b/include/sntl/limits.h new file mode 100644 index 0000000..f69a45b --- /dev/null +++ b/include/sntl/limits.h @@ -0,0 +1,21 @@ +/* + * Secure Network Transport Layer Library v2 implementation. + * (sntllv2) it superseed all versions before due to the: + * - memory consumption + * - new features such as pulse emitting + * - performance optimization + * + * This is a proprietary software. See COPYING for further details. + * + * (c) Askele Group 2013-2015 + * + */ + +#ifndef __SNTL_LIMITS_H__ +#define __SNTL_LIMITS_H__ + +#define MAX_RPC_LIST 512 + +#define MAX_RBBUF_LEN (65536 - sizeof(sntllv2_head_t)) + +#endif /* __SNTL_LIMITS_H__ */ diff --git a/include/sntl/pth_queue.h b/include/sntl/pth_queue.h deleted file mode 100644 index 521e849..0000000 --- a/include/sntl/pth_queue.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * This is a proprietary software. See COPYING for further details. - * - * (c) 2013 Copyright Askele, inc. - * (c) 2013 Copyright Askele Ingria, inc. - * (c) 2014 Copyright Confident, inc. (granted permission to use in commercial software) - */ - -/** - * @file pth_queue.h - * @author Alexander Vdolainen - * @date 4 Nov 2013, 20 Dec 2014 (dynamic polls) - * @brief queue implementation for threads intercommunication - * - */ - -#ifndef __PTH_QUEUE_H__ -#define __PTH_QUEUE_H__ - -#include -#include - -/* possible message types, ones with POLL_ prefix valid on for pth_dqtpoll_* */ -#define SYS_MSG 0x0f0affee -#define USR_MSG 0x0afeeffe -#define POLL_DECREASE 0x0afafafe -#define POLL_INCREASE 0x0afaffff -#define NIL_MSG 0x0 -#define END_MSG 0xdead0000 - -/* max amount of threads within the poll */ -#define MAX_POLL_VALUE 32 - -typedef struct pth_msg_s { - void *data; /** < message payload */ - unsigned int msgtype; /** < message type ID */ - unsigned int qlength; /** < current queue length (actual on add moment), - * it makes no sense with few readers */ - usrtc_node_t node; -} pth_msg_t; - -typedef struct pth_queue_s { - unsigned int length; - /* sync */ - pthread_mutex_t mutex; - pthread_cond_t cond; - /* queue data */ - usrtc_t qtree; - /* cache */ - usrtc_t msgcache; -} pth_queue_t; - -int pth_queue_init(pth_queue_t *queue); - -int pth_queue_add(pth_queue_t *queue, void *data, unsigned int msgtype); - -int pth_queue_get(pth_queue_t *queue, const struct timespec *timeout, - pth_msg_t *msg); - -unsigned int pth_queue_length(pth_queue_t *queue); - -int pth_queue_destroy(pth_queue_t *queue, int freedata, - void (*free_msg)(void *)); - -/* dynamic queue thread poll ... bbrrr .... ok, ok with beer - * Dynamic queue thread poll is a queue like pth_queue, - * but also it has itäs own mamagement for threads - that's - * why dynamic. - * Ideally, the model is trying to achieve the following: - * 1. one thread in queue while no or very small amount of jobs in the queue - * 2. grow until max threads is reached while too many requests - * 3. gently slide down volume of threads after job heat - * 4. minimal additional drawbacks (i hate something periodically running, - * it's bad practice) - * The model is quite simple, we should make spurious wakeups equal to zero, - * if no - decrease poll value, and, if we don't have thread available - - * create it. - */ -typedef struct pth_dqtpoll_s { - pth_queue_t *queue; /** < Job queue */ - pthread_t *poll; /** < Thread descriptors */ - int (*jobdata_callback)(void *); /** < Callback to have a deal with data */ - int flags; /** < Flags */ - idx_allocator_t *idx; /** < index allocator for the poll threads */ - pthread_rwlock_t stats_lock; /** < rwlock for stats data */ - unsigned long spurious_wakeups; /** < amount of spurios wakeups */ - int poll_value; /** < value of the poll (totally) */ - struct timeval sched_time; - int msgop; -} pth_dqtpoll_t; - -/* flags for poll */ -#define DQTPOLL_RUNNING (1 << 1) /* poll is running */ -#define DQTPOLL_DEADSTAGE (1 << 2) /* poll in the stage of destroy */ - -/* keep it stupid */ -#define DQTPOLL_DELTAMS 500000 -#define DQTPOLL_DELTASE 0 - -/* init poll, structure must be allocated */ -int pth_dqtpoll_init(pth_dqtpoll_t*, int (*jobdata_callback)(void *)); - -/* run poll: poll */ -int pth_dqtpoll_run(pth_dqtpoll_t*); - -/* add the job to the queue: poll, job data, message type */ -int pth_dqtpoll_add(pth_dqtpoll_t*, void*, unsigned int); - -/* destroy the poll: poll, force flag - * if force flag is set (!= 0), give up - * about jobs, if no, do the job, but don't - * accept the new ones, and destroy all poll - * with last thread. - */ -int pth_dqtpoll_destroy(pth_dqtpoll_t*, int); - -#endif /* __PTH_QUEUE_H__ */ diff --git a/include/sntl/sntllv2.h b/include/sntl/sntllv2.h new file mode 100644 index 0000000..896eb1d --- /dev/null +++ b/include/sntl/sntllv2.h @@ -0,0 +1,274 @@ +/* + * Secure Network Transport Layer Library v2 implementation. + * (sntllv2) it superseed all versions before due to the: + * - memory consumption + * - new features such as pulse emitting + * - performance optimization + * + * This is a proprietary software. See COPYING for further details. + * + * (c) Askele Group 2013-2015 + * + */ + +#ifndef __SNTL_SNTLLV2_H__ +#define __SNTL_SNTLLV2_H__ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#define VERIFY_DEPTH 1 /* FIXME: */ + +typedef struct __perm_context_type { + char *login; + char *passwd; + uint64_t certid; + struct in_addr *addr; + void *priv; +} perm_ctx_t; + +/* 8 byte header */ +typedef struct __sntllv2_head_type { + uint16_t msgid; + uint16_t payload_length; + uint8_t attr; + uint8_t opcode; + uint16_t reserve; +}__attribute__((packed)) sntllv2_head_t; + +struct __connections_subsys_type; +struct __channel_t; +struct __message_t; + +/* flags for the connection link */ +#define SNSX_BATCHMODE (1 << 1) +#define SNSX_MESSAGINGMODE (1 << 2) +#define SNSX_ALIVE (1 << 3) +#define SNSX_CLOSED (1 << 4) + +/* + * älä jätä kommentteja omalla kielellä! yksinkertaisia englanti sijaan! + * i found somebody who write comments and messages in non-english, + * it's a fucking practice - forget it. + */ +typedef struct __connection_t { + /* General section */ + struct __connections_subsys_type *ssys; /* < connections subsystem */ + char *uuid; /** < uuid of the connection */ + /* Channels section */ + idx_allocator_t idx_ch; /** < index allocation for channels */ + pthread_mutex_t idx_ch_lock; /** < mutex for allocating and deallocating channels */ + struct __channel_t **channels; /** < channels O(1) storage */ + /* RPC section */ + usrtc_t *rpc_list; /** < search tree of possible RPC typed lists */ + /* SSL related section */ + SSL_CTX *ctx; /** < SSL context */ + SSL *ssl; /** < SSL connection */ + int ssl_data_index; /** < SSL index for the custom data */ + pthread_mutex_t sslinout[2]; /** < SSL related locks for in and out */ + /* Security section */ + perm_ctx_t *pctx; /** < higher layer authentification context */ + /* Messages section */ + struct __message_t **messages; /** < messages O(1) storage */ + idx_allocator_t idx_msg; + pthread_mutex_t idx_msg_lock; + list_head_t write_pending; /** < list of messages waiting for write */ + pthread_mutex_t write_pending_lock; + volatile uint8_t pending_messages; /** < pending message count */ + /* Other stuff */ + pthread_t thrd_poll[8]; + volatile uint8_t flags; /** < flags of the connection */ + volatile uint8_t usecount; + usrtc_node_t csnode; /** < node to store the connection within list */ +} conn_t; + +struct __connection_rpc_list_type; +struct __message_t; + +typedef struct __pp_msg_type { + struct __message_t *msg; + list_node_t node; +} ppmsg_t; + +typedef struct __channel_t { + uint16_t cid; /** < ID of the channel */ + conn_t *connection; /** < pointer to the parent connection */ + struct __connection_rpc_list_type *rpc_list; /** < rpc functions list */ + int flags; /** < flags of the channel */ +} chnl_t; + +/* message flags */ +#define SXMSG_OPEN (1 << 1) +#define SXMSG_CLOSED (1 << 2) +#define SXMSG_PROTO (1 << 3) +#define SXMSG_LINK (1 << 4) +#define SXMSG_REPLYREQ (1 << 5) +#define SXMSG_PULSE (1 << 6) +#define SXMSG_TIMEDOUT (1 << 7) + +/** + * \brief Message used in sntl message passing + * + * This structure used to manage a message within a channel + * of the sntl structure stack. + */ +typedef struct __message_t { + chnl_t *pch; /** < channel of the message(if applicable) */ + pthread_mutex_t wait; /** < special wait mutex, used for pending list and sync */ + sntllv2_head_t mhead; + void *payload; /** < payload */ +} sxmsg_t; + +#define sxmsg_payload(m) (m)->payload +#define sxmsg_datalen(m) (m)->mhead.payload_length +#define sxmsg_rapidbuf(m) (m)->payload +#define sxmsg_retcode(m) (m)->mhead.opcode +#define sxmsg_waitlock(m) pthread_mutex_lock(&((m)->wait)) +#define sxmsg_waitunlock(m) pthread_mutex_unlock(&((m)->wait)) + +typedef struct __connection_rpc_entry_type { + char *name; + int (*rpcf)(void *, sexp_t *); + usrtc_node_t node; +} cx_rpc_t; + +typedef struct __connection_rpc_list_type { + usrtc_t *rpc_tree; /** < search tree for the rpc lookup */ + char *opt_version; /** < reserved for future implementations */ +} cx_rpc_list_t; + +#define MAX_CONNECTIONS 32768 + +/** + * \brief Connection subsystem structure. + * + * This structure used for management and control a set of a + * determined connections with the same RPC lists and the same + * mode (server, client). + * + */ +typedef struct __connections_subsys_type { + usrtc_t *connections; + pthread_rwlock_t rwlock; + char *rootca, *certpem, *certkey; /* path name to the certificates */ + cx_rpc_list_t *system_rpc; + /* special functions pointers */ + int (*validate_sslpem)(conn_t *); /** < this function used to validate SSL certificate while SSL handshake */ + int (*secure_check)(conn_t *); /** < this function authorize user to login, + * and also should check SSL cert and user, and already made sessions */ + usrtc_t* (*get_rpc_typed_list_tree)(conn_t *); /** < this function is used to set RPC list of the functions */ + int (*set_typed_list_callback)(conn_t *, int, char *); /** < this function is a callback + * during setting up a typed channel */ + void (*on_destroy)(conn_t *); /** < callback on connection destroy */ + void (*on_pulse)(conn_t *, sexp_t *); /** < callback on pulse emit */ + void *priv; +} conn_sys_t; + +#define connections_set_sslvalidate(c, f) (c)->validate_sslpem = (f) +#define connections_set_authcheck(c, f) (c)->secure_check = (f) +#define connections_set_rpcvalidator(c, f) (c)->get_rpc_typed_list_tree = (f) +#define connections_set_channelcall(c, f) (c)->set_typed_list_callback = (f) +#define connections_set_ondestroy(c, f) (c)->on_destroy = (f) +#define connections_set_onpulse(c, f) (c)->on_pulse = (f) +#define connections_set_priv(c, p) (c)->priv = (p) +#define connections_get_priv(c) (c)->priv + +typedef struct __rpc_typed_list_type { + int type_id; + char *description; + cx_rpc_list_t *rpc_list; + usrtc_node_t lnode; +} rpc_typed_list_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/* API */ +int sntl_init(void); + +int connections_init(conn_sys_t *ssys); + +conn_sys_t *connections_create(void); + +int connections_destroy(conn_sys_t *ssys); + +int connections_free(conn_sys_t *ssys); + +int connections_setsslserts(conn_sys_t *ssys, const char *rootca, + const char *certpem, const char *certkey); + +/* create links */ +conn_t *connection_master_link(conn_sys_t *ssys, int sck, struct in_addr *addr); +conn_t *connection_link(conn_sys_t *ssys, const char *host, + int port, const char *SSL_cert, const char *login, + const char *passwd); +int connection_close(conn_t *co); + +/* channels */ +chnl_t *sxchannel_open(conn_t *co, int type); +int sxchannel_close(chnl_t *channel); + +/* messages */ +/* + * creates a message with a payload. + * Will return a error code, and, if applicable, pointer to message + */ +int sxmsg_send(chnl_t *channel, const char *data, size_t datalen, sxmsg_t **msg); +/* the same - postponed message i.e. will be written to the queue - not to write immendatly */ +int sxmsg_send_pp(chnl_t *channel, const char *data, size_t datalen, sxmsg_t **msg); +/* send a pulse message */ +int sxmsg_pulse(conn_t *co, const char *data, size_t datalen); +int sxmsg_reply(sxmsg_t *msg, const char *data, size_t datalen); +int sxmsg_reply_pp(sxmsg_t *msg, const char *data, size_t datalen); +int sxmsg_rreply(sxmsg_t *msg, size_t datalen); +int sxmsg_return(sxmsg_t *msg, int opcode); +int sxmsg_return_pp(sxmsg_t *msg, int opcode); +void sxmsg_clean(sxmsg_t *msg); + +#ifdef __cplusplus +} +#endif + +/* RPC List API */ +#define SNTL_FILTER_INC 0xa +#define SNTL_FILTER_EXC 0xb +#define SNTL_FILTER_END -1 + +#ifdef __cplusplus +extern "C" { +#endif + +int sntl_rpclist_init(usrtc_t *tree); + +int sntl_rpclist_add(usrtc_t *tree, int type, const char *description, + const char *version); + +int sntl_rpclist_add_function(usrtc_t *tree, int type, const char *fu_name, + int (*rpcf)(void *, sexp_t *)); + +int sntl_rpclist_filter(usrtc_t *source, usrtc_t **dest, int flag, int *filter); + +#ifdef __cplusplus +} +#endif + +#define blub(txt) fprintf(stderr, "%s:%d in %s > %s\n", __FILE__, __LINE__, __FUNCTION__, txt) + +#define dumphead(head) fprintf(stderr, "id: %d, opcode: %d, attr: %d, len = %d\n", head->msgid, head->opcode, head->attr, head->payload_length) + +#endif /* __SNTL_SNTLLV2_H__ */ + + diff --git a/lib/Makefile.am b/lib/Makefile.am index 0005599..73bce76 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -16,7 +16,7 @@ lib_LTLIBRARIES = libsntl.la libsntl_la_SOURCES = \ - support.c queue.c rpclist.c message.c channel.c connection.c + connex.c sntllv2.c chansx.c messagesx.c rpclist.c uuid.c libsntl_la_LDFLAGS = diff --git a/lib/channel.c b/lib/channel.c deleted file mode 100644 index 9dea04d..0000000 --- a/lib/channel.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Secure Network Transport Layer Library implementation. - * This is a proprietary software. See COPYING for further details. - * - * (c) Askele Group 2013-2015 - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef WIN32 -#include -#else -#include -#include -#include -#include -#endif - - -#include -#include - -#include -#include - -#include - -extern char *__generate_uuid(void); - -extern void __destroy_msg(sxmsg_t *msg); - -extern int __create_sys_msg(sxmsg_t **msg, char *uuid, chnl_t *ch, - sxpayload_t *data); - -static long __cmp_ulong(const void *a, const void *b) -{ - return (long)(*(ulong_t *)a - *(ulong_t *)b); -} - -int __alloc_channel(ulong_t cid, conn_t *co, rpc_typed_list_t *rlist, chnl_t **channel) -{ - int r = 0; - chnl_t *ch = malloc(sizeof(chnl_t)); - usrtc_t *msg_tree = malloc(sizeof(usrtc_t)); - idx_allocator_t *idx_msg = malloc(sizeof(idx_allocator_t)); - - if(!idx_msg) goto __fin_enomem; - else if(idx_allocator_init(idx_msg, MAX_MSGINDEX, 0)) goto __fin_enomem; - - if(!ch || !msg_tree) { - __fin_up2: - idx_allocator_destroy(idx_msg); - __fin_enomem: - r = ENOMEM; - goto __fin_up; - } else { - usrtc_init(msg_tree, USRTC_REDBLACK, MAX_PENDINGMSG, __cmp_ulong); - ch->cid = cid; - ch->flags = ch->use_count = 0; - ch->uuid = NULL; - usrtc_node_init(&ch->node, ch); - if(rlist) ch->rpc_list = rlist->rpc_list; - else ch->rpc_list = NULL; - /* init locks */ - if(pthread_rwlock_init(&(ch->msglock), NULL)) { - r = ENOMEM; - goto __fin_up2; - } - if(pthread_mutex_init(&(ch->oplock), NULL)) { - pthread_rwlock_destroy(&(ch->msglock)); - r = ENOMEM; - goto __fin_up2; - } - /* assign all the stuff */ - ch->idx_msg = idx_msg; - ch->msgs_tree = msg_tree; - ch->connection = co; - } - - __fin_up: - if(r) { - if(idx_msg) free(idx_msg); - if(ch) free(ch); - if(msg_tree) free(msg_tree); - return r; - } else { - *channel = ch; - return 0; - } -} - -/* channels */ -int channel_open(conn_t *co, chnl_t **ch, int type) -{ - chnl_t *nch = NULL; - conn_sys_t *ssys = co->ssys; - int r = 0; - char *uuid_; - sxpayload_t *pl; - ulong_t cid; - rpc_typed_list_t *rpclist = NULL; - usrtc_node_t *node = NULL; - sxmsg_t *sms; - - if(!(co->flags & CXCONN_ESTABL)) { - return ESXNOCONNECT; - } - - uuid_ = __generate_uuid(); - pl = malloc(sizeof(sxpayload_t)); - node = usrtc_lookup(co->rpc_list, &type); - - if(node) rpclist = (rpc_typed_list_t *)usrtc_node_getdata(node); - - if(!uuid_) { - if(pl) free(pl); - return ENOMEM; - } - - if(!pl) { - __ffail: - if(uuid_) free(uuid_); - return ENOMEM; - } else { - pl->sx = NULL; - if(!(pl->cstr = malloc(sizeof(char)*ESX_SYSMSG_SIZE))) { - free(pl); goto __ffail; - } else memset(pl->cstr, 0, sizeof(char)*ESX_SYSMSG_SIZE); - } - - pthread_rwlock_wrlock(&(co->chnl_lock)); - cid = idx_allocate(co->idx_ch); - pthread_rwlock_unlock(&(co->chnl_lock)); - if(cid == IDX_INVAL) { - r = ENOMEM; - goto __fini_op; - } - - if((r = __alloc_channel(cid, co, rpclist, &nch))) { - goto __fini_op; - } else nch->flags |= ESXCHAN_PENDING; - - nch->uuid = uuid_; - - /* ok now we're ready to create a message and push channel to the list */ - if((r = __create_sys_msg(&sms, uuid_, nch, pl))) { - __fail_chan: - /* destroy the channel*/ - goto __fini_op; - } else { - /* put the channel to the channels search tree */ - pthread_rwlock_wrlock(&(co->chnl_lock)); - //printf("inserting cid = %d\n", nch->cid); - usrtc_insert(co->chnl_tree, &nch->node, &nch->cid); - pthread_rwlock_unlock(&(co->chnl_lock)); - - /* put system message to the run queue */ - /* first form the message */ - snprintf(pl->cstr, sizeof(char)*ESX_SYSMSG_SIZE, - "(ch-open ((:id %ld)(:uuid %s)(:type %d)))", nch->cid, nch->uuid, type); - nch->sysmsg = sms; /* assign system message to the channel */ - /* put it */ - if((r = pth_queue_add(ssys->ioqueue, (void *)sms, SYS_MSG))) { - __fail_chan_r: - /* remove it from the search tree */ - pthread_rwlock_wrlock(&(co->chnl_lock)); - usrtc_delete(co->chnl_tree, &nch->node); - pthread_rwlock_unlock(&(co->chnl_lock)); - goto __fail_chan; - } - if(!(sms->flags & ESXMSG_PENDING)) { - /* was processed too fast */ - goto __process_smsg; - } else pthread_mutex_lock(&(sms->wait)); /* will sleep until got a reply */ - __process_smsg: - if(sms->opcode) { - r = sms->opcode; - goto __fail_chan_r; - } else r = 0; - nch->flags &= ~ESXCHAN_PENDING; /* mark it as established */ - free(pl->cstr); - free(pl); - __destroy_msg(nch->sysmsg); - } - - __fini_op: - if(r) { - if(uuid_) free(uuid_); - if(pl) { - if(pl->cstr) free(pl->cstr); - free(pl); - } - pthread_rwlock_wrlock(&(co->chnl_lock)); - idx_free(co->idx_ch, nch->cid); - pthread_rwlock_unlock(&(co->chnl_lock)); - idx_allocator_destroy(nch->idx_msg); - free(nch->idx_msg); - free(nch->msgs_tree); - pthread_mutex_destroy(&(nch->oplock)); - pthread_rwlock_destroy(&(nch->msglock)); - - free(nch); - } else *ch = nch; - - return r; -} - -int channel_close(chnl_t *chnl) -{ - char *uuid_; - usrtc_node_t *node = NULL; - int r = 0; - conn_t *co = chnl->connection; - conn_sys_t *ssys = co->ssys; - sxmsg_t *sms; - sxpayload_t *pl; - - if(!(co->flags & CXCONN_ESTABL)) { - return ESXNOCONNECT; - } - - pthread_rwlock_rdlock(&(co->chnl_lock)); - node = usrtc_lookup(co->chnl_tree, &chnl->cid); - pthread_rwlock_unlock(&(co->chnl_lock)); - if(!node) { - return ENOENT; - } - - pthread_rwlock_wrlock(&(chnl->msglock)); - /* check unprocessed messages */ - if(!usrtc_isempty(chnl->msgs_tree)) { /* messages on the queue */ - pthread_rwlock_unlock(&(chnl->msglock)); - return EBUSY; - } - - uuid_ = __generate_uuid(); - pl = malloc(sizeof(sxpayload_t)); - if(!pl || !uuid_) { - pthread_rwlock_unlock(&(chnl->msglock)); - if(pl) free(pl); - if(uuid_) free(uuid_); - return ENOMEM; - } - if(__create_sys_msg(&sms, uuid_, chnl, pl)) { - pthread_rwlock_unlock(&(chnl->msglock)); - free(pl); - free(uuid_); - return ENOMEM; - } - - pl->sx = NULL; - if(!(pl->cstr = malloc(sizeof(char) * ESX_SYSMSG_SIZE))) { - pthread_rwlock_unlock(&(chnl->msglock)); - free(pl); - free(uuid_); - return ENOMEM; - } - memset(pl->cstr, 0, sizeof(char) * ESX_SYSMSG_SIZE); - - /* put system message to the run queue */ - /* first form the message */ - snprintf(pl->cstr, sizeof(char) * ESX_SYSMSG_SIZE, - "(ch-close (:id %ld))", chnl->cid); - chnl->sysmsg = sms; /* assign system message to the channel */ - /* put it */ - if((r = pth_queue_add(ssys->ioqueue, (void *)sms, SYS_MSG))) { - pthread_rwlock_unlock(&(chnl->msglock)); - return r; - } - if(!(sms->flags & ESXMSG_PENDING)) { - /* was processed too fast */ - goto __process_smsg; - } else pthread_mutex_lock(&(sms->wait)); /* will sleep until got a reply */ - - __process_smsg: - if(sms->opcode) { - pthread_rwlock_unlock(&(chnl->msglock)); - r = sms->opcode; - return r; - } else r = 0; - chnl->flags &= ~ESXCHAN_PENDING; /* mark it as established */ - /* remove channel from the search tree */ - pthread_rwlock_wrlock(&(chnl->connection->chnl_lock)); - usrtc_delete(chnl->connection->chnl_tree, &chnl->node); - /* free index */ - idx_free(co->idx_ch, chnl->cid); - pthread_rwlock_unlock(&(chnl->connection->chnl_lock)); - - pthread_rwlock_unlock(&(chnl->msglock)); - - __destroy_msg(chnl->sysmsg); - free(uuid_); - - free(pl->cstr); - free(pl); - free(chnl->uuid); - idx_allocator_destroy(chnl->idx_msg); - free(chnl->idx_msg); - free(chnl->msgs_tree); - pthread_mutex_destroy(&(chnl->oplock)); - pthread_rwlock_destroy(&(chnl->msglock)); - - free(chnl); - - return 0; -} - diff --git a/lib/chansx.c b/lib/chansx.c new file mode 100644 index 0000000..7ffac6f --- /dev/null +++ b/lib/chansx.c @@ -0,0 +1,259 @@ +/* + * Secure Network Transport Layer Library v2 implementation. + * (sntllv2) it superseed all versions before due to the: + * - memory consumption + * - new features such as pulse emitting + * - performance optimization + * + * This is a proprietary software. See COPYING for further details. + * + * (c) Askele Group 2013-2015 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#define EBADE 1 +#define NETDB_SUCCESS 0 +#else +#include +#include +#include +#include +#endif + +#include +#include + +#include +#include + +#include + +#include "internal.h" + +/* locally used functions */ +uint8_t _channel_open(conn_t *co, uint16_t *chid) +{ + chnl_t *chan; + int typeid = *chid; /* our type */ + uint16_t chidx; + usrtc_t *rpc_list = co->rpc_list; + usrtc_node_t *node; + rpc_typed_list_t *rlist; + cx_rpc_list_t *rpclist; + + node = usrtc_lookup(rpc_list, &typeid); + if(!node) return SNE_EPERM; + else rlist = (rpc_typed_list_t *)usrtc_node_getdata(node); + + if(rlist) rpclist = rlist->rpc_list; + else return SNE_FAILED; + + chan = malloc(sizeof(chnl_t)); + if(!chan) return SNE_ENOMEM; + + /* init channel */ + chan->connection = co; + chan->rpc_list = rpclist; + chan->flags = 0; + + pthread_mutex_lock(&co->idx_ch_lock); + chidx = idx_allocate(&co->idx_ch); + pthread_mutex_unlock(&co->idx_ch_lock); + + if(chidx == IDX_INVAL) { + free(chan); + return SNE_MCHANNELS; + } + + chan->cid = chidx; + co->channels[chidx] = chan; + *chid = chidx; + + return SNE_SUCCESS; +} + +uint8_t _channel_close(conn_t *co, uint16_t chid) +{ + chnl_t *chan; + ulong_t chidx = chid; + + if(chid > 512) return SNE_INVALINDEX; + else chan = co->channels[chidx]; + + if(!chan) return SNE_NOSUCHCHAN; + + pthread_mutex_lock(&co->idx_ch_lock); + idx_free(&co->idx_ch, chidx); + co->channels[chidx] = NULL; + pthread_mutex_unlock(&co->idx_ch_lock); + + free(chan); + + return SNE_SUCCESS; +} + +chnl_t *sxchannel_open(conn_t *co, int type) +{ + chnl_t *chan = NULL; + sxmsg_t *msg = NULL; + sntllv2_head_t *head; + int msgidx, r, ccid; + + if(!co) { + r = SNE_FAILED; + goto __reterr; + } + + if(!(chan = malloc(sizeof(chnl_t)))) { + __enomem: + if(chan) free(chan); + r = SNE_ENOMEM; + goto __reterr; + } + if(!(msg = malloc(sizeof(sxmsg_t)))) goto __enomem; + + /* early init for the channel */ + chan->connection = co; + chan->flags = 0; + + /* early init for the message */ + pthread_mutex_init(&msg->wait, NULL); + pthread_mutex_lock(&msg->wait); + msg->pch = chan; + msg->payload = NULL; + memset(&msg->mhead, 0, sizeof(sntllv2_head_t)); + head = &msg->mhead; + + /* form a header */ + head->attr = SXMSG_PROTO | SXMSG_REPLYREQ | SXMSG_OPEN; + head->payload_length = 0; + head->reserve = (uint16_t)type; + + /* try to alloc a message */ + pthread_mutex_lock(&co->idx_msg_lock); + msgidx = idx_allocate(&co->idx_msg); + if(msgidx != IDX_INVAL) co->messages[msgidx] = msg; + pthread_mutex_unlock(&co->idx_msg_lock); + + if(msgidx == IDX_INVAL) { r = SNE_MMESSAGES; goto __reterr2; } + else head->msgid = msgidx; + + /* now we're ready to write it */ + r = _sntll_writemsg(co, msg); + if(r == SNE_SUCCESS) pthread_mutex_lock(&msg->wait); + else goto __reterr3; + + /* we will wakeup on return */ + if(msg->mhead.opcode != SNE_SUCCESS) { r = msg->mhead.opcode; goto __reterr3; } + + /* ok all is fine */ + chan->cid = msg->mhead.reserve; + ccid = chan->cid; + pthread_mutex_lock(&co->idx_ch_lock); + idx_reserve(&co->idx_ch, ccid); + co->channels[ccid] = chan; + pthread_mutex_unlock(&co->idx_ch_lock); + + /* destroy a message */ + pthread_mutex_lock(&co->idx_msg_lock); + idx_free(&co->idx_msg, msgidx); + co->messages[msgidx] = NULL; + pthread_mutex_unlock(&co->idx_msg_lock); + + /* free allocated resources */ + pthread_mutex_unlock(&msg->wait); + pthread_mutex_destroy(&msg->wait); + free(msg); + + return chan; + + __reterr3: + pthread_mutex_lock(&co->idx_msg_lock); + idx_free(&co->idx_msg, msgidx); + co->messages[msgidx] = NULL; + pthread_mutex_unlock(&co->idx_msg_lock); + __reterr2: + pthread_mutex_unlock(&msg->wait); + pthread_mutex_destroy(&msg->wait); + __reterr: + if(chan) free(chan); + if(msg) free(msg); + errno = r; + return NULL; +} + +int sxchannel_close(chnl_t *channel) +{ + int r = SNE_SUCCESS; + sxmsg_t *msg; + sntllv2_head_t *head; + conn_t *co; + int msgidx = 0, chidx = 0; + + /* check channel validity */ + if(!channel) return SNE_FAILED; + else if(!(co = channel->connection)) return SNE_FAILED; + if(channel->cid > 512) return SNE_IGNORED; + else chidx = channel->cid; + if(channel != co->channels[chidx]) return SNE_IGNORED; + + if(!(msg = malloc(sizeof(sxmsg_t)))) return SNE_ENOMEM; + head = &msg->mhead; + memset(head, 0, sizeof(sntllv2_head_t)); + + /* setup head */ + head->attr = SXMSG_PROTO | SXMSG_REPLYREQ; /* close channel */ + head->reserve = channel->cid; + + /* setup message */ + pthread_mutex_init(&msg->wait, NULL); + pthread_mutex_lock(&msg->wait); + msg->pch = channel; + + /* allocate it */ + pthread_mutex_lock(&co->idx_msg_lock); + msgidx = idx_allocate(&co->idx_msg); + if(msgidx != IDX_INVAL) co->messages[msgidx] = msg; + pthread_mutex_unlock(&co->idx_msg_lock); + + if(msgidx == IDX_INVAL) { r = SNE_MMESSAGES; goto __reterr2; } + else head->msgid = msgidx; + + r = _sntll_writemsg(co, msg); + if(r == SNE_SUCCESS) { + pthread_mutex_lock(&msg->wait); + r = head->opcode; + + /* we will free this anyway */ + pthread_mutex_lock(&co->idx_ch_lock); + idx_free(&co->idx_ch, chidx); + co->channels[chidx] = NULL; + pthread_mutex_unlock(&co->idx_ch_lock); + free(channel); + } + + pthread_mutex_lock(&co->idx_msg_lock); + idx_free(&co->idx_msg, msgidx); + co->messages[msgidx] = NULL; + pthread_mutex_unlock(&co->idx_msg_lock); + + __reterr2: + pthread_mutex_unlock(&msg->wait); + pthread_mutex_destroy(&msg->wait); + free(msg); + + return r; +} + diff --git a/lib/connection.c b/lib/connection.c deleted file mode 100644 index ef8e47e..0000000 --- a/lib/connection.c +++ /dev/null @@ -1,2466 +0,0 @@ -/* - * Secure Network Transport Layer Library implementation. - * This is a proprietary software. See COPYING for further details. - * - * (c) Askele Group 2013-2015 - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef WIN32 -#include -#define EBADE 1 -#define NETDB_SUCCESS 0 -#else -#include -#include -#include -#include -#endif - -#include -#include - -#include -#include - -#include - -struct __rpc_job -{ - sxmsg_t *msg; - sexp_t *sx; - int (*rpcf) (void *, sexp_t *); -}; - -conn_sys_t *conn_sys = NULL; - -static int ex_ssldata_index; /** < index used to work with additional data - * provided to the special call during SSL handshake */ - -int sntl_init(void) -{ - ex_ssldata_index = SSL_get_ex_new_index(0, "__ssldata index", NULL, NULL, NULL); - - return 0; -} - -static long __cmp_ulong(const void *a, const void *b); - -/* message alloc and destroy */ -extern sxmsg_t *__allocate_msg(int *res); -extern void __destroy_msg(sxmsg_t *msg); - -/* connections */ -static void __connections_subsystem_connection_remove(conn_t *); -static void __connection_free(conn_t *co); - -/* examination */ -static inline int __exam_connection(conn_t *co) -{ - int r = 0; - - pthread_mutex_lock(&co->oplock); - if(co->flags | CXCONN_BROKEN) { - /* wake up all */ - /* destroy thread poll */ - /* free all memory and sync primitives */ - r = 1; - } - pthread_mutex_unlock(&co->oplock); - - return r; -} - -static int __rpc_callback(void *data) -{ - struct __rpc_job *job = (struct __rpc_job *)data; - int rc = 0; - - rc = job->rpcf((void *)(job->msg), job->sx); - - free(job); - - return rc; -} - -extern int __alloc_channel(ulong_t cid, conn_t *co, rpc_typed_list_t *rlist, - chnl_t **channel); - -static int __conn_read(conn_t *co, void *buf, size_t buf_len) -{ - int rfd = SSL_get_fd(co->ssl), r; - fd_set readset, writeset; - int ofcmode, read_blocked = 0, read_blocked_on_write = 0; - - /* First we make the socket nonblocking */ -#ifndef WIN32 - ofcmode = fcntl(rfd, F_GETFL,0); - ofcmode |= O_NDELAY; - if(fcntl(rfd, F_SETFL, ofcmode)) - fprintf(stderr, "Couldn't make socket nonblocking"); -#endif - - __retry: - - do { - __try_again: - r = SSL_read(co->ssl, buf, (int)buf_len); - switch(SSL_get_error (co->ssl, r)) { - case SSL_ERROR_NONE: - return r; - break; - case SSL_ERROR_WANT_READ: - /* get prepare to select */ - read_blocked = 1; - break; - case SSL_ERROR_WANT_WRITE: /* here we blocked on write */ - read_blocked_on_write = 1; - break; - case SSL_ERROR_SYSCALL: - if(errno == EAGAIN || errno == EINTR) goto __try_again; - else { - fprintf(stderr, "SSL syscall error.\n"); - goto __close_conn; - } - break; - case SSL_ERROR_WANT_CONNECT: - case SSL_ERROR_WANT_ACCEPT: - fprintf(stderr, "SSL negotiation required. Trying again.\n"); - goto __try_again; - break; - case SSL_ERROR_SSL: - fprintf(stderr, "SSL error occured. Connection will be closed.\n"); - goto __close_conn; - break; - case SSL_ERROR_ZERO_RETURN: - fprintf(stderr, "SSL connection is cleary closed.\n"); - default: - __close_conn: - fprintf(stderr, "(RD)Unknown error on %s (errno = %d)\n", co->uuid, errno); - return -1; - } - } while(SSL_pending(co->ssl) && !read_blocked); - - __select_retry: - - if(read_blocked) { - FD_ZERO(&readset); - FD_SET(rfd, &readset); - /* waits until something will be ready to read */ - r = select(rfd + 1, &readset, NULL, NULL, NULL); - if(r < 0) { - if(errno == EINTR || errno == EAGAIN) goto __select_retry; - printf("(RD) select (%d) on %s\n", errno, co->uuid); - return -1; - } - if(!r) { - printf("Nothing to wait for\n"); - return 0; - } - read_blocked = 0; - if(r && FD_ISSET(rfd, &readset)) goto __retry; /* try to read again */ - } - if(read_blocked_on_write) { /* we was blocked on write */ - FD_ZERO(&readset); - FD_ZERO(&writeset); - FD_SET(rfd, &readset); - FD_SET(rfd, &writeset); - - r = select(rfd + 1, &readset, &writeset, NULL, NULL); - read_blocked_on_write = 0; - if(r && FD_ISSET(rfd, &writeset)) goto __retry; - } - - return 0; -} - -static int __conn_write(conn_t *co, void *buf, size_t buf_len) -{ - int r, rfd = SSL_get_fd(co->ssl); - fd_set writeset; - - pthread_mutex_lock(&(co->oplock)); - __retry: - r = SSL_write(co->ssl, buf, (int)buf_len); - switch(SSL_get_error(co->ssl, r)) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - /* here we should block */ - FD_ZERO(&writeset); - FD_SET(rfd, &writeset); - r = select(rfd + 1, NULL, &writeset, NULL, NULL); - if(r && FD_ISSET(rfd, &writeset)) goto __retry; - break; - case SSL_ERROR_SYSCALL: - if(errno == EAGAIN || errno == EINTR) goto __retry; - else goto __close_conn; - break; - default: - pthread_mutex_unlock(&(co->oplock)); - __close_conn: - if(r < 0) { - fprintf(stderr, "(WR)Unknown error on %s (%d)\n", co->uuid, r); - return -1; - } else return 0; - } - pthread_mutex_unlock(&(co->oplock)); - - return 0; -} - -static long __cmp_cstr(const void *a, const void *b) -{ - return strcmp((char *)a, (char *)b); -} - -static long __cmp_int(const void *a, const void *b) -{ - return *(int *)a - *(int *)b; -} - -static long __cmp_ulong(const void *a, const void *b) -{ - return *(ulong_t *)a - *(ulong_t *)b; -} - -extern int __resolvehost(const char *hostname, char *buf, int buf_len, - struct hostent **rhp); - -static void __destroy_rpc_list_tree(usrtc_t *tree) -{ - usrtc_node_t *node; - cx_rpc_t *ent; - - for(node = usrtc_first(tree); node != NULL; node = usrtc_first(tree)) { - ent = (cx_rpc_t *)usrtc_node_getdata(node); - usrtc_delete(tree, node); - free(ent->name); - free(ent); - } - - return; -} - -static int __insert_rpc_function(usrtc_t *tree, const char *name, int (*rpcf)(void *, sexp_t *)) -{ - cx_rpc_t *ent = malloc(sizeof(cx_rpc_t)); - usrtc_node_t *node; - - if(!ent) return ENOMEM; - else node = &ent->node; - - if(!(ent->name = strdup(name))) { - free(ent); - return ENOMEM; - } else ent->rpcf = rpcf; - - usrtc_node_init(node, ent); - usrtc_insert(tree, node, ent->name); - - return 0; -} - -/* wake up all waiters on messages with given opcode */ -static void __wake_up_waiters(conn_t *co, int opcode) -{ - usrtc_node_t *node = NULL, *last_node = NULL; - usrtc_node_t *msg_node = NULL, *last_msg_node = NULL; - chnl_t *ch; - sxmsg_t *smsg = NULL; - - pthread_rwlock_wrlock(&(co->chnl_lock)); - - if(!co->chnl_tree) goto __skip; - node = usrtc_first(co->chnl_tree); - last_node = usrtc_last(co->chnl_tree); - - /* going through channels tree */ - while(!usrtc_isempty(co->chnl_tree)) { - ch = (chnl_t *)usrtc_node_getdata(node); - - pthread_rwlock_rdlock(&(ch->msglock)); - msg_node = usrtc_first(ch->msgs_tree); - last_msg_node = usrtc_last(ch->msgs_tree); - - while(!usrtc_isempty(ch->msgs_tree)) { /* messages bypassing */ - smsg = (sxmsg_t *)usrtc_node_getdata(msg_node); - smsg->opcode = opcode; - - /* wake up waiting thread */ - pthread_mutex_unlock(&(smsg->wait)); - - if(msg_node == last_msg_node) break; - msg_node = usrtc_next(ch->msgs_tree, msg_node); - } - - pthread_rwlock_unlock(&(ch->msglock)); - - if(node == last_node) break; - node = usrtc_next(co->chnl_tree, node); - } - - __skip: - pthread_rwlock_unlock(&(co->chnl_lock)); - - return; -} - -/* (!) NOTE: this call use only after all threads are dead ! */ -static void __destroy_all_channels(conn_t *co) -{ - usrtc_node_t *node = NULL; - chnl_t *ch; - - for(node = usrtc_first(co->chnl_tree); node != NULL; node = - usrtc_first(co->chnl_tree)) { - ch = (chnl_t *)usrtc_node_getdata(node); - - /* free allocated resources */ - if(ch->uuid) free(ch->uuid); - idx_allocator_destroy(ch->idx_msg); /* allocator */ - free(ch->idx_msg); - free(ch->msgs_tree); - /* locks */ - pthread_mutex_destroy(&(ch->oplock)); - pthread_rwlock_destroy(&(ch->msglock)); - - /* remove it */ - usrtc_delete(co->chnl_tree, node); - - /* free */ - free(ch); - } - - return; -} - -static int __default_auth_set_context(void *cctx, sexp_t *sx) -{ - conn_t *co = (conn_t *)cctx; - conn_sys_t *ssys = co->ssys; - char *val, *var, *tbuf = NULL; - sexp_t *lsx, *sx_iter, *sx_in; - int llen, idx, err = 0; - - //co->pctx = malloc(sizeof(perm_ctx_t)); - - /* skip keyword itself */ - lsx = sx->list->next; - /* now we expect a list of lists */ - if(lsx->ty != SEXP_LIST) { - err = ESXRCBADPROT; - goto __reply; - } - /* take length of the list */ - llen = sexp_list_length(lsx); - if(!llen) return 0; /* other side will not set any security attributes */ - SEXP_ITERATE_LIST(lsx, sx_iter, idx) { - if(SEXP_IS_LIST(sx_iter)) { - sexp_list_car(sx_iter, &sx_in); - - if(!SEXP_IS_TYPE(sx_in, SEXP_BASIC)) { - err = ESXRCBADPROT; - goto __reply; - } else val = sx_in->val; - - if(sexp_list_length(sx_iter) < 2) continue; /* we will ignore it */ - - sexp_list_cdr(sx_iter, &sx_in); - if(!SEXP_IS_TYPE(sx_in, SEXP_DQUOTE)) { - err = ESXRCBADPROT; - goto __reply; - } else var = sx_in->val; - - /* ok, now we need to analyze parameters */ - if(!strcmp(val, ":user") && var) { - co->pctx->login = strdup(var); - } else if(!strcmp(val, ":passwd") && var) { - co->pctx->passwd = strdup(var); - } else { - /* just ignore in default implementation */ - } - } else continue; /* ignore */ - } - - /* ok, now we need to fill security context */ - tbuf = malloc(2048); - if(!tbuf) { - err = ENOMEM; - goto __reply; - } - if(ssys->secure_check) - err = ssys->secure_check(co); - -__reply: - if(err) { - snprintf(tbuf, 2048, "(auth-set-error (%d))", err); - } else { - snprintf(tbuf, 2048, "(auth-set-attr (:attr %d)(:uid %ld)(:gid %ld))", - co->pctx->p_attr, co->pctx->uid, co->pctx->gid); - } - /* we will send it */ - if(__conn_write(co, tbuf, strlen(tbuf)) < 0) { - co->flags &= ~CXCONN_ESTABL; - co->flags |= CXCONN_BROKEN; - __wake_up_waiters(co, ESXNOCONNECT); - } - destroy_sexp(sx); - - free(tbuf); - return err; -} - -static int __default_auth_set_attr(void *cctx, sexp_t *sx) -{ - conn_t *co = (conn_t *)cctx; - char *val, *var; - sexp_t *lsx, *sx_iter, *sx_in; - int llen, idx, r = 0; - - /* skip keyword itself */ - lsx = sx->list->next; - /* now we expect a list of lists */ - if(lsx->ty != SEXP_LIST) { - r = ESXRCBADPROT; - goto __finish; - } - /* take length of the list */ - llen = sexp_list_length(lsx); - if(!llen) return 0; /* other side will not set any security attributes */ - SEXP_ITERATE_LIST(lsx, sx_iter, idx) { - if(SEXP_IS_LIST(sx_iter)) { - sexp_list_car(sx_iter, &sx_in); - - if(!SEXP_IS_TYPE(sx_in, SEXP_BASIC)) { - r = ESXRCBADPROT; - goto __finish; - } else val = sx_in->val; - - if(sexp_list_length(sx_iter) < 2) continue; /* we will ignore it */ - - sexp_list_cdr(sx_iter, &sx_in); - - if(!SEXP_IS_TYPE(sx_in, SEXP_BASIC)) { - r = ESXRCBADPROT; - goto __finish; - } else var = sx_in->val; - - /* ok, now we need to analyze parameters */ - if(!strcmp(val, ":attr")) { - co->pctx->p_attr = atoi(var); - } else if(!strcmp(val, ":uid")) { - co->pctx->uid = (ulong_t)atoll(var); - } else if(!strcmp(val, ":gid")) { - co->pctx->gid = (ulong_t)atoll(var); - } else { - /* just ignore in default implementation */ - } - } else continue; /* ignore */ - } - -__finish: - destroy_sexp(sx); - return r; -} - -static int __default_auth_set_error(void *cctx, sexp_t *sx) -{ - char *errstr = NULL; - int r; - - /* skip keyword itself */ - sx->list = sx->list->next; - /* be sure - this is a list */ - if(sx->ty != SEXP_LIST) return ESXRCBADPROT; - else sx = sx->list; /* get it */ - errstr = sx->list->val; - r = atoi(errstr); - - destroy_sexp(sx); - return r; -} - -static int __default_ch_get_types(void *cctx, sexp_t *sx) -{ - conn_t *co = (conn_t *)cctx; - conn_sys_t *ssys = co->ssys; - usrtc_node_t *node; - rpc_typed_list_t *list_ent; - char *tbuf = malloc(4096), *tt; - int err = 0; - - /* if we cannot allocate anything ... */ - if(!tbuf) return ENOMEM; - /* ok here we go */ - co->rpc_list = ssys->get_rpc_typed_list_tree(co); - /* ok, here we're don't need to parse anything */ - if(!usrtc_count(co->rpc_list)) { - err = ENXIO; - snprintf(tbuf, 4096, "(ch-gl-error (%d))", err); - } else { - tt = tbuf; - snprintf(tt, 4096, "(ch-set-types ("); - tt += strlen(tt); - for(node = usrtc_first(co->rpc_list); node != NULL; - node = usrtc_next(co->rpc_list, node), tt += strlen(tt)) { - list_ent = (rpc_typed_list_t *)usrtc_node_getdata(node); - snprintf(tt, 4096, "(:%d \"%s\")", list_ent->type_id, list_ent->description); - } - snprintf(tt, 4096, "))"); - } - - /* reply to this rpc */ - if(__conn_write(co, tbuf, strlen(tbuf)) < 0) { - co->flags &= ~CXCONN_ESTABL; - co->flags |= CXCONN_BROKEN; - __wake_up_waiters(co, ESXNOCONNECT); - } - free(tbuf); - destroy_sexp(sx); - - return err; -} - -static int __default_ch_set_types(void *cctx, sexp_t *sx) -{ - conn_t *co = (conn_t *)cctx; - conn_sys_t *ssys = co->ssys; - char buf[1024], *val, *var; - int r = 0, llen, typeid, idx; - sexp_t *lsx, *sx_iter, *sx_in; - - /* skip keyword itself */ - lsx = sx->list->next; - /* now we expect a list of lists */ - if(lsx->ty != SEXP_LIST) { - r = ESXRCBADPROT; - goto __send_reply; - } - - /* take length of the list */ - llen = sexp_list_length(lsx); - - if(!llen) return 0; /* other side will not set any security attributes */ - SEXP_ITERATE_LIST(lsx, sx_iter, idx) { - if(SEXP_IS_LIST(sx_iter)) { - sexp_list_car(sx_iter, &sx_in); - - if(!SEXP_IS_TYPE(sx_in, SEXP_BASIC)) { - r = ESXRCBADPROT; - goto __send_reply; - } else val = sx_in->val; - - if(sexp_list_length(sx_iter) < 2) continue; /* we will ignore it */ - - sexp_list_cdr(sx_iter, &sx_in); - - if(!SEXP_IS_TYPE(sx_in, SEXP_DQUOTE)) { - r = ESXRCBADPROT; - goto __send_reply; - } else var = sx_in->val; - - /* ok, now we need to analyze parameters */ - if(*val != ':') { - r = ESXRCBADPROT; - goto __send_reply; - } else { - if(ssys->set_typed_list_callback) { - typeid = atoi((char *)(val + sizeof(char))); - if(ssys->set_typed_list_callback(co, typeid, var)) { - destroy_sexp(sx); - return ENXIO; - } - } /* FIXME: if no function, accept or decline ? */ - } - } else continue; /* ignore */ - } - - __send_reply: - snprintf(buf, 1024, "(ch-gl-error (%d))", r); - - if(__conn_write(co, buf, strlen(buf)) < 0) { - co->flags &= ~CXCONN_ESTABL; - co->flags |= CXCONN_BROKEN; - __wake_up_waiters(co, ESXNOCONNECT); - } - destroy_sexp(sx); - - return r; -} - -static int __default_ch_gl_error(void *cctx, sexp_t *sx) -{ - int r; - char *errstr; - conn_t *co = (conn_t *)cctx; - - if(co->flags & CXCONN_ESTABL) return EINVAL; /* error, we're already have channels list */ - - /* skip keyword itself */ - sx->list = sx->list->next; - /* be sure - this is a list */ - if(sx->ty != SEXP_LIST) return ESXRCBADPROT; - else sx = sx->list; /* get it */ - errstr = sx->list->val; - r = atoi(errstr); - - if(!r) co->flags |= CXCONN_ESTABL; - - return r; -} - -static int __default_ch_open(void *cctx, sexp_t *sx) -{ - conn_t *co = (conn_t *)cctx; - usrtc_node_t *node; - char *val, *var, *uuid = NULL, *buf; - int typ = -1, idx, llen, r; - ulong_t cid; - sexp_t *lsx, *sx_iter, *sx_in; - rpc_typed_list_t *rlist; - chnl_t *channel; - - /* skip keyword itself */ - lsx = sx->list->next; - /* now we expect a list of lists */ - if(lsx->ty != SEXP_LIST) { - printf("%s:%d\n", __FUNCTION__, __LINE__); - r = ESXRCBADPROT; - goto __send_repl; - } - - /* take length of the list */ - llen = sexp_list_length(lsx); - if(!llen) return 0; /* other side will not set any security attributes */ - SEXP_ITERATE_LIST(lsx, sx_iter, idx) { - if(SEXP_IS_LIST(sx_iter)) { - sexp_list_car(sx_iter, &sx_in); - - if(!SEXP_IS_TYPE(sx_in, SEXP_BASIC)) { - printf("%s:%d\n", __FUNCTION__, __LINE__); - r = ESXRCBADPROT; - goto __send_repl; - } else val = sx_in->val; - - if(sexp_list_length(sx_iter) < 2) continue; /* we will ignore it */ - - sexp_list_cdr(sx_iter, &sx_in); - - if(!SEXP_IS_TYPE(sx_in, SEXP_BASIC)) { - r = ESXRCBADPROT; - printf("%s:%d\n", __FUNCTION__, __LINE__); - goto __send_repl; - } else var = sx_in->val; - - /* ok, now we need to analyze parameters */ - if(*val != ':') { - r = ESXRCBADPROT; - goto __send_repl; - } else { - if(!strcmp((char *)(val + sizeof(char)), "type")) - typ = atoi(var); - else if(!strcmp((char *)(val + sizeof(char)), "id")) - cid = atoll(var); - else if(!strcmp((char *)(val + sizeof(char)), "uuid")) - uuid = var; - } - } else continue; /* ignore */ - } - - /* additional check for type of the channel */ - node = usrtc_lookup(co->rpc_list, &typ); - if(!node) { - r = ESXNOCHANSUP; - /* printf("%s:%d (usrtc count: %d) (typ %d)\n", __FUNCTION__, __LINE__, - usrtc_count(co->rpc_list), typ);*/ - node = usrtc_first(co->rpc_list); - rlist = (rpc_typed_list_t *)usrtc_node_getdata(node); - printf("---- rlist->type_id = %d\n", rlist->type_id); - goto __send_repl; - } else rlist = (rpc_typed_list_t *)usrtc_node_getdata(node); - - /* now we need to check up the channel */ - pthread_rwlock_rdlock(&(co->chnl_lock)); - node = usrtc_lookup(co->chnl_tree, &cid); - if(node) { - pthread_rwlock_unlock(&(co->chnl_lock)); - r = EEXIST; - goto __send_repl; - } else { - idx_reserve(co->idx_ch, cid); - pthread_rwlock_unlock(&(co->chnl_lock)); /* now we should alloc channel */ - if((r = __alloc_channel(cid, co, rlist, &channel))) { - pthread_rwlock_wrlock(&(co->chnl_lock)); - idx_free(co->idx_ch, cid); - pthread_rwlock_unlock(&(co->chnl_lock)); - goto __send_repl; - } else { - /* now we ready to confirm channel creation */ - pthread_rwlock_wrlock(&(co->chnl_lock)); - usrtc_insert(co->chnl_tree, &(channel->node), &(channel->cid)); - pthread_rwlock_unlock(&(co->chnl_lock)); - r = 0; - } - } - - __send_repl: - buf = malloc(2048); - snprintf(buf, 2048, "(ch-open-ret ((:error %d)(:uuid %s)(:id %ld)))", r, - uuid, cid); - if(__conn_write(co, buf, strlen(buf)) < 0) { - co->flags &= ~CXCONN_ESTABL; - co->flags |= CXCONN_BROKEN; - __wake_up_waiters(co, ESXNOCONNECT); - } - destroy_sexp(sx); - free(buf); - - return r; -} - -static int __default_ch_open_ret(void *cctx, sexp_t *sx) -{ - conn_t *co = (conn_t *)cctx; - chnl_t *chan; - usrtc_node_t *node; - int err = 0, r, llen, idx; - ulong_t id; - char *val, *var; - sexp_t *lsx, *sx_iter, *sx_in; - sxmsg_t *sms = NULL; - - /* skip keyword itself */ - lsx = sx->list->next; - /* now we expect a list of lists */ - if(lsx->ty != SEXP_LIST) { - //printf("%s:%d\n", __FUNCTION__, __LINE__); - r = ESXRCBADPROT; - goto __mark_msg; - } - /* take length of the list */ - llen = sexp_list_length(lsx); - if(!llen) return EINVAL; /* !! other side will not set any security attributes */ - SEXP_ITERATE_LIST(lsx, sx_iter, idx) { - if(SEXP_IS_LIST(sx_iter)) { - sexp_list_car(sx_iter, &sx_in); - - if(!SEXP_IS_TYPE(sx_in, SEXP_BASIC)) { - r = ESXRCBADPROT; - goto __mark_msg; - } else val = sx_in->val; - - if(sexp_list_length(sx_iter) < 2) continue; /* we will ignore it */ - - sexp_list_cdr(sx_iter, &sx_in); - - if(!SEXP_IS_TYPE(sx_in, SEXP_BASIC)) { - r = ESXRCBADPROT; - goto __mark_msg; - } else var = sx_in->val; - - /* ok, now we need to analyze parameters */ - if(*val != ':') { - r = ESXRCBADPROT; - goto __mark_msg; - } else { - if(!strcmp((char *)(val + sizeof(char)), "error")) - err = atoi(var); - else if(!strcmp((char *)(val + sizeof(char)), "id")) - id = atoll(var); - } - } else continue; /* ignore */ - } - - /* try to find desired channel to intercept message */ - pthread_rwlock_rdlock(&(co->chnl_lock)); - node = usrtc_lookup(co->chnl_tree, (void *)&id); - //printf("channels (%d)\n", usrtc_count(co->chnl_tree)); - pthread_rwlock_unlock(&(co->chnl_lock)); - if(node) { - chan = (chnl_t *)usrtc_node_getdata(node); - sms = chan->sysmsg; - } - - __mark_msg: - if(!sms) return r; - sms->flags &= ~ESXMSG_PENDING; /* the message is done */ - sms->opcode = err; - - destroy_sexp(sx); - - /* unlock mutex to wake up the waiting thread */ - pthread_mutex_unlock(&(sms->wait)); - - return 0; -} - -static int __default_ch_close(void *cctx, sexp_t *sx) -{ - conn_t *co = (conn_t *)cctx; - usrtc_node_t *node; - char *val, *var, *buf; - int idx, llen, r; - ulong_t cid = -1; - sexp_t *lsx, *sx_iter, *sx_in; - chnl_t *channel = NULL; - - r = 0; - /* skip keyword itself */ - lsx = sx->list->next; - /* now we expect a list of lists */ - if(lsx->ty != SEXP_LIST) { - printf("%s:%d\n", __FUNCTION__, __LINE__); - r = ESXRCBADPROT; - goto __send_repl; - } - - /* take length of the list */ - llen = sexp_list_length(lsx); - if(!llen) return 0; /* other side will not set any security attributes */ - SEXP_ITERATE_LIST(lsx, sx_iter, idx) { - if(SEXP_IS_LIST(sx_iter)) { - continue; - } - if(!SEXP_IS_TYPE(sx_iter, SEXP_BASIC)) { - printf("%s:%d\n", __FUNCTION__, __LINE__); - r = ESXRCBADPROT; - goto __send_repl; - } else val = sx_iter->val; - - sx_in = sx_iter->next; - - if(!SEXP_IS_TYPE(sx_in, SEXP_BASIC)) { - r = ESXRCBADPROT; - printf("%s:%d\n", __FUNCTION__, __LINE__); - goto __send_repl; - } else var = sx_in->val; - - /* ok, now we need to analyze parameters */ - if(*val != ':') { - r = ESXRCBADPROT; - goto __send_repl; - } else { - if(!strcmp((char *)(val + sizeof(char)), "id")) { - cid = atoll(var); - break; - } - } - } - - /* additional check for type of the channel */ - pthread_rwlock_rdlock(&(co->chnl_lock)); - node = usrtc_lookup(co->chnl_tree, &cid); - pthread_rwlock_unlock(&(co->chnl_lock)); - if(!node) { - r = ENOENT; - /* there are no such channel exist */ - destroy_sexp(sx); - goto __send_repl; - } - channel = (chnl_t *)usrtc_node_getdata(node); - - /* check up the message queue */ - pthread_rwlock_rdlock(&(channel->msglock)); - if(usrtc_count(channel->msgs_tree)) { - /* we have some undelivered messages in the queue */ - destroy_sexp(sx); - r = EBUSY; - goto __send_repl; - } - pthread_rwlock_unlock(&(channel->msglock)); - - /* remove channel from the search tree */ - pthread_rwlock_wrlock(&(co->chnl_lock)); - usrtc_delete(co->chnl_tree, &(channel->node)); - /* free index */ - idx_free(co->idx_ch, channel->cid); - pthread_rwlock_unlock(&(co->chnl_lock)); - - idx_allocator_destroy(channel->idx_msg); - free(channel->idx_msg); - free(channel->msgs_tree); - pthread_mutex_destroy(&(channel->oplock)); - pthread_rwlock_destroy(&(channel->msglock)); - free(channel); - - destroy_sexp(sx); - - __send_repl: - buf = malloc(2048); - snprintf(buf, 2048, "(ch-close-ret ((:id %ld) (:error %d)))", cid, r); - - if(__conn_write(co, buf, strlen(buf)) < 0) { - co->flags &= ~CXCONN_ESTABL; - co->flags |= CXCONN_BROKEN; - __wake_up_waiters(co, ESXNOCONNECT); - } - free(buf); - - return 0; -} - -static int __default_ch_close_ret(void *cctx, sexp_t *sx) -{ - conn_t *co = (conn_t *)cctx; - chnl_t *chan; - usrtc_node_t *node; - int err = 0, r, llen, idx; - ulong_t id; - char *val, *var; - sexp_t *lsx, *sx_iter, *sx_in; - sxmsg_t *sms = NULL; - - /* skip keyword itself */ - lsx = sx->list->next; - /* now we expect a list of lists */ - if(lsx->ty != SEXP_LIST) { - r = ESXRCBADPROT; - goto __mark_msg; - } - /* take length of the list */ - llen = sexp_list_length(lsx); - if(!llen) return EINVAL; /* !! other side will not set any security attributes */ - - SEXP_ITERATE_LIST(lsx, sx_iter, idx) { - if(SEXP_IS_LIST(sx_iter)) { - sexp_list_car(sx_iter, &sx_in); - - if(!SEXP_IS_TYPE(sx_in, SEXP_BASIC)) { - r = ESXRCBADPROT; - goto __mark_msg; - } else val = sx_in->val; - - if(sexp_list_length(sx_iter) < 2) continue; /* we will ignore it */ - - sexp_list_cdr(sx_iter, &sx_in); - - if(!SEXP_IS_TYPE(sx_in, SEXP_BASIC)) { - r = ESXRCBADPROT; - goto __mark_msg; - } else var = sx_in->val; - - /* ok, now we need to analyze parameters */ - if(*val != ':') { - r = ESXRCBADPROT; - goto __mark_msg; - } else { - if(!strcmp((char *)(val + sizeof(char)), "error")) - err = atoi(var); - else if(!strcmp((char *)(val + sizeof(char)), "id")) - id = atoll(var); - } - } else continue; /* ignore */ - } - - /* try to find desired channel to intercept message */ - pthread_rwlock_rdlock(&(co->chnl_lock)); - node = usrtc_lookup(co->chnl_tree, (void *)&id); - pthread_rwlock_unlock(&(co->chnl_lock)); - - if(node) { - chan = (chnl_t *)usrtc_node_getdata(node); - sms = chan->sysmsg; - } - - __mark_msg: - - if(!sms) return r; - sms->flags &= ~ESXMSG_PENDING; /* the message is done */ - sms->opcode = err; - - destroy_sexp(sx); - /* unlock mutex to wake up the waiting thread */ - pthread_mutex_unlock(&(sms->wait)); - - return 0; -} - -/* create a nould of the message */ -static int __create_reg_msg_mould(sxmsg_t **msg, chnl_t *ch, ulong_t mid) -{ - int r = 0; - sxmsg_t *sm = __allocate_msg(&r); - - if(r) return r; - else { - sm->pch = ch; - sm->flags = (ESXMSG_USR | ESXMSG_PENDING); - sm->mid = mid; - - /* ok reserve message ID */ - pthread_mutex_lock(&(ch->oplock)); - idx_reserve(ch->idx_msg, mid); - pthread_mutex_unlock(&(ch->oplock)); - - pthread_mutex_lock(&(sm->wait)); - *msg = sm; - } - - return 0; -} - -static int __default_msg(void *cctx, sexp_t *sx) -{ - conn_t *co = (conn_t *)cctx; - usrtc_node_t *node = NULL; - chnl_t *chan = NULL; - int r = 0; - sexp_t *lsx = NULL, *sx_iter = NULL; - sexp_t *sx_sublist = NULL, *sx_value = NULL; - ulong_t chnl_id = -1; - ulong_t msg_id = -1; - sexp_t *msg = NULL; - sxmsg_t *smsg = NULL; - int idx; - - /* get parameters from the message */ - if(sexp_list_cdr(sx, &lsx)) return ESXRCBADPROT; - if(!SEXP_IS_LIST(lsx)) return ESXRCBADPROT; - - /* find channel id */ - SEXP_ITERATE_LIST(lsx, sx_iter, idx) { - if(SEXP_IS_LIST(sx_iter)) { - sx_sublist = sx_iter; - continue; - } else { - if(SEXP_IS_TYPE(sx_iter, SEXP_BASIC)) { - if(strcmp(sx_iter->val, ":chid")) { - continue; // ignore it - } - sx_value = sx_iter->next; - if(!sx_value || !SEXP_IS_TYPE(sx_value, SEXP_BASIC)) { - continue; - } - chnl_id = atol(sx_value->val); - } else continue; // ignore it - } - } - lsx = sx_sublist; - /* find message id */ - SEXP_ITERATE_LIST(lsx, sx_iter, idx) { - if(SEXP_IS_LIST(sx_iter)) { - msg = sx_iter; - continue; - } else { - if(SEXP_IS_TYPE(sx_iter, SEXP_BASIC)) { - if(strcmp(sx_iter->val, ":msgid")) { - continue; // ignore - } - sx_value = sx_iter->next; - if(!sx_value || !SEXP_IS_TYPE(sx_value, SEXP_BASIC)) { - continue; - } - msg_id = atol(sx_value->val); - } else continue; // ignore it - } - } - - if(msg_id < 0 || chnl_id < 0) { - return ESXRCBADPROT; - } - /* find channel */ - if(!(node = usrtc_lookup(co->chnl_tree, &chnl_id))) return ENOENT; - else chan = (chnl_t *)usrtc_node_getdata(node); - /* lookup for the message */ - pthread_rwlock_rdlock(&(chan->msglock)); - if(!(node = usrtc_lookup(chan->msgs_tree, &msg_id))) { - pthread_rwlock_unlock(&(chan->msglock)); - /* here, rpc lookup has no sense, just put this job to the queue */ - /* btw, we're need to create a message first */ - r = __create_reg_msg_mould(&smsg, chan, msg_id); - if(r) return r; - - /* assign the message */ - smsg->opcode = 0; - smsg->payload = (void *)msg; - - /* assign initial S-expression structure */ - smsg->initial_sx = sx; - - /* put the message to the search tree */ - pthread_rwlock_wrlock(&(chan->msglock)); - usrtc_insert(chan->msgs_tree, &(smsg->pendingq_node), &(smsg->mid)); - pthread_rwlock_unlock(&(chan->msglock)); - } else { - pthread_rwlock_unlock(&(chan->msglock)); - smsg = (sxmsg_t *)usrtc_node_getdata(node); - msg_return(smsg, EEXIST); - return EEXIST; - } - /* put job to the queue and give up */ - r = pth_queue_add(co->rqueue, (void *)smsg, USR_MSG); - if(r) { /* cannot put job to the queue */ - msg_return(smsg, r); - pthread_rwlock_wrlock(&(chan->msglock)); - usrtc_delete(chan->msgs_tree, &(smsg->pendingq_node)); - pthread_rwlock_unlock(&(chan->msglock)); - __destroy_msg(smsg); - return r; - } - - /* put to the IN queue */ - return r; -} - -/* TODO: optimize amount of code: (must be first) - * __default_msg_return - * __default_msg_reply - * there are many copy-n-paste code!!!!! - */ - -static int __default_msg_return(void *cctx, sexp_t *sx) -{ - conn_t *co = (conn_t *)cctx; - usrtc_node_t *node = NULL; - chnl_t *chan = NULL; - int r = 0; - sexp_t *lsx = NULL, *sx_iter = NULL; - sexp_t *sx_sublist = NULL, *sx_value = NULL; - ulong_t chnl_id = -1; - ulong_t msg_id = -1; - sexp_t *msg = NULL; - sxmsg_t *smsg = NULL; - int idx, opcode; - - /* get parameters from the message */ - if(sexp_list_cdr(sx, &lsx)) { - r = ESXRCBADPROT; - goto __finish; - } - if(!SEXP_IS_LIST(lsx)) { - r = ESXRCBADPROT; - goto __finish; - } - - /* get parameters */ - SEXP_ITERATE_LIST(lsx, sx_iter, idx) { - if(SEXP_IS_LIST(sx_iter)) { - sx_sublist = sx_iter; - continue; - } else { - if(SEXP_IS_TYPE(sx_iter, SEXP_BASIC)) { - if(strcmp(sx_iter->val, ":chid")) { - continue; // ignore it - } - sx_value = sx_iter->next; - if(!sx_value || !SEXP_IS_TYPE(sx_value, SEXP_BASIC)) { - continue; - } - chnl_id = atol(sx_value->val); - } else continue; // ignore it - } - } - lsx = sx_sublist; - /* find message id */ - SEXP_ITERATE_LIST(lsx, sx_iter, idx) { - if(SEXP_IS_LIST(sx_iter)) { - msg = sx_iter; - continue; - } else { - if(SEXP_IS_TYPE(sx_iter, SEXP_BASIC)) { - if(strcmp(sx_iter->val, ":msgid")) { - continue; // ignore - } - sx_value = sx_iter->next; - if(!sx_value || !SEXP_IS_TYPE(sx_value, SEXP_BASIC)) { - continue; - } - msg_id = atol(sx_value->val); - } else continue; // ignore it - } - } - /* get opcode */ - sexp_list_car(msg, &lsx); - opcode = atoi(lsx->val); - - if(msg_id < 0 || chnl_id < 0) { - r = ESXRCBADPROT; - goto __finish; - } - - if(!(node = usrtc_lookup(co->chnl_tree, &chnl_id))) return ENOENT; - else chan = (chnl_t *)usrtc_node_getdata(node); - /* lookup for the message */ - pthread_rwlock_rdlock(&(chan->msglock)); - if(!(node = usrtc_lookup(chan->msgs_tree, &msg_id))) { - pthread_rwlock_unlock(&(chan->msglock)); - r = ENOENT; - goto __finish; - } else { - pthread_rwlock_unlock(&(chan->msglock)); - smsg = (sxmsg_t *)usrtc_node_getdata(node); - smsg->opcode = opcode; - if(smsg->flags & ESXMSG_ISREPLY) - destroy_sexp((sexp_t *)smsg->payload); - smsg->payload = NULL; - smsg->flags |= ESXMSG_CLOSURE; - - /* Q: can we remove the message from the tree there??? */ - /* A: actually no */ - /* first remove the message from tree */ - if(smsg->flags & ESXMSG_RMONRETR) { - pthread_rwlock_wrlock(&(chan->msglock)); - usrtc_delete(chan->msgs_tree, &(smsg->pendingq_node)); - pthread_rwlock_unlock(&(chan->msglock)); - } - if(smsg->flags & ESXMSG_PENDING) { - destroy_sexp(sx); - pthread_mutex_unlock(&(smsg->wait)); - return r; - } else { - /* nobody want it */ - destroy_sexp(smsg->initial_sx); - __destroy_msg(smsg); - } - } - -__finish: - destroy_sexp(sx); - return r; -} - -static int __default_msg_rapid(void *cctx, sexp_t *sx) -{ - conn_t *co = (conn_t *)cctx; - usrtc_node_t *node = NULL; - chnl_t *chan = NULL; - int r = 0; - sexp_t *lsx = NULL, *sx_iter = NULL; - sexp_t *sx_sublist = NULL, *sx_value = NULL; - ulong_t chnl_id = -1; - ulong_t msg_id = -1; - sexp_t *msg = NULL; - sxmsg_t *smsg = NULL; - int idx; - - /* get parameters from the message */ - if(sexp_list_cdr(sx, &lsx)) { - r = ESXRCBADPROT; - goto __finish; - } - if(!SEXP_IS_LIST(lsx)) { - r = ESXRCBADPROT; - goto __finish; - } - - /* get parameters */ - SEXP_ITERATE_LIST(lsx, sx_iter, idx) { - if(SEXP_IS_LIST(sx_iter)) { - sx_sublist = sx_iter; - continue; - } else { - if(SEXP_IS_TYPE(sx_iter, SEXP_BASIC)) { - if(strcmp(sx_iter->val, ":chid")) { - continue; // ignore it - } - sx_value = sx_iter->next; - if(!sx_value || !SEXP_IS_TYPE(sx_value, SEXP_BASIC)) { - continue; - } - chnl_id = atol(sx_value->val); - } else continue; // ignore it - } - } - lsx = sx_sublist; - /* find message id */ - SEXP_ITERATE_LIST(lsx, sx_iter, idx) { - if(SEXP_IS_LIST(sx_iter)) { - msg = sx_iter; - continue; - } else { - if(SEXP_IS_TYPE(sx_iter, SEXP_BASIC)) { - if(strcmp(sx_iter->val, ":msgid")) { - continue; // ignore - } - sx_value = sx_iter->next; - if(!sx_value || !SEXP_IS_TYPE(sx_value, SEXP_BASIC)) { - continue; - } - msg_id = atol(sx_value->val); - } else continue; // ignore it - } - } - - if(msg_id < 0 || chnl_id < 0) { - r = ESXRCBADPROT; - goto __finish; - } - - if(!(node = usrtc_lookup(co->chnl_tree, &chnl_id))) return ENOENT; - else chan = (chnl_t *)usrtc_node_getdata(node); - /* lookup for the message */ - pthread_rwlock_rdlock(&(chan->msglock)); - if(!(node = usrtc_lookup(chan->msgs_tree, &msg_id))) { - pthread_rwlock_unlock(&(chan->msglock)); - r = ENOENT; - goto __finish; - } else { - pthread_rwlock_unlock(&(chan->msglock)); - smsg = (sxmsg_t *)usrtc_node_getdata(node); - smsg->opcode = ESXRAPIDREPLY; - smsg->payload = copy_sexp(msg); - smsg->flags |= ESXMSG_ISREPLY; - /* are we'are ready to remove this ? */ - if(smsg->flags & ESXMSG_RMONRETR) { - pthread_rwlock_wrlock(&(chan->msglock)); - usrtc_delete(chan->msgs_tree, &(smsg->pendingq_node)); - pthread_rwlock_unlock(&(chan->msglock)); - } - if(smsg->flags & ESXMSG_PENDING) { - destroy_sexp(sx); - pthread_mutex_unlock(&(smsg->wait)); - return r; - } else { - /* nobody want it */ - destroy_sexp(smsg->initial_sx); - __destroy_msg(smsg); - } - } - -__finish: - destroy_sexp(sx); - return r; -} - -static int __default_msg_reply(void *cctx, sexp_t *sx) -{ - conn_t *co = (conn_t *)cctx; - usrtc_node_t *node = NULL; - chnl_t *chan = NULL; - int r = 0; - sexp_t *lsx = NULL, *sx_iter = NULL; - sexp_t *sx_sublist = NULL, *sx_value = NULL; - ulong_t chnl_id = -1; - ulong_t msg_id = -1; - sexp_t *msg = NULL; - sxmsg_t *smsg = NULL; - int idx; - - /* get parameters from the message */ - if(sexp_list_cdr(sx, &lsx)) { - r = ESXRCBADPROT; - goto __finish; - } - if(!SEXP_IS_LIST(lsx)) { - r = ESXRCBADPROT; - goto __finish; - } - - /* get parameters */ - SEXP_ITERATE_LIST(lsx, sx_iter, idx) { - if(SEXP_IS_LIST(sx_iter)) { - sx_sublist = sx_iter; - continue; - } else { - if(SEXP_IS_TYPE(sx_iter, SEXP_BASIC)) { - if(strcmp(sx_iter->val, ":chid")) { - continue; // ignore it - } - sx_value = sx_iter->next; - if(!sx_value || !SEXP_IS_TYPE(sx_value, SEXP_BASIC)) { - continue; - } - chnl_id = atol(sx_value->val); - } else continue; // ignore it - } - } - lsx = sx_sublist; - /* find message id */ - SEXP_ITERATE_LIST(lsx, sx_iter, idx) { - if(SEXP_IS_LIST(sx_iter)) { - msg = sx_iter; - continue; - } else { - if(SEXP_IS_TYPE(sx_iter, SEXP_BASIC)) { - if(strcmp(sx_iter->val, ":msgid")) { - continue; // ignore - } - sx_value = sx_iter->next; - if(!sx_value || !SEXP_IS_TYPE(sx_value, SEXP_BASIC)) { - continue; - } - msg_id = atol(sx_value->val); - } else continue; // ignore it - } - } - - if(msg_id < 0 || chnl_id < 0) { - r = ESXRCBADPROT; - goto __finish; - } - - if(!(node = usrtc_lookup(co->chnl_tree, &chnl_id))) return ENOENT; - else chan = (chnl_t *)usrtc_node_getdata(node); - /* lookup for the message */ - pthread_rwlock_rdlock(&(chan->msglock)); - if(!(node = usrtc_lookup(chan->msgs_tree, &msg_id))) { - pthread_rwlock_unlock(&(chan->msglock)); - r = ENOENT; - goto __finish; - } else { - pthread_rwlock_unlock(&(chan->msglock)); - smsg = (sxmsg_t *)usrtc_node_getdata(node); - smsg->opcode = ESXOREPLYREQ; - smsg->payload = copy_sexp(msg); - smsg->flags |= ESXMSG_ISREPLY; - pthread_mutex_unlock(&(smsg->wait)); - } - -__finish: - destroy_sexp(sx); - return r; -} - -static int __init_systemrpc_tree(usrtc_t *rtree) -{ - /* security context functions */ - if(__insert_rpc_function(rtree, "auth-set-context", __default_auth_set_context)) goto __fail; - if(__insert_rpc_function(rtree, "auth-set-attr", __default_auth_set_attr)) goto __fail; - if(__insert_rpc_function(rtree, "auth-set-error", __default_auth_set_error)) goto __fail; - /* channels negotiation ops */ - if(__insert_rpc_function(rtree, "ch-get-types", __default_ch_get_types)) goto __fail; - if(__insert_rpc_function(rtree, "ch-gl-error", __default_ch_gl_error)) goto __fail; - if(__insert_rpc_function(rtree, "ch-set-types", __default_ch_set_types)) goto __fail; - if(__insert_rpc_function(rtree, "ch-open", __default_ch_open)) goto __fail; - if(__insert_rpc_function(rtree, "ch-open-ret", __default_ch_open_ret)) goto __fail; - if(__insert_rpc_function(rtree, "ch-close", __default_ch_close)) goto __fail; - if(__insert_rpc_function(rtree, "ch-close-ret", __default_ch_close_ret)) goto __fail; - /* messaging functions */ - if(__insert_rpc_function(rtree, "ch-msg", __default_msg)) goto __fail; - if(__insert_rpc_function(rtree, "ch-msg-rete", __default_msg_return)) goto __fail; - if(__insert_rpc_function(rtree, "ch-msg-rapid", __default_msg_rapid)) goto __fail; - if(__insert_rpc_function(rtree, "ch-msg-repl", __default_msg_reply)) goto __fail; - - return 0; - - __fail: - __destroy_rpc_list_tree(rtree); - return ENOMEM; -} - -static int __eval_cstr(char *cstr, cx_rpc_list_t *rpc_list, void *ctx) -{ - int r = ENOENT; - sexp_t *sx; - usrtc_node_t *node; - cx_rpc_t *rentry; - char *rpcf; - - if(!(sx = parse_sexp(cstr, strlen(cstr)))) return EBADE; - - if(sx->ty == SEXP_LIST) - rpcf = sx->list->val; - else goto __enoent; - - /* find an appropriate function */ - node = usrtc_lookup(rpc_list->rpc_tree, rpcf); - - if(!node) { - __enoent: - fprintf(stderr, "Invalid S-expression catched.\n"); - destroy_sexp(sx); - return ENOENT; - } - else rentry = (cx_rpc_t *)usrtc_node_getdata(node); - - /* call it */ - r = rentry->rpcf(ctx, sx); - //if(r) destroy_sexp(sx); - - return r; -} - -static void *__cxslave_thread_listener(void *wctx) -{ - conn_t *co = (conn_t *)wctx; - conn_sys_t *ssys = co->ssys; - char *buf = malloc(4096); - int r; - - while((r = __conn_read(co, buf, 4096)) != -1) { - buf[r] = '\0'; - r = __eval_cstr(buf, ssys->system_rpc, co); - } - co->flags &= ~CXCONN_ESTABL; - co->flags |= CXCONN_BROKEN; - - /* ok the first of all we're need to wake up all */ - __wake_up_waiters(co, ESXNOCONNECT); - /* now we need to end the poll */ - pth_dqtpoll_destroy(co->tpoll, 1); /* force */ - - __connection_free(co); - - free(buf); - - return NULL; -} - -static void *__cxmaster_thread_listener(void *wctx) -{ - conn_t *co = (conn_t *)wctx; - conn_sys_t *ssys = co->ssys; - char *buf = malloc(4096); - int r; - - while((r = __conn_read(co, buf, 4096)) != -1) { - buf[r] = '\0'; - r = __eval_cstr(buf, ssys->system_rpc, co); - } - co->flags &= ~CXCONN_ESTABL; - co->flags |= CXCONN_BROKEN; - - /* ok the first of all we're need to wake up all */ - __wake_up_waiters(co, ESXNOCONNECT); - /* now we need to end the poll */ - pth_dqtpoll_destroy(co->tpoll, 1); /* force */ - - __connection_free(co); - - free(buf); - - return NULL; -} - -static void *__rmsg_queue_thread(void *ctx) -{ - conn_t *co = (conn_t *)ctx; - pth_msg_t *tmp = malloc(sizeof(pth_msg_t)); - sxmsg_t *msg; - chnl_t *ch; - int r = 0; - char *rpcf; - sexp_t *sx; - usrtc_node_t *node = NULL; - cx_rpc_t *rpccall; - struct __rpc_job *rjob = NULL; - - if(!tmp) return NULL; - - while(1) { - r = pth_queue_get(co->rqueue, NULL, tmp); - if(r) { - __fini: - free(tmp); - return NULL; - } else if(tmp->msgtype == END_MSG) goto __fini; - msg = tmp->data; - if(!msg) continue; /* spurious !! */ - - /* check to right job */ - if(!(msg->flags & ESXMSG_USR)) { /* not a regular message */ - msg->flags |= ESXMSG_NOWAY; /* mark it's as undeliverable */ - msg->flags &= ~ESXMSG_PENDING; - - pthread_mutex_unlock(&(msg->wait)); /* wake up the waitee */ - continue; - } else { - /* now we're need to have a deal with the rpc calling, other - we don't care */ - ch = msg->pch; - sx = (sexp_t *)msg->payload; - - if(!sx) { - r = ESXRCBADPROT; - goto __err_ret; - } - /* get the function name */ - if((sx->ty == SEXP_LIST) && (sx->list != NULL)) - rpcf = sx->list->val; - else { - r = ESXRCBADPROT; - goto __err_ret; - } - - node = usrtc_lookup(ch->rpc_list->rpc_tree, rpcf); - if(!node) { - r = ENOENT; - __err_ret: - msg_return(msg, r); - } else { - rpccall = (cx_rpc_t *)usrtc_node_getdata(node); - /* call this ! */ - rjob = malloc(sizeof(struct __rpc_job)); // TODO: check it - rjob->msg = msg; - rjob->sx = sx; - rjob->rpcf = rpccall->rpcf; - pth_dqtpoll_add(co->tpoll, (void *)rjob, USR_MSG); // TODO: check it - } - } - } - - return NULL; -} - -static void *__msg_queue_thread(void *ctx) -{ - conn_t *co = (conn_t *)ctx; - pth_msg_t *tmp = malloc(sizeof(pth_msg_t)); - sxmsg_t *msg; - chnl_t *ch; - int r = 0, len; - char *buf = malloc(4096), *tb; - sexp_t *sx; - - if(!tmp || !buf) { - if(tmp) free(tmp); - if(buf) free(buf); - return NULL; - } - - while(1) { - r = pth_queue_get(co->mqueue, NULL, tmp); - if(r) { - __fini: - free(buf); - free(tmp); - return NULL; - } - - /* message workout */ - if(tmp->msgtype == END_MSG) goto __fini; - msg = tmp->data; - if(!msg) continue; /* spurious message */ - - if(!(msg->flags & ESXMSG_USR)) { /* not a regular message */ - msg->flags |= ESXMSG_NOWAY; /* mark it's as undeliverable */ - msg->flags &= ~ESXMSG_PENDING; - - pthread_mutex_unlock(&(msg->wait)); /* wake up the waitee */ - continue; - } else { - ch = msg->pch; - - /* now we need to complete the request */ - sx = (sexp_t *)msg->payload; tb = buf; - if(!(msg->flags & ESXMSG_PULSE)) { /* %s))) */ - if(!(msg->flags & ESXMSG_ISREPLY)) - snprintf(buf, 4096, "(ch-msg (:chid %lu (:msgid %lu ", ch->cid, - msg->mid); - else { - if(!sx) { - snprintf(buf, 4096, "(ch-msg-rete (:chid %lu (:msgid %lu (%d))))", ch->cid, - msg->mid, msg->opcode); - /* mark it to close */ - msg->flags |= ESXMSG_CLOSURE; - /* ok, here we will write it and wait, destroying dialog while reply */ - - goto __ssl_write; - } else { - if(msg->flags & ESXMSG_ISRAPID) { - msg->flags |= ESXMSG_CLOSURE; - pthread_mutex_unlock(&(msg->wait)); /* wake it up */ - snprintf(buf, 4096, "(ch-msg-rapid (:chid %lu (:msgid %lu ", ch->cid, - msg->mid); - } else - snprintf(buf, 4096, "(ch-msg-repl (:chid %lu (:msgid %lu ", ch->cid, - msg->mid); - } - } - - len = strlen(buf); - tb += len*sizeof(char); - if(print_sexp(tb, 4096 - (len + 4*sizeof(char)), sx) == -1) { - msg->opcode = ENOMEM; - /* we don't need to wake up anybody */ - if(msg->flags & ESXMSG_TIMEDOUT) { - /* clean up all the shit: - * 1. remove from message tree - * 2. destroy message itself - */ - destroy_sexp(msg->initial_sx); - msg->initial_sx = NULL; - msg->payload = NULL; - if(msg->flags & ESXMSG_ISREPLY) - destroy_sexp(msg->payload); - } else { - pthread_mutex_unlock(&(msg->wait)); - } - } - } - - len = strlen(tb); - tb += len*sizeof(char); - strcat(tb, ")))"); - - __ssl_write: - if(msg->flags & ESXMSG_CLOSURE) { - /* wake up it */ - pthread_mutex_unlock(&(msg->wait)); - /* first remove the message from tree */ - pthread_rwlock_wrlock(&(ch->msglock)); - usrtc_delete(ch->msgs_tree, &(msg->pendingq_node)); - pthread_rwlock_unlock(&(ch->msglock)); - /* destroy */ - destroy_sexp(msg->initial_sx); - if(msg->flags & ESXMSG_ISREPLY && msg->payload) - destroy_sexp((sexp_t *)msg->payload); - __destroy_msg(msg); - } - - /* write it */ - if(__conn_write(co, (void *)buf, strlen(buf) + sizeof(char)) < 0) { - co->flags &= ~CXCONN_ESTABL; - co->flags |= CXCONN_BROKEN; - __wake_up_waiters(co, ESXNOCONNECT); - } - - } - - len = 0; - } - - free(buf); - - return NULL; -} - -/* this function is an ugly implementation to get C string with uuid */ -extern char *__generate_uuid(void); - -/* this is a callback to perform a custom SSL certs chain validation, - * as I promised here the comments, a lot of ... - * The first shit: 0 means validation failed, 1 otherwise - * The second shit: X509 API, I guess u will love it ;-) - * openssl calls this function for each certificate in chain, - * since our case is a simple (depth of chain is one, since we're - * don't care for public certificates lists or I cannot find any reasons to - * do it ...), amount of calls reduced, and in this case we're interested - * only in top of chain i.e. actual certificate used on client side, - * the validity of signing for other certificates within chain is - * guaranteed by the ssl itself. - * u know, we need to lookup in database, or elsewhere... some information - * about client certificate, and decide - is it valid, or not?, if so - * yep I mean it's valid, we can assign it's long fucking number to - * security context, to use in ongoing full scaled connection handshaking. - */ -static int __verify_certcall(int preverify_ok, X509_STORE_CTX *ctx) -{ - // X509 *cert = X509_STORE_CTX_get_current_cert(ctx); - int err = X509_STORE_CTX_get_error(ctx), depth = X509_STORE_CTX_get_error_depth(ctx); - SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); - conn_t *co = SSL_get_ex_data(ssl, ex_ssldata_index); /* this is a custom data we're set before */ - conn_sys_t *ssys = co->ssys; - - /* now we need to check for certificates with a long chain, - * so since we have a short one, reject long ones */ - if(depth > VERIFY_DEPTH) { /* longer than we expect */ - preverify_ok = 0; /* yep, 0 means error for those function callback in openssl, fucking set */ - err = X509_V_ERR_CERT_CHAIN_TOO_LONG; - X509_STORE_CTX_set_error(ctx, err); - } - - if(!preverify_ok) return 0; - - /* ok, now we're on top of SSL (depth == 0) certs chain, - * and we can validate client certificate */ - if(!depth) { - co->pctx->certid = - ASN1_INTEGER_get((const ASN1_INTEGER *)X509_get_serialNumber(ctx->current_cert)); - /* now we're need to check the ssl cert */ - if(ssys->validate_sslpem) { - if(ssys->validate_sslpem(co)) return 0; - else return 1; - } else return 0; - } - - return preverify_ok; -} - -/* dummy just to check the server side */ -static int __verify_certcall_dummy(int preverify_ok, X509_STORE_CTX *ctx) -{ - return preverify_ok; -} - -/* thread serve the whole connections set i.e. subsystem - * actually it works with system messages - */ -static void *__system_queue_listener(void *data) -{ - conn_sys_t *ssys = (conn_sys_t *)data; - int r; - pth_msg_t *tmp = malloc(sizeof(pth_msg_t)); - sxmsg_t *sysmsg; - sxpayload_t *payload; - chnl_t *chan; - conn_t *co; - - if(!tmp) return NULL; - - while(1) { - r = pth_queue_get(ssys->ioqueue, NULL, tmp); - if(r) { - free(tmp); - return NULL; - } - - /* ok message is delivered */ - sysmsg = tmp->data; - if(!sysmsg) continue; /* ignore dummy messages */ - - if(!(sysmsg->flags & ESXMSG_SYS)) { /* not a system message */ - sysmsg->flags |= ESXMSG_NOWAY; /* mark it's as undeliverable */ - sysmsg->flags &= ~ESXMSG_PENDING; - pthread_mutex_unlock(&(sysmsg->wait)); /* wake up the waitee */ - continue; - } else { - chan = sysmsg->pch; - co = chan->connection; - payload = (sxpayload_t *)sysmsg->payload; - /* write the buf */ - if(__conn_write(co, (void *)payload->cstr, strlen(payload->cstr) + 1) < 0) { - co->flags &= ~CXCONN_ESTABL; - co->flags |= CXCONN_BROKEN; - __wake_up_waiters(co, ESXNOCONNECT); - } - } - } - - return NULL; -} - -/* general initialization must be called within app uses connection layer */ -int connections_init(conn_sys_t *ssys) -{ - int r = 0; - - if(!ssys) return EINVAL; - else if(!(ssys->connections = malloc(sizeof(usrtc_t)))) { - r = ENOMEM; - goto __fail; - } - - /* zeroing */ - ssys->rootca = ssys->certkey = ssys->certpem = NULL; - ssys->validate_sslpem = NULL; - ssys->secure_check = NULL; - ssys->on_destroy = NULL; - /* init connections list */ - usrtc_init(ssys->connections, USRTC_REDBLACK, MAX_CONNECTIONS, - __cmp_cstr); - if((r = pthread_rwlock_init(&(ssys->rwlock), NULL))) - goto __fail_1; - - /* init queues */ - if(!(ssys->ioq = malloc(sizeof(pth_queue_t)))) { /* general io queue */ - r = ENOMEM; - goto __fail_2; - } - if((r = pth_queue_init(ssys->ioq))) goto __fail_3; - if(!(ssys->ioqueue = malloc(sizeof(pth_queue_t)))) { /* system io queue */ - r = ENOMEM; - goto __fail_2; - } - if((r = pth_queue_init(ssys->ioqueue))) goto __fail_3_1; - - /* init SSL certificates checking functions */ - /* init RPC list related functions */ - if(!(ssys->system_rpc = malloc(sizeof(cx_rpc_list_t)))) { - r = ENOMEM; - goto __fail_3; - } else { - if(!(ssys->system_rpc->rpc_tree = malloc(sizeof(usrtc_t)))) { - r = ENOMEM; - __fail_rpc: - free(ssys->system_rpc); - goto __fail_3_1; - } - usrtc_init(ssys->system_rpc->rpc_tree, USRTC_SPLAY, 256, __cmp_cstr); - r = __init_systemrpc_tree(ssys->system_rpc->rpc_tree); - if(r) { - free(ssys->system_rpc->rpc_tree); - goto __fail_rpc; - } - } - - /* init SSL library */ - SSL_library_init(); - - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); - - /* create threads for queue */ - if((r = pthread_create(&ssys->ios_thread, NULL, __system_queue_listener, (void *)ssys))) { - goto __fail_rpc; - } - - return 0; - - __fail_3_1: - free(ssys->ioqueue); - __fail_3: - free(ssys->ioq); - __fail_2: - pthread_rwlock_destroy(&(ssys->rwlock)); - __fail_1: - free(ssys->connections); - __fail: - - return r; -} - -/* load certificates */ -int connections_setsslserts(conn_sys_t *ssys, const char *rootca, - const char *certpem, const char *certkey) -{ - int r = ENOMEM; - - if(!ssys) return EINVAL; - /* simply copying */ - if(!(ssys->rootca = strdup(rootca))) return ENOMEM; - if(!(ssys->certkey = strdup(certkey))) goto __fail; - if(!(ssys->certpem = strdup(certpem))) goto __fail; - - r = 0; - return 0; - __fail: - if(ssys->rootca) free(ssys->rootca); - if(ssys->certkey) free(ssys->certkey); - if(ssys->certpem) free(ssys->certpem); - - return r; -} - -int connections_setrpclist_function(conn_sys_t *ssys, - usrtc_t* (*get_rpc_typed_list_tree) - (conn_t *)) -{ - if(!ssys) return EINVAL; - ssys->get_rpc_typed_list_tree = get_rpc_typed_list_tree; - - return 0; -} - -static void __connections_subsystem_connection_remove(conn_t *co) -{ - pthread_rwlock_wrlock(&(co->ssys->rwlock)); - usrtc_delete(co->ssys->connections, &(co->csnode)); - pthread_rwlock_unlock(&(co->ssys->rwlock)); - - return; -} - -#define __TMPBUFLEN 2048 - -/* connection_initiate: perform a connection thru the socket to the - * host with master certificate, i.e. it's a slave one for client. - */ -int connection_initiate_m(conn_sys_t *ssys, conn_t *co, const char *host, - int port, const char *SSL_cert, perm_ctx_t *pctx) -{ - int r = 0, sd; - int bytes = 0; - char *uuid; - char *buf = NULL; - struct hostent *host_; - struct sockaddr_in addr; -#ifdef WIN32 - WSADATA wsaData; -#endif - usrtc_t *ch_tree, *rpc_tree; - pth_queue_t *mqueue = malloc(sizeof(pth_queue_t)); - pth_queue_t *rqueue = malloc(sizeof(pth_queue_t)); - pth_dqtpoll_t *tpoll = malloc(sizeof(pth_dqtpoll_t)); - idx_allocator_t *idx_ch = malloc(sizeof(idx_allocator_t)); - - if(!mqueue) { - __fallenomem: - r = ENOMEM; - __fall0: - if(rqueue) free(rqueue); - if(mqueue) free(mqueue); - if(tpoll) free(tpoll); - if(idx_ch) free(idx_ch); - return r; - } - -#ifdef WIN32 - WSAStartup(MAKEWORD(2, 2), &wsaData); -#endif - - if(!tpoll) goto __fallenomem; - if(!idx_ch) goto __fallenomem; - - if(!co) { - __falleinval: - r = EINVAL; - goto __fall0; - } else if(!ssys) goto __falleinval; - - if(!host) goto __falleinval; - if(!SSL_cert) goto __falleinval; - if(!pctx) goto __falleinval; - - memset(co, 0, sizeof(conn_t)); - /* setup connections set */ - co->ssys = ssys; - - - pth_dqtpoll_init(tpoll, __rpc_callback); - - if(!idx_ch) return ENOMEM; - else r = idx_allocator_init(idx_ch, MAX_CHANNELS*MAX_MULTI, 0); - if(r) return r; - - if(!(uuid = __generate_uuid())) { - return ENOMEM; - } - - if(!(ch_tree = malloc(sizeof(usrtc_t)))) { - r = ENOMEM; - goto __fail; - } - - if(!(rpc_tree = malloc(sizeof(usrtc_t)))) { - r = ENOMEM; - goto __fail_1; - } - - if((r = pthread_mutex_init(&co->oplock, NULL))) goto __fail_2; - if((r = pthread_rwlock_init(&co->chnl_lock, NULL))) goto __fail_3; - - usrtc_init(rpc_tree, USRTC_REDBLACK, MAX_RPC_LIST, __cmp_int); - usrtc_init(ch_tree, USRTC_REDBLACK, MAX_CHANNELS, __cmp_ulong); - - co->idx_ch = idx_ch; - - /* assign message queue */ - if((r = pth_queue_init(mqueue))) goto __fail_3; - co->mqueue = mqueue; - /* assign repl message queue */ - if((r = pth_queue_init(rqueue))) goto __fail_3; - co->rqueue = rqueue; - - /* init SSL certificates and context */ - co->ctx = SSL_CTX_new(TLSv1_2_client_method()); - if(!co->ctx) { ERR_print_errors_fp(stderr); - r = EINVAL; goto __fail_3; } - else { - SSL_CTX_set_verify(co->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - __verify_certcall_dummy); - SSL_CTX_set_verify_depth(co->ctx, 1); /* FIXME: use configuration */ - } - - /* load certificates */ - SSL_CTX_load_verify_locations(co->ctx, ssys->rootca, NULL); - /* set the local certificate from CertFile */ - if(SSL_CTX_use_certificate_file(co->ctx, SSL_cert, - SSL_FILETYPE_PEM)<=0) { - r = EINVAL; - goto __fail_3; - } - /* set the private key from KeyFile (may be the same as CertFile) */ - - if(SSL_CTX_use_PrivateKey_file(co->ctx, SSL_cert, - SSL_FILETYPE_PEM)<=0) { - r = EINVAL; - goto __fail_3; - } - - /* verify private key */ - if (!SSL_CTX_check_private_key(co->ctx)) { - r = EINVAL; - goto __fail_3; - } - - /* assign allocated memory */ - co->rpc_list = rpc_tree; - co->chnl_tree = ch_tree; - co->uuid = uuid; - - /* connect to the pointed server */ - /* resolve host */ - if(!(buf = malloc(__TMPBUFLEN))) { - r = ENOMEM; - goto __fail_3; - } - - //r=__resolvehost(host, buf, __TMPBUFLEN, &host_); -#ifdef WIN32 - host_ = gethostbyname(host); -#else - r = __resolvehost(host, buf, __TMPBUFLEN, &host_); -#endif - - if(r) { - r = ENOENT; - free(buf); - goto __fail_3; - } - - // /* create a socket */ - sd = socket(PF_INET, SOCK_STREAM, 0); - bzero(&addr, sizeof(addr)); - - /* try to connect it */ - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = *(uint32_t*)(host_->h_addr); - r=connect(sd, (struct sockaddr*)&addr, sizeof(addr)); - - if ( r != 0) { - printf("connect error %d %s \n",r, strerror(errno)); - close(sd); - free(buf); - r = ENOENT; /* couldn't connect to the desired host */ - goto __fail_3; - } - - /* now we will create an SSL connection */ - co->ssl = SSL_new(co->ctx); - SSL_set_fd(co->ssl, sd); /* attach connected socket */ - // BIO_set_nbio(SSL_get_rbio(co->ssl), 1); - SSL_set_connect_state(co->ssl); - if(SSL_connect(co->ssl) == -1) { - r = EBADE; - free(buf); - /* shutdown connection */ - goto __fail_3; - } /* if success we're ready to use established SSL channel */ - - /* auth and RPC contexts sync */ - co->pctx = pctx; - snprintf(buf, __TMPBUFLEN, "(auth-set-context ((:user \"%s\")(:passwd \"%s\")))", - pctx->login, pctx->passwd); - /* send an auth request */ - if(__conn_write(co, buf, strlen(buf) + sizeof(char))) { - __finalize: - co->flags &= ~CXCONN_ESTABL; - co->flags |= CXCONN_BROKEN; - r = ESXNOCONNECT; - free(buf); - __retry_shut: - if(!SSL_shutdown(co->ssl)) { - usleep(100); - goto __retry_shut; - } - /* shutdown connection */ - goto __fail_3; - } - /* read the message reply */ - - bytes = __conn_read(co, buf, __TMPBUFLEN); - if(bytes == -1) { - // we've lost the connection - co->flags &= ~CXCONN_ESTABL; - r = ESXNOCONNECT; - co->flags |= CXCONN_BROKEN; - free(buf); - /* shutdown connection */ - goto __fail_3; - } - - buf[bytes] = 0; - - /* perform an rpc call */ - r = __eval_cstr(buf, ssys->system_rpc, (void *)co); - - if(!r) { /* all is fine security context is good */ - snprintf(buf, __TMPBUFLEN, "(ch-get-types)"); /* now we should receive possible channel types */ - if(__conn_write(co, buf, strlen(buf) + sizeof(char))) { - goto __finalize; - } - - /* read the message reply */ - bytes = __conn_read(co, buf, __TMPBUFLEN); - if(bytes == -1) { - goto __finalize; - } - - buf[bytes] = 0; - /* perform an rpc call */ - r = __eval_cstr(buf, ssys->system_rpc, (void *)co); - } - - free(buf); /* now we can free the temporary buffer */ - /* a listening thread creation (incoming messages) */ - if(!r) { /* success let's start a listening thread */ - r = pthread_create(&co->cthread, NULL, __cxslave_thread_listener, (void *)co); - if(!r) { - /* add connection to the list */ - usrtc_node_init(&co->csnode, co); - co->flags = (CXCONN_SLAVE | CXCONN_ESTABL); /* set the right flags */ - pthread_rwlock_wrlock(&ssys->rwlock); - usrtc_insert(ssys->connections, &co->csnode, (void *)co->uuid); - pthread_rwlock_unlock(&ssys->rwlock); - } - r = pthread_create(&co->msgthread, NULL, __msg_queue_thread, (void *)co); - if(r) goto __finalize; - r = pthread_create(&co->rmsgthread, NULL, __rmsg_queue_thread, (void *)co); - if(r) goto __finalize; - - pth_dqtpoll_run(tpoll); - co->tpoll = tpoll; - - return 0; - } - - __fail_3: - pthread_mutex_destroy(&co->oplock); - __fail_2: - free(rpc_tree); - __fail_1: - free(ch_tree); - __fail: - free(uuid); - return r; -} - -int connection_create_fapi_m(conn_sys_t *ssys, conn_t *co, int sck, - struct in_addr *addr) -{ - int r = 0; - int bytes = 0; - char *uuid; - char *buf = NULL; - usrtc_t *ch_tree, *rpc_tree; - pth_queue_t *rqueue = malloc(sizeof(pth_queue_t)); - pth_queue_t *mqueue = malloc(sizeof(pth_queue_t)); - pth_dqtpoll_t *tpoll = malloc(sizeof(pth_dqtpoll_t)); - if(!tpoll) return ENOMEM; // TODO: fallback - idx_allocator_t *idx_ch = malloc(sizeof(idx_allocator_t)); - - if(!co) return EINVAL; - else memset(co, 0, sizeof(conn_t)); - - pth_dqtpoll_init(tpoll, __rpc_callback); // TODO: check it - - if(!idx_ch) return ENOMEM; - else r = idx_allocator_init(idx_ch, MAX_CHANNELS*MAX_MULTI, 0); - if(r) return r; - - if(!(uuid = __generate_uuid())) return ENOMEM; - if(!(ch_tree = malloc(sizeof(usrtc_t)))) { - r = ENOMEM; - goto __fail; - } - if(!(rpc_tree = malloc(sizeof(usrtc_t)))) { - r = ENOMEM; - goto __fail_1; - } - if((r = pthread_mutex_init(&co->oplock, NULL))) goto __fail_2; - if((r = pthread_rwlock_init(&co->chnl_lock, NULL))) goto __fail_3; - - usrtc_init(rpc_tree, USRTC_REDBLACK, MAX_RPC_LIST, __cmp_int); - usrtc_init(ch_tree, USRTC_REDBLACK, MAX_CHANNELS, __cmp_ulong); - - co->idx_ch = idx_ch; - - /* setup connections set */ - co->ssys = ssys; - - /* assign message queue */ - r = pth_queue_init(rqueue); - if(r) goto __fail_3; - co->rqueue = rqueue; - /* assigned outbone message queue master also has this one */ - r = pth_queue_init(mqueue); - if(r) goto __fail_3; - co->mqueue = mqueue; - - /* init SSL certificates and context */ - co->ctx = SSL_CTX_new(TLSv1_2_server_method()); - if(!co->ctx) { r = EINVAL; printf("%s:%d\n", __FUNCTION__, __LINE__);goto __fail_3; } - else { - /* set verify context */ - SSL_CTX_set_verify(co->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - __verify_certcall); - /* set verify depth */ - SSL_CTX_set_verify_depth(co->ctx, VERIFY_DEPTH); - } - - /* load certificates */ - SSL_CTX_load_verify_locations(co->ctx, ssys->rootca, NULL); - /* set the local certificate from CertFile */ - if(SSL_CTX_use_certificate_file(co->ctx, ssys->certpem, - SSL_FILETYPE_PEM)<=0) { - printf("certpem1 = %s\n", ssys->certpem); - ERR_print_errors_fp(stderr); - r = EINVAL; printf("%s:%d\n", __FUNCTION__, __LINE__); - goto __fail_3; - } - /* set the private key from KeyFile (may be the same as CertFile) */ - if(SSL_CTX_use_PrivateKey_file(co->ctx, ssys->certkey, - SSL_FILETYPE_PEM)<=0) { - r = EINVAL; printf("%s:%d\n", __FUNCTION__, __LINE__); - goto __fail_3; - } - /* verify private key */ - if (!SSL_CTX_check_private_key(co->ctx)) { - r = EINVAL; printf("%s:%d\n", __FUNCTION__, __LINE__); - goto __fail_3; - } - - /* assign allocated memory */ - co->rpc_list = rpc_tree; - co->chnl_tree = ch_tree; - co->uuid = uuid; - - if(!(buf = malloc(__TMPBUFLEN))) { - r = ENOMEM; - goto __fail_3; - } - - /* now we will create an SSL connection */ - co->ssl = SSL_new(co->ctx); - co->pctx = malloc(sizeof(perm_ctx_t)); - SSL_set_fd(co->ssl, sck); /* attach connected socket */ - /* ok now we need to initialize address */ - if(addr) { - co->pctx->addr = malloc(sizeof(struct in_addr)); - memcpy(co->pctx->addr, addr, sizeof(struct in_addr)); - } else co->pctx->addr = NULL; - - SSL_set_accept_state(co->ssl); - /* set the context to verify ssl connection */ - SSL_set_ex_data(co->ssl, ex_ssldata_index, (void *)co); - //BIO_set_nbio(SSL_get_rbio(co->ssl), 1); - SSL_set_accept_state(co->ssl); - if(SSL_accept(co->ssl) == -1) { - r = EBADE; - free(buf); - /* shutdown connection */ - goto __fail_3; - } /* if success we're ready to use established SSL channel */ - - /*******************************************/ - /*-=Protocol part of connection establish=-*/ - /*******************************************/ - while(!(co->flags & CXCONN_ESTABL)) { /* read the initiation stage connections */ - bytes = __conn_read(co, buf, __TMPBUFLEN); - if(bytes > 0) { - buf[bytes] = 0; - r = __eval_cstr(buf, ssys->system_rpc, (void *)co); - if(r) { - fprintf(stderr, "Initiation func return %d\n", r); - free(buf); - SSL_shutdown(co->ssl); - goto __fail_3; - } - } else { - if(bytes < 0) { - printf("Terminate SSL connection, the other end is lost.\n"); - co->flags &= ~CXCONN_ESTABL; - co->flags |= CXCONN_BROKEN; - free(buf); - if(ssys->on_destroy) ssys->on_destroy(co); - SSL_shutdown(co->ssl); - r = ESXNOCONNECT; - goto __fail_3; - } - } - } - - /* before it will be done assign rpc list */ - if(ssys->get_rpc_typed_list_tree) - co->rpc_list = ssys->get_rpc_typed_list_tree(co); - - free(buf); - r = pthread_create(&co->cthread, NULL, __cxmaster_thread_listener, (void *)co); - if(!r) { - /* add connection to the list */ - usrtc_node_init(&co->csnode, co); - co->flags |= CXCONN_MASTER; /* set the right flags */ - pthread_rwlock_wrlock(&ssys->rwlock); - usrtc_insert(ssys->connections, &co->csnode, (void *)co->uuid); - pthread_rwlock_unlock(&ssys->rwlock); - /* threads poll --- */ - r = pthread_create(&co->msgthread, NULL, __msg_queue_thread, (void *)co); - if(r) goto __fail_3; - r = pthread_create(&co->rmsgthread, NULL, __rmsg_queue_thread, (void *)co); - if(r) goto __fail_3; - - pth_dqtpoll_run(tpoll); - co->tpoll = tpoll; - } - - return r; - - __fail_3: - pthread_mutex_destroy(&co->oplock); - __fail_2: - free(rpc_tree); - __fail_1: - free(ch_tree); - __fail: - free(uuid); - return r; -} - -int connection_close(conn_t *co) -{ - void *nil; - - pthread_cancel(co->cthread); - /* wait for the main listener */ - pthread_join(co->cthread, &nil); - /* ok the first of all we're need to wake up all */ - __wake_up_waiters(co, ESXNOCONNECT); - /* now we need to end the poll */ - pth_dqtpoll_destroy(co->tpoll, 1); /* force */ - - __connection_free(co); - - return 0; -} - -extern int __create_reg_msg(sxmsg_t **msg, chnl_t *ch); - -extern int __create_sys_msg(sxmsg_t **msg, char *uuid, chnl_t *ch, sxpayload_t *data); - -static void __connection_free(conn_t *co) -{ - pth_msg_t msg; - void *nil; - - /* since we doesn't works with IN queue and job dispatching - * close the thread - */ - msg.msgtype = END_MSG; - msg.data = NULL; - pth_queue_add(co->rqueue, &msg, END_MSG); /* it will drop the thread */ - pthread_join(co->rmsgthread, &nil); /* wait for it */ - /* message sending dispatch thread must be finished too */ - pth_queue_add(co->mqueue, &msg, END_MSG); /* it will drop the thread */ - pthread_join(co->msgthread, &nil); /* wait for it */ - /* since we don't have any threads working with channels destroy them */ - __destroy_all_channels(co); - /* permission context and callback of exists */ - if(co->ssys->on_destroy) co->ssys->on_destroy(co); - else { /* we don't have a handler */ - if(co->pctx->login) free(co->pctx->login); - if(co->pctx->passwd) free(co->pctx->passwd); - } - __connections_subsystem_connection_remove(co); - /* now we're ready to free other resources */ - if(co->uuid) free(co->uuid); - /* idx allocator */ - idx_allocator_destroy(co->idx_ch); - free(co->idx_ch); - free(co->chnl_tree); - /* kill SSL context */ - SSL_shutdown(co->ssl); - close(SSL_get_fd(co->ssl)); - SSL_free(co->ssl); - SSL_CTX_free(co->ctx); - /* destroy queues */ - pth_queue_destroy(co->mqueue, 0, NULL); - pth_queue_destroy(co->rqueue, 0, NULL); - /* locks */ - pthread_rwlock_destroy(&(co->chnl_lock)); - pthread_mutex_destroy(&(co->oplock)); - /* kill permission context */ - if(co->pctx) free(co->pctx); - - return; -} diff --git a/lib/connex.c b/lib/connex.c new file mode 100644 index 0000000..870da90 --- /dev/null +++ b/lib/connex.c @@ -0,0 +1,291 @@ +/* + * Secure Network Transport Layer Library v2 implementation. + * (sntllv2) it superseed all versions before due to the: + * - memory consumption + * - new features such as pulse emitting + * - performance optimization + * + * This is a proprietary software. See COPYING for further details. + * + * (c) Askele Group 2013-2015 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#define EBADE 1 +#define NETDB_SUCCESS 0 +#else +#include +#include +#include +#include +#endif + +#include +#include + +#include +#include + +#include + +static int __insert_rpc_function(usrtc_t *tree, const char *name, int (*rpcf)(void *, sexp_t *)) +{ + cx_rpc_t *ent = malloc(sizeof(cx_rpc_t)); + usrtc_node_t *node; + + if(!ent) return ENOMEM; + else node = &ent->node; + + if(!(ent->name = strdup(name))) { + free(ent); + return ENOMEM; + } else ent->rpcf = rpcf; + + usrtc_node_init(node, ent); + usrtc_insert(tree, node, ent->name); + + return 0; +} + +static void __destroy_rpc_list_tree(usrtc_t *tree) +{ + usrtc_node_t *node; + cx_rpc_t *ent; + + for(node = usrtc_first(tree); node != NULL; node = usrtc_first(tree)) { + ent = (cx_rpc_t *)usrtc_node_getdata(node); + usrtc_delete(tree, node); + free(ent->name); + free(ent); + } + + return; +} + +/* negotiation functions */ +/** + * Proto: (auth-set-credentials "" "") + * Return - message return; opcode as a return of this function. + * in batch mode message with 0 index used always + */ +static int __set_credentials(void *cctx, sexp_t *sx) +{ + register int idx; + conn_t *co = (conn_t *)cctx; + conn_sys_t *ssys = co->ssys; + sexp_t *isx; + char *login = NULL; + char *passwd = NULL; + + /* take a deal with S-exp */ + SEXP_ITERATE_LIST(sx, isx, idx) { + if(isx->ty == SEXP_LIST) return SNE_BADPROTO; + + if(idx > 0 && isx->aty != SEXP_DQUOTE) return SNE_BADPROTO; + if(idx == 1) login = isx->val; + else if(idx == 2) passwd = isx->val; + else if(idx > 2) return SNE_BADPROTO; + } + + if(!login || !passwd) return SNE_BADPROTO; + + co->pctx->login = strdup(login); + co->pctx->passwd = strdup(passwd); + if(!co->pctx->login || !co->pctx->passwd) { + if(co->pctx->login) free(co->pctx->login); + if(co->pctx->passwd) free(co->pctx->passwd); + return SNE_ENOMEM; + } + + if(ssys->secure_check) return ssys->secure_check(co); + else return SNE_SUCCESS; +} + +static int __get_channels_list(void *cctx, sexp_t *sx) +{ + conn_t *co = (conn_t *)cctx; + conn_sys_t *ssys = co->ssys; + sxmsg_t *msg = co->messages[0]; + char *buf = msg->payload; + usrtc_node_t *node; + rpc_typed_list_t *list_ent; + size_t maxlen = 65535 - sizeof(sntllv2_head_t); + size_t ulen = 0; + + /* we will avoid S-exp scanning here */ + + /* call the function */ + if(ssys->get_rpc_typed_list_tree) + co->rpc_list = ssys->get_rpc_typed_list_tree(co); + if(!co->rpc_list) return SNE_EPERM; + //buf += sizeof(sntllv2_head_t); + ulen += snprintf(buf + ulen, maxlen - ulen, "(set-channels-list "); + for(node = usrtc_first(co->rpc_list); node != NULL; + node = usrtc_next(co->rpc_list, node)) { /* fill the list */ + list_ent = (rpc_typed_list_t *)usrtc_node_getdata(node); + ulen += snprintf(buf + ulen, maxlen - ulen, "(:%d \"%s\")", + list_ent->type_id, list_ent->description); + } + ulen += snprintf(buf + ulen, maxlen - ulen, ")"); + msg->mhead.payload_length = ulen + sizeof(sntllv2_head_t); + + /* we're ready for messaging mode */ + co->flags |= SNSX_MESSAGINGMODE; + + return SNE_SUCCESS; +} + +static int __set_channels_list(void *cctx, sexp_t *sx) +{ + register int idx; + conn_t *co = (conn_t *)cctx; + conn_sys_t *ssys = co->ssys; + sexp_t *isx, *iisx; + int id, r; + + SEXP_ITERATE_LIST(sx, isx, idx) { + if(!idx) continue; + + if(isx->ty != SEXP_LIST) return SNE_BADPROTO; + if(sexp_list_length(isx) != 2) return SNE_BADPROTO; + + /* get id */ + sexp_list_car(isx, &iisx); + if(iisx->ty == SEXP_LIST) return SNE_BADPROTO; + if(iisx->aty != SEXP_BASIC) return SNE_BADPROTO; + if(iisx->val[0] != ':') return SNE_BADPROTO; + id = atoi(iisx->val + sizeof(char)); + + /* get short description */ + sexp_list_cdr(isx, &iisx); + if(iisx->ty == SEXP_LIST) return SNE_BADPROTO; + if(iisx->aty != SEXP_DQUOTE) return SNE_BADPROTO; + + /* ok, here we go */ + if(ssys->set_typed_list_callback) { + r = ssys->set_typed_list_callback(co, id, iisx->val); + if(r != SNE_SUCCESS) return r; + } + } + + /* we're ready for messaging mode */ + co->flags |= SNSX_MESSAGINGMODE; + co->flags &= ~SNSX_BATCHMODE; + + return SNE_SUCCESS; +} + +static int __init_systemrpc_tree(usrtc_t *rtree) +{ + /* batch mode negotiation context functions */ + if(__insert_rpc_function(rtree, "auth-set-credentials", __set_credentials)) goto __fail; + if(__insert_rpc_function(rtree, "get-channels-list", __get_channels_list)) goto __fail; + if(__insert_rpc_function(rtree, "set-channels-list", __set_channels_list)) goto __fail; + + return 0; + + __fail: + __destroy_rpc_list_tree(rtree); + return ENOMEM; +} + +static long __cmp_cstr(const void *a, const void *b) +{ + return (long)strcmp((const char *)a, (const char *)b); +} + +int connections_init(conn_sys_t *ssys) +{ + int r = 0; + + if(!ssys) return EINVAL; + else memset(ssys, 0, sizeof(conn_sys_t)); + + if(!(ssys->connections = malloc(sizeof(usrtc_t)))) return ENOMEM; + + /* init connections list */ + usrtc_init(ssys->connections, USRTC_REDBLACK, MAX_CONNECTIONS, + __cmp_cstr); + if((r = pthread_rwlock_init(&(ssys->rwlock), NULL))) + goto __fini; + + /* init RPC list related functions */ + if(!(ssys->system_rpc = malloc(sizeof(cx_rpc_list_t)))) { + r = ENOMEM; + goto __lfini; + } else { + if(!(ssys->system_rpc->rpc_tree = malloc(sizeof(usrtc_t)))) { + r = ENOMEM; + goto __lfini; + } + usrtc_init(ssys->system_rpc->rpc_tree, USRTC_SPLAY, 256, __cmp_cstr); + r = __init_systemrpc_tree(ssys->system_rpc->rpc_tree); + if(r) { + free(ssys->system_rpc->rpc_tree); + goto __lfini; + } + } + + return 0; + + __lfini: + if(ssys->system_rpc) free(ssys->system_rpc); + pthread_rwlock_destroy(&(ssys->rwlock)); + __fini: + if(ssys->connections) free(ssys->connections); + + return r; +} + +conn_sys_t *connections_create(void) +{ + int r = 0; + conn_sys_t *nsys = malloc(sizeof(conn_sys_t)); + + if(!nsys) { + errno = ENOMEM; + return NULL; + } + + r = connections_init(nsys); + if(r) { + errno = r; + free(nsys); + return NULL; + } + + return nsys; +} + +int connections_setsslserts(conn_sys_t *ssys, const char *rootca, + const char *certpem, const char *certkey) +{ + int r = ENOMEM; + + if(!ssys) return EINVAL; + /* simply copying */ + if(!(ssys->rootca = strdup(rootca))) return ENOMEM; + if(!(ssys->certkey = strdup(certkey))) goto __fail; + if(!(ssys->certpem = strdup(certpem))) goto __fail; + + r = 0; + return 0; + __fail: + if(ssys->rootca) free(ssys->rootca); + if(ssys->certkey) free(ssys->certkey); + if(ssys->certpem) free(ssys->certpem); + + return r; +} diff --git a/lib/internal.h b/lib/internal.h new file mode 100644 index 0000000..121106a --- /dev/null +++ b/lib/internal.h @@ -0,0 +1,14 @@ +#ifndef __SNTLL_INTERNAL_H__ +#define __SNTLL_INTERNAL_H__ + +/* link related */ +int _sntll_writemsg(conn_t *co, sxmsg_t *msg); + +/* channel operations */ +uint8_t _channel_open(conn_t *co, uint16_t *chid); +uint8_t _channel_close(conn_t *co, uint16_t chid); + +/* messages */ +void _message_process(sxmsg_t *msg); + +#endif /* __SNTLL_INTERNAL_H__ */ diff --git a/lib/libsntl.pc.in b/lib/libsntl.pc.in index 622a506..cd463bb 100644 --- a/lib/libsntl.pc.in +++ b/lib/libsntl.pc.in @@ -5,7 +5,7 @@ datarootdir=@datarootdir@ datadir=@datadir@ includedir=@includedir@ -Name: libsntl +Name: libsntllv2 Description: Secure Network Transport Layer library implementation Version: @VERSION@ Requires: diff --git a/lib/message.c b/lib/message.c deleted file mode 100644 index 9df5d3f..0000000 --- a/lib/message.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Secure Network Transport Layer Library implementation. - * This is a proprietary software. See COPYING for further details. - * - * (c) Askele Group 2013-2015 - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#ifdef WIN32 -#include -#else -#include -#include -#include -#include -#endif - -#include -#include - -#include -#include - -#include - -void __destroy_msg(sxmsg_t *msg) -{ - chnl_t *ch = msg->pch; - - if(msg->flags & ESXMSG_USR) { - pthread_mutex_lock(&(ch->oplock)); - idx_free(ch->idx_msg, msg->mid); - pthread_mutex_unlock(&(ch->oplock)); - } else if(msg->flags & ESXMSG_SYS) { - //if(msg->uuid) free(msg->uuid); - } - - pthread_mutex_unlock(&(msg->wait)); - pthread_mutex_destroy(&(msg->wait)); - free(msg); - return; -} - -sxmsg_t *__allocate_msg(int *res) -{ - sxmsg_t *msg = malloc(sizeof(sxmsg_t)); - int r = 0; - - if(!msg) { - *res = ENOMEM; - return NULL; - } else { - memset(msg, 0, sizeof(sxmsg_t)); - if((r = pthread_mutex_init(&(msg->wait), NULL))) { - free(msg); - *res = r; - return NULL; - } - - usrtc_node_init(&(msg->pendingq_node), msg); - } - - *res = 0; - - return msg; -} - -int __create_reg_msg(sxmsg_t **msg, chnl_t *ch) -{ - int r = 0; - sxmsg_t *sm = __allocate_msg(&r); - - if(r) return r; - else { - sm->pch = ch; - sm->flags = (ESXMSG_USR | ESXMSG_PENDING); - - /* ok allocate message ID */ - pthread_mutex_lock(&(ch->oplock)); - sm->mid = idx_allocate(ch->idx_msg); - pthread_mutex_unlock(&(ch->oplock)); - - pthread_mutex_lock(&(sm->wait)); - *msg = sm; - } - - return 0; -} - -int __create_sys_msg(sxmsg_t **msg, char *uuid, chnl_t *ch, sxpayload_t *data) -{ - int r = 0; - sxmsg_t *m = __allocate_msg(&r); - - if(r) return r; - else { - /* fill values */ - m->pch = ch; - m->uuid = uuid; - m->payload = data; - /* set the right flags */ - m->flags = (ESXMSG_SYS | ESXMSG_PENDING); - /* we need to lock the wait mutex */ - pthread_mutex_lock(&(m->wait)); - - *msg = m; - } - - return 0; -} - -/* message passing */ - -/* - * How message sending works: - * 1. Create a message structure assigned to the channel, - * 2. Put S-expression context to it - * 3. Put the message to the queue - * 4. expect the result waiting on the lock mutex - */ -static int __message_send(chnl_t *ch, sexp_t *sx, sxmsg_t **msg, struct timespec *tio) -{ - int r = 0; - sxmsg_t *m = NULL; - conn_t *co = ch->connection; - - if(!(co->flags & CXCONN_ESTABL)) { - destroy_sexp(sx); - return ESXNOCONNECT; - } - - *msg = NULL; - - r = __create_reg_msg(&m, ch); - if(r) return r; - else { - /* put the message to the search tree */ - pthread_rwlock_wrlock(&(ch->msglock)); - usrtc_insert(ch->msgs_tree, &(m->pendingq_node), &(m->mid)); - pthread_rwlock_unlock(&(ch->msglock)); - - /* message assign */ - m->opcode = 0; - m->payload = (void *)sx; - /* assign initial sx */ - m->initial_sx = sx; - - /* put the message to the run queue */ - r = pth_queue_add(co->mqueue, (void *)m, USR_MSG); - if(r) return r; /* FIXME: better give up */ - - if(m->flags & ESXMSG_PENDING) { - if(!tio) pthread_mutex_lock(&(m->wait)); - else pthread_mutex_timedlock(&(m->wait), tio); - } - if(tio && (m->flags & ESXMSG_PENDING)) - return ESXOTIMEDOUT; - if(!m->payload) { - r = m->opcode; - /* first remove the message from tree */ - pthread_rwlock_wrlock(&(ch->msglock)); - usrtc_delete(ch->msgs_tree, &(m->pendingq_node)); - pthread_rwlock_unlock(&(ch->msglock)); - /* destroy s expression */ - destroy_sexp(m->initial_sx); - /* destroy */ - __destroy_msg(m); - } else { - *msg = m; - if(m->opcode == ESXNOCONNECT || m->opcode == ESXRAPIDREPLY) - r = m->opcode; - else r = ESXOREPLYREQ; - /* FIXME: remove ugly code */ - if(m->opcode == ESXRAPIDREPLY) { - /* first remove the message from tree */ - pthread_rwlock_wrlock(&(ch->msglock)); - usrtc_delete(ch->msgs_tree, &(m->pendingq_node)); - pthread_rwlock_unlock(&(ch->msglock)); - } - } - } - - return r; -} - -int msg_send(chnl_t *ch, sexp_t *sx, sxmsg_t **msg) -{ - return __message_send(ch, sx, msg, NULL); -} - -int msg_send_timed(chnl_t *ch, sexp_t *sx, sxmsg_t **msg, struct timespec *tio) -{ - return __message_send(ch, sx, msg, tio); -} - -static int __msg_reply(sxmsg_t *msg, sexp_t *sx, struct timespec *tio, int opcode, - int israpid) -{ - int r = 0; - chnl_t *ch = msg->pch; - conn_t *co = ch->connection; - - if(!(co->flags & CXCONN_ESTABL)) { - destroy_sexp(sx); - return ESXNOCONNECT; - } - - if(msg->flags & ESXMSG_ISREPLY) - destroy_sexp((sexp_t *)msg->payload); - - msg->payload = sx; - msg->opcode = opcode; - msg->flags |= ESXMSG_PENDING; /* pending */ - msg->flags |= ESXMSG_ISREPLY; /* this is a reply */ - if(israpid) msg->flags |= ESXMSG_ISRAPID; /* message is a rapid message */ - - if(!sx || israpid) msg->flags &= ~ESXMSG_PENDING; - else msg->flags |= ESXMSG_RMONRETR; - - /* put the message to the queue */ - r = pth_queue_add(co->mqueue, (void *)msg, USR_MSG); - if(r) return r; /* FIXME: better give up */ - if(!sx || israpid) { - /* wait for write */ - //pthread_mutex_lock(&(msg->wait)); - return 0; - } - - if(msg->flags & ESXMSG_PENDING) { - if(!tio) pthread_mutex_lock(&(msg->wait)); - else pthread_mutex_timedlock(&(msg->wait), tio); - } - - if(tio && (msg->flags & ESXMSG_PENDING)) { - msg->flags &= ~ESXMSG_PENDING; /* we will not wait for it */ - return ESXOTIMEDOUT; - } - - r = msg->opcode; - - if(msg->flags & ESXMSG_CLOSURE) { - __destroy_msg(msg); - } - - return r; -} - -int msg_return(sxmsg_t *msg, int opcode) -{ - return __msg_reply(msg, NULL, NULL, opcode, 0); -} - -int msg_reply(sxmsg_t *msg, sexp_t *sx) -{ - return __msg_reply(msg, sx, NULL, 0, 0); -} - -int msg_reply_timed(sxmsg_t *msg, sexp_t *sx, struct timespec *tio) -{ - return __msg_reply(msg, sx, tio, 0, 0); -} - -int msg_reply_rapid(sxmsg_t *msg, sexp_t *sx) -{ - return __msg_reply(msg, sx, NULL, 0, 1); -} - -int msg_rapid_clean(sxmsg_t *msg) -{ - destroy_sexp(msg->initial_sx); - if(msg->payload) destroy_sexp(msg->payload); - __destroy_msg(msg); - - return 0; -} diff --git a/lib/messagesx.c b/lib/messagesx.c new file mode 100644 index 0000000..6e33d74 --- /dev/null +++ b/lib/messagesx.c @@ -0,0 +1,340 @@ +/* + * Secure Network Transport Layer Library v2 implementation. + * (sntllv2) it superseed all versions before due to the: + * - memory consumption + * - new features such as pulse emitting + * - performance optimization + * + * This is a proprietary software. See COPYING for further details. + * + * (c) Askele Group 2013-2015 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "internal.h" + +void _message_process(sxmsg_t *msg) +{ + chnl_t *chan = msg->pch; + sexp_t *sx, *isx; + usrtc_t *listrpc = chan->rpc_list->rpc_tree; + usrtc_node_t *node; + cx_rpc_t *rpcc; + int r; + + sx = parse_sexp(msg->payload, msg->mhead.payload_length); + if(!sx) sxmsg_return(msg, SNE_BADPROTO); + + sexp_list_car(sx, &isx); + if(!isx) { r = SNE_BADPROTO; goto __return_err; } + if(isx->ty == SEXP_LIST) { r = SNE_BADPROTO; goto __return_err; } + if(isx->aty != SEXP_BASIC) { r = SNE_BADPROTO; goto __return_err; } + + node = usrtc_lookup(listrpc, (void *)isx->val); + if(!node) { r = SNE_ENORPC; goto __return_err; } + else rpcc = (cx_rpc_t *)usrtc_node_getdata(node); + + rpcc->rpcf((void *)msg, sx); + + destroy_sexp(sx); + + return; + + __return_err: + destroy_sexp(sx); + sxmsg_return(msg, r); + return; +} + +static inline int __sxmsg_send(chnl_t *channel, const char *data, size_t datalen, + sxmsg_t **omsg, int pp) +{ + conn_t *co; + sxmsg_t *msg; + sntllv2_head_t *head; + ppmsg_t *ppm; + int msgidx, r; + + if(!channel) return SNE_FAILED; + if(!data || !datalen) return SNE_FAILED; + + if(!(msg = malloc(sizeof(sxmsg_t)))) return SNE_ENOMEM; + else memset(msg, 0, sizeof(sxmsg_t)); + + co = channel->connection; + head = &msg->mhead; + /* form a head */ + head->attr = SXMSG_OPEN | SXMSG_REPLYREQ; + head->reserve = channel->cid; + head->payload_length = datalen; + /* init message itself */ + pthread_mutex_init(&msg->wait, NULL); + pthread_mutex_lock(&msg->wait); + msg->pch = channel; + msg->payload = (void *)data; + + pthread_mutex_lock(&co->idx_msg_lock); + msgidx = idx_allocate(&co->idx_msg); + if(msgidx != IDX_INVAL) co->messages[msgidx] = msg; + pthread_mutex_unlock(&co->idx_msg_lock); + + if(msgidx == IDX_INVAL) { r = SNE_MMESSAGES; goto __freemsg; } + else head->msgid = (uint16_t)msgidx; + + /* ready to send it */ + if(!pp) { + r = _sntll_writemsg(co, msg); + if(r != SNE_SUCCESS) goto __closemsg; + } else { /* postponed */ + if(!(ppm = malloc(sizeof(ppmsg_t)))) { r = SNE_ENOMEM; goto __closemsg; } + list_init_node(&ppm->node); + ppm->msg = msg; + + /* under locking here */ + pthread_mutex_lock(&co->write_pending_lock); + co->pending_messages++; + list_add2tail(&co->write_pending, &ppm->node); /* push it to the FIFO */ + pthread_mutex_unlock(&co->write_pending_lock); + } + + pthread_mutex_lock(&msg->wait); /* we will sleep here */ + + if(head->payload_length) { + *omsg = msg; + return head->opcode; + } else r = head->opcode; + + __closemsg: + pthread_mutex_lock(&co->idx_msg_lock); + idx_free(&co->idx_msg, msgidx); + co->messages[msgidx] = NULL; + pthread_mutex_unlock(&co->idx_msg_lock); + __freemsg: + /* free resources for message */ + pthread_mutex_unlock(&msg->wait); + pthread_mutex_destroy(&msg->wait); + free(msg); + + return r; +} + +int sxmsg_send(chnl_t *channel, const char *data, size_t datalen, sxmsg_t **omsg) +{ + return __sxmsg_send(channel, data, datalen, omsg, 0); +} + +/* the same - postponed message i.e. will be written to the queue - not to write immendatly */ +int sxmsg_send_pp(chnl_t *channel, const char *data, size_t datalen, sxmsg_t **omsg) +{ + return __sxmsg_send(channel, data, datalen, omsg, 1); +} + +/* send a pulse message */ +int sxmsg_pulse(conn_t *co, const char *data, size_t datalen) +{ + sxmsg_t *msg = malloc(sizeof(sxmsg_t)); + sntllv2_head_t *head; + int r; + + /* a little bit of paranoid tests */ + if(!msg) return SNE_ENOMEM; + else memset(msg, 0, sizeof(sxmsg_t)); + + /* prepare it */ + head = &msg->mhead; + head->attr = 0; + head->attr = SXMSG_PULSE | SXMSG_LINK; + head->opcode = SNE_RAPIDMSG; + head->payload_length = datalen; + msg->payload = (void *)data; + + r = _sntll_writemsg(co, msg); + + free(msg); + + return r; +} + +static inline int __sxmsg_reply(sxmsg_t *msg, const char *data, + size_t datalen, int pp) +{ + chnl_t *ch; + conn_t *co; + sntllv2_head_t *head; + ppmsg_t *ppm; + int r, i; + pthread_t self = pthread_self(); + + /* a little bit of paranoid tests */ + if(!msg) return SNE_FAILED; + if(!(ch = msg->pch)) return SNE_FAILED; + if(!(co = ch->connection)) return SNE_FAILED; + + /* test for blocking */ + for(i = 0; i < 8; i++) + if(pthread_equal(self, co->thrd_poll[i])) return SNE_WOULDBLOCK; + + /* prepare it */ + head = &msg->mhead; + head->attr = 0; + head->attr |= SXMSG_REPLYREQ; + head->opcode = SNE_REPLYREQ; + head->payload_length = datalen; + msg->payload = (void *)data; + + if(!pp) { + r = _sntll_writemsg(co, msg); + if(r != SNE_SUCCESS) return r; + } else { + if(!(ppm = malloc(sizeof(ppmsg_t)))) return SNE_ENOMEM; + list_init_node(&ppm->node); + ppm->msg = msg; + + /* under locking here */ + pthread_mutex_lock(&co->write_pending_lock); + co->pending_messages++; + list_add2tail(&co->write_pending, &ppm->node); /* push it to the FIFO */ + pthread_mutex_unlock(&co->write_pending_lock); + } + + pthread_mutex_lock(&msg->wait); /* wait */ + + r = head->opcode; + + if((head->attr & SXMSG_CLOSED) && !head->payload_length) { /* dialog closed and no data exists */ + pthread_mutex_unlock(&msg->wait); /* we able to invalidate it */ + pthread_mutex_destroy(&msg->wait); + free(msg); + } + + return r; +} + +int sxmsg_reply(sxmsg_t *msg, const char *data, size_t datalen) +{ + return __sxmsg_reply(msg, data, datalen, 0); +} + +int sxmsg_reply_pp(sxmsg_t *msg, const char *data, size_t datalen) +{ + return __sxmsg_reply(msg, data, datalen, 1); +} + +int sxmsg_rreply(sxmsg_t *msg, size_t datalen) +{ + chnl_t *ch; + conn_t *co; + sntllv2_head_t *head; + int r, mid; + + /* a little bit of paranoid tests */ + if(!msg) return SNE_FAILED; + if(!(ch = msg->pch)) return SNE_FAILED; + if(!(co = ch->connection)) return SNE_FAILED; + + /* prepare it */ + head = &msg->mhead; + head->attr = 0; + head->attr |= SXMSG_CLOSED; + head->opcode = SNE_RAPIDMSG; + head->payload_length = datalen; + + mid = head->msgid; + pthread_mutex_lock(&co->idx_msg_lock); + idx_free(&co->idx_msg, mid); + co->messages[mid] = NULL; + pthread_mutex_unlock(&co->idx_msg_lock); + + r = _sntll_writemsg(co, msg); + + pthread_mutex_unlock(&msg->wait); /* we able to invalidate it */ + pthread_mutex_destroy(&msg->wait); + free(msg); + + return r; +} + +static inline int __sxmsg_return(sxmsg_t *msg, int opcode, int pp) +{ + chnl_t *ch; + conn_t *co; + sntllv2_head_t *head; + ppmsg_t *ppm; + int r, mid; + + /* a little bit of paranoid tests */ + if(!msg) return SNE_FAILED; + if(!(ch = msg->pch)) return SNE_FAILED; + if(!(co = ch->connection)) return SNE_FAILED; + + head = &msg->mhead; + head->attr = 0; + head->attr |= SXMSG_CLOSED; + head->opcode = opcode; + head->payload_length = 0; + mid = head->msgid; + + if(!pp) { + /* free index */ + pthread_mutex_lock(&co->idx_msg_lock); + idx_free(&co->idx_msg, mid); + co->messages[mid] = NULL; + pthread_mutex_unlock(&co->idx_msg_lock); + + r = _sntll_writemsg(co, msg); + } else { + if(!(ppm = malloc(sizeof(ppmsg_t)))) return SNE_ENOMEM; + else { /* remove it */ + pthread_mutex_lock(&co->idx_msg_lock); + idx_free(&co->idx_msg, mid); + co->messages[mid] = NULL; + pthread_mutex_unlock(&co->idx_msg_lock); + } + + list_init_node(&ppm->node); + ppm->msg = msg; + + /* under locking here */ + pthread_mutex_lock(&co->write_pending_lock); + co->pending_messages++; + list_add2tail(&co->write_pending, &ppm->node); /* push it to the FIFO */ + pthread_mutex_unlock(&co->write_pending_lock); + + r = SNE_SUCCESS; + } + + return r; +} + +int sxmsg_return(sxmsg_t *msg, int opcode) +{ + return __sxmsg_return(msg, opcode, 0); +} + +int sxmsg_return_pp(sxmsg_t *msg, int opcode) +{ + return __sxmsg_return(msg, opcode, 1); +} + +void sxmsg_clean(sxmsg_t *msg) +{ + free(msg->payload); + free(msg); + return; +} diff --git a/lib/queue.c b/lib/queue.c deleted file mode 100644 index 2dce4e1..0000000 --- a/lib/queue.c +++ /dev/null @@ -1,465 +0,0 @@ -/* - * This is a proprietary software. See COPYING for further details. - * - * - * - * (c) Askele Group 2013-2015 - */ - -#include -#include -#include -#include -#include -#include -#include -/**/ -#ifdef WIN32 -#include -#else - - -#include - -#endif - -/**/ -// #include -#include -#include - -#include - -#include - -#define MAX_QUEUE_SIZE 4096 -#define MAX_QUEUE_POOL 256 - -static long __cmp_uint(const void *a, const void *b) -{ - return (long)(*(unsigned int *)a - *(unsigned int *)b); -} - -static inline pth_msg_t *__get_newmsg(pth_queue_t *queue) -{ - usrtc_t *tree = &queue->msgcache; - usrtc_node_t *node; - pth_msg_t *tmp; - - if(usrtc_count(tree)) { - node = usrtc_first(tree); - tmp = (pth_msg_t *)usrtc_node_getdata(node); - usrtc_delete(tree, node); - } else { - tmp = malloc(sizeof(pth_msg_t)); - tree = &queue->qtree; - node = &tmp->node; - usrtc_node_init(node, tmp); - } - /* insert it */ - tree = &queue->qtree; - tmp->qlength = usrtc_count(tree); - usrtc_insert(tree, node, (void *)(&tmp->qlength)); - - return tmp; -} - -static inline void __release_msg(pth_queue_t *queue, pth_msg_t *msg) -{ - usrtc_node_t *node = &msg->node; - usrtc_t *tree = &queue->qtree; - - tree = &queue->qtree; /* remove from queue */ - usrtc_delete(tree, node); - - tree = &queue->msgcache; - - if(usrtc_count(tree) >= MAX_QUEUE_POOL) - free(msg); - else { - msg->data = NULL; - msg->msgtype = NIL_MSG; - usrtc_insert(tree, node, (void *)&msg->qlength); - } - - return; -} - -int pth_queue_init(pth_queue_t *queue) -{ - int r = 0; - - memset(queue, 0, sizeof(pth_queue_t)); - if((r = pthread_cond_init(&queue->cond, NULL))) - return r; - - if((r = pthread_mutex_init(&queue->mutex, NULL))) { - pthread_cond_destroy(&queue->cond); - return r; - } - - usrtc_init(&queue->qtree, USRTC_AVL, MAX_QUEUE_SIZE, __cmp_uint); - usrtc_init(&queue->msgcache, USRTC_AVL, MAX_QUEUE_POOL, __cmp_uint); - - return r; -} - -int pth_queue_add(pth_queue_t *queue, void *data, unsigned int msgtype) -{ - pth_msg_t *newmsg; - - pthread_mutex_lock(&queue->mutex); - newmsg = __get_newmsg(queue); - if (newmsg == NULL) { - pthread_mutex_unlock(&queue->mutex); - return ENOMEM; - } - - newmsg->data = data; - newmsg->msgtype = msgtype; - - if(queue->length == 0) - pthread_cond_broadcast(&queue->cond); - queue->length++; - pthread_mutex_unlock(&queue->mutex); - - return 0; -} - -int pth_queue_get(pth_queue_t *queue, const struct timespec *timeout, pth_msg_t *msg) -{ - usrtc_t *tree; - usrtc_node_t *node = NULL; - pth_msg_t *tmp; - int r = 0; - struct timespec abstimeout; - - if (queue == NULL || msg == NULL) - return EINVAL; - else - tree = &queue->qtree; - - if (timeout) { /* setup timeout */ - struct timeval now; - - gettimeofday(&now, NULL); - abstimeout.tv_sec = now.tv_sec + timeout->tv_sec; - abstimeout.tv_nsec = (now.tv_usec * 1000) + timeout->tv_nsec; - if (abstimeout.tv_nsec >= 1000000000) { - abstimeout.tv_sec++; - abstimeout.tv_nsec -= 1000000000; - } - } - - pthread_mutex_lock(&queue->mutex); - - /* Will wait until awakened by a signal or broadcast */ - while ((node = usrtc_first(tree)) == NULL && r != ETIMEDOUT) { /* Need to loop to handle spurious wakeups */ - if (timeout) - r = pthread_cond_timedwait(&queue->cond, &queue->mutex, &abstimeout); - else - pthread_cond_wait(&queue->cond, &queue->mutex); - } - if (r == ETIMEDOUT) { - pthread_mutex_unlock(&queue->mutex); - return r; - } - - tmp = (pth_msg_t *)usrtc_node_getdata(node); - queue->length--; - - msg->data = tmp->data; - msg->msgtype = tmp->msgtype; - msg->qlength = tmp->qlength; /* we will hold the msg id instead of size here */ - - __release_msg(queue, tmp); - pthread_mutex_unlock(&queue->mutex); - - return 0; -} - -int pth_queue_destroy(pth_queue_t *queue, int freedata, void (*free_msg)(void *)) -{ - int r = 0; - usrtc_t *tree = &queue->qtree; - usrtc_node_t *node = NULL; - pth_msg_t *msg; - - if (queue == NULL) return EINVAL; - - pthread_mutex_lock(&queue->mutex); - - for (node = usrtc_first(tree); node != NULL; node = usrtc_first(tree)) { - usrtc_delete(tree, node); - msg = (pth_msg_t *)usrtc_node_getdata(node); - - if(freedata) free(msg->data); - else if(free_msg) free_msg(msg->data); - - free(msg); - } - /* free cache */ - tree = &queue->msgcache; - for (node = usrtc_first(tree); node != NULL; node = usrtc_first(tree)) { - usrtc_delete(tree, node); - free(usrtc_node_getdata(node)); - } - - pthread_mutex_unlock(&queue->mutex); - r = pthread_mutex_destroy(&queue->mutex); - pthread_cond_destroy(&queue->cond); - - return r; -} - -unsigned int pth_queue_length(pth_queue_t *queue) -{ - unsigned int c; - - pthread_mutex_lock(&queue->mutex); - c = queue->length; - pthread_mutex_unlock(&queue->mutex); - - return c; -} - -/* dynamic queue thread poll */ - -struct __pthrd_data { - pth_dqtpoll_t *pthrd; - int myid; -}; - -static void *__poll_thread(void *poll) -{ - int r = 0; - struct __pthrd_data *thrdata = (struct __pthrd_data *)poll; - struct __pthrd_data *npoll = NULL; - pth_msg_t msgbuf, sysbuf; - pth_dqtpoll_t *p = thrdata->pthrd; - pth_queue_t *q = p->queue; - ulong_t myid = thrdata->myid; - struct timeval now; - int resched = 0; - long tusec, si, mdrate; - - while(1) { - resched = 0; - r = pth_queue_get(q, NULL, &msgbuf); - pthread_rwlock_wrlock(&(p->stats_lock)); - if(p->flags & DQTPOLL_DEADSTAGE) { /* poll going to be killed */ - pthread_rwlock_unlock(&(p->stats_lock)); - idx_free(p->idx, myid); - p->poll_value--; - - return NULL; - } - - /* now get the time */ - gettimeofday(&now, NULL); - if((now.tv_sec >= p->sched_time.tv_sec) && - (now.tv_usec >= p->sched_time.tv_usec)) { - resched = 1; - /* set the new schedule time */ - si = 0; - tusec = DQTPOLL_DELTAMS + now.tv_usec; - if(tusec > 1000000) { - tusec -= 1000000; - si++; - } - p->sched_time.tv_sec += si + DQTPOLL_DELTASE; - p->sched_time.tv_usec = tusec; - } - - if(resched) { /* ok now we need to resched and descrease/increase thread poll volume */ - if(p->msgop) mdrate = ((DQTPOLL_DELTASE*1000000) + DQTPOLL_DELTAMS)/p->msgop; - else mdrate = 0; - if((mdrate > p->poll_value) && (p->poll_value < MAX_POLL_VALUE)) { /* increase ! */ - if((npoll = malloc(sizeof(struct __pthrd_data)))) { - npoll->myid = idx_allocate(p->idx); - npoll->pthrd = p; - p->poll_value++; - /* create thread here */ - if(pthread_create(&(p->poll[npoll->myid]), NULL, __poll_thread, npoll)) { - idx_free(p->idx, npoll->myid); - p->poll_value--; - free(npoll); - } - } - } else if((p->poll_value > 2) && (mdrate < p->poll_value)) /* decrease */ { - memset(&sysbuf, 0, sizeof(pth_msg_t)); - pth_queue_add(p->queue, &sysbuf, POLL_DECREASE); - } - - /* init all other stuff */ - p->msgop = 0; - } - - if(r) { - pthread_rwlock_unlock(&(p->stats_lock)); - continue; - } else p->msgop++; - pthread_rwlock_unlock(&(p->stats_lock)); - - switch(msgbuf.msgtype) { - case USR_MSG: - /* do the job */ - p->jobdata_callback(msgbuf.data); - break; - case POLL_DECREASE: - pthread_rwlock_rdlock(&(p->stats_lock)); - if(p->poll_value > 2) r = 1; /* exit now */ - pthread_rwlock_unlock(&(p->stats_lock)); - if(r) { - pthread_rwlock_wrlock(&(p->stats_lock)); - idx_free(p->idx, myid); - p->poll_value--; - pthread_rwlock_unlock(&(p->stats_lock)); - free(poll); - return NULL; - } - break; - default: - /* TODO: do something ... */ - break; - } - } - - return NULL; -} - -/* init poll, structure must be allocated */ -int pth_dqtpoll_init(pth_dqtpoll_t *tpoll, int (*jobdata_callback)(void *)) -{ - int r = 0; - pth_queue_t *queue = malloc(sizeof(pth_queue_t)); - pthread_t *poll = malloc(sizeof(pthread_t)*MAX_POLL_VALUE); - idx_allocator_t *idx = malloc(sizeof(idx_allocator_t)); - struct __pthrd_data *ndata = malloc(sizeof(struct __pthrd_data)); - - /* check it for allocation */ - if(!ndata) goto __enomem; - if(!idx) goto __enomem; - if(!queue) goto __enomem; - if(!poll) goto __enomem; - - /* init all the stuff */ - if(idx_allocator_init(idx, MAX_POLL_VALUE*16, 0)) { - __enomem: - r = ENOMEM; - goto __finish; - } - if(pth_queue_init(queue)) goto __enomem; /* init queue */ - if(pthread_rwlock_init(&(tpoll->stats_lock), NULL)) goto __enomem; - - /* set parameters */ - memset(poll, 0, sizeof(pthread_t)*MAX_POLL_VALUE); - tpoll->flags = 0; - tpoll->idx = idx; - tpoll->poll = poll; - tpoll->queue = queue; - tpoll->poll_value = 2; - tpoll->spurious_wakeups = 0; - tpoll->msgop = 0; - tpoll->jobdata_callback = jobdata_callback; - - /* first thread initiation */ - idx_reserve(idx, 0); - ndata->myid = 0; - ndata->pthrd = tpoll; - if(pthread_create(&(poll[0]), NULL, __poll_thread, ndata)) { - __eadd: - pthread_rwlock_destroy(&(tpoll->stats_lock)); - goto __enomem; - } - /* second thread initiation */ - ndata = malloc(sizeof(struct __pthrd_data)); - if(!ndata) goto __eadd; - idx_reserve(idx, 1); - ndata->myid = 1; - ndata->pthrd = tpoll; - if(pthread_create(&(poll[1]), NULL, __poll_thread, ndata)) { - pthread_rwlock_destroy(&(tpoll->stats_lock)); - goto __enomem; - } - - gettimeofday(&(tpoll->sched_time), NULL); - - __finish: - if(r) { - if(ndata) free(ndata); - if(idx) free(idx); - if(queue) { - pth_queue_destroy(queue, 0, NULL); - free(queue); - } - if(poll) free(poll); - } - return r; -} - -/* run poll: poll */ -int pth_dqtpoll_run(pth_dqtpoll_t *tpoll) -{ - int r = 0; - - pthread_rwlock_wrlock(&(tpoll->stats_lock)); - if((tpoll->flags & DQTPOLL_RUNNING) || (tpoll->flags & DQTPOLL_DEADSTAGE)) r = EINVAL; - else { - tpoll->flags |= DQTPOLL_RUNNING; - } - pthread_rwlock_unlock(&(tpoll->stats_lock)); - - return r; -} - -/* add the job to the queue: poll, job data, message type */ -int pth_dqtpoll_add(pth_dqtpoll_t *tpoll, void *job, unsigned int type) -{ - int r = 0; - - r = pth_queue_add(tpoll->queue, job, type); - - return r; -} - -/* destroy the poll: poll, force flag - * if force flag is set (!= 0), give up - * about jobs, if no, do the job, but don't - * accept the new ones, and destroy all poll - * with last thread. - */ -int pth_dqtpoll_destroy(pth_dqtpoll_t *tpoll, int force) -{ - int r = 0; - pth_msg_t tmpmsg; - - pthread_rwlock_wrlock(&(tpoll->stats_lock)); - tpoll->flags |= DQTPOLL_DEADSTAGE; - pthread_rwlock_unlock(&(tpoll->stats_lock)); - - /* now we need to wait */ - while(1) { - pthread_rwlock_rdlock(&(tpoll->stats_lock)); - if(!tpoll->poll_value) { - pthread_rwlock_unlock(&(tpoll->stats_lock)); - break; - } else { - pthread_rwlock_unlock(&(tpoll->stats_lock)); - pth_queue_add(tpoll->queue, &tmpmsg, 0); /* spurious */ - } - usleep(100); /* just to sleep and free timeslice to others */ - } - - /* free all */ - pth_queue_destroy(tpoll->queue, 0, NULL); - idx_allocator_destroy(tpoll->idx); - pthread_rwlock_destroy(&(tpoll->stats_lock)); - - free(tpoll->poll); - free(tpoll->queue); - free(tpoll->idx); - - return r; -} - diff --git a/lib/rpclist.c b/lib/rpclist.c index 444b3ad..456656f 100644 --- a/lib/rpclist.c +++ b/lib/rpclist.c @@ -10,29 +10,14 @@ #include #include #include -#include -#include -#include -#ifdef WIN32 -#include -#else -#include -#include -#include -#include -#endif - #include - - -#include - #include #include -#include +#include +#include static long __cmp_int(const void *a, const void *b) { diff --git a/lib/sntllv2.c b/lib/sntllv2.c new file mode 100644 index 0000000..332b0fc --- /dev/null +++ b/lib/sntllv2.c @@ -0,0 +1,1216 @@ +/* + * Secure Network Transport Layer Library v2 implementation. + * (sntllv2) it superseed all versions before due to the: + * - memory consumption + * - new features such as pulse emitting + * - performance optimization + * + * This is a proprietary software. See COPYING for further details. + * + * (c) Askele Group 2013-2015 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#define EBADE 1 +#define NETDB_SUCCESS 0 +#else +#include +#include +#include +#include +#endif + +#include +#include + +#include +#include +#include + +#include + +#include "internal.h" + +typedef struct __sntll_bundle_type { + void *buf; + conn_t *conn; +} sntllv2_bundle_t; + +/* networking helpers */ +#ifndef WIN32 +int __resolvehost(const char *hostname, char *buf, int buf_len, + struct hostent **rhp) +{ + struct hostent *hostbuf ;//= malloc(sizeof(struct hostent)); + struct hostent *hp = *rhp = NULL; + int herr = 0, hres = 0; + + + hostbuf = malloc(sizeof(struct hostent)); + if(!hostbuf) return NO_ADDRESS; + hres = gethostbyname_r(hostname, hostbuf, + buf, buf_len, &hp, &herr); + + if(hres) return NO_ADDRESS; + *rhp = hp; + + return NETDB_SUCCESS; +} +#endif + +static int __conn_read(conn_t *co, void *buf, size_t buf_len) +{ + int rfd = SSL_get_fd(co->ssl), r; + fd_set readset, writeset; + int ofcmode, read_blocked = 0, read_blocked_on_write = 0; + + /* First we make the socket nonblocking */ +#ifndef WIN32 + ofcmode = fcntl(rfd, F_GETFL,0); + ofcmode |= O_NDELAY; + if(fcntl(rfd, F_SETFL, ofcmode)) + fprintf(stderr, "[sntllv2] (RD)Couldn't make socket nonblocking"); +#endif + + __retry: + + do { + __try_again: + r = SSL_read(co->ssl, buf, (int)buf_len); + switch(SSL_get_error (co->ssl, r)) { + case SSL_ERROR_NONE: + return r; + break; + case SSL_ERROR_WANT_READ: + /* get prepare to select */ + read_blocked = 1; + break; + case SSL_ERROR_WANT_WRITE: /* here we blocked on write */ + read_blocked_on_write = 1; + break; + case SSL_ERROR_SYSCALL: + if(errno == EAGAIN || errno == EINTR) goto __try_again; + else { + fprintf(stderr, "[sntllv2] (RD)SSL syscall error.\n"); + goto __close_conn; + } + break; + case SSL_ERROR_WANT_CONNECT: + case SSL_ERROR_WANT_ACCEPT: + fprintf(stderr, "[sntllv2] (RD)SSL negotiation required. Trying again.\n"); + goto __try_again; + break; + case SSL_ERROR_SSL: + fprintf(stderr, "[sntllv2] (RD)SSL error occured. Connection will be closed.\n"); + goto __close_conn; + break; + case SSL_ERROR_ZERO_RETURN: + fprintf(stderr, "[sntllv2] (RD)SSL connection is cleary closed.\n"); + default: + __close_conn: + fprintf(stderr, "[sntllv2] (RD)Unknown error on %s (errno = %d)\n", co->uuid, errno); + return -1; + } + } while(SSL_pending(co->ssl) && !read_blocked); + + __select_retry: + + if(read_blocked) { + FD_ZERO(&readset); + FD_SET(rfd, &readset); + /* waits until something will be ready to read */ + r = select(rfd + 1, &readset, NULL, NULL, NULL); + if(r < 0) { + if(errno == EINTR || errno == EAGAIN) goto __select_retry; + fprintf(stderr, "[sntllv2] (RD)Select (%d) on %s\n", errno, co->uuid); + return -1; + } + if(!r) { + fprintf(stderr, "[sntllv2] (RD)Nothing to wait for\n"); + return 0; + } + read_blocked = 0; + if(r && FD_ISSET(rfd, &readset)) goto __retry; /* try to read again */ + } + if(read_blocked_on_write) { /* we was blocked on write */ + FD_ZERO(&readset); + FD_ZERO(&writeset); + FD_SET(rfd, &readset); + FD_SET(rfd, &writeset); + + r = select(rfd + 1, &readset, &writeset, NULL, NULL); + read_blocked_on_write = 0; + if(r && FD_ISSET(rfd, &writeset)) goto __retry; + } + + return r; +} + +static int __conn_write(conn_t *co, void *buf, size_t buf_len) +{ + int r, rfd = SSL_get_fd(co->ssl); + fd_set writeset; + + __retry: + r = SSL_write(co->ssl, buf, (int)buf_len); + switch(SSL_get_error(co->ssl, r)) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* here we should block */ + FD_ZERO(&writeset); + FD_SET(rfd, &writeset); + r = select(rfd + 1, NULL, &writeset, NULL, NULL); + if(r && FD_ISSET(rfd, &writeset)) goto __retry; + break; + case SSL_ERROR_SYSCALL: + if(errno == EAGAIN || errno == EINTR) goto __retry; + else goto __close_conn; + break; + default: + __close_conn: + if(r < 0) { + fprintf(stderr, "[sntllv2] (WR)Unknown error on %s (%d)\n", co->uuid, r); + return -1; + } else return r; + } + + return r; +} + +int _sntll_writemsg(conn_t *co, sxmsg_t *msg) +{ + sntllv2_head_t *head; + size_t rd; + int r; + + if(!co || !msg) return SNE_FAILED; + + /* check message for validity */ + head = &msg->mhead; + if(head->payload_length && !msg->payload) return SNE_FAILED; + + /* write the head and payload if applicable */ + pthread_mutex_lock(&co->sslinout[1]); + rd = __conn_write(co, head, sizeof(sntllv2_head_t)); + if(rd < 0) { + co->flags |= SNSX_CLOSED; + r = SNE_ESSL; + } else if(head->payload_length) { + rd = __conn_write(co, msg->payload, head->payload_length); + /* check up again */ + if(rd < 0) { co->flags |= SNSX_CLOSED; r = SNE_ESSL; } + } + pthread_mutex_unlock(&co->sslinout[1]); + + if(!(co->flags & SNSX_CLOSED)) r = SNE_SUCCESS; + + return r; +} + +static sntllv2_bundle_t *__sntll_bundle_create(conn_t *co) +{ + sntllv2_bundle_t *n = malloc(sizeof(sntllv2_bundle_t)); + + if(!n) return NULL; + else memset(n, 0, sizeof(sntllv2_bundle_t)); + + n->buf = mmap(NULL, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if(n->buf == MAP_FAILED) { + free(n); + return NULL; + } + + n->conn = co; + + return n; +} + +static void __sntll_bundle_destroy(sntllv2_bundle_t *n) +{ + munmap(n->buf, 65536); + free(n); + return; +} + +static int ex_ssldata_index; /** < index used to work with additional data + * provided to the special call during SSL handshake */ + +/* this function is an ugly implementation to get C string with uuid */ +extern char *__generate_uuid(void); + +/* this is a callback to perform a custom SSL certs chain validation, + * as I promised here the comments, a lot of ... + * The first shit: 0 means validation failed, 1 otherwise + * The second shit: X509 API, I guess u will love it ;-) + * openssl calls this function for each certificate in chain, + * since our case is a simple (depth of chain is one, since we're + * don't care for public certificates lists or I cannot find any reasons to + * do it ...), amount of calls reduced, and in this case we're interested + * only in top of chain i.e. actual certificate used on client side, + * the validity of signing for other certificates within chain is + * guaranteed by the ssl itself. + * u know, we need to lookup in database, or elsewhere... some information + * about client certificate, and decide - is it valid, or not?, if so + * yep I mean it's valid, we can assign it's long fucking number to + * security context, to use in ongoing full scaled connection handshaking. + */ +static int __verify_certcall(int preverify_ok, X509_STORE_CTX *ctx) +{ + // X509 *cert = X509_STORE_CTX_get_current_cert(ctx); + int err = X509_STORE_CTX_get_error(ctx), depth = X509_STORE_CTX_get_error_depth(ctx); + SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + conn_t *co = SSL_get_ex_data(ssl, ex_ssldata_index); /* this is a custom data we're set before */ + conn_sys_t *ssys = co->ssys; + + /* now we need to check for certificates with a long chain, + * so since we have a short one, reject long ones */ + if(depth > VERIFY_DEPTH) { /* longer than we expect */ + preverify_ok = 0; /* yep, 0 means error for those function callback in openssl, fucking set */ + err = X509_V_ERR_CERT_CHAIN_TOO_LONG; + X509_STORE_CTX_set_error(ctx, err); + } + + if(!preverify_ok) return 0; + + /* ok, now we're on top of SSL (depth == 0) certs chain, + * and we can validate client certificate */ + if(!depth) { + co->pctx->certid = + ASN1_INTEGER_get((const ASN1_INTEGER *)X509_get_serialNumber(ctx->current_cert)); + /* now we're need to check the ssl cert */ + if(ssys->validate_sslpem) { + if(ssys->validate_sslpem(co)) return 0; + else return 1; + } else return 0; + } + + return preverify_ok; +} + +/* dummy just to check the server side */ +static int __verify_certcall_dummy(int preverify_ok, X509_STORE_CTX *ctx) +{ + return preverify_ok; +} + +int sntl_init(void) +{ + /* init SSL library */ + SSL_library_init(); + + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + + ex_ssldata_index = SSL_get_ex_new_index(0, "__ssldata index", NULL, NULL, NULL); + + return 0; +} + +conn_t *__connection_minimal_alloc(struct in_addr *addr) +{ + conn_t *co = malloc(sizeof(conn_t)); + int r; + + if(!co) { r = ENOMEM; goto __fail; } + else memset(co, 0, sizeof(conn_t)); + + if(!(co->messages = malloc(sizeof(uintptr_t)*1024))) { r = ENOMEM; goto __fail; } + else memset(co->messages, 0, sizeof(uintptr_t)*1024); + + if(!(co->pctx = malloc(sizeof(perm_ctx_t)))) { r = ENOMEM; goto __fail; } + else memset(co->pctx, 0, sizeof(perm_ctx_t)); + if(addr) { + if(!(co->pctx->addr = malloc(sizeof(struct in_addr)))) { r = ENOMEM; goto __fail; } + + memcpy(co->pctx->addr, addr, sizeof(struct in_addr)); + } + + if(!(co->uuid = __generate_uuid())) { r = ENOMEM; goto __fail; } + + return co; + + __fail: + if(co) { + if(co->pctx) { + if(co->pctx->addr) free(co->pctx->addr); + free(co->pctx); + } + if(co->messages) free(co->messages); + free(co); + } + errno = r; + return NULL; +} + +static int __connection_second_alloc(conn_t *co) +{ + usrtc_node_init(&co->csnode, co); + + memset(&co->idx_ch, 0, sizeof(idx_allocator_t)); + memset(&co->idx_msg, 0, sizeof(idx_allocator_t)); + if((idx_allocator_init(&co->idx_ch, 512, 0))) goto __fail; + if((idx_allocator_init(&co->idx_msg, 1024, 0))) goto __fail; + + if(!(co->channels = malloc(sizeof(uintptr_t)*512))) goto __fail; + else memset(co->channels, 0, sizeof(uintptr_t)*512); + + /* init mutexes */ + pthread_mutex_init(&co->idx_ch_lock, NULL); + pthread_mutex_init(&co->idx_msg_lock, NULL); + pthread_mutex_init(&co->write_pending_lock, NULL); + pthread_mutex_init(&co->sslinout[0], NULL); + pthread_mutex_init(&co->sslinout[1], NULL); + + /* init list */ + list_init_head(&co->write_pending); + + return SNE_SUCCESS; + + __fail: + idx_allocator_destroy(&co->idx_msg); + idx_allocator_destroy(&co->idx_ch); + return SNE_ENOMEM; +} + +static void __connection_second_free(conn_t *co) +{ + if(co->channels) free(co->channels); + idx_allocator_destroy(&co->idx_msg); + idx_allocator_destroy(&co->idx_ch); + + pthread_mutex_destroy(&co->idx_ch_lock); + pthread_mutex_destroy(&co->idx_msg_lock); + pthread_mutex_destroy(&co->write_pending_lock); + pthread_mutex_destroy(&co->sslinout[0]); + pthread_mutex_destroy(&co->sslinout[1]); + + return; +} + +static void __connection_minimal_free(conn_t *co) +{ + if(co) { + if(co->pctx) { + if(co->pctx->addr) free(co->pctx->addr); + free(co->pctx); + } + if(co->messages) free(co->messages); + free(co->uuid); + free(co); + } + + return; +} + +static int __eval_syssexp(conn_t *co, sexp_t *sx) +{ + cx_rpc_list_t *rpc_list = co->ssys->system_rpc; + usrtc_node_t *node; + cx_rpc_t *rentry; + char *rpcf; + + if(sx->ty == SEXP_LIST) + rpcf = sx->list->val; + else return SNE_BADPROTO; + + /* find an appropriate function */ + node = usrtc_lookup(rpc_list->rpc_tree, rpcf); + + if(!node) return SNE_ENORPC; + else rentry = (cx_rpc_t *)usrtc_node_getdata(node); + + /* call it */ + return rentry->rpcf((void *)co, sx); +} + +#define _CONN_INUSE(co) (co)->usecount++; +#define _CONN_NOTINUSE(co) (co)->usecount--; +#define _CONN_UCOUNT(co) (co)->usecount + +static void __connection_destroy(conn_t *co) +{ + int i = 0; + sxmsg_t *msg, *omsg; + ppmsg_t *ppm; + list_node_t *iter, *siter; + chnl_t *chan; + sntllv2_head_t *head; + conn_sys_t *ssys = co->ssys; + + /* first we will unpin all messages and mark it as errors on */ + if(co->pending_messages) { + pthread_mutex_lock(&co->write_pending_lock); + list_for_each_safe(&co->write_pending, iter, siter) { + ppm = container_of(iter, ppmsg_t, node); + omsg = ppm->msg; + + /* ok, now we're able to remove it from list */ + list_del(&ppm->node); + if(omsg->mhead.attr & SXMSG_CLOSED) { /* message is closed - destroy it */ + pthread_mutex_unlock(&omsg->wait); + pthread_mutex_destroy(&omsg->wait); + free(omsg); + } else { /* wake up */ + omsg->mhead.opcode = SNE_LINKERROR; + pthread_mutex_unlock(&omsg->wait); + } + free(ppm); + co->pending_messages--; + } + pthread_mutex_unlock(&co->write_pending_lock); + } + + /* go thru messages */ + pthread_mutex_lock(&co->idx_msg_lock); + for(i = 0; i < 1024; i++) { + msg = co->messages[i]; + if(!msg) continue; + else head = &msg->mhead; + head->opcode = SNE_LINKERROR; + pthread_mutex_unlock(&msg->wait); + co->messages[i] = NULL; + idx_free(&co->idx_msg, i); + } + pthread_mutex_unlock(&co->idx_msg_lock); + + /* update use count */ + _CONN_NOTINUSE(co); + + /* ok, let's free other if we can */ + if(!_CONN_UCOUNT(co)) { + /* ok now we will free the channels */ + pthread_mutex_lock(&co->idx_ch_lock); + for(i = 0; i < 512; i++) { + chan = co->channels[i]; + if(!chan) continue; + idx_free(&co->idx_ch, i); + free(chan); + } + pthread_mutex_unlock(&co->idx_ch_lock); + + if(ssys->on_destroy) ssys->on_destroy(co); + if(co->pctx->login) free(co->pctx->login); + if(co->pctx->passwd) free(co->pctx->passwd); + + SSL_shutdown(co->ssl); + close(SSL_get_fd(co->ssl)); + SSL_free(co->ssl); + SSL_CTX_free(co->ctx); + __connection_second_free(co); + __connection_minimal_free(co); + } + + return; +} + +static void *__sntll_thread(void *b) +{ + sntllv2_bundle_t *bun = (sntllv2_bundle_t *)b; + conn_t *co = bun->conn; + void *buf = bun->buf; + char *bbuf = (char*)buf; + sntllv2_head_t *mhead = (sntllv2_head_t *)buf; + sxmsg_t *msg, *omsg; + sexp_t *sx; + chnl_t *channel; + list_node_t *iter, *siter; + ppmsg_t *ppm; + pthread_t self = pthread_self(); + struct timespec wtick; + int dispatch = 0, e; + size_t rd, wr; + ulong_t mid; + + /* byte buffer is following head */ + bbuf += sizeof(sntllv2_head_t); + + __wait_alive: + /* flag test - FIXME: make it atomic (it will works atomically on x86, btw on others not) */ + if(!(co->flags & SNSX_ALIVE)) { + if(co->flags & SNSX_CLOSED) goto __finish; + else { + usleep(20); + goto __wait_alive; + } + } + + /* check up a thread */ + if(pthread_equal(self, co->thrd_poll[7])) /* dispatcher */ + dispatch = 1; + + /* update use count */ + _CONN_INUSE(co); + + /* the following logic : (except dispatcher) + * 1. check up pending write -> if exists write one and start again., otherwise go next + * 2. read from ssl connection (we will sleep if other already acquire the lock) + */ + while(1) { + __again: + if(co->flags & SNSX_CLOSED) goto __finish; /* go away if required asap */ + /* works with pending messages */ + if(co->pending_messages) { + pthread_mutex_lock(&co->write_pending_lock); + list_for_each_safe(&co->write_pending, iter, siter) { + ppm = container_of(iter, ppmsg_t, node); + omsg = ppm->msg; + if(_sntll_writemsg(co, omsg) != SNE_SUCCESS) { + pthread_mutex_unlock(&co->write_pending_lock); + goto __finish; /* write failed - finishing ... */ + } + + /* ok, now we're able to remove it from list */ + list_del(&ppm->node); + if(omsg->mhead.attr & SXMSG_CLOSED) { /* message is closed - destroy it */ + pthread_mutex_unlock(&omsg->wait); + pthread_mutex_destroy(&omsg->wait); + free(omsg); + } + free(ppm); + co->pending_messages--; + } + pthread_mutex_unlock(&co->write_pending_lock); + } + + if(!dispatch) pthread_mutex_lock(&(co->sslinout[0])); + else { /* dispatch thread ticking every ət */ + wtick.tv_sec = time(NULL) + 1; + e = pthread_mutex_timedlock(&(co->sslinout[0]), &wtick); + if(e == ETIMEDOUT) goto __again; + } + if(co->flags & SNSX_CLOSED) { + pthread_mutex_unlock(&(co->sslinout[0])); + goto __finish; + } + rd = __conn_read(co, mhead, sizeof(sntllv2_head_t)); +#ifdef _VERBOSE_DEBUG + dumphead(mhead); +#endif + if(rd != sizeof(sntllv2_head_t)) { + __sslproto_error: + co->flags |= SNSX_CLOSED; + pthread_mutex_unlock(&(co->sslinout[0])); + goto __finish; + } else { + /* check up if we can read or not */ + if(mhead->payload_length) { + rd = __conn_read(co, bbuf, mhead->payload_length); + if(rd < 0) goto __sslproto_error; + else pthread_mutex_unlock(&(co->sslinout[0])); + if(rd != mhead->payload_length) { + mid = mhead->msgid; + /* if we're need to do something */ + if(mhead->msgid >= 1024) { + mhead->opcode = SNE_INVALINDEX; + goto __return_error; + } else { + pthread_mutex_lock(&co->idx_msg_lock); + msg = co->messages[mid]; + pthread_mutex_unlock(&co->idx_msg_lock); + } + if(!msg) { + if(mhead->attr & SXMSG_OPEN) mhead->opcode = SNE_BADPROTO; + else { + if((mhead->attr & SXMSG_PROTO) || (mhead->attr & SXMSG_LINK)) + mhead->opcode = SNE_BADPROTO; + else mhead->opcode = SNE_NOSUCHMSG; + } + } + __return_error: + mhead->attr |= SXMSG_CLOSED; + mhead->payload_length = 0; + pthread_mutex_lock(&(co->sslinout[1])); + wr = __conn_write(co, mhead, sizeof(sntllv2_head_t)); + pthread_mutex_unlock(&(co->sslinout[1])); + if(wr < 0) goto __finish; + else goto __again; + } + } else pthread_mutex_unlock(&(co->sslinout[0])); + /* take a message */ + if(mhead->attr & SXMSG_PROTO) { /* protocol message i.e. channel open/close */ + /* ok, check up the side */ + if(mhead->attr & SXMSG_REPLYREQ) { /* means we're not initiators and we don't need to allocate a message */ + if(mhead->attr & SXMSG_OPEN) + mhead->opcode = _channel_open(co, &mhead->reserve); + else mhead->opcode = _channel_close(co, mhead->reserve); + + /* set flags */ + mhead->payload_length = 0; + mhead->attr &= ~SXMSG_REPLYREQ; + pthread_mutex_lock(&(co->sslinout[1])); + wr = __conn_write(co, mhead, sizeof(sntllv2_head_t)); + pthread_mutex_unlock(&(co->sslinout[1])); + if(wr < 0) goto __finish; + } else { /* it's came back */ + /* reply came ... */ + if(mhead->msgid >= 1024) { + __inval_idx_nor: + fprintf(stderr, "[sntllv2] Invalid index of the message.\n"); + goto __again; + } + mid = mhead->msgid; + pthread_mutex_lock(&co->idx_msg_lock); + msg = co->messages[mid]; + pthread_mutex_unlock(&co->idx_msg_lock); + if(!msg) goto __inval_idx_nor; + + /* ok now we'are copy data and unlock wait mutex */ + memcpy(&msg->mhead, mhead, sizeof(sntllv2_head_t)); + pthread_mutex_unlock(&msg->wait); + } + } else if(mhead->attr & SXMSG_LINK) { /* link layer messages */ + if(mhead->attr & SXMSG_CLOSED) goto __finish; /* close the link */ + if(mhead->attr & SXMSG_PULSE) { /* it's a link pulse messages */ + /* TODO: syncronization and so on */ + if(mhead->opcode == SNE_RAPIDMSG) { /* custom pulse */ + sx = parse_sexp(bbuf, mhead->payload_length); + if(sx && co->ssys->on_pulse) co->ssys->on_pulse(co, sx); + if(sx) destroy_sexp(sx); + } + } + } else { /* regular messages */ + if((mhead->attr & SXMSG_OPEN) && (mhead->attr & SXMSG_REPLYREQ)) { /* dialog initiation */ + channel = co->channels[mhead->reserve]; + if(!channel) { /* ok, we'are failed */ + mhead->opcode = SNE_NOSUCHCHAN; + __ret_regerr: + mhead->payload_length = 0; + mhead->attr &= ~SXMSG_REPLYREQ; + mhead->attr &= ~SXMSG_OPEN; + mhead->attr |= SXMSG_CLOSED; + pthread_mutex_lock(&(co->sslinout[1])); + wr = __conn_write(co, mhead, sizeof(sntllv2_head_t)); + pthread_mutex_unlock(&(co->sslinout[1])); + if(wr < 0) goto __finish; + else goto __again; + } + /* if message is busy - fails */ + mid = mhead->msgid; + msg = co->messages[mid]; + if(msg) { mhead->opcode = SNE_EBUSY; goto __ret_regerr; } + + /* now we will take a deal */ + if(!(msg = malloc(sizeof(sxmsg_t)))) { + mhead->opcode = SNE_ENOMEM; goto __ret_regerr; + } else { + /* set mutex and channel */ + pthread_mutex_init(&msg->wait, NULL); + pthread_mutex_lock(&msg->wait); + msg->pch = channel; + /* copy header only */ + memcpy(&msg->mhead, mhead, sizeof(sntllv2_head_t)); + if(mhead->payload_length) msg->payload = bbuf; + } + + pthread_mutex_lock(&co->idx_msg_lock); + idx_reserve(&co->idx_msg, mid); + co->messages[mid] = msg; + pthread_mutex_unlock(&co->idx_msg_lock); + + /* now we are able to process the message */ + _message_process(msg); + } else if(mhead->attr & SXMSG_CLOSED) { + /* check for the message */ + if(mhead->msgid >= 1024) goto __inval_idx_nor; + mid = mhead->msgid; + pthread_mutex_lock(&co->idx_msg_lock); + msg = co->messages[mid]; + if(!msg) { + pthread_mutex_unlock(&co->idx_msg_lock); goto __inval_idx_nor; } + + /* message dialog is closed - remove this right now */ + idx_free(&co->idx_msg, mid); + co->messages[mid] = NULL; + pthread_mutex_unlock(&co->idx_msg_lock); + + if(msg->mhead.attr & SXMSG_TIMEDOUT) { /* nobody wait for it */ + /* now just free it */ + pthread_mutex_unlock(&msg->wait); + pthread_mutex_destroy(&msg->wait); + free(msg); + } else { + memcpy(&msg->mhead, mhead, sizeof(sntllv2_head_t)); + if(mhead->payload_length) { + msg->payload = malloc(mhead->payload_length); + if(msg->payload) memcpy(msg->payload, bbuf, mhead->payload_length); + else msg->mhead.opcode = SNE_ENOMEM; + } + + pthread_mutex_unlock(&msg->wait); /* wake up thread waiting for */ + } + } else if((!(mhead->attr & SXMSG_CLOSED) && !(mhead->attr & SXMSG_OPEN)) && + (mhead->attr & SXMSG_REPLYREQ)) { /* ongoing dialog */ + /* check for the message */ + if(mhead->msgid >= 1024) goto __inval_idx_nor; + mid = mhead->msgid; + msg = co->messages[mid]; + if(!msg) goto __inval_idx_nor; + + if(msg->mhead.attr & SXMSG_TIMEDOUT) { /* nobody wait for it */ + pthread_mutex_lock(&co->idx_msg_lock); + idx_free(&co->idx_msg, mid); + co->messages[mid] = NULL; + pthread_mutex_unlock(&co->idx_msg_lock); + + /* now just free it */ + pthread_mutex_destroy(&msg->wait); + free(msg); + + /* we must reply */ + mhead->opcode = SNE_ETIMEDOUT; + goto __ret_regerr; + } else { + memcpy(&msg->mhead, mhead, sizeof(sntllv2_head_t)); + if(mhead->payload_length) { + msg->payload = malloc(mhead->payload_length); + if(msg->payload) memcpy(msg->payload, bbuf, mhead->payload_length); + else { + mhead->opcode = msg->mhead.opcode = SNE_ENOMEM; /* we will return it to waitee */ + msg->mhead.attr &= ~SXMSG_REPLYREQ; /* doesn't need to reply */ + /* reply here now */ + mhead->payload_length = 0; + mhead->attr &= ~SXMSG_REPLYREQ; + mhead->attr &= ~SXMSG_OPEN; + mhead->attr |= SXMSG_CLOSED; + pthread_mutex_lock(&(co->sslinout[1])); + wr = __conn_write(co, mhead, sizeof(sntllv2_head_t)); + pthread_mutex_unlock(&(co->sslinout[1])); + if(wr < 0) goto __finish; + } + } + pthread_mutex_unlock(&msg->wait); /* wake up thread waiting for */ + } + } else { mhead->opcode = SNE_BADPROTO; goto __ret_regerr; } + } + } + } + + __finish: + co->flags |= SNSX_CLOSED; + __connection_destroy(co); + __sntll_bundle_destroy(b); /* destroy bundle */ + return NULL; +} + +conn_t *connection_master_link(conn_sys_t *ssys, int sck, struct in_addr *addr) +{ + void *buf = NULL; + char *bbuf; + conn_t *co = __connection_minimal_alloc(addr); + sxmsg_t *msg = NULL; + sntllv2_head_t *head; + sntllv2_bundle_t *bundle; + size_t rd; + int r = SNE_FAILED; + + if(!co) { + errno = SNE_ENOMEM; + return NULL; + } + + /* ok, now we need to init ssl stuff */ + co->ssys = ssys; + + /* init SSL certificates and context */ + co->ctx = SSL_CTX_new(TLSv1_2_server_method()); + if(!co->ctx) { r = SNE_ENOMEM; goto __fail; } + else { + /* set verify context */ + SSL_CTX_set_verify(co->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + __verify_certcall); + /* set verify depth */ + SSL_CTX_set_verify_depth(co->ctx, VERIFY_DEPTH); + } + + /* load certificates */ + SSL_CTX_load_verify_locations(co->ctx, ssys->rootca, NULL); + /* set the local certificate from CertFile */ + if(SSL_CTX_use_certificate_file(co->ctx, ssys->certpem, + SSL_FILETYPE_PEM)<=0) { + ERR_print_errors_fp(stderr); + r = SNE_ESSL; + goto __fail; + } + /* set the private key from KeyFile (may be the same as CertFile) */ + if(SSL_CTX_use_PrivateKey_file(co->ctx, ssys->certkey, + SSL_FILETYPE_PEM)<=0) { + r = SNE_ESSL; + goto __fail; + } + /* verify private key */ + if (!SSL_CTX_check_private_key(co->ctx)) { + r = SNE_ESSL; + goto __fail; + } + + /* now we will create an SSL connection */ + co->ssl = SSL_new(co->ctx); + if(!co->ssl) { r = SNE_ENOMEM; goto __fail; } + else SSL_set_fd(co->ssl, sck); /* attach connected socket */ + + SSL_set_accept_state(co->ssl); + /* set the context to verify ssl connection */ + SSL_set_ex_data(co->ssl, ex_ssldata_index, (void *)co); + SSL_set_accept_state(co->ssl); + if(SSL_accept(co->ssl) == -1) { r = SNE_EPERM; goto __fail; } + + /* ok, now we are able to allocate and so on */ + /* set connection to the batch mode */ + co->flags |= SNSX_BATCHMODE; + /* allocate our first buffer */ + buf = mmap(NULL, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if(buf == MAP_FAILED) { r = SNE_ENOMEM; goto __fail2; } + /* allocate first message */ + if(!(msg = malloc(sizeof(sxmsg_t)))) { r = SNE_ENOMEM; goto __fail2; } + else { + memset(msg, 0, sizeof(sxmsg_t)); + co->messages[0] = msg; + } + bbuf = (char *)buf; + bbuf += sizeof(sntllv2_head_t); + + sexp_t *sx; + while(co->flags & SNSX_BATCHMODE) { + rd = __conn_read(co, buf, sizeof(sntllv2_head_t)); + if(rd == sizeof(sntllv2_head_t)) { + head = (sntllv2_head_t *)buf; + + /* check for returns */ + if(head->opcode != SNE_SUCCESS) { r = head->opcode; goto __fail3; } + else { /* opcode is fine */ + /* if we're ready for messaging mode, turn off batch mode */ + if(co->flags & SNSX_MESSAGINGMODE) { + co->flags &= ~SNSX_BATCHMODE; + break; + } + } + + if(!head->payload_length) continue; /* pass the following check up */ + + rd = __conn_read(co, bbuf, head->payload_length); + if(rd != head->payload_length) { r = SNE_LINKERROR; goto __fail3; } + bbuf[rd] = '\0'; + sx = parse_sexp(bbuf, rd); + if(!sx) goto __fail3; + + /* initialize message */ + msg->payload = bbuf; + msg->mhead.payload_length = 0; + /* deal with it */ + r = __eval_syssexp(co, sx); + memcpy(head, &msg->mhead, sizeof(sntllv2_head_t)); + head->opcode = r; + if(r != SNE_SUCCESS) { /* we finish */ + head->payload_length = 0; + __conn_write(co, head, sizeof(sntllv2_head_t)); + destroy_sexp(sx); + goto __fail3; + } + rd = __conn_write(co, buf, sizeof(sntllv2_head_t) + msg->mhead.payload_length); + if(rd != sizeof(sntllv2_head_t) + msg->mhead.payload_length) { + destroy_sexp(sx); + goto __fail3; + } + + destroy_sexp(sx); + } else { r = SNE_LINKERROR; goto __fail3; } + } + + /* if we're there - negotiation is done, going to init messaging mode */ + r = __connection_second_alloc(co); + if(r != SNE_SUCCESS) goto __fail3; + + /* free message */ + co->messages[0] = NULL; + free(msg); + + /* and now we're need to create a thread poll */ + if(!(bundle = malloc(sizeof(sntllv2_bundle_t)))) { r = SNE_ENOMEM; goto __fail4; } + else { + bundle->buf = buf; + bundle->conn = co; + } + int i; + for(i = 0; i < 8; i++) { + if(bundle == (void *)0xdead) bundle = __sntll_bundle_create(co); + if(!bundle) goto __fail5; + r = pthread_create(&co->thrd_poll[i], NULL, __sntll_thread, bundle); + if(r) goto __fail5; + else bundle = (void *)0xdead; + } + + /* all is done, connection now ready */ + co->flags |= SNSX_ALIVE; + + r = SNE_SUCCESS; + errno = r; + + return co; + + __fail5: + r = SNE_ENOMEM; + /* bundles will be freed by the threads when SSL_read will fails. */ + __fail4: + __connection_second_free(co); + __fail3: + if(ssys->on_destroy) ssys->on_destroy(co); + __fail2: + if(msg) free(msg); + if(buf != MAP_FAILED) munmap(buf, 65536); + SSL_shutdown(co->ssl); + __fail: + if(co) { + if(co->ssl) SSL_free(co->ssl); + if(co->ctx) SSL_CTX_free(co->ctx); + __connection_minimal_free(co); + } + close(sck); + errno = r; + + return NULL; +} + +conn_t *connection_link(conn_sys_t *ssys, const char *host, + int port, const char *SSL_cert, const char *login, + const char *passwd) +{ + conn_t *co = __connection_minimal_alloc(NULL); + struct hostent *host_; + struct sockaddr_in addr; + int r = SNE_SUCCESS, sck; +#ifdef WIN32 + WSADATA wsaData; +#endif + char hostbuf[2048]; + void *buf = NULL; + char *bbuf; + sntllv2_head_t *head; + sntllv2_bundle_t *bundle; + sxmsg_t *msg; + size_t rd, wr; + int i; + + r = SNE_IGNORED; + if(!host || !SSL_cert) goto __fail; + if(!co) { r = SNE_ENOMEM; goto __fail; } + +#ifdef WIN32 + WSAStartup(MAKEWORD(2, 2), &wsaData); +#endif + + /* ok, now we need to init ssl stuff */ + co->ssys = ssys; + + /* init SSL certificates and context */ + co->ctx = SSL_CTX_new(TLSv1_2_client_method()); + if(!co->ctx) { r = SNE_ENOMEM; goto __fail; } + else { + /* set verify context */ + SSL_CTX_set_verify(co->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + __verify_certcall_dummy); + /* set verify depth */ + SSL_CTX_set_verify_depth(co->ctx, VERIFY_DEPTH); + } + + /* load certificates */ + SSL_CTX_load_verify_locations(co->ctx, ssys->rootca, NULL); + /* set the local certificate from CertFile */ + if(SSL_CTX_use_certificate_file(co->ctx, SSL_cert, + SSL_FILETYPE_PEM)<=0) { + ERR_print_errors_fp(stderr); + r = SNE_ESSL; + goto __fail; + } + /* set the private key from KeyFile (may be the same as CertFile) */ + if(SSL_CTX_use_PrivateKey_file(co->ctx, SSL_cert, + SSL_FILETYPE_PEM)<=0) { + r = SNE_ESSL; + goto __fail; + } + /* verify private key */ + if (!SSL_CTX_check_private_key(co->ctx)) { + r = SNE_ESSL; + goto __fail; + } + + /* resolve host */ +#ifdef WIN32 + host_ = gethostbyname(host); +#else + r = __resolvehost(host, hostbuf, 2048, &host_); +#endif + if(r) { + r = SNE_FAILED; + goto __fail; + } + + /* create a socket */ + sck = socket(PF_INET, SOCK_STREAM, 0); + bzero(&addr, sizeof(addr)); + + /* try to connect it */ + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = *(uint32_t*)(host_->h_addr); + r = connect(sck, (struct sockaddr*)&addr, sizeof(addr)); + if(r) { + close(sck); + r = SNE_FAILED; /* couldn't connect to the desired host */ + goto __fail; + } + + /* SSL handshake */ + co->ssl = SSL_new(co->ctx); /* TODO: checkout for it */ + SSL_set_fd(co->ssl, sck); /* attach connected socket */ + SSL_set_connect_state(co->ssl); + if(SSL_connect(co->ssl) == -1) { + r = SNE_EPERM; + /* shutdown connection */ + goto __fail; + } /* if success we're ready to use established SSL channel */ + + /* set connection to the batch mode */ + co->flags |= SNSX_BATCHMODE; + + /* allocate our first buffer */ + buf = mmap(NULL, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if(buf == MAP_FAILED) { r = SNE_ENOMEM; goto __fail2; } + /* allocate first message */ + if(!(msg = malloc(sizeof(sxmsg_t)))) { r = SNE_ENOMEM; goto __fail2; } + else { + memset(msg, 0, sizeof(sxmsg_t)); + co->messages[0] = msg; + } + bbuf = (char *)buf; + bbuf += sizeof(sntllv2_head_t); + head = (sntllv2_head_t *)buf; + + sexp_t *sx; + size_t ln; + while(co->flags & SNSX_BATCHMODE) { + /* form a message -- credentials */ + ln = snprintf(bbuf, 65535 - sizeof(sntllv2_head_t), "(auth-set-credentials \"%s\" \"%s\")", + login ? login : "nil", passwd ? passwd : "nil"); + + head->opcode = SNE_SUCCESS; + head->payload_length = ln; + wr = __conn_write(co, buf, ln + sizeof(sntllv2_head_t)); + if(wr < 0) goto __fail2; + + rd = __conn_read(co, head, sizeof(sntllv2_head_t)); + if(rd < 0) goto __fail2; + if(head->opcode != SNE_SUCCESS) { + r = head->opcode; + goto __fail2; + } + + /* ok, get available channels */ + head->opcode = SNE_SUCCESS; + ln = snprintf(bbuf, 65535 - sizeof(sntllv2_head_t), "(get-channels-list)"); + head->payload_length = ln; + wr = __conn_write(co, buf, ln + sizeof(sntllv2_head_t)); + if(wr < 0) goto __fail2; + + rd = __conn_read(co, head, sizeof(sntllv2_head_t)); + if(rd < 0) goto __fail2; + if(head->opcode != SNE_SUCCESS) goto __fail2; + if(!head->payload_length) goto __fail2; + rd = __conn_read(co, bbuf, head->payload_length); + if(rd < 0) goto __fail2; + + /* perform a parsing of the desired message */ + bbuf[rd] = '\0'; + sx = parse_sexp(bbuf, rd); + if(!sx) { r = SNE_BADPROTO; goto __fail2; } + r = __eval_syssexp(co, sx); + if(!r) r = SNE_SUCCESS; + destroy_sexp(sx); + + /* write back */ + head->opcode = r; + head->payload_length = 0; + wr = __conn_write(co, head, sizeof(sntllv2_head_t)); + if(wr < 0) { + blub("fuck"); + r = SNE_LINKERROR; goto __fail2;} + if(r != SNE_SUCCESS) { r = SNE_LINKERROR; goto __fail2;} + } + + /* if we're there - negotiation is done, going to init messaging mode */ + r = __connection_second_alloc(co); + if(r != SNE_SUCCESS) goto __fail3; + + /* free message */ + co->messages[0] = NULL; + free(msg); + + /* and now we're need to create a thread poll */ + if(!(bundle = malloc(sizeof(sntllv2_bundle_t)))) { r = SNE_ENOMEM; goto __fail4; } + else { + bundle->buf = buf; + bundle->conn = co; + } + for(i = 0; i < 8; i++) { + if(bundle == (void *)0xdead) bundle = __sntll_bundle_create(co); + if(!bundle) goto __fail5; + r = pthread_create(&co->thrd_poll[i], NULL, __sntll_thread, bundle); + if(r) goto __fail5; + else bundle = (void *)0xdead; + } + + /* all is done, connection now ready */ + co->flags |= SNSX_ALIVE; + + return co; + + __fail5: + r = SNE_ENOMEM; + /* bundles will be freed by the threads when SSL_read will fails. */ + __fail4: + __connection_second_free(co); + __fail3: + if(ssys->on_destroy) ssys->on_destroy(co); + __fail2: + if(buf != MAP_FAILED) munmap(buf, 65536); + SSL_shutdown(co->ssl); + close(sck); + __fail: + if(co) { + if(co->ssl) SSL_free(co->ssl); + if(co->ctx) SSL_CTX_free(co->ctx); + __connection_minimal_free(co); + } + errno = r; + return NULL; +} + +int connection_close(conn_t *co) +{ + sntllv2_head_t mhead; + + memset(&mhead, 0, sizeof(sntllv2_head_t)); + /* setup header */ + mhead.attr = SXMSG_LINK | SXMSG_CLOSED; + + pthread_mutex_lock(&(co->sslinout[1])); + __conn_write(co, &mhead, sizeof(sntllv2_head_t)); + pthread_mutex_unlock(&(co->sslinout[1])); + + /* we will not wait anything */ + co->flags |= SNSX_CLOSED; + + return SNE_SUCCESS; +} diff --git a/lib/support.c b/lib/uuid.c similarity index 71% rename from lib/support.c rename to lib/uuid.c index a6e5883..873addd 100644 --- a/lib/support.c +++ b/lib/uuid.c @@ -31,14 +31,6 @@ #include -#include -#include - -#include -#include - -#include - #ifdef WIN32 #define UUID_T_LENGTH 16 @@ -101,25 +93,3 @@ char *__generate_uuid(void) #endif } -/* networking helpers */ -#ifndef WIN32 -int __resolvehost(const char *hostname, char *buf, int buf_len, - struct hostent **rhp) -{ - struct hostent *hostbuf ;//= malloc(sizeof(struct hostent)); - struct hostent *hp = *rhp = NULL; - int herr = 0, hres = 0; - - - hostbuf = malloc(sizeof(struct hostent)); - if(!hostbuf) return NO_ADDRESS; - hres = gethostbyname_r(hostname, hostbuf, - buf, buf_len, &hp, &herr); - - if(hres) return NO_ADDRESS; - *rhp = hp; - - return NETDB_SUCCESS; -} -#endif - diff --git a/man/Makefile.am b/man/Makefile.am new file mode 100644 index 0000000..8075986 --- /dev/null +++ b/man/Makefile.am @@ -0,0 +1,3 @@ +man_MANS = sxmsg_rreply.3 connections_create.3 connections_init.3 connections_destroy.3 connections_free.3 \ + sntl_init.3 connections_setsslserts.3 connections_set_priv.3 connections_get_priv.3 \ + connections_set_ondestroy.3 diff --git a/man/connections_create.3 b/man/connections_create.3 new file mode 100644 index 0000000..ade3951 --- /dev/null +++ b/man/connections_create.3 @@ -0,0 +1,80 @@ +.TH CONNECTIONS_CREATE 3 "20 July 2015" "SNTLLv2" "SNTL Library Manual" +.SH NAME +connections_create \- Allocate and initialize connections system +.br +connections_init \- Initialize connections system +.br +connections_destroy \- Destroy connections system +.br +connections_free \- Free all stuff allocated for connections system +.SH SYNOPSIS +.B #include +.sp +conn_sys_t *connections_create(void); + +int connections_init(conn_sys_t *ssys); + +int connections_destroy(conn_sys_t *ssys); + +int connections_free(conn_sys_t *ssys); + +.br +.sp +.SH DESCRIPTION +.B connections_create +will create a separate connections set used for handling creation and initialization of the connection links. +.B connections_init +will do the same, but it will take an already preallocated pointer. +.br +.B connections_destroy +will deallocate all related to the connections set, include pointer +.BI ssys +, but +.B connections_free +will not free this pointer consider it will be deallocated by user. +.br +.SH RETURN VALUE +Upon successful completion, the function +.B connections_create +will return a valid pointer to the connections set. +.B connections_init +will return +.B 0 +.br +Otherwise NULL will be returned and errno will be set, or error will be returned, in case of +.B connections_init + use. +.SH ERRORS +.B connections_init +will return, +.B connection_create will set +.B errno +to: +.br +.B ENOMEM +Not enough memory to allocate all required stuff. +.br +.B EINVAL +Returned if invalid pointer given. +.br +.SH BUGS +Not known yet. +.SH EXAMPLE +None. +.SH APPLICATION USAGE +None. +.SH RATIONALE +Connection link destroy function might be used carefully, the logic of there functions is a +send a special link layer message that will close the connection link on the master side. +The reason to care about is a delivery of all messages, channels and other stuff, because +when this functions called all pending messages are drops. +.SH SEE ALSO +.BI connections_setsslserts(3) +, +.BI sntl_init(3) +.SH COPYRIGHT +This is a proprietary software. See COPYING for further details. +.br +(c) Askele Group 2013-2015 +.SH AUTHOR +Alexander Vdolainen (vdo@askele.com) diff --git a/man/connections_destroy.3 b/man/connections_destroy.3 new file mode 120000 index 0000000..5df5f7f --- /dev/null +++ b/man/connections_destroy.3 @@ -0,0 +1 @@ +connections_create.3 \ No newline at end of file diff --git a/man/connections_free.3 b/man/connections_free.3 new file mode 120000 index 0000000..5df5f7f --- /dev/null +++ b/man/connections_free.3 @@ -0,0 +1 @@ +connections_create.3 \ No newline at end of file diff --git a/man/connections_get_priv.3 b/man/connections_get_priv.3 new file mode 120000 index 0000000..2aeb521 --- /dev/null +++ b/man/connections_get_priv.3 @@ -0,0 +1 @@ +connections_set_priv.3 \ No newline at end of file diff --git a/man/connections_init.3 b/man/connections_init.3 new file mode 120000 index 0000000..5df5f7f --- /dev/null +++ b/man/connections_init.3 @@ -0,0 +1 @@ +connections_create.3 \ No newline at end of file diff --git a/man/connections_set_ondestroy.3 b/man/connections_set_ondestroy.3 new file mode 100644 index 0000000..7518364 --- /dev/null +++ b/man/connections_set_ondestroy.3 @@ -0,0 +1,48 @@ +.TH CONNECTIONS_ON_DESTROY 3 "20 July 2015" "SNTLLv2" "SNTL Library Manual" +.SH NAME +connections_on_destroy \- Set callback for the connection links based on the connections set, called in case of link destroying +.br +.SH SYNOPSIS +.B #include +.sp +#define connections_set_ondestroy(c, f) + +.br +.sp +.SH DESCRIPTION +This macro should be used if you want to setup callback function fired upon a connection is closed or broken. +.br +This macro will set on +.B c +connections link set callback +.B f +which is a function pointer of the type: +.br +.sp +.B typedef void (*ondestroy_t)(conn_t *); +.br +.sp +This function will be called if connection link failed, broken etc. It will pass a pointer to the failed connection. +.SH RETURN VALUE +None. +.SH ERRORS +None. +.SH BUGS +None known yet. +.SH EXAMPLE +None. +.SH APPLICATION USAGE +None. +.SH RATIONALE +None. +.SH SEE ALSO +.BI connections_set_priv(3) +, +.BI connections_get_priv(3) +.SH COPYRIGHT +This is a proprietary software. See COPYING for further details. +.br +(c) Askele Group 2013-2015 +.SH AUTHOR +Alexander Vdolainen (vdo@askele.com) + diff --git a/man/connections_set_priv.3 b/man/connections_set_priv.3 new file mode 100644 index 0000000..66188a9 --- /dev/null +++ b/man/connections_set_priv.3 @@ -0,0 +1,58 @@ +.TH CONNECTIONS_SET_PRIV 3 "20 July 2015" "SNTLLv2" "SNTL Library Manual" +.SH NAME +connections_set_priv \- Set a private pointer to the connection links set +.br +connections_get_priv \- Get a private pointer of the connection links set +.br +.SH SYNOPSIS +.B #include +.sp +#define connections_set_priv(c, p) +.br +#define connections_get_priv(c) + +.br +.sp +.SH DESCRIPTION +There macros should be used in case if you want set and get back your private pointer for something. +.br +Pointer is a +.B void* +i.e. it might be anything you pointed in the memory. +.br +.B connections_set_priv +will set a pointer +.B p +to connections set +.B c +.br +.B connections_get_priv +will return your pointer set to +.B c +connection set. +.br +.SH RETURN VALUE +None in case of +.B connections_set_priv +, a private pointer set in case of +.B connections_get_priv +.br +.SH ERRORS +None. +.br +.SH BUGS +None known yet. +.SH EXAMPLE +None. +.SH APPLICATION USAGE +None. +.SH RATIONALE +None. +.SH SEE ALSO +.BI connections_set_ondestroy(3) +.SH COPYRIGHT +This is a proprietary software. See COPYING for further details. +.br +(c) Askele Group 2013-2015 +.SH AUTHOR +Alexander Vdolainen (vdo@askele.com) diff --git a/man/connections_setsslserts.3 b/man/connections_setsslserts.3 new file mode 100644 index 0000000..4977cea --- /dev/null +++ b/man/connections_setsslserts.3 @@ -0,0 +1,63 @@ +.TH CONNECTIONS_SETSSLSERTS 3 "20 July 2015" "SNTLLv2" "SNTL Library Manual" +.SH NAME +connections_setsslserts \- Setup root public certificate to check up, and also setups key and public certificate used for connections set +.br +.SH SYNOPSIS +.B #include +.sp +int connections_setsslserts(conn_sys_t *ssys, const char *rootca, + const char *certpem, const char *certkey); + +.br +.sp +.SH DESCRIPTION +.B connections_setsslserts +will setup root public certificate and private key and public certificate used to run connections set. +This is required always, see below for more information. +.br +.sp +This function just set a pathname of the certificates for +.BI ssys +where +.BI rootca +is a pathname to the public X.509 certificate +.BI certpem +is a your own X.509 certificate and +.BI certkey +is a your own key for this certificate. Therefore, +.BI certpem +and +.BI certkey +might be a single file. +.br +.SH RETURN VALUE +Will return 0 on success or errors below. +.br +.SH ERRORS +.B EINVAL +Invalid pointer given. +.br +.B ENOMEM +There are no available memory for operation. +.br +.SH BUGS +None known yet, but name is historically wrong it should be called connections_setsslcerts, btw there are many +code was written with this mistake. +.SH EXAMPLE +None. +.SH APPLICATION USAGE +Call this function *always* before linking a connection. Non master link will always avoid non-root certificate and key, +since you will required to pass it via call. +.SH RATIONALE +None. +.SH SEE ALSO +.BI connection_link(3) +, +.BI connection_link_master(3) +.SH COPYRIGHT +This is a proprietary software. See COPYING for further details. +.br +(c) Askele Group 2013-2015 +.SH AUTHOR +Alexander Vdolainen (vdo@askele.com) + diff --git a/man/sntl_init.3 b/man/sntl_init.3 new file mode 100644 index 0000000..9306b68 --- /dev/null +++ b/man/sntl_init.3 @@ -0,0 +1,45 @@ +.TH SNTL_INIT 3 "20 July 2015" "SNTLLv2" "SNTL Library Manual" +.SH NAME +sntl_init \- Initialize all required globals to run sntl library functions +.br +.SH SYNOPSIS +.B #include +.sp +int *sntl_init(void); + +.br +.sp +.SH DESCRIPTION +.B sntl_init +will initialize all globals required to run sntl related functions, it mostly about +openssl library initialization. +.br +.SH RETURN VALUE +Always returns +.B 0 +since we cannot track openssl global initialization routines. +.br +.SH ERRORS +None errors might be returned. +.SH BUGS +None known yet. +.SH EXAMPLE +None. +.SH APPLICATION USAGE +Call this function from the main thread before using sntl library. Otherwise it will not works. There are also +useful to know if you are using other openssl functionality there are no need to initialize globals for it. +.SH RATIONALE +None. +.SH SEE ALSO +.BI connections_create(3) +, +.BI connections_destroy(3) +.SH COPYRIGHT +This is a proprietary software. See COPYING for further details. +.br +(c) Askele Group 2013-2015 +.SH AUTHOR +Alexander Vdolainen (vdo@askele.com) + + + diff --git a/man/sxmsg_rreply.3 b/man/sxmsg_rreply.3 new file mode 100644 index 0000000..9019bd9 --- /dev/null +++ b/man/sxmsg_rreply.3 @@ -0,0 +1,59 @@ +.TH SXMSG_RREPLY 3 "20 July 2015" "SNTLLv2" "SNTL Library Manual" +.SH NAME +sxmsg_rreply \- Function used to send a rapid reply without confirmation +.SH SYNOPSIS +.B #include +.br +.B #include +.sp +int sxmsg_rreply(sxmsg_t +.BI *msg +, size_t +.BI datalen +); +.br +.sp +.SH DESCRIPTION +.B sxmsg_rreply +Will reply rapidly to the message using already allocated buffer, which must be +retrieved via +.B sxmsg_rapidbuf(). +This function will write desired message as soon +as possible. It will not wait any write or delivery confirmation. It will close +message dialog if message is valid. +.br +.SH RETURN VALUE +Upon successful completion, the function shall rapidly send a message reply and close +the message dialog returning +.B SNE_SUCCESS +Othrewise other error code will be returned. +.SH ERRORS +.B SNE_FAILED +returns if message has invalid index, channel or message pointer is NULL. +.br +.B SNE_ESSL +returns if write was failed i.e. connection link was broken, or SSL error occurs. +.br +.B SNE_SUCCESS +returns on success. +.br +.SH BUGS +Not known yet. +.SH EXAMPLE +.B Reply rapidly from the RPC function call. +.RS +.nf +.if t .ft CW +char *buf = sxmsg_rapidbuf(msg); +int ln = snprintf(buf, MAX_RBBUF_LEN, "(is-done)"); +return sxmsg_rreply(msg, str, ln); +.SH APPLICATION USAGE +This function will be useful in RPC functions required to reply ASAP i.e. for getting some data. It RPC function takes a lot of time it's better to use other functions and postponed message processing. +.SH RATIONALE +Use for lightweight RPC functions, this method may be used only in RPC call i.e. within SNTL thread context. +.SH COPYRIGHT +This is a proprietary software. See COPYING for further details. +.br +(c) Askele Group 2013-2015 +.SH AUTHOR +Alexander Vdolainen (vdo@askele.com) diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..3753f65 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,37 @@ +## AUTOMAKE_OPTIONS = foreign + +AM_CPPFLAGS = \ + -DPACKAGE_LOCALE_DIR=\""$(localedir)"\" \ + -DPACKAGE_SRC_DIR=\""$(srcdir)"\" \ + -DPACKAGE_DATA_DIR=\""$(pkgdatadir)"\" \ + -DCNFPATH=\""$(prefix)/etc"\" \ + -I../include \ + -I../lib + +AM_CFLAGS = -Wall -g + +# where to find libsntl +libsntl = ../lib/.libs/libsntl.la + +if !BUILD_WIN32 + +bin_PROGRAMS = lv2sd lv2sc + +lv2sd_SOURCES = lv2sd.c +lv2sd_LDADD = $(LIBTDATA_LIBS) $(LIBSEXPR_LIBS) $(OPENSSL_LIBS) \ + $(LIBUUID_LIBS) $(libsntl) + +lv2sc_SOURCES = lv2sc.c +lv2sc_LDADD = $(LIBTDATA_LIBS) $(LIBSEXPR_LIBS) $(OPENSSL_LIBS) \ + $(LIBUUID_LIBS) $(libsntl) + +else BUILD_WIN32 + +bin_PROGRAMS = lv2sc + +lv2sc_SOURCES = lv2sc.c +lv2sc_LDADD = $(LIBTDATA_LIBS) $(LIBSEXPR_LIBS) $(OPENSSL_LIBS) \ + $(LIBUUID_LIBS) $(libsntl) -lws2_32 + +endif BUILD_WIN32 + diff --git a/tests/lv2ftp-proto.txt b/tests/lv2ftp-proto.txt new file mode 100644 index 0000000..191fa40 --- /dev/null +++ b/tests/lv2ftp-proto.txt @@ -0,0 +1,30 @@ +1. lv2ftp isn's serious, just a test and benchmark of the sntllv2 +2. there are few stages: +2.1 Simple v.1 + This protocol is enough to donwload a directory subtree with files. +2.1.1 Directory workout + * Open directory stream: (dir-open "") + * Return: error, or rapidly replies with (dir-stream ) + * Read directory entry: (dir-read ) + * Return: error, or rapidly replies with the following: + * (dir-end ) + * (dir-entry "" ""), where filetype the following: + * regular - regular file + * directory - directory + * block - block device + * char - char device + * fifo - named pipe + * link - symbolic link + * socket - socket + * unknown - unknown file type + * Close directory entry: (dir-close ) + * Return: error or SNE_SUCCESS +2.1.2 File workout + * Open file: (file-open "") + * Return: error, or rapidly replies with (file-id ) + * Get simple stat: (file-stat ) + * Return error, or rapidle replies with (file-stat ) + * Read file data: (file-read ) + * Return error, or rapidly replies with 30k(raw data, in base64 it will be more) (file-data "") + * Close file: (file-close ) + * Return error or SNE_SUCCESS diff --git a/tests/lv2ftpc.c b/tests/lv2ftpc.c new file mode 100644 index 0000000..1edc43e --- /dev/null +++ b/tests/lv2ftpc.c @@ -0,0 +1,19 @@ +/* + * Secure Network Transport Layer Library v2 implementation. + * (sntllv2) it superseed all versions before due to the: + * - memory consumption + * - new features such as pulse emitting + * - performance optimization + * + * This is a proprietary software. See COPYING for further details. + * + * (c) Askele Group 2013-2015 + * + */ + +#include + +int main(int argc, char **argv) +{ + return 0; +} diff --git a/tests/lv2ftpd.c b/tests/lv2ftpd.c new file mode 100644 index 0000000..1edc43e --- /dev/null +++ b/tests/lv2ftpd.c @@ -0,0 +1,19 @@ +/* + * Secure Network Transport Layer Library v2 implementation. + * (sntllv2) it superseed all versions before due to the: + * - memory consumption + * - new features such as pulse emitting + * - performance optimization + * + * This is a proprietary software. See COPYING for further details. + * + * (c) Askele Group 2013-2015 + * + */ + +#include + +int main(int argc, char **argv) +{ + return 0; +} diff --git a/tests/lv2sc.c b/tests/lv2sc.c new file mode 100644 index 0000000..999f2cc --- /dev/null +++ b/tests/lv2sc.c @@ -0,0 +1,282 @@ +#include +#define __USE_GNU +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include + +/* define a little bit */ +#define DEFAULT_PORT 13133 +#define CHANNEL_COUNT 200 +#define CLIENT_COUNT 256 +#define MESSAGES_PER_SESSION 10000 +#define ITERATION_COUNT 1000 + +#define FAILS_ONLY + +struct testdata { + int uc; + pthread_mutex_t ulock; + conn_t *co; +}; + +static int __init_testdata(struct testdata *t, conn_t *co) +{ + t->uc = 0; + pthread_mutex_init(&t->ulock, NULL); + t->co = co; + return 0; +} + +static void __wait_completion(struct testdata *t) +{ + pthread_mutex_lock(&t->ulock); + if(t->uc) { + pthread_mutex_lock(&t->ulock); + } + return; +} + +static int __set_typed_list_callback(conn_t *co, int ch, char *desc) +{ + printf("allowed channel %d (%s)\n", ch, desc); + return SNE_SUCCESS; +} + +static void *__addsthrd(void *a) +{ + struct testdata *t = a; + conn_t *co = t->co; + chnl_t *mch; + sxmsg_t *msg; + char mmbuf[1024]; + size_t ln; + int mr, i; + + pthread_mutex_lock(&t->ulock); + t->uc++; + pthread_mutex_unlock(&t->ulock); + + /* here we go */ + mch = sxchannel_open(co, 12); + + if(!mch) { + fprintf(stderr, "Failed to openchannel with %d\n", errno); + goto __fini; + } + + for(i = 0; i < MESSAGES_PER_SESSION; i++) { + ln = snprintf(mmbuf, 1024, "(ar-add (10 10))"); + mr = sxmsg_send(mch, mmbuf, ln, &msg); + switch(mr) { + case SNE_RAPIDMSG: + //fprintf(stdout, "Rapidly replied: %s\n", (char *)sxmsg_payload(msg)); + sxmsg_clean(msg); + break; + case SNE_REPLYREQ: + if(sxmsg_datalen(msg)) fprintf(stdout, "Replied (confirmation required): %s\n", + (char *)sxmsg_payload(msg)); + mr = sxmsg_return(msg, SNE_SUCCESS); + fprintf(stderr, "mr = %d\n", mr); + break; + case SNE_SUCCESS: + fprintf(stdout, "Success.\n"); + break; + default: + fprintf(stderr, "ERROR: %d\n", mr); + break; + } + } + + sxchannel_close(mch); + + __fini: + t->uc--; + if(t->uc <= 1) pthread_mutex_unlock(&t->ulock); + + return NULL; +} + +int main(int argc, char **argv) +{ + char *rootca = NULL, *cert = NULL; + int port = DEFAULT_PORT; + char *addr = NULL, *login = NULL, *password = NULL; + int opt; + conn_sys_t *ssys; + conn_t *co; + + while((opt = getopt(argc, argv, "p:r:a:u:l:w:")) != -1) { + switch(opt) { + case 'p': + port = atoi(optarg); + break; + case 'r': + rootca = strdup(optarg); + break; + case 'a': + addr = strdup(optarg); + break; + case 'u': + cert = strdup(optarg); + break; + case 'l': + login = strdup(optarg); + break; + case 'w': + password = strdup(optarg); + break; + default: + fprintf(stderr, "usage: %s [-p ] -r -a -u -l -w \n", argv[0]); + return EINVAL; + } + } + + if(!rootca) { + fprintf(stderr, "Root CA not pointed.\n Failure.\n"); + return EINVAL; + } + + if(!addr) { + fprintf(stderr, "Server address not pointed.\n Failure.\n"); + return EINVAL; + } + + if(!cert) { + fprintf(stderr, "User certificate not pointed.\n Failure.\n"); + return EINVAL; + } + + if(!login) { + fprintf(stderr, "User login not pointed.\n Failure.\n"); + return EINVAL; + } + + if(!password) { + fprintf(stderr, "User password not pointed.\n Failure.\n"); + return EINVAL; + } + + sntl_init(); + /* all is fine let's init connection subsystem */ + ssys = connections_create(); + if(!ssys) { + fprintf(stderr, "Subsystem init failed: %d\n", errno); + return errno; + } + /* set working certificates */ + opt = connections_setsslserts(ssys, rootca, cert, cert); + if(opt) { + fprintf(stderr, "Subsystem init failed (set SSL x.509 pems): %d\n", opt); + return opt; + } + + /* Tests */ + struct timeval beg, end; + /* try to open connection */ + connections_set_channelcall(ssys, __set_typed_list_callback); + + gettimeofday(&beg, NULL); + co = connection_link(ssys, addr, port, cert, login, password); + + if(!co) { + fprintf(stderr, "Failed to connection with %d\n", errno); + return errno; + } + gettimeofday(&end, NULL); + + if((end.tv_sec - beg.tv_sec) > 0) { + printf("Seconds: %ld ", end.tv_sec - beg.tv_sec); + printf("µS: %ld\n", end.tv_usec + (1000000 - beg.tv_usec)); + } else printf("µS: %ld\n", end.tv_usec - beg.tv_usec); + + /* ok now we should open a channel */ + chnl_t *testchannel = sxchannel_open(co, 12); + + if(!testchannel) { + fprintf(stderr, "Failed to openchannel with %d\n", errno); + return errno; + } + gettimeofday(&end, NULL); + + if((end.tv_sec - beg.tv_sec) > 0) { + printf("Seconds: %ld ", end.tv_sec - beg.tv_sec); + printf("µS: %ld\n", end.tv_usec + (1000000 - beg.tv_usec)); + } else printf("µS: %ld\n", end.tv_usec - beg.tv_usec); + + /* ok, send a message */ + char mmbuf[1024]; + sxmsg_t *msg; + size_t ln; + ln = snprintf(mmbuf, 1024, "(ar-add (10 10))"); + int mr = sxmsg_send(testchannel, mmbuf, ln, &msg); + switch(mr) { + case SNE_RAPIDMSG: + fprintf(stdout, "Rapidly replied: %s\n", (char *)sxmsg_payload(msg)); + sxmsg_clean(msg); + break; + case SNE_REPLYREQ: + if(sxmsg_datalen(msg)) fprintf(stdout, "Replied (confirmation required): %s\n", + (char *)sxmsg_payload(msg)); + mr = sxmsg_return(msg, SNE_SUCCESS); + fprintf(stderr, "mr = %d\n", mr); + break; + case SNE_SUCCESS: + fprintf(stdout, "Success.\n"); + break; + default: + fprintf(stderr, "ERROR: %d\n", mr); + break; + } + + int ee = sxchannel_close(testchannel); + printf("ee = %d\n", ee); + gettimeofday(&end, NULL); + + if((end.tv_sec - beg.tv_sec) > 0) { + printf("Seconds: %ld ", end.tv_sec - beg.tv_sec); + printf("µS: %ld\n", end.tv_usec + (1000000 - beg.tv_usec)); + } else printf("µS: %ld\n", end.tv_usec - beg.tv_usec); + sleep(10); + /* ok, now we need to create many threads */ + struct testdata trd; + pthread_t thrd; + int i; + __init_testdata(&trd, co); + + for(i = 0; i < 256; i++) pthread_create(&thrd, NULL, __addsthrd, &trd); + + __wait_completion(&trd); + + connection_close(co); + + return 0; +} + diff --git a/tests/lv2sd.c b/tests/lv2sd.c new file mode 100644 index 0000000..feb455b --- /dev/null +++ b/tests/lv2sd.c @@ -0,0 +1,276 @@ +/* + * Secure Network Transport Layer Library v2 implementation. + * (sntllv2) it superseed all versions before due to the: + * - memory consumption + * - new features such as pulse emitting + * - performance optimization + * + * This is a proprietary software. See COPYING for further details. + * + * (c) Askele Group 2013-2015 + * + */ + + +#include +#define __USE_GNU +#include +#include +#include +#ifdef WIN32 +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include + +#include +// #include +#include +#include +#include + +/* helper functions */ +int __openlistener(int port) +{ + int sd; + struct sockaddr_in addr; + + sd = socket(PF_INET, SOCK_STREAM, 0); + bzero(&addr, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 ) { + perror("can't bind port"); + abort(); + } + if ( listen(sd, 10) != 0 ) { + perror("Can't configure listening port"); + abort(); + } + + return sd; +} + +/* + * Validation of the SSL certificate + * this function must be exist. + */ +static int __validate_sslpem(conn_t *co) +{ + return 0; +} + +/* + * validate - authorize user with password + */ +static int __secure_check(conn_t *co) +{ + return SNE_SUCCESS; +} + +/* + * typed list callback + */ +static int __set_typed_list_callback(conn_t *co, int ch, char *desc) +{ + printf("allowed channel %d (%s)\n", ch, desc); + return SNE_SUCCESS; +} + +/* list of rpc calls functions */ +usrtc_t *fulist; + +/* our fake */ +usrtc_t *__rettlist(conn_t *c) +{ + return fulist; +} + +/* RPC functions implementation */ +static int __ar_add(void *m, sexp_t *sx) +{ + sexp_t *lsx = NULL, *sx_iter; + sxmsg_t *msg = (sxmsg_t *)m; + char *buf; + int idx; + size_t ln = 0; + + if(sexp_list_cdr(sx, &lsx)) { + printf("Invalid protocol\n"); + return sxmsg_return(msg, SNE_BADPROTO); + } + + long int sum = 0; + SEXP_ITERATE_LIST(lsx, sx_iter, idx) { + if(!SEXP_IS_TYPE(sx_iter, SEXP_BASIC)) { + fprintf(stderr, "Invalid value type\n"); + return sxmsg_return(msg, SNE_BADPROTO); + } + sum += atoi(sx_iter->val); + } + + buf = sxmsg_rapidbuf(msg); + ln = snprintf(buf, MAX_RBBUF_LEN, "(add-result %ld)", sum); + + return sxmsg_rreply(msg, ln); +} + +static int __ar_multiply(void *data, sexp_t *sx) +{ + sexp_t *lsx = NULL; + sxmsg_t *msg = (sxmsg_t *)data; + char *buf; + int idx; + sexp_t *sx_iter; + size_t ln; + + if(sexp_list_cdr(sx, &lsx)) { + printf("Invalid protocol\n"); + return sxmsg_return(msg, SNE_BADPROTO); + } + + long int mult = 0; + SEXP_ITERATE_LIST(lsx, sx_iter, idx) { + if(!SEXP_IS_TYPE(sx_iter, SEXP_BASIC)) { + printf("Invalid value type\n"); + return sxmsg_return(msg, SNE_BADPROTO); + } + mult *= atoi(sx_iter->val); + } + + buf = sxmsg_rapidbuf(msg); + ln = snprintf(buf, MAX_RBBUF_LEN, "(multiply-result %ld)", mult); + + return sxmsg_rreply(msg, ln); +} + +/* define a little bit */ +#define DEFAULT_PORT 13133 + +static void sigpipe_handler(int a) +{ + return; +} + +int main(int argc, char **argv) +{ + char *rootca = NULL, *cert = NULL; + conn_sys_t *ssys = connections_create(); + int port = DEFAULT_PORT; + int opt; + + signal(SIGPIPE, SIG_IGN); + + while((opt = getopt(argc, argv, "p:r:u:")) != -1) { + switch(opt) { + case 'p': + port = atoi(optarg); + break; + case 'r': + rootca = strdup(optarg); + break; + case 'u': + cert = strdup(optarg); + break; + default: + fprintf(stderr, "usage: %s [-p ] -r -u \n", argv[0]); + return EINVAL; + } + } + + if(!rootca) { + fprintf(stderr, "Root CA not pointed.\n Failure.\n"); + return EINVAL; + } + + if(!cert) { + fprintf(stderr, "User certificate not pointed.\n Failure.\n"); + return EINVAL; + } + + sntl_init(); + /* all is fine let's init connection subsystem */ + if(!ssys) { + fprintf(stderr, "Subsystem init failed: %d\n", opt); + return 2; + } + /* set wroking certificates */ + opt = connections_setsslserts(ssys, rootca, cert, cert); + if(opt) { + fprintf(stderr, "Subsystem init failed (set SSL x.509 pems): %d\n", opt); + return opt; + } + + /* clean up */ + free(rootca); + free(cert); + + /* set important callbacks to do the security checking */ + connections_set_authcheck(ssys, __secure_check); + connections_set_sslvalidate(ssys, __validate_sslpem); + /* set a callback, it's optional and doesn't required in server side apps */ + connections_set_channelcall(ssys, __set_typed_list_callback); + + /* ok, now we need to construct RPC lists (channels) */ + if(!(fulist = malloc(sizeof(usrtc_t)))) { + fprintf(stderr, "Cannot allocate memory for RPC lists\n Failure.\n"); + return ENOMEM; + } + opt = sntl_rpclist_init(fulist); + if(opt) { + fprintf(stderr, "Failed to init rpc list\n Failure.\n"); + return opt; + } + + /* we will add one channel with type id 12 "Demo rpc list" */ + opt = sntl_rpclist_add(fulist, 12, "Demo RPC list", NULL); + if(opt) { + fprintf(stderr, "Failed to add typed RPC channel\n Failure.\n"); + return opt; + } + /* ok, let's add two functions */ + opt = sntl_rpclist_add_function(fulist, 12, "ar-add", __ar_add); + if(opt) { + __fail: + fprintf(stderr, "Failed to add functions to typed RPC channel\n Failure.\n"); + return opt; + } + opt = sntl_rpclist_add_function(fulist, 12, "ar-multiply", __ar_multiply); + if(opt) goto __fail; + + /* ok, setup it */ + connections_set_rpcvalidator(ssys, __rettlist); + + /* now we're ready to run the listen process */ + int srv = __openlistener(port); + while(1) { + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + conn_t *co; + + int client = accept(srv, (struct sockaddr*)&addr, &len); /* accept connection as usual */ + co = connection_master_link(ssys, client, NULL); /* create connection, that's all */ + if(!co) { + fprintf(stderr, "Cannot create connetion (%d)\n", opt); + } + } + + return 0; +}