sources := LibUpperTester.ttcn
\ No newline at end of file
sources := module/src/Abstract_Socket.cc
includes := module/src
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2000-2019 Ericsson Telecom AB
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v2.0
which accompanies this distribution, and is available at
https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
File: Abstract_Socket_CNL113384.tpd
Description: tpd project file
Rev: R9B
Prodnr: CNL 113 384
-->
<TITAN_Project_File_Information version="1.0">
<ProjectName>Abstract_Socket_CNL113384</ProjectName>
<Files>
<FileResource projectRelativePath="Abstract_Socket.cc" relativeURI="src/Abstract_Socket.cc"/>
<FileResource projectRelativePath="Abstract_Socket.hh" relativeURI="src/Abstract_Socket.hh"/>
</Files>
<ActiveConfiguration>SSL</ActiveConfiguration>
<Configurations>
<Configuration name="Default">
<ProjectProperties>
<MakefileSettings>
<GNUMake>true</GNUMake>
<targetExecutable>bin/Abstract_Socket_CNL113384</targetExecutable>
</MakefileSettings>
<LocalBuildSettings>
<workingDirectory>bin</workingDirectory>
</LocalBuildSettings>
</ProjectProperties>
</Configuration>
<Configuration name="SSL">
<ProjectProperties>
<MakefileSettings>
<generateInternalMakefile>true</generateInternalMakefile>
<GNUMake>true</GNUMake>
<targetExecutable>bin_ssl/Abstract_Socket_CNL113384</targetExecutable>
<preprocessorDefines>
<listItem>AS_USE_SSL</listItem>
</preprocessorDefines>
<preprocessorIncludes>
<listItem>[OPENSSL_DIR]/include</listItem>
</preprocessorIncludes>
<linkerLibraries>
<listItem>ssl</listItem>
</linkerLibraries>
<linkerLibrarySearchPath>
<listItem>[OPENSSL_DIR]/lib</listItem>
</linkerLibrarySearchPath>
</MakefileSettings>
<LocalBuildSettings>
<workingDirectory>bin_ssl</workingDirectory>
</LocalBuildSettings>
</ProjectProperties>
</Configuration>
</Configurations>
</TITAN_Project_File_Information>
Eclipse Public License - v 2.0
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
1. DEFINITIONS
"Contribution" means:
a) in the case of the initial Contributor, the initial content
Distributed under this Agreement, and
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
where such changes and/or additions to the Program originate from
and are Distributed by that particular Contributor. A Contribution
"originates" from a Contributor if it was added to the Program by
such Contributor itself or anyone acting on such Contributor's behalf.
Contributions do not include changes or additions to the Program that
are not Modified Works.
"Contributor" means any person or entity that Distributes the Program.
"Licensed Patents" mean patent claims licensable by a Contributor which
are necessarily infringed by the use or sale of its Contribution alone
or when combined with the Program.
"Program" means the Contributions Distributed in accordance with this
Agreement.
"Recipient" means anyone who receives the Program under this Agreement
or any Secondary License (as applicable), including Contributors.
"Derivative Works" shall mean any work, whether in Source Code or other
form, that is based on (or derived from) the Program and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship.
"Modified Works" shall mean any work in Source Code or other form that
results from an addition to, deletion from, or modification of the
contents of the Program, including, for purposes of clarity any new file
in Source Code form that contains any contents of the Program. Modified
Works shall not include works that contain only declarations,
interfaces, types, classes, structures, or files of the Program solely
in each case in order to link to, bind by name, or subclass the Program
or Modified Works thereof.
"Distribute" means the acts of a) distributing or b) making available
in any manner that enables the transfer of a copy.
"Source Code" means the form of a Program preferred for making
modifications, including but not limited to software source code,
documentation source, and configuration files.
"Secondary License" means either the GNU General Public License,
Version 2.0, or any later versions of that license, including any
exceptions or additional permissions as identified by the initial
Contributor.
2. GRANT OF RIGHTS
a) Subject to the terms of this Agreement, each Contributor hereby
grants Recipient a non-exclusive, worldwide, royalty-free copyright
license to reproduce, prepare Derivative Works of, publicly display,
publicly perform, Distribute and sublicense the Contribution of such
Contributor, if any, and such Derivative Works.
b) Subject to the terms of this Agreement, each Contributor hereby
grants Recipient a non-exclusive, worldwide, royalty-free patent
license under Licensed Patents to make, use, sell, offer to sell,
import and otherwise transfer the Contribution of such Contributor,
if any, in Source Code or other form. This patent license shall
apply to the combination of the Contribution and the Program if, at
the time the Contribution is added by the Contributor, such addition
of the Contribution causes such combination to be covered by the
Licensed Patents. The patent license shall not apply to any other
combinations which include the Contribution. No hardware per se is
licensed hereunder.
c) Recipient understands that although each Contributor grants the
licenses to its Contributions set forth herein, no assurances are
provided by any Contributor that the Program does not infringe the
patent or other intellectual property rights of any other entity.
Each Contributor disclaims any liability to Recipient for claims
brought by any other entity based on infringement of intellectual
property rights or otherwise. As a condition to exercising the
rights and licenses granted hereunder, each Recipient hereby
assumes sole responsibility to secure any other intellectual
property rights needed, if any. For example, if a third party
patent license is required to allow Recipient to Distribute the
Program, it is Recipient's responsibility to acquire that license
before distributing the Program.
d) Each Contributor represents that to its knowledge it has
sufficient copyright rights in its Contribution, if any, to grant
the copyright license set forth in this Agreement.
e) Notwithstanding the terms of any Secondary License, no
Contributor makes additional grants to any Recipient (other than
those set forth in this Agreement) as a result of such Recipient's
receipt of the Program under the terms of a Secondary License
(if permitted under the terms of Section 3).
3. REQUIREMENTS
3.1 If a Contributor Distributes the Program in any form, then:
a) the Program must also be made available as Source Code, in
accordance with section 3.2, and the Contributor must accompany
the Program with a statement that the Source Code for the Program
is available under this Agreement, and informs Recipients how to
obtain it in a reasonable manner on or through a medium customarily
used for software exchange; and
b) the Contributor may Distribute the Program under a license
different than this Agreement, provided that such license:
i) effectively disclaims on behalf of all other Contributors all
warranties and conditions, express and implied, including
warranties or conditions of title and non-infringement, and
implied warranties or conditions of merchantability and fitness
for a particular purpose;
ii) effectively excludes on behalf of all other Contributors all
liability for damages, including direct, indirect, special,
incidental and consequential damages, such as lost profits;
iii) does not attempt to limit or alter the recipients' rights
in the Source Code under section 3.2; and
iv) requires any subsequent distribution of the Program by any
party to be under a license that satisfies the requirements
of this section 3.
3.2 When the Program is Distributed as Source Code:
a) it must be made available under this Agreement, or if the
Program (i) is combined with other material in a separate file or
files made available under a Secondary License, and (ii) the initial
Contributor attached to the Source Code the notice described in
Exhibit A of this Agreement, then the Program may be made available
under the terms of such Secondary Licenses, and
b) a copy of this Agreement must be included with each copy of
the Program.
3.3 Contributors may not remove or alter any copyright, patent,
trademark, attribution notices, disclaimers of warranty, or limitations
of liability ("notices") contained within the Program from any copy of
the Program which they Distribute, provided that Contributors may add
their own appropriate notices.
4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain responsibilities
with respect to end users, business partners and the like. While this
license is intended to facilitate the commercial use of the Program,
the Contributor who includes the Program in a commercial product
offering should do so in a manner which does not create potential
liability for other Contributors. Therefore, if a Contributor includes
the Program in a commercial product offering, such Contributor
("Commercial Contributor") hereby agrees to defend and indemnify every
other Contributor ("Indemnified Contributor") against any losses,
damages and costs (collectively "Losses") arising from claims, lawsuits
and other legal actions brought by a third party against the Indemnified
Contributor to the extent caused by the acts or omissions of such
Commercial Contributor in connection with its distribution of the Program
in a commercial product offering. The obligations in this section do not
apply to any claims or Losses relating to any actual or alleged
intellectual property infringement. In order to qualify, an Indemnified
Contributor must: a) promptly notify the Commercial Contributor in
writing of such claim, and b) allow the Commercial Contributor to control,
and cooperate with the Commercial Contributor in, the defense and any
related settlement negotiations. The Indemnified Contributor may
participate in any such claim at its own expense.
For example, a Contributor might include the Program in a commercial
product offering, Product X. That Contributor is then a Commercial
Contributor. If that Commercial Contributor then makes performance
claims, or offers warranties related to Product X, those performance
claims and warranties are such Commercial Contributor's responsibility
alone. Under this section, the Commercial Contributor would have to
defend claims against the other Contributors related to those performance
claims and warranties, and if a court requires any other Contributor to
pay any damages as a result, the Commercial Contributor must pay
those damages.
5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
PURPOSE. Each Recipient is solely responsible for determining the
appropriateness of using and distributing the Program and assumes all
risks associated with its exercise of rights under this Agreement,
including but not limited to the risks and costs of program errors,
compliance with applicable laws, damage to or loss of data, programs
or equipment, and unavailability or interruption of operations.
6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
7. GENERAL
If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this Agreement, and without further
action by the parties hereto, such provision shall be reformed to the
minimum extent necessary to make such provision valid and enforceable.
If Recipient institutes patent litigation against any entity
(including a cross-claim or counterclaim in a lawsuit) alleging that the
Program itself (excluding combinations of the Program with other software
or hardware) infringes such Recipient's patent(s), then such Recipient's
rights granted under Section 2(b) shall terminate as of the date such
litigation is filed.
All Recipient's rights under this Agreement shall terminate if it
fails to comply with any of the material terms or conditions of this
Agreement and does not cure such failure in a reasonable period of
time after becoming aware of such noncompliance. If all Recipient's
rights under this Agreement terminate, Recipient agrees to cease use
and distribution of the Program as soon as reasonably practicable.
However, Recipient's obligations under this Agreement and any licenses
granted by Recipient relating to the Program shall continue and survive.
Everyone is permitted to copy and distribute copies of this Agreement,
but in order to avoid inconsistency the Agreement is copyrighted and
may only be modified in the following manner. The Agreement Steward
reserves the right to publish new versions (including revisions) of
this Agreement from time to time. No one other than the Agreement
Steward has the right to modify this Agreement. The Eclipse Foundation
is the initial Agreement Steward. The Eclipse Foundation may assign the
responsibility to serve as the Agreement Steward to a suitable separate
entity. Each new version of the Agreement will be given a distinguishing
version number. The Program (including Contributions) may always be
Distributed subject to the version of the Agreement under which it was
received. In addition, after a new version of the Agreement is published,
Contributor may elect to Distribute the Program (including its
Contributions) under the new version.
Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
receives no rights or licenses to the intellectual property of any
Contributor under this Agreement, whether expressly, by implication,
estoppel or otherwise. All rights in the Program not expressly granted
under this Agreement are reserved. Nothing in this Agreement is intended
to be enforceable by any entity that is not a Contributor or Recipient.
No third-party beneficiary rights are created under this Agreement.
Exhibit A - Form of Secondary Licenses Notice
"This Source Code may also be made available under the following
Secondary Licenses when the conditions for such availability set forth
in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
version(s), and exceptions or additional permissions here}."
Simply including a copy of this Agreement, including this Exhibit A
is not sufficient to license the Source Code under Secondary Licenses.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to
look for such a notice.
You may add additional accurate notices of copyright ownership.
\ No newline at end of file
# !!! IMPORTANT !!!
### This repository has moved under https://gitlab.eclipse.org/eclipse/titan/ on 2021.04.20 and will not be updated further. Please check out the new location for updates!
#
# titan.TestPorts.Common_Components.Abstract_Socket
Main project page:
https://projects.eclipse.org/projects/tools.titan
The source code of the TTCN-3 compiler and executor:
https://github.com/eclipse/titan.core
---
Author: Gábor Szalai
Version: 1551-CNL 113 384, Rev. A
Date: 2015-01-20
---
= Abstract Socket Test Port for TTCN-3 Toolset with TITAN, Description
:author: Gábor Szalai
:revnumber: 1551-CNL 113 384, Rev. A
:revdate: 2015-01-20
:toc:
== About This Document
=== How to Read This Document
This is the User Guide for the Abstract Socket test port. The Abstract Socket test port is developed for the TTCN-3 Toolset.
=== Presumed Knowledge
To use this protocol module the knowledge of the TTCN-3 language <<_1, [1]>> is essential.
= Functionality
== System Requirements
In order to use the Abstract Socket test port the following system requirements must be satisfied:
* Platform: any platform supported by TITAN RTE and OpenSSL
* TITAN TTCN-3 Test Executor version R8A (1.8.pl0) or higher installed.
NOTE: This Abstract Socket version is not compatible with TITAN releases earlier than R8A.
If SSL is used, the same OpenSSL must be installed as used in TITAN. For an OpenSSL installation guide see <<_3, [3]>>.
== Fundamental Concepts
The test port establishes connection between the TTCN-3 test executor and SUT and transmits/receives messages. The transport channel can be TCP or SSL. The connect and listen operations can be initiated from the test suite using the `use_connection_ASPs` test port parameter.
=== Module Structure
The Abstract Socket common component is implemented in the following files:
* __Abstract_Socket.hh__
* __Abstract_Socket.cc__
== Start Procedure
=== Connection ASPs
When choosing to use connection ASPs, the Abstract Socket is able to open a server listening port (acting like a server) or client connections at the same time.
=== Server Mode
When the test port is mapped by TITAN RTE, the server creates a TCP socket and starts listening on it. Depending on the transport channel specified in the runtime configuration file, it will accept either TCP or SSL connections.
=== Client Mode
When the test port is mapped by TITAN RTE, the client creates a TCP socket and tries to connect to the server. If the transport channel is SSL, the client starts an SSL handshake after the TCP connection is established. If the SSL handshake is successful, the SSL connection is established and the `map` operation is finished.
The SSL handshake may fail due to several reasons (e.g. no shared ciphers, verification failure, etc.).
[[sending-receiving-messages]]
== Sending/Receiving Messages
Only basic octetstring sending and receiving is handled by the Abstract Socket. This functionality probably must be extended in order to build a test port for the desired protocol. First the TTCN-3 mapping of the target protocol messages must be elaborated and then the message processing functions can be developed.
== Logging
The type of information that will be logged can be categorized into two groups. The first one consists of information that shows the flow of the internal execution of the test port, e.g. important events, which function that is currently executing etc. The second group deals with presenting valuable data, e.g. presenting the content of a PDU. The logging printouts will be directed to the RTE log file. The user is able to decide whether logging is to take place or not by setting appropriate configuration data.
== Error Handling
Erroneous behavior detected during runtime is directed into the RTE log file. The following two types of messages are taken care of:
* Errors: information about errors detected is provided. If an error occurs the execution of the test case will stop immediately. The test ports will be unmapped.
* Warnings: information about warnings detected is provided. The execution continues after the warning is shown.
== Closing Down
The connection can be shut down either performing the `unmap` operation on the port or if connection ASPs are used with the appropriate ASP.
== IPv6 Support
It is possible to select the address family used for server socket and client connections in the configuration file or during runtime. The following address families are supported: IPv4, IPv6 and UNSPEC.
== SSL Functionality
The Abstract Socket can use SSL or TCP as the transport channel. The same version of OpenSSL library must be used as in TITAN.
The supported SSL/TLS versions are determined by the used OpenSSL library. It is possible to disable a specific TLS/SSL version in the run time configuration file.
=== Compilation
The usage of SSL and even the compilation of the SSL related code parts are optional. This is because SSL related code parts cannot be compiled without the OpenSSL installed.
The compilation of SSL related code parts can be disabled by not defining the `AS_USE_SSL` macro in the _Makefile_ during the compilation. If the macro is defined in the _Makefile_, the SSL code parts are compiled to the executable test code. The usage of the SSL then can be enabled/disabled in the runtime configuration file. Naturally, the test port parameter will be ignored if the `AS_USE_SSL` macro is not defined during compilation.
=== Authentication
The Abstract Socket provides both server side and client side authentication. When authenticating the other side, a certificate is requested and the own trusted certificate authorities’ list is sent. The received certificate is verified whether it is a valid certificate or not (the public and private keys are matching). No further authentication is performed (e.g. whether hostname is present in the certificate). The verification can be enabled/disabled in the runtime configuration file.
In server mode the test port will always send its certificate and trusted certificate authorities’ list to its clients. If verification is enabled in the runtime configuration file, the server will request for a client’s certificate. In this case, if the client does not send a valid certificate or does not send a certificate at all, the connection will be refused. If the verification is disabled, the connection will never be refused due to verification failure.
In client mode the test port will send its certificate to the server on the server’s request. If verification is enabled in the runtime configuration file, the client will send its own trusted certificate authorities’ list to the server and will verify the server’s certificate as well. If the server’s certificate is not valid, the SSL connection will not be established. If verification is disabled, the connection will never be refused due to verification failure.
The own certificate(s), the own private key file, the optional password protecting the own private key file and the trusted certificate authorities’ list file can be specified in the runtime configuration file.
The test port will check the consistency between its own private key and the public key (based on the own certificate) automatically. If the check fails, a warning is issued and execution continues.
=== Other Features
The usage of SSL session resumption can be enabled/disabled in the runtime configuration file.
The allowed ciphering suites can be restricted in the runtime configuration file, see.
The SSL re-handshaking requests are accepted and processed, however re-handshaking cannot be initiated from the test port.
=== Limitations
* SSL re-handshaking cannot be initiated from the test port.
* The own certificate file(s), the own private key file and the trusted certificate authorities’ list file must be in PEM format. Other formats are not supported.
= The Test Port
== Overview
The Abstract Socket is a common component that can serve as a basis for test ports that need TCP connections with or without SSL on the top. The TCP socket can be used either with blocking or non-blocking socket. The Abstract Socket implements basic sending, receiving and socket handling routines, furthermore it supports the development of test ports that can work as a client or as a server. By extending the Abstract Socket component with additional functionality the desired test port can be built.
See the functioning of the Abstract Socket below:
image:images/Abstract socket.png[alt]
== Installation
Since the Abstract Socket test port is used as a part of the TTCN-3 test environment this requires TTCN-3 Test Executor to be installed before any operation of the Abstract Socket test port.
The compilation of SSL related code parts can be disabled by not defining the `AS_USE_SSL` macro in the _Makefile_ during the compilation.
When building the executable test suite the libraries compiled for the OpenSSL toolkit (if the `AS_USE_SSL` macro is defined) should also be linked into the executable along with the TTCN-3 Test Executor, i.e. the OpenSSL libraries should be added to the __Makefile__ generated by the TITAN executor (see example in section <<warning_messages_in_case_SSL_connections_are_used, Warning Messages in case SSL Connections Are Used>>). To compile the source files you will also need the OpenSSL developer toolkit which contains the header files used by the source. If Share Objects (_.so_) are used in the OpenSSL toolkit, to run the executable, the path of the OpenSSL libraries must be added to the `LD_LIBRARY_PATH` environment variable.
NOTE: If you are using the test port on Solaris, you have to set the `PLATFORM` macro to the proper value. It shall be `_SOLARIS_` in case of Solaris 6 (SunOS 5.6) and `_SOLARIS8_` in case of Solaris 8 (SunOS 5.8).
== Configuration
The executable test program behavior is determined via the run-time configuration file. This is a simple text file, which contains various sections (for example, `[TESTPORT_PARAMETERS]`) after each other. The usual suffix of configuration files is _.cfg._ For further information on the configuration file see <<_2, [2]>>.
The listed port parameters (<<Abstract_Socket_Test_Port_Parameters_in_the_Test_Port_Configuration_File, Abstract Socket Test Port Parameters in the Test Port Configuration File>>) are managed by default by the Abstract Socket. They have to be defined only in the TTCN configuration files. Though, if Abstract Socket's parameter handling is not appropriate for your application, you can ignore it completely and handle the variables directly from your port. It is recommended to implement your own test port parameter name passing functions so that your test port will not depend on the test port parameter names in the Abstract Socket.
[[Abstract_Socket_Test_Port_Parameters_in_the_Test_Port_Configuration_File]]
=== Abstract Socket Test Port Parameters in the Test Port Configuration File
In the `[TESTPORT_PARAMETERS]` section the following parameters can be set for the Abstract Socket based test port. The parameter names are case-sensitive; the parameter values are not case-sensitive (i.e. `_"YES"_`, `_"yes"_`, `_"Yes"_` values are identical).
[[abstract-socket-test-port-parameters-in-the-test-port-configuration-file-if-the-transport-channel-is-tcp-ip]]
==== Abstract Socket Test Port Parameters in the Test Port Configuration File if the Transport Channel is TCP/IP
* `use_connection_ASPs`
+
The parameter is optional, and can be used to specify whether the Abstract Socket is controlled by connection ASPs. The default value is `_"no"_`.
+
If the value is `_"yes"_`, the functionalities of the Abstract Socket have to be controlled by calling its `open_client_connection`, `open_listen_port`, `remove_client`, `remove_all_clients` and `close_listen_port` functions. The Abstract Socket will not create any connection or listening port when calling the `map_user` function. Using this parameter, more than one connection can be opened in client mode operation. The Abstract Socket will call the `listen_port_opened`, `client_connection_opened`, `peer_connected` and `peer_disconnected` functions of the test port implementing it when corresponding events happen. This allows test ports and TTCN code to directly handle TCP connection initiations and events.
* `server_mode`
+
The parameter is optional, and can be used to specify whether the test port shall act as a server or a client. If the value is `_"yes"_`, the test port will act as a server. If the value is `_"no"_`, the test port will act as a client. The default value is `_"no"_` . The parameter has no meaning if `use_connection_ASPs` is set to `_"yes"_` because the `open_listen_port` initiates the listening on a server port.
* `socket_debugging`
+
The parameter is optional, and can be used to enable debug logging related to the transport channel (TCP socket and SSL operations) in the test port. The default value is `_"no"_`.
* `halt_on_connection_reset`
+
The parameter is optional, and can be used to specify whether the test port shall stop on errors occurred during connection setup (including connection refusing), sending and receiving, disconnection (including the detection of the disconnection). The value `_"yes"_` means the test port will stop, the value `_"no"_` means that it will not stop on such errors. The default value is `_"no"_` in server mode and `_"yes"_` in client mode.
+
The parameter has no meaning if `use_connection_ASPs` is set to `_"yes"_`, because the `peer_disconnected` function of the test port is called on the event.
* `nagling`
+
The parameter is optional, and can be used to specify whether concatenation occurs on TCP layer. If value is `_"yes"_`, concatenation is enabled. If value is `_"no"_`, it is disabled.
+
NOTE: The `nagling` setting is valid only for the outgoing messages. The `nagling` for the incoming messages shall be set by the sending party. The default value is `_"no"_`.
* `remote_address`
+
The parameter can be used to specify the server's IP address. Mandatory in client mode and not used in server mode.
+
The parameter has no meaning if `use_connection_ASPs` is set to `_"yes"_`, because the `open_client_connection` function receives the remote and optionally the local address.
* `remote_port`
+
The parameter can be used to specify the server's listening port. Mandatory in client mode and not used in server mode.
+
The parameter has no meaning if `use_connection_ASPs` is set to `_"yes"_`, because the `open_client_connection` function receives the remote and optionally the local address.
* `local_port`
+
The parameter can be used to specify the port where the server is listening for connections. Mandatory in server mode and optional in client mode.
+
The parameter serves as a default if `use_connection_ASPs` is set to `_"yes"_`.
* `ai_family`
+
The parameter can be used to specify the address family to use when opening listening ports or creating client connections. If its value is set to `_"IPv4"_` or `_"AF_INET"_` only IPv4 addresses are used. If it is set to `_"IPv6"_` or `_"AF_INET6"_` only IPv6 connections are allowed. The values `_`"UNSPEC"`_` and `"AF_UNSPEC"` can be used if the address family is not specified. The `_"UNSPEC"_` value allows using IPv4 and IPv6 addresses at the same time. The selection is made automatically depending on the actual value of the local and remote addresses.
+
This parameter is optional. The default value is `_"AF_UNSPEC"_`.
* `server_backlog`
+
The parameter can be used to specify the number of allowed pending (queued) connection requests on the port the server listens. It is optional in server mode and not used in client mode. The default value is `_"1"_`.
* `TCP_reconnect_attempts`
+
This parameter can be used to specify the maximum number of times the connection is attempted to be established in TCP reconnect mode. The default value is `_"5"_`.
+
The parameter has no meaning if `use_connection_ASPs` is set to `_"yes"_`, because the `peer_disconnected` function is called when the event happens, and it’s up to the test port or TTCN code how to continue.
* `TCP_reconnect_delay`
+
This parameter can be used to specify the time (in seconds) the test port waits between to TCP reconnection attempt. The default value is `_"1"_`.
+
The parameter has no meaning if `use_connection_ASPs` is set to `_"yes"_`, because the `peer_disconnected` function is called when the event happens, and it’s up to the test port or TTCN code how to continue.
* `client_TCP_reconnect`
+
If the test port is in client mode and the connection was interrupted by the other side, it tries to reconnect again. The default value is ``no''.
+
The parameter has no meaning if `use_connection_ASPs` is set to `_"yes"_`, because the `peer_disconnected` function is called when the event happens, and it’s up to the test port or TTCN code how to continue.
* `use_non_blocking_socket`
+
This parameter can be used to specify whether the Test Port shall use blocking or non-blocking TCP socket. Using this parameter, the `send` TTCN-3 operation will block until the data is sent, but an algorithm is implemented to avoid TCP deadlock.
+
The parameter is optional, the default value is `_"no"_`.
==== Additional Abstract Socket Test Port Parameters in the Test Port Configuration File if the Transport Channel is SSL
These parameters available only if `AS_USE_SSL` macro is defined during compilation.
* `ssl_use_ssl`
+
The parameter is optional, and can be used to specify whether to use SSL on the top of the TCP connection or not. The default value is `_"no"_`.
* `ssl_verify_certificate`
+
The parameter is optional, and can be used to tell the test port whether to check the certificate of the other side. If it is defined `_"yes"_`, the test port will query and check the certificate. If the certificate is not valid (i.e. the public and private keys do not match), it will exit with a corresponding error message. If it is defined `_"no"_`, the test port will not check the validity of the certificate. The default value is `_"no"_`.
* `ssl_use_session_resumption`
+
The parameter is optional, and can be used to specify whether to use/support SSL session resumptions or not. The default value is `_"yes"_`.
* `ssl_certificate_chain_file`
+
It specifies a PEM encoded file’s path on the file system containing the certificate chain. Mandatory in server mode and optional in client mode. Note that the server may require client authentication. In this case no connection can be established without a client certificate.
* `ssl_private_key_file`
+
It specifies a PEM encoded file’s path on the file system containing the server’s RSA private key. Mandatory in server mode and optional in client mode.
* `ssl_private_key_password`
+
The parameter is optional, and can be used to specify the password protecting the private key file. If not defined, the SSL toolkit will ask for it.
* `ssl_trustedCAlist_file`
+
It specifies a PEM encoded file’s path on the file system containing the certificates of the trusted CA authorities to use. Mandatory in server mode, and mandatory in client mode if `ssl_verify_certificate="yes"`.
* `ssl_allowed_ciphers_list`
+
The parameter is optional, and can be used to specify the allowed cipher list. The value is passed directly to the SSL toolkit.
* `ssl_disable_SSLv2` +
`ssl_disable_SSLv3` +
`ssl_disable_TLSv1` +
`ssl_disable_TLSv1_1` +
`ssl_disable_TLSv1_2`
+
The usage of a specific SSL/TLS version can be disabled by setting the parameter to `_"yes"_`. Please note that the available SSL/TLS versions are depends of the used OpenSSL library.
== The `AbstractSocket` API
In the derived test port the following functions can be used:
[[map-unmap-the-test-port]]
=== Map/Unmap the Test Port
In the `user_map` and `user_unmap` functions of the derived test port these functions should be called:
[source]
----
void map_user();
void unmap_user();
----
=== Setting Test Port Parameters
[source]
----
bool parameter_set(const char __parameter_name, const char __parameter_value);
----
Call this function in the `set_parameter` function of the derived test port to set the test port parameters of AbstractSocket.
=== Open a Listening Port
To open a server socket call the following function:
[source]
----
int open_listen_port(const char* localHostname, const char* localService);
----
This function supports both IPv4 and IPv6 addresses. The parameter `localHostname` should specify the local hostname. It can be the name of the host or an IP address. The parameter `localService` should be a string containing the port number. One of the two parameters can be `_NULL_`, meaning `_ANY_` for that parameter. The address family used is specified either by the `ai_family_name()` testport parameter or set by the function `set_ai_family(int)`.
The following function only supports IPv4:
`int open_listen_port(const struct sockaddr_in & localAddr);`
NOTE: This function is deprecated. It is kept for compatibility with previous versions of test ports that use `AbstractSocket`
After calling the `open_listen_port` function, the function virtual void `listen_port_opened(int port_number)` is called automatically with the listening port number, or `_-1_` if the opening of the listening port failed. This function can be overridden in the derived test port to implement specific behavior depending on the listen result. This can, for example, call `incoming_message` to generate an incoming `ListenResult` message in the test port.
Subsequent calls of the function `open_listen_port` results in closing the previous listening port and opening a new one. This means that only one server port is supported by `AbstractSocket`.
When a client connects to the listening port the following functions are called to notify the derived test port about the new client connection:
[source]
----
virtual void peer_connected(int client_id, const char * host, const int port)
virtual void peer_connected(int client_id, sockaddr_in& remote_addr);
----
Only one of these functions should be overridden in the derived test port. Note, that the second is obsolete. It is kept for backward compatibility only.
Similar functions for client disconnects:
[source]
----
virtual void peer_disconnected(int client_id);
virtual void peer_half_closed(int client_id);
----
The `client_id` parameter specifies which client has disconnected/half closed. The `peer_half_closed` function is called when the client closes the socket for writing, while `peer_disconnected` is called when the client is disconnected. Both functions can be overridden in the derived test port.
=== Close the Listening Port
`void close_listen_port()`
This function closes the listening port.
=== Open a Client Connection
[source]
----
int open_client_connection(const char* remoteHostname, const char* remoteService, const char* localHostname, const char* localService);
----
This function creates an IPv4 or IPv6 connection from the local address `localHostname/localService` to the remote address `remoteHostname/remoteService`.
If `localHostname` or `localService` is `_NULL_`, it will be assigned automatically.
The parameters for the remote address cannot be `_NULL_`. The local or remote service parameters should be numbers in string format, while the addresses should be names or IP addresses in IPv4 or IPv6 format.
The `open_client_connection` function above makes the following function obsolete:
[source]
----
int open_client_connection(const struct sockaddr_in & new_remote_addr, const struct sockaddr_in & new_local_addr)
----
It is kept for backward compatibility for derived test ports that were not adapted to the IPv6 supporting function.
After calling the `open_client_connection` function, AbstractSocket calls automatically the function `virtual void client_connection_opened(int client_id)` to inform the test port about the result. The `client_id` parameter is set to the id of the client, or `_-1_` if the connection could not be established to the remote address. This function can be overridden in the derived test port.
=== Send Message
[source]
void send_outgoing(const unsigned char* message_buffer, int length, int client_id = -1);
With this function a message can be sent to the specified client.
==== To Receive a Message
When a message is received, the following function is called automatically:
[source]
----
virtual void message_incoming(const unsigned char* message_buffer, int length, int client_id = -1)
----
This function must be overridden in the derived test port. To generate an incoming TTCN3 message, the test port shall call the `incoming_message` function of the Titan API within this function.
In order that this function could be called automatically, the derived test port shall define these functions:
[source]
----
virtual void Handler_Install(const fd_set* read_fds, const fd_set* write_fds, const fd_set* error_fds, double call_interval);
virtual void Handler_Uninstall();
----
In `Handler_Install` the `Install_Handler` Titan API function is called.
Also in the `Event_Handler` Titan API function, the function
[source]
----
void Handle_Event(const fd_set *read_fds, const fd_set __write_fds, const fd_set __error_fds, double time_since_last_call)
----
is called.
=== Close a Client Connection
[source]
----
virtual void remove_client(int client_id);
virtual void remove_all_clients();
----
The first closes the connection for a given client the second function closes the connection of all clients.
=== Test Port Parameter Names
The default AbstractSocket test port parameter names can be redefined in the derived test port by overriding the appropriate function below:
[source]
----
virtual const char* local_port_name();
virtual const char* remote_address_name();
virtual const char* local_address_name();
virtual const char* remote_port_name();
virtual const char* ai_family_name();
virtual const char* use_connection_ASPs_name();
virtual const char* halt_on_connection_reset_name();
virtual const char* client_TCP_reconnect_name();
virtual const char* TCP_reconnect_attempts_name();
virtual const char* TCP_reconnect_delay_name();
virtual const char* server_mode_name();
virtual const char* socket_debugging_name();
virtual const char* nagling_name();
virtual const char* use_non_blocking_socket_name();
virtual const char* server_backlog_name();
virtual const char* ssl_disable_SSLv2();
virtual const char* ssl_disable_SSLv3();
virtual const char* ssl_disable_TLSv1();
virtual const char* ssl_disable_TLSv1_1();
virtual const char* ssl_disable_TLSv1_2();
----
=== Parameter Accessor Functions
The following functions can be use to get/set the AbstractSocket parameters:
[source]
----
bool get_nagling() const
bool get_use_non_blocking_socket() const
bool get_server_mode() const
bool get_socket_debugging() const
bool get_halt_on_connection_reset() const
bool get_use_connection_ASPs() const
bool get_handle_half_close() const
int set_non_block_mode(int fd, bool enable_nonblock);
bool increase_send_buffer(int fd, int &old_size, int& new_size);
const char* get_local_host_name()
const unsigned int get_local_port_number()
const char* get_remote_host_name()
const unsigned int get_remote_port_number()
const int& get_ai_family() const
void set_ai_family(int parameter_value)
bool get_ttcn_buffer_usercontrol() const
void set_nagling(bool parameter_value)
void set_server_mode(bool parameter_value)
void set_handle_half_close(bool parameter_value)
void set_socket_debugging(bool parameter_value)
void set_halt_on_connection_reset(bool parameter_value)
void set_ttcn_buffer_usercontrol(bool parameter_value)
----
=== Logging Functions
The following functions log a given message in different ways:
[source]
----
void log_debug(const char *fmt, …) const
void log_warning(const char *fmt, …) const
void log_error(const char *fmt, …) const
void log_hex(const char __prompt, const unsigned char __msg, size_t length) const;
----
=== Error Reporting
[source]
----
virtual void report_error(int client_id, int msg_length, int sent_length, const unsigned char* msg, const char* error_text);
----
This function is called automatically if an error occurs during send operation in `AbstractSocket`. This function can be overridden in the derived test port to override the default error reporting behavior of `AbstractSocket`, which is calling the `log_error` function. This function can also be called by the derived test port to initiate the error reporting mechanism.
= Tips and Tricks
== Usage
In order to build a test port based on `Abstract_Socket` the following steps must be completed:
1. Deriving the test port class (see <<deriving_the_test_port_class, Deriving the Test Port Class>>)
2. Implementation of the logger functions if needed (see <<implementation_of_the_logger_functions, Implementation of the Logger Functions>>)
3. Function translations (see <<function_translations, Function Translations>>)
4. Installing the handlers (see <<functions_for_manipulating_the_set_of_events_for_which_the_port_waits, Functions for Manipulating the Set of Events for Which the Port Waits>>)
5. Final steps (see <<final_steps, Final Steps>>)
These steps are discussed in detail in the following subsections.
[[deriving_the_test_port_class]]
=== Deriving the Test Port Class
Inherit your test port class beside the test port base also from the `Abstract_Socket` class, if you do not want to use SSL at all. If you plan to use SSL, inherit the test port from the `SSL_Socket` class. In case your SSL implementation is just optional, you have to make sure that it is possible to disable SSL related code parts at compile time. In the AS if the `AS_USE_SSL` macro is defined, then SSL is enabled, otherwise disabled.
Example:
[source]
----
#ifdef AS_USE_SSL
class myport__PT : public SSL_Socket, public myport__PT_BASE {
#else
class myport__PT : public Abstract_Socket, public myport__PT_BASE {
#endif
----
[[implementation_of_the_logger_functions]]
=== Implementation of the Logger Functions
Implement the `log_debug`, `log_error`, `log_warning` and `log_hex` virtual functions if you need other implementation than the default. (they can be empty implementations if logging is not needed)
[width="100%",cols="20%,80%",options="header",]
|===============================================================
|Function |Description
|`log_debug` |does the debug-logging
|`log_hex` |does the logging of the message content in hex format
|`log_warning` |does the logging of warning messages
|`log_error` |is expecting the test port to stop with a TTCN_ERROR
|===============================================================
You can use the logger functions implemented in the AS. In this case you have to call the AS constructor with your test port type and name. In this way the AS will log messages acting like your test port.
[[function_translations]]
=== Function Translations
Translate the port's functions to the socket's functions. By translating we mean a function call with unchanged parameters like this:
[source]
----
void myport__PT::set_parameter(const char *parameter_name,
const char* parameter_value) {
parameter_set(parameter_name ,parameter_value);
}
----
The list of functions to be translated:
[cols=",",options="header",]
|====================================
|Port functions: |Socket functions:
|`set_parameter` |`parameter_set`
|`Handle_Fd_Event` |`Handle_Socket_Event`
|`Handle_Timeout` |`Handle_Timeout_Event`
|`user_map` |`map_user`
|`user_unmap` |`unmap_user`
|====================================
If you might need other functions also to be performed during `map`, `unmap` or `set_parameter`, you can add them right after the socket's function calls.
Example:
[source]
----
void myport__PT::set_parameter(const char *parameter_name,
const char *parameter_value)
{
parameter_set(parameter_name ,parameter_value);
if(strcmp(parameter_name,"dallas_addr") == 0){
destHostId = getHostId(parameter_value);
destAddr.sin_family = AF_INET;
destAddr.sin_addr.s_addr = htonl(destHostId);
return;
}
}
----
The translation of the `outgoing_send` to `send_outgoing` function needs some parameter formatting since the `outgoing_send` has parameters inherited from your TTCN-3 structures, while `Abstract_Socket`'s `outgoing_send` has parameters as ``(char* message, int messageLength, int client_id)``.
The same applies for the incoming function calls where you have to write your own `message_incoming` to `incoming_message` translation.
Example:
[source]
----
void myport__PT::outgoing_send(const TTCN3__Structure& send_par)
{
if(send_par.client__id().ispresent()) {
send_outgoing((char*)(const unsigned char*)send_par.data(),
send_par.data().lengthof(), send_par.client__id()());
} else {
send_outgoing((char*)(const unsigned char*)send_par.data(),
send_par.data().lengthof());
}
}
----
[[functions_for_manipulating_the_set_of_events_for_which_the_port_waits]]
=== Functions for Manipulating the Set of Events for Which the Port Waits
Add the following (virtual) member functions to your port (class definition) unchanged:
[source]
----
void Add_Fd_Read_Handler(int fd) { Handler_Add_Fd_Read(fd); }
void Add_Fd_Write_Handler(int fd) { Handler_Add_Fd_Write(fd); }
void Remove_Fd_Read_Handler(int fd) { Handler_Remove_Fd_Read(fd); }
void Remove_Fd_Write_Handler(int fd) { Handler_Remove_Fd_Write(fd); }
void Remove_Fd_All_Handlers(int fd) { Handler_Remove_Fd(fd); }
void Handler_Uninstall() { Uninstall_Handler(); }
void Timer_Set_Handler(double call_interval, boolean is_timeout = TRUE,
boolean call_anyway = TRUE, boolean is_periodic = TRUE) {
Handler_Set_Timer(call_interval, is_timeout, call_anyway, is_periodic);
}
----
NOTE: These member functions are required and used by Abstract Socket. They are defined in Abstract Socket as virtual and are to be overridden in the descendant Test Port class. They are implemented in the (Test Port) class definition only for the sake of simplicity.)
[[final_steps]]
=== Final Steps
Finally, the function definitions must be added to the header file accordingly. Then, you are ready to go ahead and develop your test port.
[[using-ttcn-buffer-in-test-ports]]
== Using `TTCN_Buffer` in Test Ports
The Abstract Socket uses a `TTCN_Buffer` <<_2, [2]>> to store incoming message portions. If the test port also would like to store incoming messages, here is a description how to do that:
The test port can access the `TTCN_Buffer` with `get_buffer`() and can operate on it. If the test port uses the buffer to store data, it must set the `ttcn_buffer_usercontrol` variable to `_true_`, so that the AS will not clear the buffer content.
In this case the test port can use the buffer in the following ways:
* `get_buffer`() to fetch the `TTCN_Buffer` associated with the client
* Optionally modify content; or wait for complete TLV
* Once a message portion is sent to the TTCN-3 test suite, cut the sent message from the buffer because the AS will not do that. In case the test port simply passed the message to the test suite and uses no storage of it (e.g. TCP Test Port), it can leave `ttcn_buffer_usercontrol` in false (which is the default value) so that the AS will take care of buffer cleanups.
== Using SSL on Top of a TCP Connection
SSL can be used on top of the TCP connection. The authentication mode can be configured via a test port parameter.
=== Server Mode
In server mode, first a TCP socket is created. The server starts to listen on this port (upon the `user_map`() operation or in case of connection ASPs, calling the `open_listen_port`() operation). Once a TCP connect request is received, the TCP connection is set up. After this the SSL handshake begins. The SSL is mapped to the file descriptor of the TCP socket. The BIO, which is an I/O abstraction that hides many of the underlying I/O details from an application, is automatically created by OpenSSL inheriting the characteristics of the socket (non-blocking mode). The BIO is completely transparent. The server always sends its certificate to the client. If configured so, the server will request the certificate of the client and check if it is a valid certificate. If not, the SSL connection is refused. If configured not to verify the certificate, the server will not request it from the client and the SSL connection is accepted. If usage of the SSL session resumption is enabled and the client refers to a previous SSL session, the server will accept it, unless it is not found in the SSL context cache. Once the connection is negotiated, data can be sent/received. The SSL connection is shut down using an `unmap`() operation. The shutdown process does not follow the standard: the server simply shuts down and does not expect any acknowledgement from the client.
Clients connected to the server are distinguished with their file descriptor numbers. When a message is received, the file descriptor number is also passed, so the client can be identified.
=== Client Mode
In client mode, first a TCP connection is requested to the server (upon the `user_map`() operation or in case of connection ASPs, calling the `open_client_connection`() operation). Once it is accepted, the SSL endpoint is created. If configured so, the client tries to use the SSL session Id from the previous connection, if available (e.g. it is not the first connection). If no SSL session Id is available, or the server does not accept it, a full handshake is performed. If configured so, the certificate of the server is verified. If the verification fails, the SSL connection is interrupted by the client. If no verification required, the received certificate is still verified, however the result does not affect the connection even though it failed.
=== Authentication Flow
In summary, the authentication is done according to this flow:
* ssl handshake begins (new client tries to connect)
* `virtual int ssl_verify_certificates_at_handshake(int preverify_ok, X509_STORE_CTX *ssl_ctx)` is called. During this handshake you can perform additional authentication.
* If the connection is accepted, the SSL handshake is finished and SSL is established. Now the function virtual `bool ssl_verify_certificates`() is called where you may perform other authentication if you want.
`ssl_verify_certificates`() is a virtual function. It is called after the SSL connection is up. Test ports may use it to check other peer's certificate and do actions. If the return value is 0, then the SSL connection is closed. In case of client mode, the test port exits with an error (`*verification_error*`). In server mode the test port just removes client data, but keeps running.
== Adapting Derived Test Ports to Support IPv6
Derived test ports should be updated in the following way to support IPv4 and IPv6:
All calls of functions
[source]
----
const struct sockaddr_in & get_remote_addr()
const struct sockaddr_in & get_local_addr()
----
should be removed. They can be replaced by calling these functions:
[source]
----
virtual const char* local_port_name();
virtual const char* local_address_name();
virtual const char* remote_port_name();
virtual const char* remote_address_name();
----
The function `int open_listen_port(const struct sockaddr_in & localAddr);` does not support IPv6. The function below should be used instead:
`int open_listen_port(const char* localHostname, const char* localServicename);`
The same has to be done for the `open_client_connection` function. Replace any call of the function
[source]
int open_client_connection(const struct sockaddr_in & new_remote_addr, const struct sockaddr_in & new_local_addr)
with the function:
[source]
----
int open_client_connection(const char* remoteHostname, const char* remoteService, const char* localHostname, const char* localService);
----
If the following callback function is overridden in the derived test port:
[source]
----
virtual void peer_connected(int client_id, sockaddr_in& remote_addr);
----
it should be removed and the following function should be overridden instead:
[source]
----
virtual void peer_connected(int client_id, const char * host, const int port)
----
The following function should not be used:
`void get_host_id(const char* hostName, struct sockaddr_in *addr)`
The socket API function `getaddrinfo` should be used instead.
= Error Messages
== Error Messages In Case TCP Connections Are Used
`*Parameter value <value> not recognized for parameter <name>*`
The specified `<value>` in the runtime configuration file is not recognized for the parameter <name>.
`*Invalid input as TCP_reconnect_attempts counter given: <value>*`
The specified `<value>` in the runtime configuration file must be a positive whole number.
`*TCP_reconnect_attempts must be greater than 0, <value> is given*`
The specified `<value>` for `TCP_reconnect_attempts` in the runtime configuration file must be greater than `_0_`.
`*Invalid input as TCP_reconnect_delay given: <value>*`
The specified `<value>` for the `TCP_reconnect_delay` parameter in the runtime configuration file must be a whole number not less than `_0_`.
`*TCP_reconnect_delay must not be less than 0, <value> is given*`
The specified `<value>` for the `TCP_reconnect_delay` parameter in the runtime configuration file must be a whole number not less than `_0_`.
`*Invalid input as port number given: <value>*`
The specified `<value>` in the runtime configuration file is cannot be interpreted as a valid port number (e.g. string is given).
`*Port number must be between 0 and 65535, <value> is given*`
The specified `<value>` in the runtime configuration file is cannot be interpreted as a valid port number. Port numbers must be in the range 0..65535.
`*Invalid input as server backlog given: <value>*`
The specified `<value>` in the runtime configuration file is cannot be interpreted as a valid server backlog number (e.g. string is given).
`*Cannot accept connection at port*`
Connection could not be accepted on TCP socket.
`*Error when reading the received TCP PDU*`
System error occurred during reading from the TCP socket.
`*Cannot open socket*`
Creation of the listener socket failed.
`*Setsockopt failed*`
Setting of socket options failed.
`*Cannot bind to port*`
Binding of a socket to a port failed.
`*Cannot listen at port*`
Listen on the listener socket failed.
`*getsockname() system call failed on the server socket*`
The query of the listening port number failed.
`*AbstractSocket: getnameinfo: <error>*`
The `getnameinfo` function returned an error.
`*getaddrinfo: <errortext> for host <host> service <service>*`
The `getaddrinfo` function returned an error.
`*Malformed message: invalid length: <length>. The length should be at least <lenght>.*`
The message received contains invalid length information.
`*Already tried <value> times, giving up*`
The deadlock counter exceeds the hard coded limit when trying to connect to a server in client mode. When connecting on a socket, sometimes it is unsuccessful. The next try usually solves the problem and the connection will be successfully accepted. The test port retries to connect as a workaround. The number of tries however limited to avoid hanging the test port.
Different operating systems behave in a different way. This problem is rare on Solaris, Unix and Linux systems, but much more often on Cygwin.
`*Cannot connect to server*`
Connection to a server on TCP failed.
`*Connection was interrupted by the other side*`
The TCP or SSL connection was refused by the other peer, or broken.
`*Client Id not specified although not only 1 client exists*`
It should never show up.
`*There is no connection alive, use the `ASP_TCP_Connect' before sending anything.*`
An attempt was made by the test port to send data before setting up any connection. The `open_client_connection` function has to be called before sending any data.
`*Send system call failed: There is no client connected to the TCP server*`
A send operation is performed to a non-existing client.
`*Send system call failed: <value> bytes were sent instead of <value>*`
The send operation failed.
`*<name> is not defined in the configuration file*`
The test port parameter <name> is not defined in the runtime configuration file, although its presence is mandatory (or conditional and the condition is true).
`*The host name <name> is not valid in the configuration file*`
The host name specified in the configuration file could not be resolved.
`*Number of clients<>0 but cannot get first client, programming error*`
It should never show up.
`*Index <value> exceeds length of peer list*`
It should never show up.
`*Abstract_Socket::get_peer: Client <value> does not exist*`
It should never show up.
`*Invalid Client Id is given: <value>*`
It should never show up.
`*Peer <value> does not exist*`
It should never show up.
`*Set blocking mode failed.*`
Test port could not set socket option: `O_NONBLOCK`
== Additional Error Messages In Case SSL Connections Are Used
Apart from the previously mentioned error messages, the following messages are used in case SSL is used:
`*No SSL CTX found, SSL not initialized*`
It should never show up.
`*Creation of SSL object failed*`
Creation of the SSL object is failed.
`*Binding of SSL object to socket failed*`
The SSL object could not be bound to the TCP socket.
`*SSL error occurred*`
A general SSL error occurred. Check the test port logs to see previous error messages showing the real problem.
`*<name> is not defined in the configuration file although <value>=yes*`
[source]
<name>: ssl_trustedCAlist_file_name(), <value>: ssl_verifycertificate_name()
`*No SSL data available for client <value>*`
It should never show up.
`*Could not read from /dev/urandom*`
The read operation on the installed random device is failed.
`*Could not read from /dev/random*`
The read operation on the installed random device is failed.
`*Could not seed the Pseudo Random Number Generator with enough data*`
As no random devices found, a workaround is used to seed the SSL PRNG. The seeding failed.
`*SSL method creation failed*`
The creation of the SSL method object failed.
`*SSL context creation failed*`
The creation of the SSL context object failed.
`*Can't read certificate file*`
The specified certificate file could not be read.
`*Can't read key file*`
The specified private key file could not be read.
`*Can't read trustedCAlist file*`
The specified certificate of the trusted CAs file could not be read.
`*Cipher list restriction failed for <value>*`
The specified cipher restriction list could not be set.
`*Activation of SSL session resumption failed on server*`
The activation of the SSL session resumption on the server failed.
`*Unknown SSL error code <value>*`
It should never show up.
= Warning Messages
== Warning Messages In Case TCP Connections Are Used
`*Error when reading the received TCP PDU*`
The received TCP PDU cannot be read.
`*connect() returned error code EADDRINUSE. Perhaps this is a kernel bug. Trying to connect again.*`
When connecting on a socket, sometimes it is unsuccessful. The next try usually solves the problem and the connection will be successfully accepted. The test port retries to connect as a workaround. The number of tries however limited to avoid hanging the test port.
Different operating systems behave in a different way. This problem is rare on Solaris, Unix and Linux systems, but much more often on Cygwin.
`*Connect() returned error code <value>, trying to connect again (TCP reconnect mode)*`
When connecting on a socket, sometimes it is unsuccessful and the given error code was returned. The next try usually solves the problem and the connection will be successfully accepted. The test port retries to connect as a workaround.
`*TCP connection was interrupted by the other side, trying to reconnect again.*`
The TCP or SSL connection was refused by the other peer, or it was broken. The test port tries to reconnect again.
`*TCP reconnect successfully finished.*`
This warning message is given if the reconnection was successful.
`*Parameter <name> has no meaning if use_connection_ASPs is used.*`
There is no effect of setting this parameter when `use_connection_ASPs` is set to `_"yes"_`.
`*Abstract_Socket::remove_client: <value> is the server listening port, can not be removed!*`
The `client_id` given in the `remove_client` function is currently used as the server’s listening port, it can not be removed. To close the server listening port, use the `close_listen_port` function.
`*Client <value> has not been removed, programming error*`
It should never show up.
`*Sending data on file descriptor <value>.The sending operation would block execution. The size of the outgoing buffer was increased from <old_value> to <new_value> bytes.*`
When the Abstract Socket is used with non-blocking socket, if the sending operation would block, first the size of the sending buffer is increased. This is the first step to avoid TCP deadlock. In the second step the Test Port will try to receive some data.
`*Sending data on file descriptor <value>.The sending operation would block execution and it is not possible to further increase the size of the outgoing buffer. Trying to process incoming data to avoid deadlock.*`
When the Abstract Socket is used with non-blocking socket, if the sending operation would block, and the size of the outgoing buffer cannot be increased, the Test Port tries to receive some data to avoid deadlock.
`*System call fcntl(F_GETFL) failed on file descriptor %d.*`
`*System call fcntl(F_SETFL) failed on file descriptor %d.*`
`*Setsockopt failed when trying to open the listen port: <port>*`
The `setsockopt` function failed.
`*Cannot bind to port when trying to open the listen port: <port>*`
The bind system call failed.
`*Cannot listen at port when trying to open the listen port: <port>*`
The listen system call failed.
`*getsockname() system call failed on the server socket when trying to open the listen port: <port>*`
The getsockname system call failed.
`*getaddrinfo: <errortexr> for host <host> service <service>*`
The getaddrinfo system call failed.
`*getnameinfo() system call failed on the server socket when trying to open the listen port: <port>*`
The getnameinfo system call failed.
`*Cannot open socket when trying to open client connection: <errortext>*`
The socket system call failed.
`*Setsockopt failed when trying to open client connection: <errormessage>*`
The setsockopt system call failed.
`*Cannot bind to port when trying to open client connection: <errortext>*`
The bind system call failed.
`*Already tried <n> times, giving up when trying to open client connection: <errortext>*`
The deadlock counter exceeds the hard coded limit when trying to connect to a server in client mode. When connecting on a socket, sometimes it is unsuccessful. The next try usually solves the problem and the connection will be successfully accepted. The test port retries to connect as a workaround. The number of tries however limited to avoid hanging the test port.
Different operating systems behave in a different way. This problem is rare on Solaris, Unix and Linux systems, but much more often on Cygwin.
[[warning_messages_in_case_SSL_connections_are_used]]
== Warning Messages In Case SSL Connections Are Used
`*Warning: race condition while setting current client object pointer.*`
The current client object pointer is already set.
`*Connection from client <value> is refused*`
The connection from a client is refused in the server.
`*Connection to server is refused*`
The connection from the client is refused by the server.
`*Server did not send a session ID*`
The SSL server did not send a session ID.
`*Verification failed*`
The verification of the other side is failed. The connection will be shut down.
`*SSL object not found for client <value>*`
It should never show up.
`*SSL_Socket::receive_message_on_fd: SSL connection was interrupted by the other side*`
The TLS/SSL connection has been closed. If the protocol version is SSL 3.0 or TLS 1.0, this warning appears only if a closure alert has occurred in the protocol, i.e. if the connection has been closed cleanly. Note that in this case it does not necessarily indicate that the underlying transport has been closed.
`*Other side does not have certificate*`
The other side of the SSL connection does not have a certificate.
`*Solaris patches to provide random generation devices are not installed*`
Solaris patches to provide random generation devices are not installed. A workaround will be used to seed the PRNG.
`*Private key does not match the certificate public key*`
The private key specified for the test port does not match with the public key.
= Terminology
*Sockets:* +
The socket is a method for communication between a client program and a server program in a network. A socket is defined as "the endpoint in a connection". Sockets are created and used with a set of programming requests or "function calls" sometimes called the sockets application programming interface (API). The most common socket API is the Berkeley UNIX C language interface for sockets. Sockets can also be used for communication between processes within the same computer.
*Blocking and non-blocking sockets:* +
Using a blocking socket, some socket operations (send, receive, connect, accept) will block until the operation is finished or an error occurs. Using a non-blocking socket, these operations will never block but return with an error and set `errno` to the appropriate value.
*OpenSSL:* +
The OpenSSL Project is a collaborative effort to develop a robust, commercial-grade, full-featured, and open source toolkit implementing the Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS v1) protocols as well as a full-strength general purpose cryptography library. For more information on the OpenSSL project see <<_3, [3]>>
= Abbreviations
AS:: Abstract Socket
ASP:: Abstract Service Primitive
IPv4:: Internet Protocol version 4
IPv6:: Internet Protocol version 6
PEM:: Privacy Enhanced Mail
RTE:: Run-Time Environment
SSL:: Secure Sockets Layer
SUT:: System Under Test
TCP:: Transmission Control Protocol
TLS:: Transport Layer Security
TTCN-3:: Testing and Test Control Notation version 3
= References
[[_1]]
[1] ETSI ES 201 873-1 (2002) +
The Testing and Test Control Notation version 3. Part 1: Core Language
[[_2]]
[2] User Guide for TITAN TTCN–3 Test Executor
[[_3]]
[3] OpenSSL toolkit +
http://www.openssl.org
/******************************************************************************
* Copyright (c) 2000-2019 Ericsson Telecom AB
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
*
* Contributors:
* Zoltan Bibo - initial implementation and initial documentation
* Gergely Futo
* Oliver Ferenc Czerman
* Balasko Jeno
* Zoltan Bibo
* Eduard Czimbalmos
* Kulcsár Endre
* Gabor Szalai
* Jozsef Gyurusi
* Csöndes Tibor
* Zoltan Jasz
******************************************************************************/
//
// File: Abstract_Socket.cc
// Description: Abstract_Socket implementation file
// Rev: R9B
// Prodnr: CNL 113 384
//
#include "Abstract_Socket.hh"
#include <stdio.h>
#include <unistd.h>
#include <strings.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <sys/stat.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <poll.h>
#if defined SOLARIS8
# include <signal.h>
#endif
#define AS_TCP_CHUNCK_SIZE 4096
#define AS_SSL_CHUNCK_SIZE 16384
// Used for the 'address already in use' bug workaround
#define AS_DEADLOCK_COUNTER 16
// character buffer length to store temporary SSL informations, 256 is usually enough
#define SSL_CHARBUF_LENGTH 256
// number of bytes to read from the random devices
#define SSL_PRNG_LENGTH 1024
#ifndef NI_MAXHOST
#define NI_MAXHOST 1024
#endif
#ifndef NI_MAXSERV
#define NI_MAXSERV 32
#endif
/********************************
** PacketHeaderDescr
** used for fragmentation and concatenation
** of fixed format messages
*********************************/
unsigned long PacketHeaderDescr::Get_Message_Length(const unsigned char* buff) const
{
unsigned long m_length = 0;
for (unsigned long i = 0; i < nr_bytes_in_length; i++) {
unsigned long shift_count =
byte_order == Header_MSB ? nr_bytes_in_length - 1 - i : i;
m_length |= buff[length_offset + i] << (8 * shift_count);
}
m_length *= length_multiplier;
if (value_offset < 0 && (long)m_length < -value_offset) return 0;
else return m_length + value_offset;
}
////////////////////////////////////////////////////////////////////////
///// Default log functions
////////////////////////////////////////////////////////////////////////
void Abstract_Socket::log_debug(const char *fmt, ...) const
{
if (socket_debugging) {
TTCN_Logger::begin_event(TTCN_DEBUG);
if (test_port_type != NULL && test_port_name != NULL)
TTCN_Logger::log_event("%s test port (%s): ", test_port_type,
test_port_name);
else TTCN_Logger::log_event_str("Abstract socket: ");
va_list args;
va_start(args, fmt);
TTCN_Logger::log_event_va_list(fmt, args);
va_end(args);
TTCN_Logger::end_event();
}
}
void Abstract_Socket::log_warning(const char *fmt, ...) const
{
TTCN_Logger::begin_event(TTCN_WARNING);
if (test_port_type != NULL && test_port_name != NULL)
TTCN_Logger::log_event("%s test port (%s): warning: ", test_port_type,
test_port_name);
else TTCN_Logger::log_event_str("Abstract socket: warning: ");
va_list args;
va_start(args, fmt);
TTCN_Logger::log_event_va_list(fmt, args);
va_end(args);
TTCN_Logger::end_event();
}
void Abstract_Socket::log_error(const char *fmt, ...) const
{
va_list args;
va_start(args, fmt);
char *error_str = mprintf_va_list(fmt, args);
va_end(args);
try {
if (test_port_type != NULL && test_port_name != NULL)
TTCN_error("%s test port (%s): %s", test_port_type, test_port_name,
error_str);
else TTCN_error("Abstract socket: %s", error_str);
} catch (...) {
Free(error_str);
throw;
}
Free(error_str);
}
void Abstract_Socket::log_hex(const char *prompt, const unsigned char *msg,
size_t length) const
{
if (socket_debugging) {
TTCN_Logger::begin_event(TTCN_DEBUG);
if (test_port_type != NULL && test_port_name != NULL)
TTCN_Logger::log_event("%s test port (%s): ", test_port_type,
test_port_name);
else TTCN_Logger::log_event_str("Abstract socket: ");
if (prompt != NULL) TTCN_Logger::log_event_str(prompt);
TTCN_Logger::log_event("Size: %lu, Msg:", (unsigned long)length);
for (size_t i = 0; i < length; i++) TTCN_Logger::log_event(" %02x", msg[i]);
TTCN_Logger::end_event();
}
}
/********************************
** Abstract_Socket
** abstract base type for TCP socket handling
*********************************/
Abstract_Socket::Abstract_Socket() {
server_mode=false;
socket_debugging=false;
nagling=false;
use_non_blocking_socket=false;
halt_on_connection_reset=true;
halt_on_connection_reset_set=false;
client_TCP_reconnect=false;
TCP_reconnect_attempts=5;
TCP_reconnect_delay=1;
listen_fd=-1;
memset(&remoteAddr, 0, sizeof(remoteAddr));
memset(&localAddr, 0, sizeof(localAddr));
server_backlog=1;
peer_list_length=0;
local_host_name = NULL;
local_port_number = 0;
remote_host_name = NULL;
remote_port_number = 0;
ai_family = AF_UNSPEC; // default: Auto
test_port_type=NULL;
test_port_name=NULL;
ttcn_buffer_usercontrol=false;
use_connection_ASPs=false;
handle_half_close = false;
peer_list_root = NULL;
}
Abstract_Socket::Abstract_Socket(const char *tp_type, const char *tp_name) {
server_mode=false;
socket_debugging=false;
nagling=false;
use_non_blocking_socket=false;
halt_on_connection_reset=true;
halt_on_connection_reset_set=false;
client_TCP_reconnect=false;
TCP_reconnect_attempts=5;
TCP_reconnect_delay=1;
listen_fd=-1;
memset(&remoteAddr, 0, sizeof(remoteAddr));
memset(&localAddr, 0, sizeof(localAddr));
server_backlog=1;
peer_list_length=0;
local_host_name = NULL;
local_port_number = 0;
remote_host_name = NULL;
remote_port_number = 0;
ai_family = AF_UNSPEC; // default: Auto
test_port_type=tp_type;
test_port_name=tp_name;
ttcn_buffer_usercontrol=false;
use_connection_ASPs=false;
handle_half_close = false;
peer_list_root = NULL;
}
Abstract_Socket::~Abstract_Socket() {
peer_list_reset_peer();
Free(local_host_name);
Free(remote_host_name);
}
bool Abstract_Socket::parameter_set(const char *parameter_name,
const char *parameter_value)
{
log_debug("entering Abstract_Socket::parameter_set(%s, %s)", parameter_name, parameter_value);
if (strcmp(parameter_name, socket_debugging_name()) == 0) {
if (strcasecmp(parameter_value,"yes")==0) socket_debugging = true;
else if (strcasecmp(parameter_value,"no")==0) socket_debugging = false;
else log_error("Parameter value '%s' not recognized for parameter '%s'", parameter_value, socket_debugging_name());
} else if (strcmp(parameter_name, server_mode_name()) == 0) {
if (strcasecmp(parameter_value,"yes")==0) server_mode = true;
else if (strcasecmp(parameter_value,"no")==0) server_mode = false;
else log_error("Parameter value '%s' not recognized for parameter '%s'", parameter_value, server_mode_name());
} else if (strcmp(parameter_name, use_connection_ASPs_name()) == 0) {
if (strcasecmp(parameter_value,"yes")==0) use_connection_ASPs = true;
else if (strcasecmp(parameter_value,"no")==0) use_connection_ASPs = false;
else log_error("Parameter value '%s' not recognized for parameter '%s'", parameter_value, use_connection_ASPs_name());
} else if (strcmp(parameter_name, halt_on_connection_reset_name()) == 0) {
halt_on_connection_reset_set=true;
if (strcasecmp(parameter_value,"yes")==0) halt_on_connection_reset = true;
else if (strcasecmp(parameter_value,"no")==0) halt_on_connection_reset = false;
else log_error("Parameter value '%s' not recognized for parameter '%s'", parameter_value, halt_on_connection_reset_name());
} else if (strcmp(parameter_name, client_TCP_reconnect_name()) == 0) {
if (strcasecmp(parameter_value,"yes")==0) client_TCP_reconnect = true;
else if (strcasecmp(parameter_value,"no")==0) client_TCP_reconnect = false;
else log_error("Parameter value '%s' not recognized for parameter '%s'", parameter_value, client_TCP_reconnect_name());
} else if (strcmp(parameter_name, TCP_reconnect_attempts_name()) == 0) {
if (sscanf(parameter_value, "%d", &TCP_reconnect_attempts)!=1) log_error("Invalid input as TCP_reconnect_attempts counter given: %s", parameter_value);
if (TCP_reconnect_attempts<=0) log_error("TCP_reconnect_attempts must be greater than 0, %d is given", TCP_reconnect_attempts);
} else if (strcmp(parameter_name, TCP_reconnect_delay_name()) == 0) {
if (sscanf(parameter_value, "%d", &TCP_reconnect_delay)!=1) log_error("Invalid input as TCP_reconnect_delay given: %s", parameter_value);
if (TCP_reconnect_delay<0) log_error("TCP_reconnect_delay must not be less than 0, %d is given", TCP_reconnect_delay);
} else if(strcmp(parameter_name, remote_address_name()) == 0){
Free(remote_host_name);
remote_host_name = mcopystr(parameter_value);
} else if(strcmp(parameter_name, local_address_name()) == 0){ // only for backward compatibility
Free(local_host_name);
local_host_name = mcopystr(parameter_value);
} else if(strcmp(parameter_name, remote_port_name()) == 0){
int a;
if (sscanf(parameter_value, "%d", &a)!=1) log_error("Invalid input as port number given: %s", parameter_value);
if (a>65535 || a<0){ log_error("Port number must be between 0 and 65535, %d is given", remote_port_number);}
else {remote_port_number=a;}
} else if(strcmp(parameter_name, ai_family_name()) == 0){
if (strcasecmp(parameter_value,"IPv6")==0 || strcasecmp(parameter_value,"AF_INET6")==0) ai_family = AF_INET6;
else if (strcasecmp(parameter_value,"IPv4")==0 || strcasecmp(parameter_value,"AF_INET")==0) ai_family = AF_INET;
else if (strcasecmp(parameter_value,"UNSPEC")==0 || strcasecmp(parameter_value,"AF_UNSPEC")==0) ai_family = AF_UNSPEC;
else log_error("Parameter value '%s' not recognized for parameter '%s'", parameter_value, ai_family_name());
} else if(strcmp(parameter_name, local_port_name()) == 0){
int a;
if (sscanf(parameter_value, "%d", &a)!=1) log_error("Invalid input as port number given: %s", parameter_value);
if (a>65535 || a<0) {log_error("Port number must be between 0 and 65535, %d is given", local_port_number);}
else {local_port_number=a;}
} else if (strcmp(parameter_name, nagling_name()) == 0) {
if (strcasecmp(parameter_value,"yes")==0) nagling = true;
else if (strcasecmp(parameter_value,"no")==0) nagling = false;
else log_error("Parameter value '%s' not recognized for parameter '%s'", parameter_value, nagling_name());
} else if (strcmp(parameter_name, use_non_blocking_socket_name()) == 0){
if (strcasecmp(parameter_value, "yes") == 0) use_non_blocking_socket = true;
else if (strcasecmp(parameter_value, "no") == 0) use_non_blocking_socket = false;
} else if (strcmp(parameter_name, server_backlog_name()) == 0) {
if (sscanf(parameter_value, "%d", &server_backlog)!=1) log_error("Invalid input as server backlog given: %s", parameter_value);
} else {
log_debug("leaving Abstract_Socket::parameter_set(%s, %s)", parameter_name, parameter_value);
return false;
}
log_debug("leaving Abstract_Socket::parameter_set(%s, %s)", parameter_name, parameter_value);
return true;
}
void Abstract_Socket::Handle_Socket_Event(int fd, boolean is_readable, boolean is_writable, boolean is_error)
{
log_debug("entering Abstract_Socket::Handle_Socket_Event(): fd: %d%s%s%s", fd,
is_readable ? " readable" : "", is_writable ? " writable" : "", is_error ? " error" : "");
if (fd != listen_fd /* on server the connection requests are handled after the user messages */
&& peer_list_root[fd] != NULL && (is_readable || is_writable)
&& get_peer(fd)->reading_state != STATE_DONT_RECEIVE) {
log_debug("receiving data");
int messageLength = receive_message_on_fd(fd);
if (messageLength == 0) { // peer disconnected
as_client_struct * client_data = get_peer(fd);
log_debug("Abstract_Socket::Handle_Socket_Event(). Client %d closed connection.", fd);
switch (client_data->reading_state) {
case STATE_BLOCK_FOR_SENDING:
log_debug("Abstract_Socket::Handle_Socket_Event(): state is STATE_BLOCK_FOR_SENDING, don't close connection.");
Remove_Fd_Read_Handler(fd);
client_data->reading_state = STATE_DONT_CLOSE;
log_debug("Abstract_Socket::Handle_Socket_Event(): setting socket state to STATE_DONT_CLOSE");
break;
case STATE_DONT_CLOSE:
log_debug("Abstract_Socket::Handle_Socket_Event(): state is STATE_DONT_CLOSE, don't close connection.");
break;
default:
if((client_data->tcp_state == CLOSE_WAIT) || (client_data->tcp_state == FIN_WAIT)) {
remove_client(fd);
peer_disconnected(fd);
} else {
if(shutdown(fd, SHUT_RD) != 0) {
if(errno == ENOTCONN) {
errno = 0;
} else {
log_error("shutdown(SHUT_RD) system call failed");
}
}
client_data->tcp_state = CLOSE_WAIT;
Remove_Fd_Read_Handler(fd);
peer_half_closed(fd);
}
} // switch (client_data->reading_state)
} else if (messageLength > 0) {
as_client_struct *client_data=get_peer(fd);
if (socket_debugging) {
struct sockaddr_storage clientAddr = client_data->clientAddr;
#ifdef WIN32
log_debug("Message received from address %s:%d", inet_ntoa(((struct sockaddr_in*)&clientAddr)->sin_addr), ntohs(((struct sockaddr_in *)&clientAddr)->sin_port));
#else
char hname[NI_MAXHOST];
char sname[NI_MAXSERV];
#if defined LINUX || defined FREEBSD || defined SOLARIS8
socklen_t
#else /* SOLARIS or WIN32 */
int
#endif
clientAddrlen = client_data->clientAddrlen;
int error = getnameinfo((struct sockaddr *)&clientAddr, clientAddrlen,
hname, sizeof (hname), sname, sizeof (sname), NI_NUMERICHOST|NI_NUMERICSERV);
if (error) log_error("AbstractSocket: getnameinfo 2: %s\n", gai_strerror(error));
log_debug("Message received from address (addr) %s/%s", hname, sname);
#endif
}
log_hex("Message received, buffer content: ", get_buffer(fd)->get_data(), get_buffer(fd)->get_len());
handle_message(fd);
} /* else if (messageLength == -2) =>
used in case of SSL: means that reading would bloc.
in this case I stop receiving message on the file descriptor */
} // if ... (not new connection request)
if (fd == listen_fd && is_readable) {
// new connection request arrived
log_debug("waiting for accept");
// receiving new connection on the TCP server
struct sockaddr_storage clientAddr;
#if defined LINUX || defined FREEBSD || defined SOLARIS8
socklen_t
#else /* SOLARIS or WIN32 */
int
#endif
clientAddrlen = sizeof(clientAddr);
#if defined LINUX || defined FREEBSD || defined SOLARIS8
int newclient_fd = accept(listen_fd, (struct sockaddr *) &clientAddr, (socklen_t*)&clientAddrlen);
#else
int newclient_fd = accept(listen_fd, (struct sockaddr *) &clientAddr, (int*)&clientAddrlen);
#endif
if(newclient_fd < 0) log_error("Cannot accept connection at port");
as_client_struct *client_data=peer_list_add_peer(newclient_fd);
Add_Fd_Read_Handler(newclient_fd); // Done here - as in case of error: remove_client expects the handler as added
log_debug("Abstract_Socket::Handle_Socket_Event(). Handler set to other fd %d", newclient_fd);
client_data->fd_buff = new TTCN_Buffer;
client_data->clientAddr = clientAddr;
client_data->clientAddrlen = clientAddrlen;
client_data->tcp_state = ESTABLISHED;
client_data->reading_state = STATE_NORMAL;
if (add_user_data(newclient_fd)) {
char hname[NI_MAXHOST];
int clientPort = 0;
#ifdef WIN32
clientPort=ntohs(((struct sockaddr_in *)&clientAddr)->sin_port);
char* tmp=inet_ntoa(((struct sockaddr_in*)&clientAddr)->sin_addr);
strcpy(hname,tmp);
#else
int error;
char sname[NI_MAXSERV];
error = getnameinfo((struct sockaddr *)&clientAddr, clientAddrlen,
hname, sizeof (hname), sname, sizeof (sname), NI_NUMERICHOST|NI_NUMERICSERV);
if (error) {
log_error("AbstractSocket: getnameinfo: %s\n",
gai_strerror(error));
}
clientPort = atoi(sname);
#endif
log_debug("Client %d connected from address %s/%d", newclient_fd, hname, clientPort);
peer_connected(newclient_fd, hname, clientPort);
peer_connected(newclient_fd, *((struct sockaddr_in *)&clientAddr)); /* calling deprecated function also */
log_debug("Handle_Socket_Event updated with client %d ", newclient_fd);
if (set_non_block_mode(newclient_fd, use_non_blocking_socket) < 0) {
log_error("Set blocking mode failed.");
}
} else {
remove_client(newclient_fd);
peer_disconnected(newclient_fd);
}
} // if (fd == listen_fd && is_readable)
log_debug("leaving Abstract_Socket::Handle_Socket_Event()");
}
int Abstract_Socket::receive_message_on_fd(int client_id)
{
as_client_struct * client_data = get_peer(client_id);
TTCN_Buffer* recv_tb = client_data->fd_buff;
unsigned char *end_ptr;
size_t end_len=AS_TCP_CHUNCK_SIZE;
recv_tb->get_end(end_ptr, end_len);
int messageLength = recv(client_id, (char *)end_ptr, end_len, 0);
if (messageLength==0) return messageLength; // peer disconnected
else if (messageLength < 0) {
log_warning("Error when reading the received TCP PDU: %s", strerror(errno));
errno = 0;
return 0;
}
recv_tb->increase_length(messageLength);
return messageLength;
}
int Abstract_Socket::send_message_on_fd(int client_id, const unsigned char* send_par, int message_length)
{
get_peer(client_id);
return send(client_id, (const char *)send_par, message_length, 0);
}
//Tthe EAGAIN errno value set by the send operation means that
//the sending operation would block.
//First I try to increase the length of the sending buffer (increase_send_buffer()).
//If the outgoing buffer cannot be increased, the block_for_sending function will
//be called. This function will block until the file descriptor given as its argument
//is ready to write. While the block for sending operation calls the Event_Handler,
//states must be used to indicate that the Event_Handler is called when the
//execution is blocking.
//STATE_BLOCK_FOR_SENDING: the block for sending operation has been called
//STATE_DONT_CLOSE: if the other side close the connection before the block_for_sending
// operation returns, in the Event_Handler the connection
// must not be closed and the block_for_sending must return before we can
// close the connection. This state means that the other side closed the connection
// during the block_for_sending operation
//STATE_NORMAL: normal state
int Abstract_Socket::send_message_on_nonblocking_fd(int client_id,
const unsigned char* send_par,
int length){
log_debug("entering Abstract_Socket::"
"send_message_on_nonblocking_fd(id: %d)", client_id);
as_client_struct * client_data = get_peer(client_id);
int sent_len = 0;
while(sent_len < length){
int ret;
log_debug("Abstract_Socket::send_message_on_nonblocking_fd(id: %d): new iteration", client_id);
if (client_data->reading_state == STATE_DONT_CLOSE){
goto client_closed_connection;
} else ret = send(client_id, send_par + sent_len, length - sent_len, 0);
if (ret > 0) sent_len+=ret;
else{
switch(errno){
case EINTR:{ //signal: do nothing, try again
errno = 0;
break;
}
case EPIPE:{ //client closed connection
goto client_closed_connection;
}
case EAGAIN:{ // the output buffer is full:
//try to increase it if possible
errno = 0;
int old_bufsize, new_bufsize;
if (increase_send_buffer(
client_id, old_bufsize, new_bufsize)) {
log_warning("Sending data on on file descriptor %d",
client_id);
log_warning("The sending operation would"
"block execution. The size of the "
"outgoing buffer was increased from %d to "
"%d bytes.",old_bufsize,
new_bufsize);
} else {
log_warning("Sending data on file descriptor %d",
client_id);
log_warning("The sending operation would block "
"execution and it is not possible to "
"further increase the size of the "
"outgoing buffer. Trying to process incoming"
"data to avoid deadlock.");
log_debug("Abstract_Socket::"
"send_message_on_nonblocking_fd():"
" setting socket state to "
"STATE_BLOCK_FOR_SENDING");
client_data->reading_state = STATE_BLOCK_FOR_SENDING;
TTCN_Snapshot::block_for_sending(client_id);
}
break;
}
default:{
log_debug("Abstract_Socket::"
"send_message_on_nonblocking_fd(): "
"setting socket state to STATE_NORMAL");
client_data->reading_state = STATE_NORMAL;
log_debug("leaving Abstract_Socket::"
"send_message_on_nonblocking_fd(id: %d)"
" with error", client_id);
return -1;
}
} //end of switch
}//end of else
} //end of while
log_debug("Abstract_Socket::send_message_on_nonblocking_fd():"
"setting socket state to STATE_NORMAL");
client_data->reading_state = STATE_NORMAL;
log_debug("leaving Abstract_Socket::"
"send_message_on_nonblocking_fd(id: %d)", client_id);
return sent_len;
client_closed_connection:
log_debug("Abstract_Socket::send_message_on_nonblocking_fd(): setting socket state to STATE_NORMAL");
client_data->reading_state = STATE_NORMAL;
log_debug("leaving Abstract_Socket::"
"send_message_on_nonblocking_fd(id: %d)", client_id);
errno = EPIPE;
return -1;
}
const PacketHeaderDescr* Abstract_Socket::Get_Header_Descriptor() const
{
return NULL;
}
void Abstract_Socket::peer_connected(int /*client_id*/, sockaddr_in& /*remote_addr*/)
{
}
void Abstract_Socket::handle_message(int client_id)
{
const PacketHeaderDescr* head_descr = Get_Header_Descriptor();
as_client_struct * client_data = get_peer(client_id);
TTCN_Buffer *recv_tb = client_data->fd_buff;
if(!head_descr){
message_incoming(recv_tb->get_data(), recv_tb->get_len(), client_id);
if (!ttcn_buffer_usercontrol) recv_tb->clear();
} else {
recv_tb->rewind();
unsigned long valid_header_length = head_descr->Get_Valid_Header_Length();
while (recv_tb->get_len() > 0) {
if ((unsigned long)recv_tb->get_len() < valid_header_length) {
// this is a message without a valid header
// recv_tb->handle_fragment();
return;
}
unsigned long message_length =
head_descr->Get_Message_Length(recv_tb->get_data());
if (message_length < valid_header_length) {
// this is a message with a malformed length
log_error("Malformed message: invalid length: %lu. The length should "
"be at least %lu.", message_length, valid_header_length);
}
if((unsigned long)recv_tb->get_len() < message_length){
// this is a fragmented message with a valid header
// recv_tb->handle_fragment();
return;
}
// this a valid message
message_incoming(recv_tb->get_data(), message_length, client_id);
if (!ttcn_buffer_usercontrol) {
recv_tb->set_pos(message_length);
recv_tb->cut();
}
}
}
log_debug("leaving Abstract_Socket::handle_message()");
}
void Abstract_Socket::map_user()
{
log_debug("entering Abstract_Socket::map_user()");
#if defined SOLARIS8
sigignore(SIGPIPE);
#endif
if(!use_connection_ASPs)
{
// If halt_on_connection_reset is not set explicitly
// set it to the default value: true on clients, false on servers
if (!halt_on_connection_reset_set) {
if (local_port_number != 0) halt_on_connection_reset=false;
else halt_on_connection_reset=true;
}
}
all_mandatory_configparameters_present();
char remotePort[6];
char localPort[6];
sprintf(localPort, "%u", local_port_number);
sprintf(remotePort, "%u", remote_port_number);
if(!use_connection_ASPs)
{
if(server_mode) {
//open_listen_port(localAddr);
open_listen_port(local_host_name,(char*)&localPort);
} else {
//open_client_connection(remoteAddr, localAddr);
open_client_connection(remote_host_name,(char*)&remotePort,local_host_name,(char*)&localPort);
}
}
log_debug("leaving Abstract_Socket::map_user()");
}
int Abstract_Socket::open_listen_port(const struct sockaddr_in & new_local_addr)
{
#ifndef WIN32
log_debug("**** DEPRECATED FUNCTION CALLED: Abstract_Socket::open_listen_port(const struct sockaddr_in & new_local_addr)."
" USE Abstract_Socket::open_listen_port(const char* localHostname, const char* localServicename) INSTEAD! ****");
#endif
log_debug("Local address: %s:%d", inet_ntoa(new_local_addr.sin_addr), ntohs(new_local_addr.sin_port));
close_listen_port();
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if(listen_fd<0) {
if(use_connection_ASPs)
{
log_warning("Cannot open socket when trying to open the listen port: %s", strerror(errno));
listen_port_opened(-1);
errno = 0;
return -1;
}
else log_error("Cannot open socket");
}
if(!nagling) {
int on = 1;
setsockopt(listen_fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(on));
}
int val = 1;
if(setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&val, sizeof(val)) < 0) {
close(listen_fd);
listen_fd = -1;
if(use_connection_ASPs)
{
log_warning("Setsockopt failed when trying to open the listen port: %s", strerror(errno));
listen_port_opened(-1);
errno = 0;
return -1;
}
else log_error("Setsockopt failed");
}
int rc = 0;
log_debug("Bind to port...");
rc = bind(listen_fd, (const struct sockaddr *)&new_local_addr, sizeof(new_local_addr));
if(rc<0) {
close(listen_fd);
listen_fd = -1;
if(use_connection_ASPs)
{
log_warning("Cannot bind to port when trying to open the listen port: %s", strerror(errno));
listen_port_opened(-1);
errno = 0;
return -1;
}
else log_error("Cannot bind to port");
}
log_debug("Bind successful on server.");
rc = listen(listen_fd, server_backlog);
if(rc<0) {
close(listen_fd);
listen_fd = -1;
if(use_connection_ASPs)
{
log_warning("Cannot listen at port when trying to open the listen port: %s", strerror(errno));
listen_port_opened(-1);
errno = 0;
return -1;
}
else log_error("Cannot listen at port");
}
// to avoid dead-locks and make possible
// handling of multiple clients "accept" is placed in the Event_Handler
#if defined LINUX || defined FREEBSD || defined SOLARIS8
socklen_t
#else /* SOLARIS or WIN32 */
int
#endif
addr_len = sizeof(new_local_addr);
if (getsockname(listen_fd, (struct sockaddr*)&new_local_addr, &addr_len)) {
close(listen_fd);
listen_fd = -1;
if(use_connection_ASPs)
{
log_warning("getsockname() system call failed on the server socket when trying to open the listen port: %s", strerror(errno));
listen_port_opened(-1);
errno = 0;
return -1;
}
else log_error("getsockname() system call failed on the server socket");
}
log_debug("Listen successful on server port %d", ntohs(new_local_addr.sin_port));
Add_Fd_Read_Handler(listen_fd); // Done here - after all error checks: as closed fd should not be left added
log_debug("Abstract_Socket::open_listen_port(): Handler set to socket fd %d", listen_fd);
//localAddr = new_local_addr;
if(use_connection_ASPs)
listen_port_opened(ntohs(new_local_addr.sin_port));
return new_local_addr.sin_port;
}
int Abstract_Socket::open_listen_port(const char* localHostname, const char* localServicename) {
log_debug("Local address: %s/%s", (localHostname!=NULL)?localHostname:"UNSPEC",(localServicename!=NULL)?localServicename:"UNSPEC");
#ifdef WIN32
struct sockaddr_in new_local_addr;
memset(&new_local_addr, 0, sizeof(new_local_addr));
if(localHostname!=NULL){
get_host_id(localHostname,&new_local_addr);
}
if(localServicename!=NULL){
new_local_addr.sin_port=htons(atoi(localServicename));
}
return open_listen_port(new_local_addr);
#else
close_listen_port();
struct addrinfo *aip;
struct addrinfo hints;
int sock_opt;
int error;
/* Set up a socket to listen for connections. */
bzero(&hints, sizeof (hints));
hints.ai_flags = /*AI_ALL|*/AI_ADDRCONFIG|AI_PASSIVE;
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = ai_family;
error = getaddrinfo(localHostname, localServicename, &hints, &aip);
if (error != 0) {
if(use_connection_ASPs)
{
log_warning("getaddrinfo: %s for host %s service %s", gai_strerror(error),
(localHostname!=NULL)?localHostname:"UNSPEC",(localServicename!=NULL)?localServicename:"UNSPEC");
listen_port_opened(-1);
return -1;
}
else log_error("getaddrinfo: %s for host %s service %s", gai_strerror(error),
(localHostname!=NULL)?localHostname:"UNSPEC",(localServicename!=NULL)?localServicename:"UNSPEC");
}
struct addrinfo *res;
if (socket_debugging) {
/* count the returned addresses: */
int counter = 0;
for (res = aip; res != NULL; res = res->ai_next,++counter) {};
log_debug("Number of local addresses: %d\n", counter);
}
for (res = aip; res != NULL; res = res->ai_next) {
listen_fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
log_debug("Waiting for connection via: %s\n",
((res->ai_family==AF_INET)?"IPv4":
((res->ai_family==AF_INET6)?"IPv6":"unknown")));
if (listen_fd == -1) {
if(use_connection_ASPs)
{
log_warning("Cannot open socket when trying to open the listen port: %s", strerror(errno));
listen_port_opened(-1);
errno = 0;
freeaddrinfo(aip);
return -1;
}
else log_error("Cannot open socket");
}
/* Tell the system to allow local addresses to be reused. */
sock_opt = 1;
if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt,
sizeof (sock_opt)) == -1) {
close(listen_fd);
listen_fd = -1;
if(use_connection_ASPs)
{
log_warning("Setsockopt failed when trying to open the listen port: %s", strerror(errno));
listen_port_opened(-1);
errno = 0;
freeaddrinfo(aip);
return -1;
}
else log_error("Setsockopt failed");
}
if(!nagling) {
int on = 1;
setsockopt(listen_fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(on));
}
log_debug("Bind to port...");
if (bind(listen_fd, res->ai_addr, res->ai_addrlen) == -1) {
error = errno; // save it for the warning message
close(listen_fd);
listen_fd = -1;
log_debug("Cannot bind to port when trying to open the listen port: %s", strerror(errno));
errno = 0;
continue;
}
log_debug("Bind successful on server.");
break;
}
if (res==NULL) {
if(use_connection_ASPs)
{
log_warning("Cannot bind to port when trying to open the listen port: %s", strerror(error));
listen_port_opened(-1);
error = 0;
freeaddrinfo(aip);
return -1;
}
else log_error("Cannot bind to port");
}
if (listen(listen_fd, server_backlog) == -1) {
close(listen_fd);
listen_fd = -1;
if(use_connection_ASPs)
{
log_warning("Cannot listen at port when trying to open the listen port: %s", strerror(errno));
listen_port_opened(-1);
errno = 0;
freeaddrinfo(aip);
return -1;
}
else log_error("Cannot listen at port");
}
// to avoid dead-locks and make possible
// handling of multiple clients "accept" is placed in Handle_Socket_Event
// to determine the local address:
if (getsockname(listen_fd, res->ai_addr, &res->ai_addrlen)) {
close(listen_fd);
listen_fd = -1;
if(use_connection_ASPs)
{
log_warning("getsockname() system call failed on the server socket when trying to open the listen port: %s", strerror(errno));
listen_port_opened(-1);
errno = 0;
freeaddrinfo(aip);
return -1;
}
else log_error("getsockname() system call failed on the server socket");
}
char hname[NI_MAXHOST];
char sname[NI_MAXSERV];
/* error = getnameinfo(res->ai_addr, res->ai_addrlen,
hname, sizeof (hname), sname, sizeof (sname), NI_NUMERICSERV);
if (error) {
close(listen_fd);
listen_fd = -1;
if(use_connection_ASPs)
{
log_warning("getnameinfo() system call failed on the server socket when trying to open the listen port: %s", gai_strerror(error));
listen_port_opened(-1);
freeaddrinfo(aip);
return -1;
}
else log_error("getsockname() system call failed on the server socket");
} else {
log_debug("Listening on (name): %s/%s\n",
hname, sname);
}*/
error = getnameinfo(res->ai_addr, res->ai_addrlen,
hname, sizeof (hname), sname, sizeof (sname), NI_NUMERICHOST|NI_NUMERICSERV);
if (error) {
close(listen_fd);
listen_fd = -1;
if(use_connection_ASPs)
{
log_warning("getnameinfo() system call failed on the server socket when trying to open the listen port: %s", gai_strerror(error));
listen_port_opened(-1);
freeaddrinfo(aip);
return -1;
}
else log_error("getsockname() system call failed on the server socket");
} else {
log_debug("Listening on (addr): %s/%s\n",
hname, sname);
}
Add_Fd_Read_Handler(listen_fd); // Done here - after all error checks: as closed fd should not be left added
log_debug("Abstract_Socket::open_listen_port(): Handler set to socket fd %d", listen_fd);
log_debug("new_local_addr Addr family: %s\n",
((res->ai_addr->sa_family==AF_INET)?"IPv4":
((res->ai_addr->sa_family==AF_INET6)?"IPv6":"unknown"))
);
int listenPort = atoi(sname);
if(use_connection_ASPs)
listen_port_opened(listenPort);
freeaddrinfo(aip);
return listenPort;
#endif
}
void Abstract_Socket::listen_port_opened(int /*port_number*/)
{
// Intentionally blank
}
void Abstract_Socket::close_listen_port()
{
// close current listening port if it is alive
if(listen_fd != -1)
{
Remove_Fd_Read_Handler(listen_fd);
close(listen_fd);
log_debug("Closed listening port of fd: %d", listen_fd);
listen_fd = -1;
}
}
int Abstract_Socket::get_socket_fd() const{
if(server_mode) return listen_fd;
if(peer_list_get_nr_of_peers()==0) return -1;
return peer_list_get_first_peer();
}
int Abstract_Socket::open_client_connection(const struct sockaddr_in & new_remote_addr, const struct sockaddr_in & new_local_addr)
{
#ifdef WIN32
log_debug("**** DEPRECATED FUNCTION CALLED: Abstract_Socket::open_client_connection(const struct sockaddr_in & new_remote_addr, const struct sockaddr_in & new_local_addr)."
" USE open_client_connection(const char* remoteHostname, const char* remoteServicename, const char* localHostname, const char* localServicename) INSTEAD! ****");
#endif
log_debug("Remote address: %s:%d", inet_ntoa(new_remote_addr.sin_addr), ntohs(new_remote_addr.sin_port));
int deadlock_counter = AS_DEADLOCK_COUNTER;
int TCP_reconnect_counter = TCP_reconnect_attempts;
// workaround for the 'address already used' bug
// used also when TCP reconnect is used
as_start_connecting:
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if(socket_fd<0) {
if(use_connection_ASPs)
{
log_warning("Cannot open socket when trying to open client connection: %s", strerror(errno));
client_connection_opened(-1);
errno = 0;
return -1;
}
else log_error("Cannot open socket.");
}
if(!nagling) {
int on = 1;
setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(on));
}
int rc;
// when using client mode there is no separate file_desriptor for listening and target
log_debug("Connecting to server from address %s:%d", inet_ntoa(new_local_addr.sin_addr), ntohs(new_local_addr.sin_port));
if (new_local_addr.sin_port != ntohs(0)) { // specific port to use
int val = 1;
if(setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&val, sizeof(val)) < 0) {
if(use_connection_ASPs)
{
log_warning("Setsockopt failed when trying to open client connection: %s", strerror(errno));
client_connection_opened(-1);
errno = 0;
return -1;
}
else log_error("Setsockopt failed.");
}
rc = bind(socket_fd, (const struct sockaddr *)&new_local_addr, sizeof(new_local_addr));
if(rc<0) {
if(use_connection_ASPs)
{
log_warning("Cannot bind to port when trying to open client connection: %s", strerror(errno));
client_connection_opened(-1);
errno = 0;
return -1;
}
else log_error("Cannot bind to port.");
}
log_debug("Bind successful on client.");
}
rc = connect(socket_fd, (const struct sockaddr *)&new_remote_addr, sizeof(new_remote_addr));
if(rc<0){
if (errno == EADDRINUSE) {
log_warning("connect() returned error code EADDRINUSE. Perhaps this is a kernel bug. Trying to connect again.");
close(socket_fd);
errno = 0;
deadlock_counter--;
if (deadlock_counter<0) {
if(use_connection_ASPs)
{
log_warning("Already tried %d times, giving up when trying to open client connection: %s", AS_DEADLOCK_COUNTER, strerror(errno));
client_connection_opened(-1);
errno = 0;
return -1;
}
else log_error("Already tried %d times, giving up", AS_DEADLOCK_COUNTER);
}
goto as_start_connecting;
} else if (client_TCP_reconnect && errno != 0) {
log_warning("connect() returned error code %d, trying to connect again (TCP reconnect mode).", errno);
close(socket_fd);
errno = 0;
TCP_reconnect_counter--;
if (TCP_reconnect_counter<0) {
if(use_connection_ASPs)
{
log_warning("Already tried %d times, giving up when trying to open client connection: %s", TCP_reconnect_attempts, strerror(errno));
client_connection_opened(-1);
errno = 0;
return -1;
}
else log_error("Already tried %d times, giving up", TCP_reconnect_attempts);
}
sleep(TCP_reconnect_delay);
goto as_start_connecting;
}
if(use_connection_ASPs)
{
log_warning("Cannot connect to server when trying to open client connection: %s", strerror(errno));
client_connection_opened(-1);
errno = 0;
return -1;
}
else log_error("Cannot connect to server");
}
// Non-blocking mode is set before updating bookkeping to handle the error case properly.
if (set_non_block_mode(socket_fd, use_non_blocking_socket) < 0){
close(socket_fd);
if (use_connection_ASPs){
client_connection_opened(-1);
errno = 0;
return -1;
}
else log_error("Set blocking mode failed.");
}
as_client_struct * client_data=peer_list_add_peer(socket_fd);
Add_Fd_Read_Handler(socket_fd); // Done here - as in case of error: remove_client expects the handler as added
log_debug("Abstract_Socket::open_client_connection(). Handler set to socket fd %d", socket_fd);
client_data->fd_buff = new TTCN_Buffer;
// client_data->clientAddr = *(struct sockaddr_storage*)&new_remote_addr;
memset(&client_data->clientAddr,0,sizeof(client_data->clientAddr));
memcpy(&client_data->clientAddr,&new_remote_addr,sizeof(new_remote_addr));
client_data->clientAddrlen = sizeof(new_remote_addr);
client_data->tcp_state = ESTABLISHED;
client_data->reading_state = STATE_NORMAL;
if (!add_user_data(socket_fd)) {
remove_client(socket_fd);
peer_disconnected(socket_fd);
return -1;
}
// localAddr = new_local_addr;
// remoteAddr = new_remote_addr;
client_connection_opened(socket_fd);
return socket_fd;
}
int Abstract_Socket::open_client_connection(const char* remoteHostname, const char* remoteServicename, const char* localHostname, const char* localServicename) {
log_debug("Abstract_Socket::open_client_connection(remoteAddr: %s/%s, localAddr: %s/%s) called",
remoteHostname,remoteServicename,
(localHostname!=NULL)?localHostname:"UNSPEC",(localServicename!=NULL)?localServicename:"UNSPEC");
#ifdef WIN32
struct sockaddr_in new_local_addr;
struct sockaddr_in new_remote_addr;
memset(&new_local_addr, 0, sizeof(new_local_addr));
memset(&new_local_addr, 0, sizeof(new_remote_addr));
if(localHostname!=NULL){
get_host_id(localHostname,&new_local_addr);
}
if(localServicename!=NULL){
new_local_addr.sin_port=htons(atoi(localServicename));
}
if(remoteHostname!=NULL){
get_host_id(remoteHostname,&new_remote_addr);
}
if(remoteServicename!=NULL){
new_remote_addr.sin_port=htons(atoi(remoteServicename));
}
return open_client_connection(new_remote_addr,new_local_addr);
#else
int deadlock_counter = AS_DEADLOCK_COUNTER;
int TCP_reconnect_counter = TCP_reconnect_attempts;
struct addrinfo *res, *aip;
struct addrinfo hints;
int socket_fd = -1;
int error;
/* Get host address. Any type of address will do. */
bzero(&hints, sizeof (hints));
hints.ai_flags = AI_ADDRCONFIG; /* |AI_ALL*/
if (localHostname!=NULL || localServicename!=NULL) { /* use specific local address */
hints.ai_flags |= AI_PASSIVE;
}
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = ai_family;
error = getaddrinfo(remoteHostname, remoteServicename, &hints, &res);
if (error != 0) {
if(use_connection_ASPs)
{
log_warning("getaddrinfo: %s for host %s service %s",
gai_strerror(error), remoteHostname, remoteServicename);
client_connection_opened(-1);
return -1;
}
else { log_error("getaddrinfo: %s for host %s service %s",
gai_strerror(error), remoteHostname, remoteServicename);
}
}
if (socket_debugging) {
/* count the returned addresses: */
int counter = 0;
for (aip = res; aip != NULL; aip = aip->ai_next,++counter) {};
log_debug("Number of remote addresses: %d\n", counter);
}
// workaround for the 'address already used' bug
// used also when TCP reconnect is used
as_start_connecting:
/* Try all returned addresses until one works */
for (aip = res; aip != NULL; aip = aip->ai_next) {
/*
* Open socket. The address type depends on what
* getaddrinfo() gave us.
*/
socket_fd = socket(aip->ai_family, aip->ai_socktype,
aip->ai_protocol);
if (socket_fd == -1) {
if(use_connection_ASPs)
{
log_warning("Cannot open socket when trying to open client connection: %s", strerror(errno));
client_connection_opened(-1);
freeaddrinfo(res);
return -1;
}
else {
freeaddrinfo(res);
log_error("Cannot open socket.");
}
}
log_debug("Using address family for socket %d: %s",socket_fd,
((aip->ai_family==AF_INET)?"IPv4":
((aip->ai_family==AF_INET6)?"IPv6":"unknown"))
);
if(!nagling) {
int on = 1;
setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(on));
}
// when using client mode there is no separate file_descriptor for listening and target
log_debug("Connecting to server from address %s/%s",
(localHostname!=NULL)?localHostname:"UNSPEC",(localServicename!=NULL)?localServicename:"UNSPEC");
if (localHostname!=NULL || localServicename!=NULL) { // specific localaddress/port to use
int val = 1;
if(setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&val, sizeof(val)) < 0) {
if(use_connection_ASPs)
{
log_warning("Setsockopt failed when trying to open client connection: %s", strerror(errno));
client_connection_opened(-1);
errno = 0;
return -1;
}
else log_error("Setsockopt failed.");
}
// determine the local address:
struct addrinfo *localAddrinfo;
/* Get host address. Any type of address will do. */
bzero(&hints, sizeof (hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = ai_family;//aip->ai_family; // NOTE: On solaris 10 if is set to aip->ai_family, getaddrinfo will crash for IPv4-mapped addresses!
error = getaddrinfo(localHostname, localServicename, &hints, &localAddrinfo);
if (error != 0) {
if(use_connection_ASPs)
{
log_warning("getaddrinfo: %s for host %s service %s",
gai_strerror(error), (localHostname!=NULL)?localHostname:"UNSPEC",(localServicename!=NULL)?localServicename:"UNSPEC");
client_connection_opened(-1);
return -1;
}
else { log_error("getaddrinfo: %s for host %s service %s",
gai_strerror(error), (localHostname!=NULL)?localHostname:"UNSPEC",(localServicename!=NULL)?localServicename:"UNSPEC");
}
}
if (socket_debugging) {
/* count the returned addresses: */
int counter = 0;
for (struct addrinfo* aip2 = localAddrinfo; aip2 != NULL; aip2 = aip2->ai_next,++counter) {};
log_debug("Number of local addresses: %d\n", counter);
}
/* Try all returned addresses until one works */
struct addrinfo* aip2;
for (aip2 = localAddrinfo; aip2 != NULL; aip2 = aip2->ai_next) {
log_debug("Using address family for bind: %s",
((aip2->ai_family==AF_INET)?"IPv4":
((aip2->ai_family==AF_INET6)?"IPv6":"unknown"))
);
if(bind(socket_fd, aip2->ai_addr, aip2->ai_addrlen)<0) {
/* if(use_connection_ASPs) // the if else branches are the same
{*/
log_debug("Cannot bind to port when trying to open client connection: %s", strerror(errno));
//client_connection_opened(-1);
//freeaddrinfo(localAddrinfo);
errno = 0;
continue; //aip2 cycle
//return -1;
/* }
else {
//freeaddrinfo(localAddrinfo);
//log_error("Cannot bind to port.");
log_debug("Cannot bind to port when trying to open client connection: %s", strerror(errno));
errno = 0;
continue; //aip2 cycle
}*/
}
log_debug("Bind successful on client.");
freeaddrinfo(localAddrinfo);
break;
}
if (aip2==NULL) {
log_debug("Bind failed for all local addresses.");
freeaddrinfo(localAddrinfo);
continue; // aip cycle
}
}
/* Connect to the host. */
if (connect(socket_fd, aip->ai_addr, aip->ai_addrlen) == -1) {
if (errno == EADDRINUSE) {
log_warning("connect() returned error code EADDRINUSE. Perhaps this is a kernel bug. Trying to connect again.");
close(socket_fd);
socket_fd = -1;
errno = 0;
deadlock_counter--;
if (deadlock_counter<0) {
if(use_connection_ASPs)
{
log_warning("Already tried %d times, giving up when trying to open client connection: %s", AS_DEADLOCK_COUNTER, strerror(errno));
client_connection_opened(-1);
errno = 0;
return -1;
}
else log_error("Already tried %d times, giving up", AS_DEADLOCK_COUNTER);
}
goto as_start_connecting;
} else if (client_TCP_reconnect && errno != 0) {
log_warning("connect() returned error code %d (%s), trying to connect again (TCP reconnect mode).", errno, strerror(errno));
close(socket_fd);
socket_fd = -1;
errno = 0;
if (aip->ai_next==NULL) { /* Last address is tried and there is still an error */
TCP_reconnect_counter--;
if (TCP_reconnect_counter<0) {
if(use_connection_ASPs)
{
log_warning("Already tried %d times, giving up when trying to open client connection: %s", TCP_reconnect_attempts, strerror(errno));
client_connection_opened(-1);
errno = 0;
return -1;
}
else { log_error("Already tried %d times, giving up", TCP_reconnect_attempts); }
}
}
sleep(TCP_reconnect_delay);
goto as_start_connecting;
} else {
log_debug("Cannot connect to server: %s", strerror(errno));
(void) close(socket_fd);
socket_fd = -1;
}
if (aip->ai_next==NULL) {
if(use_connection_ASPs)
{
log_warning("Cannot connect to server when trying to open client connection: %s", strerror(errno));
client_connection_opened(-1);
errno = 0;
return -1;
}
else log_error("Cannot connect to server");
}
continue; //aip cycle
}
// to determine the local address:
if (getsockname(socket_fd, aip->ai_addr, &aip->ai_addrlen)) {
close(socket_fd);
if(use_connection_ASPs) {
log_warning("getsockname() system call failed on the client socket when trying to connect to server: %s", strerror(errno));
client_connection_opened(-1);
errno = 0;
return -1;
}
else log_error("getsockname() system call failed on the client socket when trying to connect to server: %s", strerror(errno));
}
char hname[NI_MAXHOST];
char sname[NI_MAXSERV];
/* error = getnameinfo(aip->ai_addr, aip->ai_addrlen,
hname, sizeof (hname), sname, sizeof (sname), NI_NUMERICSERV);
if (error) {
close(socket_fd);
if(use_connection_ASPs)
{
log_warning("getnameinfo() system call failed on the client socket when trying to connect to server: %s", gai_strerror(error));
client_connection_opened(-1);
return -1;
}
else log_error("getnameinfo() system call failed on the client socket when trying to connect to server: %s", gai_strerror(error));
} else {
log_debug("Connection established (name): %s/%s -> %s/%s\n",
hname, sname,
remoteHostname, remoteServicename);
}*/
error = getnameinfo(aip->ai_addr, aip->ai_addrlen,
hname, sizeof (hname), sname, sizeof (sname), NI_NUMERICHOST|NI_NUMERICSERV);
if (error) {
/* close(socket_fd);
if(use_connection_ASPs)
{
log_warning("getnameinfo() system call failed on the client socket when trying to connect to server: %s", gai_strerror(error));
// client_connection_opened(-1);
// return -1;
}
else*/
log_warning("getnameinfo() system call failed on the client socket when trying to connect to server: %s", gai_strerror(error));
} else {
log_debug("Connection established (addr): %s/%s -> %s/%s\n",
hname, sname,
remoteHostname, remoteServicename);
}
log_debug(
"connected to: host %s service %s via address family %s\n",
remoteHostname, remoteServicename,
((aip->ai_family==AF_INET)?"IPv4":
((aip->ai_family==AF_INET6)?"IPv6":"unknown")));
break;
}
if (aip==NULL) {
if(use_connection_ASPs)
{
log_warning("Cannot connect to server");
client_connection_opened(-1);
freeaddrinfo(res);
return -1;
}
else log_error("Cannot connect to server");
}
// Non-blocking mode is set before updating bookkeping to handle the error case properly.
if (set_non_block_mode(socket_fd, use_non_blocking_socket) < 0) {
freeaddrinfo(res);
close(socket_fd);
if (use_connection_ASPs){
log_warning("Set blocking mode failed.");
client_connection_opened(-1);
errno = 0;
return -1;
}
else log_error("Set blocking mode failed.");
}
as_client_struct * client_data=peer_list_add_peer(socket_fd);
Add_Fd_Read_Handler(socket_fd); // Done here - as in case of error: remove_client expects the handler as added
log_debug("Abstract_Socket::open_client_connection(). Handler set to socket fd %d", socket_fd);
client_data->fd_buff = new TTCN_Buffer;
// client_data->clientAddr = *(struct sockaddr_storage*)aip->ai_addr;
memset(&client_data->clientAddr,0,sizeof(client_data->clientAddr));
memcpy(&client_data->clientAddr,aip->ai_addr,sizeof(*aip->ai_addr));
client_data->clientAddrlen = aip->ai_addrlen;
client_data->tcp_state = ESTABLISHED;
client_data->reading_state = STATE_NORMAL;
freeaddrinfo(res);
if (!add_user_data(socket_fd)) {
remove_client(socket_fd);
peer_disconnected(socket_fd);
return -1;
}
client_connection_opened(socket_fd);
return socket_fd;
#endif
}
void Abstract_Socket::client_connection_opened(int /*client_id*/)
{
// Intentionally blank
}
void Abstract_Socket::unmap_user()
{
log_debug("entering Abstract_Socket::unmap_user()");
remove_all_clients();
close_listen_port();
Handler_Uninstall(); // For robustness only
log_debug("leaving Abstract_Socket::unmap_user()");
}
void Abstract_Socket::peer_disconnected(int /*fd*/)
{
// virtual peer_disconnected() needs to be overriden in test ports!
if(!use_connection_ASPs) {
if (halt_on_connection_reset)
log_error("Connection was interrupted by the other side.");
if (client_TCP_reconnect){
log_warning("TCP connection was interrupted by the other side, trying to reconnect again...");
unmap_user();
map_user();
log_warning("TCP reconnect successfuly finished");
}
}
}
void Abstract_Socket::peer_half_closed(int fd)
{
log_debug("Entering Abstract_Socket::peer_half_closed()");
remove_client(fd);
peer_disconnected(fd);
log_debug("Leaving Abstract_Socket::peer_half_closed()");
}
void Abstract_Socket::send_shutdown(int client_id)
{
log_debug("entering Abstract_Socket::send_shutdown()");
int dest_fd = client_id;
if (dest_fd == -1) {
if(peer_list_get_nr_of_peers() > 1)
log_error("Client Id not specified altough not only 1 client exists");
else if(peer_list_get_nr_of_peers() == 0)
log_error("There is no connection alive, connect before sending anything.");
dest_fd = peer_list_get_first_peer();
}
as_client_struct * client_data = get_peer(dest_fd);
if(client_data->tcp_state != ESTABLISHED)
log_error("TCP state of client nr %i does not allow to shut down its connection for writing!", dest_fd);
if(shutdown(dest_fd, SHUT_WR) != 0)
{
if(errno == ENOTCONN)
{
remove_client(dest_fd);
peer_disconnected(dest_fd);
errno = 0;
}
else
log_error("shutdown() system call failed");
}
else client_data->tcp_state = FIN_WAIT;
// dest_fd is not removed from readfds, data can be received
log_debug("leaving Abstract_Socket::send_shutdown()");
}
void Abstract_Socket::send_outgoing(const unsigned char* send_par, int length, int client_id)
{
log_debug("entering Abstract_Socket::send_outgoing()");
log_hex("Sending data: ", send_par, length);
int dest_fd;
int nrOfBytesSent;
dest_fd = client_id;
if (dest_fd == -1) {
if(peer_list_get_nr_of_peers() > 1)
log_error("Client Id not specified altough not only 1 client exists");
else if(peer_list_get_nr_of_peers() == 0)
log_error("There is no connection alive, use a Connect ASP before sending anything.");
dest_fd = peer_list_get_first_peer();
}
as_client_struct * client_data = get_peer(dest_fd,true);
if(!client_data || ((client_data->tcp_state != ESTABLISHED) && (client_data->tcp_state != CLOSE_WAIT))){
char *error_text=mprintf("client nr %i has no established connection", dest_fd);
report_error(client_id,length,-2,send_par,error_text);
Free(error_text);
log_debug("leaving Abstract_Socket::send_outgoing()");
return;
}
nrOfBytesSent = use_non_blocking_socket ? send_message_on_nonblocking_fd(dest_fd, send_par, length) :
send_message_on_fd(dest_fd, send_par, length);
if (nrOfBytesSent == -1){
log_debug("Client %d closed connection. Error: %d %s", client_id, errno, strerror(errno));
report_unsent(dest_fd,length,nrOfBytesSent,send_par,"Client closed the connection");
if(client_data->tcp_state == CLOSE_WAIT){
log_debug("Client %d waiting for close ASP.", client_id);
} else {
errno = 0;
log_debug("Client %d closed connection", client_id);
client_data->tcp_state = CLOSE_WAIT;
Remove_Fd_Read_Handler(dest_fd);
peer_half_closed(dest_fd);
}
}else if (nrOfBytesSent != length) {
char *error_text=mprintf("Send system call failed: %d bytes were sent instead of %d", nrOfBytesSent, length);
report_error(client_id,length,nrOfBytesSent,send_par,error_text);
Free(error_text);
} else {
log_debug("Nr of bytes sent = %d", nrOfBytesSent);
}
log_debug("leaving Abstract_Socket::send_outgoing()");
}
void Abstract_Socket::report_error(int /*client_id*/, int /*msg_length*/, int /*sent_length*/, const unsigned char* /*msg*/, const char* error_text)
{
log_error("%s",error_text);
}
void Abstract_Socket::report_unsent(int /*client_id*/, int /*msg_length*/, int /*sent_length*/, const unsigned char* /*msg*/, const char* error_text)
{
log_debug("%s",error_text);
}
void Abstract_Socket::all_mandatory_configparameters_present()
{
if(!use_connection_ASPs)
{
if(server_mode) {
if(local_port_number == 0) {
log_error("%s is not defined in the configuration file", local_port_name());
}
}
else { // client mode
if (remote_host_name == NULL) {
log_error("%s is not defined in the configuration file", remote_address_name());
}
if(remote_port_number == 0){
log_error("%s is not defined in the configuration file", remote_port_name());
}
}
}
user_all_mandatory_configparameters_present();
}
void Abstract_Socket::get_host_id(const char* hostName, struct sockaddr_in *addr)
{
log_debug("Abstract_Socket::get_host_id called");
unsigned int port = addr->sin_port;
memset(addr, 0, sizeof(*addr));
addr->sin_family = AF_INET;
addr->sin_port = port;
struct hostent *hptr;
if(strcmp("localhost", hostName) != 0)
{
hptr = gethostbyname(hostName);
if (hptr != NULL) memcpy(&addr->sin_addr, hptr->h_addr_list[0], hptr->h_length);
else log_error("The host name %s is not valid in the configuration file.", hostName);
log_debug("The address set to %s[%s]", hptr->h_name, inet_ntoa(addr->sin_addr));
}
else
{
addr->sin_addr.s_addr = htonl(INADDR_ANY);
log_debug("The address set to [%s]", inet_ntoa(addr->sin_addr));
}
}
void Abstract_Socket::remove_client(int fd)
{
log_debug("entering Abstract_Socket::remove_client(%d)", fd);
if(fd != listen_fd) {
get_peer(fd); // check if client exists, log_error && fail if not
// TODO FIXME: remove the Add_Fd_Read_Handler(fd); if TITAN is fixed
Add_Fd_Read_Handler(fd);
Remove_Fd_All_Handlers(fd);
remove_user_data(fd);
delete get_peer(fd)->fd_buff;
peer_list_remove_peer(fd);
close(fd);
log_debug("Removed client %d.", fd);
}
else log_warning("Abstract_Socket::remove_client: %d is the server listening port, can not be removed!", fd);
log_debug("leaving Abstract_Socket::remove_client(%d)", fd);
}
void Abstract_Socket::remove_all_clients()
{
log_debug("entering Abstract_Socket::remove_all_clients");
for(int i = 0; peer_list_root != NULL && i < peer_list_length; i++)
{
if(i != listen_fd && peer_list_root[i] != NULL)
remove_client(i);
}
// check if no stucked data
while (peer_list_get_nr_of_peers()) {
int client_id = peer_list_get_first_peer();
if (client_id >= 0) log_warning("Client %d has not been removed, programming error", client_id);
else log_error("Number of clients<>0 but cannot get first client, programming error");
peer_list_remove_peer(client_id);
}
log_debug("leaving Abstract_Socket::remove_all_clients");
}
int Abstract_Socket::set_non_block_mode(int fd, bool enable_nonblock){
int flags = fcntl(fd, F_GETFL);
if (flags < 0) {
log_warning("System call fcntl(F_GETFL) failed on file "
"descriptor %d.", fd);
return -1;
}
if (enable_nonblock) flags |= O_NONBLOCK;
else flags &= ~O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1) {
log_warning("System call fcntl(F_SETFL) failed on file "
"descriptor %d.", fd);
return -1;
}
return 0;
}
bool Abstract_Socket::increase_send_buffer(int fd,
int &old_size, int& new_size)
{
int set_size;
#if defined LINUX || defined FREEBSD || defined SOLARIS8
socklen_t
#else /* SOLARIS or WIN32 */
int
#endif
optlen = sizeof(old_size);
// obtaining the current buffer size first
if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char*)&old_size, &optlen))
goto getsockopt_failure;
if (old_size <= 0) {
log_warning("System call getsockopt(SO_SNDBUF) "
"returned invalid buffer size (%d) on file descriptor %d.",
old_size, fd);
return false;
}
// trying to double the buffer size
set_size = 2 * old_size;
if (set_size > old_size) {
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const char*)&set_size,
sizeof(set_size))) {
// the operation failed
switch (errno) {
case ENOMEM:
case ENOBUFS:
errno = 0;
break;
default:
// other error codes indicate a fatal error
goto setsockopt_failure;
}
} else {
// the operation was successful
goto success;
}
}
// trying to perform a binary search to determine the maximum buffer size
set_size = old_size;
for (int size_step = old_size / 2; size_step > 0; size_step /= 2) {
int tried_size = set_size + size_step;
if (tried_size > set_size) {
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const char*)&tried_size,
sizeof(tried_size))) {
// the operation failed
switch (errno) {
case ENOMEM:
case ENOBUFS:
errno = 0;
break;
default:
// other error codes indicate a fatal error
goto setsockopt_failure;
}
} else {
// the operation was successful
set_size = tried_size;
}
}
}
if (set_size <= old_size) return false;
success:
// querying the new effective buffer size (it might be smaller
// than set_size but should not be smaller than old_size)
optlen = sizeof(new_size);
if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char*)&new_size,
&optlen)) goto getsockopt_failure;
if (new_size > old_size) return true;
else {
if (new_size < old_size)
log_warning("System call getsockopt(SO_SNDBUF) returned unexpected buffer size "
"(%d, after increasing it from %d to %d) on file descriptor %d.",
new_size, old_size, set_size, fd);
return false;
}
getsockopt_failure:
log_warning("System call getsockopt(SO_SNDBUF) failed on file "
"descriptor %d. (%s)", fd, strerror(errno));
return false;
setsockopt_failure:
log_warning("System call setsockopt(SO_SNDBUF) failed on file "
"descriptor %d. (%s)", fd, strerror(errno));
return false;
}
const char* Abstract_Socket::local_port_name() { return "serverPort";}
const char* Abstract_Socket::remote_address_name() { return "destIPAddr";}
const char* Abstract_Socket::local_address_name() { return "serverIPAddr";}
const char* Abstract_Socket::remote_port_name() { return "destPort";}
const char* Abstract_Socket::ai_family_name() { return "ai_family";}
const char* Abstract_Socket::use_connection_ASPs_name() { return "use_connection_ASPs";}
const char* Abstract_Socket::halt_on_connection_reset_name(){ return "halt_on_connection_reset";}
const char* Abstract_Socket::client_TCP_reconnect_name() { return "client_TCP_reconnect";}
const char* Abstract_Socket::TCP_reconnect_attempts_name() { return "TCP_reconnect_attempts";}
const char* Abstract_Socket::TCP_reconnect_delay_name() { return "TCP_reconnect_delay";}
const char* Abstract_Socket::server_mode_name() { return "server_mode";}
const char* Abstract_Socket::socket_debugging_name() { return "socket_debugging";}
const char* Abstract_Socket::nagling_name() { return "nagling";}
const char* Abstract_Socket::use_non_blocking_socket_name() { return "use_non_blocking_socket";}
const char* Abstract_Socket::server_backlog_name() { return "server_backlog";}
bool Abstract_Socket::add_user_data(int) {return true;}
bool Abstract_Socket::remove_user_data(int) {return true;}
bool Abstract_Socket::user_all_mandatory_configparameters_present() { return true; }
////////////////////////////////////////////////////////////////////////
///// Peer handling functions
////////////////////////////////////////////////////////////////////////
void Abstract_Socket::peer_list_reset_peer() {
log_debug("Abstract_Socket::peer_list_reset_peer: Resetting peer array");
for (int i = 0; i < peer_list_length; i++)
if (peer_list_root[i] != NULL) {
delete peer_list_root[i];
peer_list_root[i] = NULL;
}
peer_list_resize_list(-1);
log_debug("Abstract_Socket::peer_list_reset_peer: New length is %d", peer_list_length);
}
void Abstract_Socket::peer_list_resize_list(int client_id) {
int new_length=client_id;
if (new_length<0) new_length = peer_list_get_last_peer();
new_length++; // index starts from 0
log_debug("Abstract_Socket::peer_list_resize_list: Resizing to %d", new_length);
peer_list_root = (as_client_struct **)Realloc(peer_list_root, new_length*sizeof(as_client_struct *));
// initialize new entries
for (int i = peer_list_length; i < new_length; i++)
peer_list_root[i] = NULL;
peer_list_length = new_length;
log_debug("Abstract_Socket::peer_list_resize_list: New length is %d", peer_list_length);
}
int Abstract_Socket::peer_list_get_first_peer() const {
log_debug("Abstract_Socket::peer_list_get_first_peer: Finding first peer of the peer array");
for (int i = 0; i < peer_list_length; i++) {
if (peer_list_root[i] != NULL) {
log_debug("Abstract_Socket::peer_list_get_first_peer: First peer is %d", i);
return i;
}
}
log_debug("Abstract_Socket::peer_list_get_first_peer: No active peer found");
return -1; // this indicates an empty list
}
int Abstract_Socket::peer_list_get_last_peer() const
{
log_debug("Abstract_Socket::peer_list_get_last_peer: Finding last peer of the peer array");
if (peer_list_length==0) {
log_debug("Abstract_Socket::peer_list_get_last_peer: No active peer found");
return -1;
}
for (int i = peer_list_length - 1; i >= 0; i--) {
if (peer_list_root[i] != NULL) {
log_debug("Abstract_Socket::peer_list_get_last_peer: Last peer is %u", i);
return i;
}
}
log_debug("Abstract_Socket::peer_list_get_last_peer: No active peer found");
return -1; // this indicates an empty list
}
int Abstract_Socket::peer_list_get_nr_of_peers() const
{
int nr=0;
for (int i = 0; i < peer_list_length; i++)
if (peer_list_root[i] != NULL) nr++;
log_debug("Abstract_Socket::peer_list_get_nr_of_peers: Number of active peers = %d", nr);
return nr;
}
Abstract_Socket::as_client_struct *Abstract_Socket::get_peer (int client_id, bool no_error) const
{
if (client_id >= peer_list_length){
if(no_error) return NULL;
else log_error ("Index %d exceeds length of peer list.", client_id);
}
if (peer_list_root[client_id]==NULL){
if(no_error) return NULL;
else log_error("Abstract_Socket::get_peer: Client %d does not exist", client_id);
}
return peer_list_root[client_id];
}
Abstract_Socket::as_client_struct * Abstract_Socket::peer_list_add_peer (int client_id) {
log_debug("Abstract_Socket::peer_list_add_peer: Adding client %d to peer list", client_id);
if (client_id<0) log_error("Invalid Client Id is given: %d.", client_id);
if (client_id>peer_list_get_last_peer()) peer_list_resize_list(client_id);
peer_list_root[client_id] = new as_client_struct;
peer_list_root[client_id]->user_data = NULL;
peer_list_root[client_id]->fd_buff = NULL;
peer_list_root[client_id]->tcp_state = CLOSED;
peer_list_root[client_id]->reading_state = STATE_NORMAL;
return peer_list_root[client_id];
}
void Abstract_Socket::peer_list_remove_peer (int client_id) {
log_debug("Abstract_Socket::peer_list_remove_peer: Removing client %d from peer list", client_id);
if (client_id >= peer_list_length || client_id<0) log_error("Invalid Client Id is given: %d.", client_id);
if (peer_list_root[client_id] == NULL) log_error("Peer %d does not exist.", client_id);
delete peer_list_root[client_id];
peer_list_root[client_id] = NULL;
peer_list_resize_list(-1);
}
#ifdef AS_USE_SSL
/*
* Server mode
When the mode is server, first a TCP socket is created. The server starts
to listen on this port. Once a TCP connect request is received, the TCP
connection is setup. After this the SSL handshake begins.
The SSL is mapped to the file descriptor of the TCP socket. The BIO is
automatically created by OpenSSL inheriting the characteristics of the
socket (non-blocking mode). The BIO is completely transparent.
The server always sends its certificate to the client. If configured so,
the server will request the certificate of the client and check if it is
a valid certificate. If not, the SSL connection is refused.
If configured not to verify the certificate, the server will not request
it from the client and the SSL connection is accepted.
If usage of the SSL ssl_session resumption is enabled and
the client refers to a previous ssl_session, the server will accept it,
unless it is not found in the SSL context cache.
Once the connection is negotiated, data can be sent/received.
The SSL connection is shutted down on an unmap() operation. The shutdown
process does not follow the standard. The server simply shuts down and
does not expect any acknowledgement from the client.
Clients connected to the server are distinguished with their file
descriptor numbers. When a message is received, the file descriptor
number is also passed, so the client can be identified.
* Client mode
When the mode is client, first a TCP connection is requested to the
server. Once accepted, the SSL endpoint is created.
If configured so, the client tries to use the ssl_session Id from the
previous connection, if available (e.g. not the first connection).
If no ssl_session Id is available or the server does not accept it,
a full handshake if performed.
If configured so, the certificate of the server is verified.
If the verification fails, the SSL connection is interrupted by the
client. If no verification required, the received certificate is
still verified, however the result does not affect the connection
(might fail).
* ssl_verify_certificates() is a virtual function. It is called after
SSL connection is up. Testports may use it to check other peer's
certificate and do actions. If the return value is 0, then the
SSL connection is closed. In case of a client, the test port
exits with an error (verification_error). The server just removes
client data, but keeps running.
If ssl_verifiycertificate == "yes", then accept connections only
where certificate is valid
Further checks can be done using SSL_Socket::ssl_verify_certificates()
after the SSL connection is established with the following function call
sequence:
<other test port related cleanup>
remove_client(dest_fd);
peer_disconnected(dest_fd);
*/
// ssl_session ID context of the server
static unsigned char ssl_server_context_name[] = "McHalls&EduardWasHere";
const unsigned char * SSL_Socket::ssl_server_auth_session_id_context = ssl_server_context_name;
// Password pointer
void *SSL_Socket::ssl_current_client = NULL;
SSL_Socket::SSL_Socket()
{
ssl_use_ssl=false;
ssl_initialized=false;
ssl_key_file=NULL;
ssl_certificate_file=NULL;
ssl_trustedCAlist_file=NULL;
ssl_cipher_list=NULL;
ssl_verify_certificate=false;
ssl_use_session_resumption=true;
ssl_session=NULL;
ssl_password=NULL;
test_port_type=NULL;
test_port_name=NULL;
ssl_ctx = NULL;
ssl_current_ssl = NULL;
SSLv2=true;
SSLv3=true;
TLSv1=true;
TLSv1_1=true;
TLSv1_2=true;
}
SSL_Socket::SSL_Socket(const char *tp_type, const char *tp_name)
{
ssl_use_ssl=false;
ssl_initialized=false;
ssl_key_file=NULL;
ssl_certificate_file=NULL;
ssl_trustedCAlist_file=NULL;
ssl_cipher_list=NULL;
ssl_verify_certificate=false;
ssl_use_session_resumption=true;
ssl_session=NULL;
ssl_password=NULL;
test_port_type=tp_type;
test_port_name=tp_name;
ssl_ctx = NULL;
ssl_current_ssl = NULL;
SSLv2=true;
SSLv3=true;
TLSv1=true;
TLSv1_1=true;
TLSv1_2=true;
}
SSL_Socket::~SSL_Socket()
{
// now SSL context can be removed
if (ssl_use_ssl && ssl_ctx!=NULL) {
SSL_CTX_free(ssl_ctx);
}
delete [] ssl_key_file;
delete [] ssl_certificate_file;
delete [] ssl_trustedCAlist_file;
delete [] ssl_cipher_list;
delete [] ssl_password;
}
bool SSL_Socket::parameter_set(const char *parameter_name,
const char *parameter_value)
{
log_debug("entering SSL_Socket::parameter_set(%s, %s)", parameter_name, parameter_value);
if(strcmp(parameter_name, ssl_use_ssl_name()) == 0) {
if(strcasecmp(parameter_value, "yes") == 0) ssl_use_ssl = true;
else if(strcasecmp(parameter_value, "no") == 0) ssl_use_ssl = false;
else log_error("Parameter value '%s' not recognized for parameter '%s'", parameter_value, ssl_use_ssl_name());
} else if(strcmp(parameter_name, ssl_use_session_resumption_name()) == 0) {
if(strcasecmp(parameter_value, "yes") == 0) ssl_use_session_resumption = true;
else if(strcasecmp(parameter_value, "no") == 0) ssl_use_session_resumption = false;
else log_error("Parameter value '%s' not recognized for parameter '%s'", parameter_value, ssl_use_session_resumption_name());
} else if(strcmp(parameter_name, ssl_private_key_file_name()) == 0) {
delete [] ssl_key_file;
ssl_key_file=new char[strlen(parameter_value)+1];
strcpy(ssl_key_file, parameter_value);
} else if(strcmp(parameter_name, ssl_trustedCAlist_file_name()) == 0) {
delete [] ssl_trustedCAlist_file;
ssl_trustedCAlist_file=new char[strlen(parameter_value)+1];
strcpy(ssl_trustedCAlist_file, parameter_value);
} else if(strcmp(parameter_name, ssl_certificate_file_name()) == 0) {
delete [] ssl_certificate_file;
ssl_certificate_file=new char[strlen(parameter_value)+1];
strcpy(ssl_certificate_file, parameter_value);
} else if(strcmp(parameter_name, ssl_cipher_list_name()) == 0) {
delete [] ssl_cipher_list;
ssl_cipher_list=new char[strlen(parameter_value)+1];
strcpy(ssl_cipher_list, parameter_value);
} else if(strcmp(parameter_name, ssl_password_name()) == 0) {
ssl_password=new char[strlen(parameter_value)+1];
strcpy(ssl_password, parameter_value);
} else if(strcmp(parameter_name, ssl_verifycertificate_name()) == 0) {
if(strcasecmp(parameter_value, "yes") == 0) ssl_verify_certificate = true;
else if(strcasecmp(parameter_value, "no") == 0) ssl_verify_certificate = false;
else log_error("Parameter value '%s' not recognized for parameter '%s'", parameter_value, ssl_verifycertificate_name());
} else if(strcasecmp(parameter_name, ssl_disable_SSLv2()) == 0) {
if(strcasecmp(parameter_value, "yes") == 0) SSLv2= false;
else if(strcasecmp(parameter_value, "no") == 0) SSLv2 = true;
else log_error("Parameter value '%s' not recognized for parameter '%s'", parameter_value, ssl_disable_SSLv2());
} else if(strcasecmp(parameter_name, ssl_disable_SSLv3()) == 0) {
if(strcasecmp(parameter_value, "yes") == 0) SSLv2 = false;
else if(strcasecmp(parameter_value, "no") == 0) SSLv2 = true;
else log_error("Parameter value '%s' not recognized for parameter '%s'", parameter_value, ssl_disable_SSLv3());
} else if(strcasecmp(parameter_name, ssl_disable_TLSv1()) == 0) {
if(strcasecmp(parameter_value, "yes") == 0) TLSv1= false;
else if(strcasecmp(parameter_value, "no") == 0) TLSv1 = true;
else log_error("Parameter value '%s' not recognized for parameter '%s'", parameter_value, ssl_disable_TLSv1());
} else if(strcasecmp(parameter_name, ssl_disable_TLSv1_1()) == 0) {
if(strcasecmp(parameter_value, "yes") == 0) TLSv1_1 = false;
else if(strcasecmp(parameter_value, "no") == 0) TLSv1_1 = true;
else log_error("Parameter value '%s' not recognized for parameter '%s'", parameter_value, ssl_disable_TLSv1_1());
} else if(strcasecmp(parameter_name, ssl_disable_TLSv1_2()) == 0) {
if(strcasecmp(parameter_value, "yes") == 0) TLSv1_2 = false;
else if(strcasecmp(parameter_value, "no") == 0) TLSv1_2 = true;
else log_error("Parameter value '%s' not recognized for parameter '%s'", parameter_value, ssl_disable_TLSv1_2());
} else {
log_debug("leaving SSL_Socket::parameter_set(%s, %s)", parameter_name, parameter_value);
return Abstract_Socket::parameter_set(parameter_name, parameter_value);
}
log_debug("leaving SSL_Socket::parameter_set(%s, %s)", parameter_name, parameter_value);
return true;
}
bool SSL_Socket::add_user_data(int client_id) {
log_debug("entering SSL_Socket::add_user_data()");
if (!ssl_use_ssl) {
log_debug("leaving SSL_Socket::add_user_data()");
return Abstract_Socket::add_user_data(client_id);
}
ssl_init_SSL();
log_debug("Create a new SSL object");
if (ssl_ctx==NULL)
log_error("No SSL CTX found, SSL not initialized");
ssl_current_ssl=SSL_new(ssl_ctx);
if (ssl_current_ssl==NULL)
log_error("Creation of SSL object failed");
#ifdef SSL_OP_NO_SSLv2
if(!SSLv2){
SSL_set_options(ssl_current_ssl,SSL_OP_NO_SSLv2);
}
#endif
#ifdef SSL_OP_NO_SSLv3
if(!SSLv3){
SSL_set_options(ssl_current_ssl,SSL_OP_NO_SSLv3);
}
#endif
#ifdef SSL_OP_NO_TLSv1
if(!TLSv1){
SSL_set_options(ssl_current_ssl,SSL_OP_NO_TLSv1);
}
#endif
#ifdef SSL_OP_NO_TLSv1_1
if(!TLSv1_1){
SSL_set_options(ssl_current_ssl,SSL_OP_NO_TLSv1_1);
}
#endif
#ifdef SSL_OP_NO_TLSv1_2
if(!TLSv1_2){
SSL_set_options(ssl_current_ssl,SSL_OP_NO_TLSv1_2);
}
#endif
set_user_data(client_id, ssl_current_ssl);
log_debug("New client added with key '%d'", client_id);
log_debug("Binding SSL to the socket");
if (SSL_set_fd(ssl_current_ssl, client_id)!=1)
log_error("Binding of SSL object to socket failed");
// Conext change for SSL objects may come here in the
// future.
if (Abstract_Socket::get_server_mode()) {
log_debug("Accept SSL connection request");
if (ssl_current_client!=NULL) log_warning("Warning: race condition while setting current client object pointer");
ssl_current_client=(SSL_Socket *)this;
if (ssl_getresult(SSL_accept(ssl_current_ssl))!=SSL_ERROR_NONE) {
log_warning("Connection from client %d is refused", client_id);
ssl_current_client=NULL;
log_debug("leaving SSL_Socket::add_user_data()");
return false;
}
ssl_current_client=NULL;
} else {
if (ssl_use_session_resumption && ssl_session!=NULL) {
log_debug("Try to use ssl_session resumption");
if (ssl_getresult(SSL_set_session(ssl_current_ssl, ssl_session))!=SSL_ERROR_NONE)
log_error("SSL error occured");
}
log_debug("Connect to server");
if (ssl_current_client!=NULL) log_warning("Warning: race condition while setting current client object pointer");
ssl_current_client=(SSL_Socket *)this;
//
while(true)
{
int res = ssl_getresult(SSL_connect(ssl_current_ssl));
switch (res) {
case SSL_ERROR_NONE: break;
case SSL_ERROR_WANT_WRITE:
ssl_current_client = NULL;
TTCN_Snapshot::block_for_sending(client_id);
continue;
case SSL_ERROR_WANT_READ:
for(;;) {
pollfd pollClientFd = { client_id, POLLIN, 0 };
int nEvents = poll(&pollClientFd, 1, 0);
if (nEvents == 1 && (pollClientFd.revents & (POLLIN | POLLHUP)) != 0)
break;
if(nEvents < 0 && errno != EINTR)
log_error("System call poll() failed on file descriptor %d", client_id);
}
continue;
default:
log_warning("Connection to server is refused");
ssl_current_client=NULL;
log_debug("leaving SSL_Socket::add_user_data()");
return false;
}
break;
} //while
ssl_current_client=NULL;
if (ssl_use_session_resumption) {
log_debug("Connected, get new ssl_session");
ssl_session=SSL_get1_session(ssl_current_ssl);
if (ssl_session==NULL)
log_warning("Server did not send a session ID");
}
}
if (ssl_use_session_resumption) {
if (SSL_session_reused(ssl_current_ssl)) log_debug("Session was reused");
else log_debug("Session was not reused");
}
if (!ssl_verify_certificates()) { // remove client
log_warning("Verification failed");
log_debug("leaving SSL_Socket::add_user_data()");
return false;
}
log_debug("leaving SSL_Socket::add_user_data()");
return true;
}
bool SSL_Socket::remove_user_data(int client_id) {
log_debug("entering SSL_Socket::remove_user_data()");
if (!ssl_use_ssl) {
log_debug("leaving SSL_Socket::remove_user_data()");
return Abstract_Socket::remove_user_data(client_id);
}
ssl_current_ssl = (SSL*)get_user_data(client_id);
if (ssl_current_ssl!=NULL) {
SSL_shutdown(ssl_current_ssl);
SSL_free(ssl_current_ssl);
} else
log_warning("SSL object not found for client %d", client_id);
log_debug("leaving SSL_Socket::remove_user_data()");
return true;
}
bool SSL_Socket::user_all_mandatory_configparameters_present() {
if (!ssl_use_ssl) { return true; }
if (Abstract_Socket::get_server_mode()) {
if (ssl_certificate_file==NULL)
log_error("%s is not defined in the configuration file", ssl_certificate_file_name());
if (ssl_trustedCAlist_file==NULL)
log_error("%s is not defined in the configuration file", ssl_trustedCAlist_file_name());
if (ssl_key_file==NULL)
log_error("%s is not defined in the configuration file", ssl_private_key_file_name());
} else {
if (ssl_verify_certificate && ssl_trustedCAlist_file==NULL)
log_error("%s is not defined in the configuration file altough %s=yes", ssl_trustedCAlist_file_name(), ssl_verifycertificate_name());
}
return true;
}
//STATE_WAIT_FOR_RECEIVE_CALLBACK: if the SSL_read operation would
// block because the socket is not ready for writing,
// I set the socket state to this state and add the file
// descriptor to the Event_Handler. The Event_Handler will
// wake up and call the receive_message_on_fd operation
// if the socket is ready to write.
//If the SSL_read operation would block because the socket is not ready for
//reading, I do nothing
int SSL_Socket::receive_message_on_fd(int client_id)
{
log_debug("entering SSL_Socket::receive_message_on_fd()");
if (!ssl_use_ssl) {
log_debug("leaving SSL_Socket::receive_message_on_fd()");
return Abstract_Socket::receive_message_on_fd(client_id);
}
if (ssl_current_client!=NULL) log_warning("Warning: race condition while setting current client object pointer");
ssl_current_client=(SSL_Socket *)this;
as_client_struct* peer = get_peer(client_id); // check if client exists
if (peer->reading_state == STATE_WAIT_FOR_RECEIVE_CALLBACK){
Remove_Fd_Write_Handler(client_id);
log_debug("SSL_Socket::receive_message_on_fd: setting socket state to STATE_NORMAL");
peer->reading_state = STATE_NORMAL;
}
TTCN_Buffer* recv_tb = get_buffer(client_id);
ssl_current_ssl=(SSL*)get_user_data(client_id);
int messageLength=0;
size_t end_len=AS_SSL_CHUNCK_SIZE;
unsigned char *end_ptr;
while (messageLength<=0) {
log_debug(" one read cycle started");
recv_tb->get_end(end_ptr, end_len);
messageLength = SSL_read(ssl_current_ssl, end_ptr, end_len);
if (messageLength <= 0) {
int res=ssl_getresult(messageLength);
switch (res) {
case SSL_ERROR_ZERO_RETURN:
log_debug("SSL_Socket::receive_message_on_fd: SSL connection was interrupted by the other side");
SSL_set_quiet_shutdown(ssl_current_ssl, 1);
log_debug("SSL_ERROR_ZERO_RETURN is received, setting SSL SHUTDOWN mode to QUIET");
ssl_current_client=NULL;
log_debug("leaving SSL_Socket::receive_message_on_fd() with SSL_ERROR_ZERO_RETURN");
return 0;
case SSL_ERROR_WANT_WRITE://writing would block
if (get_use_non_blocking_socket()){
Add_Fd_Write_Handler(client_id);
log_debug("SSL_Socket::receive_message_on_fd: setting socket state to STATE_WAIT_FOR_RECEIVE_CALLBACK");
peer->reading_state = STATE_WAIT_FOR_RECEIVE_CALLBACK;
ssl_current_client=NULL;
log_debug("leaving SSL_Socket::receive_message_on_fd()");
return -2;
}
case SSL_ERROR_WANT_READ: //reading would block, continue processing data
if (get_use_non_blocking_socket()){
log_debug("SSL_Socket::receive_message_on_fd: reading would block, leaving SSL_Socket::receive_message_on_fd()");
ssl_current_client = NULL;
log_debug("leaving SSL_Socket::receive_message_on_fd()");
return -2;
}
log_debug("repeat the read operation to finish the pending SSL handshake");
break;
default:
log_error("SSL error occured");
}
} else {
recv_tb->increase_length(messageLength);
}
}
ssl_current_client=NULL;
log_debug("leaving SSL_Socket::receive_message_on_fd() with number of bytes read: %d", messageLength);
return messageLength;
}
int SSL_Socket::send_message_on_fd(int client_id, const unsigned char* send_par, int message_length)
{
log_debug("entering SSL_Socket::send_message_on_fd()");
if (!ssl_use_ssl) {
log_debug("leaving SSL_Socket::send_message_on_fd()");
return Abstract_Socket::send_message_on_fd(client_id, send_par, message_length);
}
if (ssl_current_client!=NULL) log_warning("Warning: race condition while setting current client object pointer");
ssl_current_client=(SSL_Socket *)this;
get_peer(client_id); // check if client exists
ssl_current_ssl=(SSL*)get_user_data(client_id);
if (ssl_current_ssl==NULL) { log_error("No SSL data available for client %d", client_id); }
log_debug("Client ID = %d", client_id);
while (true) {
log_debug(" one write cycle started");
int res = ssl_getresult(SSL_write(ssl_current_ssl, send_par, message_length));
switch (res) {
case SSL_ERROR_NONE:
ssl_current_client=NULL;
log_debug("leaving SSL_Socket::send_message_on_fd()");
return message_length;
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_READ:
log_debug("repeat the write operation to finish the pending SSL handshake");
break;
case SSL_ERROR_ZERO_RETURN:
log_warning("SSL_Socket::send_message_on_fd: SSL connection was interrupted by the other side");
SSL_set_quiet_shutdown(ssl_current_ssl, 1);
log_debug("SSL_ERROR_ZERO_RETURN is received, setting SSL SHUTDOWN mode to QUIET");
ssl_current_client=NULL;
log_debug("leaving SSL_Socket::send_message_on_fd()");
return -1;
default:
log_debug("SSL error occured");
return -1;
}
}
// avoid compiler warnings
return 0;
}
//If the socket is not ready for writing, the same mechanism is used
//as described at the Abstract_Socket class
//If the socket is not ready for reading, I block the execution using
//the take_new operation while the socket is not ready for reading.
//While this operation will call the Event_Handler,
//I indicate with the STATE_DONT_RECEIVE state that from the Event_Handler the receive_message_on_fd
//operation must not be called for this socket.
int SSL_Socket::send_message_on_nonblocking_fd(int client_id, const unsigned char* send_par, int message_length){
log_debug("entering SSL_Socket::send_message_on_nonblocking_fd()");
if (!ssl_use_ssl) {
log_debug("leaving SSL_Socket::send_message_on_nonblocking_fd()");
return Abstract_Socket::send_message_on_nonblocking_fd(client_id, send_par, message_length);
}
as_client_struct* peer;
if (ssl_current_client!=NULL) log_warning("Warning: race condition while setting current client object pointer");
ssl_current_client=(SSL_Socket *)this;
get_peer(client_id);
ssl_current_ssl=(SSL*)get_user_data(client_id);
if (ssl_current_ssl==NULL) { log_error("No SSL data available for client %d", client_id); }
log_debug("Client ID = %d", client_id);
while (true) {
int res;
peer = get_peer(client_id); // check if client exists
log_debug(" one write cycle started");
ssl_current_ssl = (SSL*)get_user_data(client_id);
if (peer -> reading_state == STATE_DONT_CLOSE){
goto client_closed_connection;
}else res = ssl_getresult(SSL_write(ssl_current_ssl, send_par, message_length));
switch (res) {
case SSL_ERROR_NONE:
ssl_current_client=NULL;
log_debug("leaving SSL_Socket::send_message_on_nonblocking_fd()");
log_debug("SSL_Socket::send_message_on_nonblocking_fd: setting socket state to STATE_NORMAL");
peer -> reading_state = STATE_NORMAL;
return message_length;
case SSL_ERROR_WANT_WRITE:
if (peer == NULL){
log_error("SSL_Socket::send_message_on_nonblocking_fd, Client ID %d does not exist.", client_id);
}
int old_bufsize, new_bufsize;
if (increase_send_buffer(client_id, old_bufsize, new_bufsize)) {
log_debug("Sending data on on file descriptor %d",client_id);
log_debug("The sending operation would block execution. The "
"size of the outgoing buffer was increased from %d to "
"%d bytes.",old_bufsize,
new_bufsize);
} else {
log_warning("Sending data on file descriptor %d", client_id);
log_warning("The sending operation would block execution and it "
"is not possible to further increase the size of the "
"outgoing buffer. Trying to process incoming data to "
"avoid deadlock.");
ssl_current_client=NULL;
log_debug("SSL_Socket::send_message_on_nonblocking_fd: setting socket state to STATE_BLOCK_FOR_SENDING");
peer->reading_state = STATE_BLOCK_FOR_SENDING;
TTCN_Snapshot::block_for_sending(client_id);
}
peer = get_peer(client_id); // check if client exists
if (peer == NULL){
log_error("SSL_Socket::send_message_on_nonblocking_fd, Client ID %d does not exist.", client_id);
}
break;
case SSL_ERROR_WANT_READ:
//receiving buffer is probably empty thus reading would block execution
log_debug("SSL_write cannot read data from socket %d. Trying to process data to avoid deadlock.", client_id);
log_debug("SSL_Socket::send_message_on_nonblocking_fd: setting socket state to STATE_DONT_RECEIVE");
peer -> reading_state = STATE_DONT_RECEIVE; //don't call receive_message_on_fd() to this socket
for (;;) {
TTCN_Snapshot::take_new(TRUE);
pollfd pollClientFd = { client_id, POLLIN, 0 };
int nEvents = poll(&pollClientFd, 1, 0);
if (nEvents == 1 && (pollClientFd.revents & (POLLIN | POLLHUP)) != 0)
break;
if (nEvents < 0 && errno != EINTR)
log_error("System call poll() failed on file descriptor %d", client_id);
}
log_debug("Deadlock resolved");
break;
case SSL_ERROR_ZERO_RETURN:
goto client_closed_connection;
default:
log_warning("SSL error occured");
return -1;
}
}
client_closed_connection:
log_warning("SSL_Socket::send_message_on_nonblocking_fd: SSL connection was interrupted by the other side");
SSL_set_quiet_shutdown(ssl_current_ssl, 1);
log_debug("Setting SSL SHUTDOWN mode to QUIET");
ssl_current_client=NULL;
log_debug("leaving SSL_Socket::send_message_on_nonblocking_fd()");
log_debug("SSL_Socket::send_message_on_nonblocking_fd: setting socket state to STATE_NORMAL");
peer -> reading_state = STATE_NORMAL;
errno = EPIPE;
return -1;
}
bool SSL_Socket::ssl_verify_certificates()
{
char str[SSL_CHARBUF_LENGTH];
log_debug("entering SSL_Socket::ssl_verify_certificates()");
ssl_log_SSL_info();
// Get the other side's certificate
log_debug("Check certificate of the other party");
X509 *cert = SSL_get_peer_certificate (ssl_current_ssl);
if (cert != NULL) {
{
log_debug("Certificate information:");
X509_NAME_oneline (X509_get_subject_name (cert), str, SSL_CHARBUF_LENGTH);
log_debug(" subject: %s", str);
}
// We could do all sorts of certificate verification stuff here before
// deallocating the certificate.
// Just a basic check that the certificate is valid
// Other checks (e.g. Name in certificate vs. hostname) shall be
// done on application level
if (ssl_verify_certificate)
log_debug("Verification state is: %s", X509_verify_cert_error_string(SSL_get_verify_result(ssl_current_ssl)));
X509_free (cert);
} else
log_warning("Other side does not have certificate.");
log_debug("leaving SSL_Socket::ssl_verify_certificates()");
return true;
}
// Data set/get functions
char * SSL_Socket::get_ssl_password() const {return ssl_password;}
void SSL_Socket::set_ssl_use_ssl(bool par) {ssl_use_ssl=par;}
void SSL_Socket::set_ssl_verifycertificate(bool par) {ssl_verify_certificate=par;}
void SSL_Socket::set_ssl_use_session_resumption(bool par) {ssl_use_session_resumption=par;}
void SSL_Socket::set_ssl_key_file(char * par) {
delete [] ssl_key_file;
ssl_key_file=par;
}
void SSL_Socket::set_ssl_certificate_file(char * par) {
delete [] ssl_certificate_file;
ssl_certificate_file=par;
}
void SSL_Socket::set_ssl_trustedCAlist_file(char * par) {
delete [] ssl_trustedCAlist_file;
ssl_trustedCAlist_file=par;
}
void SSL_Socket::set_ssl_cipher_list(char * par) {
delete [] ssl_cipher_list;
ssl_cipher_list=par;
}
void SSL_Socket::set_ssl_server_auth_session_id_context(const unsigned char * par) {
ssl_server_auth_session_id_context=par;
}
// Default parameter names
const char* SSL_Socket::ssl_use_ssl_name() { return "ssl_use_ssl";}
const char* SSL_Socket::ssl_use_session_resumption_name() { return "ssl_use_session_resumption";}
const char* SSL_Socket::ssl_private_key_file_name() { return "ssl_private_key_file";}
const char* SSL_Socket::ssl_trustedCAlist_file_name() { return "ssl_trustedCAlist_file";}
const char* SSL_Socket::ssl_certificate_file_name() { return "ssl_certificate_chain_file";}
const char* SSL_Socket::ssl_password_name() { return "ssl_private_key_password";}
const char* SSL_Socket::ssl_cipher_list_name() { return "ssl_allowed_ciphers_list";}
const char* SSL_Socket::ssl_verifycertificate_name() { return "ssl_verify_certificate";}
const char* SSL_Socket::ssl_disable_SSLv2() { return "ssl_disable_SSLv2";}
const char* SSL_Socket::ssl_disable_SSLv3() { return "ssl_disable_SSLv3";}
const char* SSL_Socket::ssl_disable_TLSv1() { return "ssl_disable_TLSv1";}
const char* SSL_Socket::ssl_disable_TLSv1_1() { return "ssl_disable_TLSv1_1";}
const char* SSL_Socket::ssl_disable_TLSv1_2() { return "ssl_disable_TLSv1_2";}
void SSL_Socket::ssl_actions_to_seed_PRNG() {
struct stat randstat;
if(RAND_status()) {
log_debug("PRNG already initialized, no action needed");
return;
}
log_debug("Seeding PRND");
// OpenSSL tries to use random devives automatically
// these would not be necessary
if (!stat("/dev/urandom", &randstat)) {
log_debug("Using installed random device /dev/urandom for seeding the PRNG with %d bytes.", SSL_PRNG_LENGTH);
if (RAND_load_file("/dev/urandom", SSL_PRNG_LENGTH)!=SSL_PRNG_LENGTH)
log_error("Could not read from /dev/urandom");
} else if (!stat("/dev/random", &randstat)) {
log_debug("Using installed random device /dev/random for seeding the PRNG with %d bytes.", SSL_PRNG_LENGTH);
if (RAND_load_file("/dev/random", SSL_PRNG_LENGTH)!=SSL_PRNG_LENGTH)
log_error("Could not read from /dev/random");
} else {
/* Neither /dev/random nor /dev/urandom are present, so add
entropy to the SSL PRNG a hard way. */
log_warning("Solaris patches to provide random generation devices are not installed.\nSee http://www.openssl.org/support/faq.html \"Why do I get a \"PRNG not seeded\" error message?\"\nA workaround will be used.");
for (int i = 0; i < 10000 && !RAND_status(); ++i) {
char buf[4];
struct timeval tv;
gettimeofday(&tv, 0);
buf[0] = tv.tv_usec & 0xF;
buf[2] = (tv.tv_usec & 0xF0) >> 4;
buf[3] = (tv.tv_usec & 0xF00) >> 8;
buf[1] = (tv.tv_usec & 0xF000) >> 12;
RAND_add(buf, sizeof buf, 0.1);
}
return;
}
if(!RAND_status()) {
log_error("Could not seed the Pseudo Random Number Generator with enough data.");
} else {
log_debug("PRNG successfully initialized.");
}
}
void SSL_Socket::ssl_init_SSL()
{
if (ssl_initialized) {
log_debug("SSL already initialized, no action needed");
return;
}
{
log_debug("Init SSL started");
log_debug("Using %s (%lx)", SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER);
}
SSL_library_init(); // initialize library
SSL_load_error_strings(); // readable error messages
// Create SSL method: both server and client understanding SSLv2, SSLv3, TLSv1
// ssl_method = SSLv23_method();
// if (ssl_method==NULL)
// log_error("SSL method creation failed.");
// Create context
ssl_ctx = SSL_CTX_new (SSLv23_method());
if (ssl_ctx==NULL)
log_error("SSL context creation failed.");
// valid for all SSL objects created from this context afterwards
if(ssl_certificate_file!=NULL) {
log_debug("Loading certificate file");
if(SSL_CTX_use_certificate_chain_file(ssl_ctx, ssl_certificate_file)!=1)
log_error("Can't read certificate file ");
}
// valid for all SSL objects created from this context afterwards
if(ssl_key_file!=NULL) {
log_debug("Loading key file");
if (ssl_current_client!=NULL) log_warning("Warning: race condition while setting current client object pointer");
ssl_current_client=(SSL_Socket *)this;
if(ssl_password!=NULL)
SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_password_cb);
if(SSL_CTX_use_PrivateKey_file(ssl_ctx, ssl_key_file, SSL_FILETYPE_PEM)!=1)
log_error("Can't read key file ");
ssl_current_client=NULL;
}
if (ssl_trustedCAlist_file!=NULL) {
log_debug("Loading trusted CA list file");
if (SSL_CTX_load_verify_locations(ssl_ctx, ssl_trustedCAlist_file, NULL)!=1)
log_error("Can't read trustedCAlist file ");
}
if (ssl_certificate_file!=NULL && ssl_key_file!=NULL) {
log_debug("Check for consistency between private and public keys");
if (SSL_CTX_check_private_key(ssl_ctx)!=1)
log_warning("Private key does not match the certificate public key");
}
// check the other side's certificates
if (ssl_verify_certificate) {
log_debug("Setting verification behaviour: verification required and do not allow to continue on failure..");
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, ssl_verify_callback);
} else {
log_debug("Setting verification behaviour: verification not required and do allow to continue on failure..");
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, ssl_verify_callback);
}
if (ssl_cipher_list!=NULL) {
log_debug("Setting ssl_cipher list restrictions");
if (SSL_CTX_set_cipher_list(ssl_ctx, ssl_cipher_list)!=1)
log_error("Cipher list restriction failed for %s", ssl_cipher_list);
}
ssl_actions_to_seed_PRNG();
if (Abstract_Socket::get_server_mode() && ssl_use_session_resumption) {
log_debug("Prepare server for ssl_session resumption");
log_debug("Context is: %s; length = %lu", ssl_server_auth_session_id_context, (unsigned long)strlen((const char*)ssl_server_auth_session_id_context));
if (SSL_CTX_set_session_id_context(ssl_ctx, ssl_server_auth_session_id_context, strlen((const char*)ssl_server_auth_session_id_context))!=1)
log_error("Activation of SSL ssl_session resumption failed on server");
}
ssl_initialized=true;
log_debug("Init SSL successfully finished");
}
void SSL_Socket::ssl_log_SSL_info()
{
char str[SSL_CHARBUF_LENGTH];
log_debug("Check SSL description");
const SSL_CIPHER *ssl_cipher=SSL_get_current_cipher(ssl_current_ssl);
if (ssl_cipher!=NULL) {
SSL_CIPHER_description(SSL_get_current_cipher(ssl_current_ssl), str, SSL_CHARBUF_LENGTH);
{
log_debug("SSL description:");
log_debug("%s", str);
}
}
}
// Log the SSL error and flush the error queue
// Can be used after the followings:
// SSL_connect(), SSL_accept(), SSL_do_handshake(),
// SSL_read(), SSL_peek(), or SSL_write()
int SSL_Socket::ssl_getresult(int res)
{
int err = SSL_get_error(ssl_current_ssl, res);
log_debug("SSL operation result:");
switch(err) {
case SSL_ERROR_NONE:
log_debug("SSL_ERROR_NONE");
break;
case SSL_ERROR_ZERO_RETURN:
log_debug("SSL_ERROR_ZERO_RETURN");
break;
case SSL_ERROR_WANT_READ:
log_debug("SSL_ERROR_WANT_READ");
break;
case SSL_ERROR_WANT_WRITE:
log_debug("SSL_ERROR_WANT_WRITE");
break;
case SSL_ERROR_WANT_CONNECT:
log_debug("SSL_ERROR_WANT_CONNECT");
break;
case SSL_ERROR_WANT_ACCEPT:
log_debug("SSL_ERROR_WANT_ACCEPT");
break;
case SSL_ERROR_WANT_X509_LOOKUP:
log_debug("SSL_ERROR_WANT_X509_LOOKUP");
break;
case SSL_ERROR_SYSCALL:
log_debug("SSL_ERROR_SYSCALL");
log_debug("EOF was observed that violates the protocol, peer disconnected; treated as a normal disconnect");
return SSL_ERROR_ZERO_RETURN;
break;
case SSL_ERROR_SSL:
log_debug("SSL_ERROR_SSL");
break;
default:
log_error("Unknown SSL error code: %d", err);
}
// get the copy of the error string in readable format
unsigned long e=ERR_get_error();
while (e) {
log_debug("SSL error queue content:");
log_debug(" Library: %s", ERR_lib_error_string(e));
log_debug(" Function: %s", ERR_func_error_string(e));
log_debug(" Reason: %s", ERR_reason_error_string(e));
e=ERR_get_error();
}
//It does the same but more simple:
// ERR_print_errors_fp(stderr);
return err;
}
int SSL_Socket::ssl_verify_certificates_at_handshake(int /*preverify_ok*/, X509_STORE_CTX */*ssl_ctx*/) {
// don't care by default
return -1;
}
// Callback function used by OpenSSL.
// Called when a password is needed to decrypt the private key file.
// NOTE: not thread safe
int SSL_Socket::ssl_password_cb(char *buf, int num, int /*rwflag*/,void */*userdata*/) {
if (ssl_current_client!=NULL) {
char *ssl_client_password;
ssl_client_password=((SSL_Socket *)ssl_current_client)->get_ssl_password();
if(ssl_client_password==NULL) return 0;
const char* pass = (const char*) ssl_client_password;
int pass_len = strlen(pass) + 1;
if (num < pass_len) return 0;
strcpy(buf, pass);
return(strlen(pass));
} else { // go on with no password set
fprintf(stderr, "Warning: no current SSL object found but ssl_password_cb is called, programming error\n");
return 0;
}
}
// Callback function used by OpenSSL.
// Called during SSL handshake with a pre-verification status.
int SSL_Socket::ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ssl_ctx)
{
SSL *ssl_pointer;
SSL_CTX *ctx_pointer;
int user_result;
ssl_pointer = (SSL *)X509_STORE_CTX_get_ex_data(ssl_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
ctx_pointer = SSL_get_SSL_CTX(ssl_pointer);
if (ssl_current_client!=NULL) {
// if ssl_verifiycertificate == "no", then always accept connections
if(((SSL_Socket *)ssl_current_client)->ssl_verify_certificate) {
user_result=((SSL_Socket *)ssl_current_client)->ssl_verify_certificates_at_handshake(preverify_ok, ssl_ctx);
if (user_result>=0) return user_result;
} else {
return 1;
}
} else { // go on with default authentication
fprintf(stderr, "Warning: no current SSL object found but ssl_verify_callback is called, programming error\n");
}
// if ssl_verifiycertificate == "no", then always accept connections
if (SSL_CTX_get_verify_mode(ctx_pointer) == SSL_VERIFY_NONE)
return 1;
// if ssl_verifiycertificate == "yes", then accept connections only if the
// certificate is valid
else if (SSL_CTX_get_verify_mode(ctx_pointer) & SSL_VERIFY_PEER) {
return preverify_ok;
}
// something went wrong
else
return 0;
}
#endif
<!--
/******************************************************************************
* Copyright (c) 2000-2019 Ericsson Telecom AB
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
*
* Contributors:
* Zoltan Bibo - initial implementation and initial documentation
* Gergely Futo
* Oliver Ferenc Czerman
* Balasko Jeno
* Zoltan Bibo
* Eduard Czimbalmos
* Kulcsr Endre
* Gabor Szalai
* Jozsef Gyurusi
* Csndes Tibor
* Zoltan Jasz
******************************************************************************/
//
// File: Abstract_Socket.grp
// Description: Abstract_Socket group file
// Rev: R9B
// Prodnr: CNL 113 384
//
-->
<!DOCTYPE TITAN_GUI_FileGroup_file>
<FileGroup TITAN_version="1.8.pl2" >
<File_Group name="AbstractSocket" >
<File path="Abstract_Socket.cc" />
<File path="Abstract_Socket.hh" />
</File_Group>
</FileGroup>
/******************************************************************************
* Copyright (c) 2000-2019 Ericsson Telecom AB
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
*
* Contributors:
* Zoltan Bibo - initial implementation and initial documentation
* Gergely Futo
* Oliver Ferenc Czerman
* Balasko Jeno
* Zoltan Bibo
* Eduard Czimbalmos
* Kulcsr Endre
* Gabor Szalai
* Jozsef Gyurusi
* Csndes Tibor
* Zoltan Jasz
******************************************************************************/
//
// File: Abstract_Socket.hh
// Description: Abstract_Socket header file
// Rev: R9B
// Prodnr: CNL 113 384
//
#ifndef Abstract_Socket_HH
#define Abstract_Socket_HH
#ifdef AS_USE_SSL
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#endif
#include <sys/socket.h>
#include <netinet/in.h>
#include <TTCN3.hh>
// to support systems not supporting IPv6 define AF_INET6 to some dummy value:
#ifndef AF_INET6
#define AF_INET6 (-255)
#endif
class PacketHeaderDescr {
public:
// Byte order in the header
enum HeaderByteOrder{ Header_MSB, Header_LSB };
private:
unsigned long length_offset;
unsigned long nr_bytes_in_length;
HeaderByteOrder byte_order;
long value_offset;
unsigned long length_multiplier;
public:
PacketHeaderDescr(unsigned long p_length_offset,
unsigned long p_nr_bytes_in_length, HeaderByteOrder p_byte_order,
long p_value_offset = 0, unsigned long p_length_multiplier = 1)
: length_offset(p_length_offset), nr_bytes_in_length(p_nr_bytes_in_length),
byte_order(p_byte_order), value_offset(p_value_offset),
length_multiplier(p_length_multiplier) { }
// returns the message length
unsigned long Get_Message_Length(const unsigned char* buffer_pointer) const;
// returns the number of bytes needed to have a valid message length
inline unsigned long Get_Valid_Header_Length() const
{ return length_offset + nr_bytes_in_length; }
};
class Abstract_Socket
{
protected:
enum TCP_STATES {CLOSED, LISTEN, ESTABLISHED, CLOSE_WAIT, FIN_WAIT};
enum READING_STATES {STATE_DONT_RECEIVE, STATE_WAIT_FOR_RECEIVE_CALLBACK, STATE_BLOCK_FOR_SENDING, STATE_DONT_CLOSE, STATE_NORMAL};
// client data
struct as_client_struct {
void *user_data; // pointer to any additional data needed by the user
TTCN_Buffer *fd_buff; // pointer to the data buffer
struct sockaddr_storage clientAddr;// client address
#if defined LINUX || defined FREEBSD || defined SOLARIS8
socklen_t
#else /* SOLARIS or WIN32 */
int
#endif
clientAddrlen;
TCP_STATES tcp_state; // TCP state
READING_STATES reading_state; //used when SSL_write returns SSL_ERROR_WANT_READ an we are using non-blocking socket
};
Abstract_Socket();
Abstract_Socket(const char *testport_type, const char *testport_name);
virtual ~Abstract_Socket();
// Shall be called from set_parameter()
bool parameter_set(const char *parameter_name, const char *parameter_value);
// Shall be called from user_map()
void map_user();
// Shall be called from user_unmap()
void unmap_user();
// puts the IP address in the addr
void get_host_id(const char* hostName, struct sockaddr_in *addr); /* This function should not be used! Use getaddrinfo instead! */
// Closes the current listening port and opens the specified one
int open_listen_port(const struct sockaddr_in & localAddr); /* This function should be removed! Deprecated by: */
int open_listen_port(const char* localHostname, const char* localServicename);
// Closes the current listening port
void close_listen_port();
virtual void listen_port_opened(int port_number);
// Opens a new client connection
int open_client_connection(const struct sockaddr_in & new_remote_addr, const struct sockaddr_in & new_local_addr); /* This function should be removed! Deprecated by: */
int open_client_connection(const char* remoteHostname, const char* remoteService, const char* localHostname, const char* localService);
virtual void client_connection_opened(int client_id);
// Shall be called from Handle_Fd_Event()
void Handle_Socket_Event(int fd, boolean is_readable, boolean is_writable, boolean is_error);
// Shall be called from Handle_Timeout() - for possible future development
void Handle_Timeout_Event(double /*time_since_last_call*/) {};
// Shall be called from outgoing_send()
void send_outgoing(const unsigned char* message_buffer, int length, int client_id = -1);
void send_shutdown(int client_id = -1);
// Access to private variables
bool get_nagling() const {return nagling;}
bool get_use_non_blocking_socket() const {return use_non_blocking_socket;};
bool get_server_mode() const {return server_mode;}
bool get_socket_debugging() const {return socket_debugging;}
bool get_halt_on_connection_reset() const {return halt_on_connection_reset;}
bool get_use_connection_ASPs() const {return use_connection_ASPs;}
bool get_handle_half_close() const {return handle_half_close;}
int get_socket_fd() const;
int get_listen_fd() const {return listen_fd;}
//set non-blocking mode
int set_non_block_mode(int fd, bool enable_nonblock);
//increase buffer size
bool increase_send_buffer(int fd, int &old_size, int& new_size);
const char* get_local_host_name(){return local_host_name; };
unsigned int get_local_port_number(){return local_port_number; };
const char* get_remote_host_name(){return remote_host_name; };
unsigned int get_remote_port_number(){return remote_port_number; };
const struct sockaddr_in & get_remote_addr() {return remoteAddr; }; /* FIXME: This function is deprecated and should be removed! */
const struct sockaddr_in & get_local_addr() {return localAddr; }; /* FIXME: This function is deprecated and should be removed! */
const int& get_ai_family() const {return ai_family;}
void set_ai_family(int parameter_value) {ai_family=parameter_value;}
bool get_ttcn_buffer_usercontrol() const {return ttcn_buffer_usercontrol; }
void set_nagling(bool parameter_value) {nagling=parameter_value;}
void set_server_mode(bool parameter_value) {server_mode=parameter_value;}
void set_handle_half_close(bool parameter_value) {handle_half_close=parameter_value;}
void set_socket_debugging(bool parameter_value) {socket_debugging=parameter_value;}
void set_halt_on_connection_reset(bool parameter_value) {halt_on_connection_reset=parameter_value;}
void set_ttcn_buffer_usercontrol(bool parameter_value) {ttcn_buffer_usercontrol=parameter_value;}
const char *test_port_type;
const char *test_port_name;
// Called when a message is received
virtual void message_incoming(const unsigned char* message_buffer, int length, int client_id = -1) = 0;
virtual void Add_Fd_Read_Handler(int fd) = 0;
virtual void Add_Fd_Write_Handler(int fd) = 0;
virtual void Remove_Fd_Read_Handler(int fd) = 0;
virtual void Remove_Fd_Write_Handler(int fd) = 0;
virtual void Remove_Fd_All_Handlers(int fd) = 0;
virtual void Handler_Uninstall() = 0;
virtual void Timer_Set_Handler(double call_interval, boolean is_timeout = TRUE,
boolean call_anyway = TRUE, boolean is_periodic = TRUE) = 0; // unused - for possible future development
virtual const PacketHeaderDescr* Get_Header_Descriptor() const;
// Logging functions
void log_debug(const char *fmt, ...) const
__attribute__ ((__format__ (__printf__, 2, 3)));
void log_warning(const char *fmt, ...) const
__attribute__ ((__format__ (__printf__, 2, 3)));
void log_error(const char *fmt, ...) const
__attribute__ ((__format__ (__printf__, 2, 3), __noreturn__));
void log_hex(const char *prompt, const unsigned char *msg, size_t length) const;
// Called when a message is to be received (an event detected)
virtual int receive_message_on_fd(int client_id);
// Called when a message is to be sent
virtual int send_message_on_fd(int client_id, const unsigned char* message_buffer, int message_length);
virtual int send_message_on_nonblocking_fd(int client_id, const unsigned char *message_buffer, int message_length);
// Called after a peer is connected
virtual void peer_connected(int client_id, sockaddr_in& remote_addr); /* This function should be removed! deprecated by: */
virtual void peer_connected(int /*client_id*/, const char * /*host*/, const int /*port*/) {};
// Called after a peer is disconnected
virtual void peer_disconnected(int client_id);
// Called when a peer shut down its fd for writing
virtual void peer_half_closed(int client_id);
// Called after a send error
virtual void report_error(int client_id, int msg_length, int sent_length, const unsigned char* msg, const char* error_text);
// Called after a unsent message
virtual void report_unsent(int client_id, int msg_length, int sent_length, const unsigned char* msg, const char* error_text);
// Test port parameters
virtual const char* local_port_name();
virtual const char* remote_address_name();
virtual const char* local_address_name();
virtual const char* remote_port_name();
virtual const char* ai_family_name();
virtual const char* use_connection_ASPs_name();
virtual const char* halt_on_connection_reset_name();
virtual const char* client_TCP_reconnect_name();
virtual const char* TCP_reconnect_attempts_name();
virtual const char* TCP_reconnect_delay_name();
virtual const char* server_mode_name();
virtual const char* socket_debugging_name();
virtual const char* nagling_name();
virtual const char* use_non_blocking_socket_name();
virtual const char* server_backlog_name();
// Fetch/Set user data pointer
void* get_user_data(int client_id) {return get_peer(client_id)->user_data;}
void set_user_data(int client_id, void *uptr) {get_peer(client_id)->user_data = uptr;}
// Called after a TCP connection is established
virtual bool add_user_data(int client_id);
// Called before the TCP connection is drop down
virtual bool remove_user_data(int client_id);
// Called when a client shall be removed
virtual void remove_client(int client_id);
// Called when all clients shall be removed
virtual void remove_all_clients();
// Called at the beginning of map() to check mandatory parameter presence
virtual bool user_all_mandatory_configparameters_present();
TTCN_Buffer *get_buffer(int client_id) {return get_peer(client_id)->fd_buff; }
// Client data management functions
// add peer to the list
as_client_struct *peer_list_add_peer(int client_id);
// remove peer from list
void peer_list_remove_peer(int client_id);
// remove all peers from list
void peer_list_reset_peer();
// returns back the structure of the peer
as_client_struct *get_peer(int client_id, bool no_error=false) const;
// length of the list
int peer_list_get_length() const { return peer_list_length; }
// number of peers in the list
int peer_list_get_nr_of_peers() const;
// fd of the last peer in the list
int peer_list_get_last_peer() const;
// fd of the first peer in the list
int peer_list_get_first_peer() const;
private:
void handle_message(int client_id = -1);
void all_mandatory_configparameters_present();
bool halt_on_connection_reset_set;
bool halt_on_connection_reset;
bool client_TCP_reconnect;
int TCP_reconnect_attempts;
int TCP_reconnect_delay;
bool server_mode;
bool use_connection_ASPs;
bool handle_half_close;
bool socket_debugging;
bool nagling;
bool use_non_blocking_socket;
bool ttcn_buffer_usercontrol;
char* local_host_name;
unsigned int local_port_number;
char* remote_host_name;
unsigned int remote_port_number;
int ai_family; // address family to use
// remoteAddr and localAddr is filled when map_user is called
struct sockaddr_in remoteAddr; /* FIXME: not used! should be removed */
struct sockaddr_in localAddr; /* FIXME: not used! should be removed */
int server_backlog;
int deadlock_counter;
int listen_fd;
int peer_list_length;
// Client data management functions
as_client_struct **peer_list_root;
void peer_list_resize_list(int client_id);
};
#ifdef AS_USE_SSL
class SSL_Socket: public Abstract_Socket
{
protected:
SSL_Socket();
SSL_Socket(const char *tp_type, const char *tp_name);
virtual ~SSL_Socket();
bool parameter_set(const char * parameter_name, const char * parameter_value);
// Called after a TCP connection is established (client side or server accepted a connection).
// It will create a new SSL conenction on the top of the TCP connection.
virtual bool add_user_data(int client_id);
// Called after a TCP connection is closed.
// It will delete the SSL conenction.
virtual bool remove_user_data(int client_id);
// Called from all_mandatory_configparameters_present() function
// during map() operation to check mandatory parameter presents.
virtual bool user_all_mandatory_configparameters_present();
// Called after an SSL connection is established (handshake finished) for further
// authentication. Shall return 'true' if verification
// is OK, otherwise 'false'. If return value was 'true', the connection is kept, otherwise
// the connection will be shutted down.
virtual bool ssl_verify_certificates();
// Call during SSL handshake (and rehandshake as well) by OpenSSL
// Return values:
// ==1: user authentication is passed, go on with handshake
// ==0: user authentication failed, refuse the connection to the other peer
// <0 : user don't care, go on with default basic checks
virtual int ssl_verify_certificates_at_handshake(int preverify_ok, X509_STORE_CTX *ssl_ctx);
// Called to receive from the socket if data is available (select()).
// Shall return with 0 if the peer is disconnected or with the number of bytes read.
// If error occured, execution shall stop in the function by calling log_error()
virtual int receive_message_on_fd(int client_id);
// Called to send a message on the socket.
// Shall return with 0 if the peer is disconnected or with the number of bytes written.
// If error occured, execution shall stop in the function by calling log_error()
virtual int send_message_on_fd(int client_id, const unsigned char * message_buffer, int length_of_message);
virtual int send_message_on_nonblocking_fd(int client_id, const unsigned char * message_buffer, int length_of_message);
// The following members can be called to fetch the current values
bool get_ssl_use_ssl() const {return ssl_use_ssl;}
bool get_ssl_verifycertificate() const {return ssl_verify_certificate;}
bool get_ssl_use_session_resumption() const {return ssl_use_session_resumption;}
bool get_ssl_initialized() const {return ssl_initialized;}
char * get_ssl_key_file() const {return ssl_key_file;}
char * get_ssl_certificate_file() const {return ssl_certificate_file;}
char * get_ssl_trustedCAlist_file() const {return ssl_trustedCAlist_file;}
char * get_ssl_cipher_list() const {return ssl_cipher_list;}
char * get_ssl_password() const;
const unsigned char * get_ssl_server_auth_session_id_context() const {return ssl_server_auth_session_id_context;}
// const SSL_METHOD * get_current_ssl_method() const {return ssl_method;}
// const SSL_CIPHER * get_current_ssl_cipher() const {return ssl_cipher;}
SSL_SESSION* get_current_ssl_session() const {return ssl_session;}
SSL_CTX * get_current_ssl_ctx() const {return ssl_ctx;}
SSL * get_current_ssl() const {return ssl_current_ssl;}
// The following members can be called to set the current values
// NOTE that in case the parameter_value is a char *pointer, the old character
// array is deleted by these functions automatically.
void set_ssl_use_ssl(bool parameter_value);
void set_ssl_verifycertificate(bool parameter_value);
void set_ssl_use_session_resumption(bool parameter_value);
void set_ssl_key_file(char * parameter_value);
void set_ssl_certificate_file(char * parameter_value);
void set_ssl_trustedCAlist_file(char * parameter_value);
void set_ssl_cipher_list(char * parameter_value);
void set_ssl_server_auth_session_id_context(const unsigned char * parameter_value);
// The following members can be called to fetch the default test port parameter names
virtual const char* ssl_use_ssl_name();
virtual const char* ssl_use_session_resumption_name();
virtual const char* ssl_private_key_file_name();
virtual const char* ssl_trustedCAlist_file_name();
virtual const char* ssl_certificate_file_name();
virtual const char* ssl_password_name();
virtual const char* ssl_cipher_list_name();
virtual const char* ssl_verifycertificate_name();
virtual const char* ssl_disable_SSLv2();
virtual const char* ssl_disable_SSLv3();
virtual const char* ssl_disable_TLSv1();
virtual const char* ssl_disable_TLSv1_1();
virtual const char* ssl_disable_TLSv1_2();
private:
bool ssl_verify_certificate; // verify other part's certificate or not
bool ssl_use_ssl; // whether to use SSL
bool ssl_initialized; // whether SSL already initialized or not
bool ssl_use_session_resumption; // use SSL sessions or not
bool SSLv2;
bool SSLv3;
bool TLSv1;
bool TLSv1_1;
bool TLSv1_2;
char *ssl_key_file; // private key file
char *ssl_certificate_file; // own certificate file
char *ssl_trustedCAlist_file; // trusted CA list file
char *ssl_cipher_list; // ssl_cipher list restriction to apply
char *ssl_password; // password to decode the private key
static const unsigned char * ssl_server_auth_session_id_context;
// const SSL_METHOD *ssl_method; // SSL context method
SSL_CTX *ssl_ctx; // SSL context
// const SSL_CIPHER *ssl_cipher; // used SSL ssl_cipher
SSL_SESSION *ssl_session; // SSL ssl_session
SSL *ssl_current_ssl; // currently used SSL object
static void *ssl_current_client; // current SSL object, used only during authentication
void ssl_actions_to_seed_PRNG(); // Seed the PRNG with enough random data
void ssl_init_SSL(); // Initialize SSL libraries and create the SSL context
void ssl_log_SSL_info(); // Log the currently used SSL setting (debug)
int ssl_getresult(int result_code); // Fetch and log the SSL error code from I/O operation result codes
// Callback function to pass the password to OpenSSL. Called by OpenSSL
// during SSL handshake.
static int ssl_password_cb(char * password_buffer, int length_of_password, int rw_flag, void * user_data);
// Callback function to perform authentication during SSL handshake. Called by OpenSSL.
// NOTE: for further authentication, use ssl_verify_certificates().
static int ssl_verify_callback(int preverify_status, X509_STORE_CTX * ssl_context);
};
#endif
#endif
suite := AtsImsIot
sources := \
AtsImsIot_TypesAndValues.ttcn \
AtsImsIot_Diameter_Templates.ttcn \
AtsImsIot_Templates.ttcn \
AtsImsIot_Templates_GM.ttcn \
AtsImsIot_Templates_IC.ttcn \
AtsImsIot_Templates_MW.ttcn \
AtsImsIot_Templates_MI.ttcn \
AtsImsIot_Templates_MM.ttcn \
AtsImsIot_Templates_MX.ttcn \
AtsImsIot_Templates_ML.ttcn \
AtsImsIot_TestConfiguration.ttcn \
AtsImsIot_TP_behavior_CX.ttcn \
AtsImsIot_TP_behavior_ISC.ttcn \
AtsImsIot_TP_behavior_SH.ttcn \
AtsImsIot_TP_behavior_GM.ttcn \
AtsImsIot_TP_behavior_MW_IS.ttcn \
AtsImsIot_TP_behavior_RX.ttcn \
AtsImsIot_TP_behavior_GX.ttcn \
AtsImsIot_TP_behavior_MW_PS.ttcn \
AtsImsIot_TP_behavior_MM.ttcn \
AtsImsIot_TP_behavior_ML.ttcn \
AtsImsIot_TP_behavior_MI.ttcn \
AtsImsIot_TP_behavior_MX.ttcn \
AtsImsIot_TP_behavior_S6A.ttcn \
AtsImsIot_TD_ATT.ttcn \
AtsImsIot_TD_REG.ttcn \
AtsImsIot_Functions.ttcn \
AtsImsIot_Emergency.ttcn \
AtsImsIot_TD_DRG.ttcn \
AtsImsIot_TD_DTC.ttcn \
AtsImsIot_TestControl.ttcn \
AtsImsIot_PIXITS.ttcn \
AtsImsIot_TD_INI.ttcn \
../patch_ats_ims_iot/ttcn/AtsImsIot_TestSystem.ttcn \
AtsImsIot_TP_behavior_IC.ttcn \
AtsImsIot_TP_behavior_MW_SI.ttcn \
AtsImsIot_TP_behavior_S9.ttcn \
modules := ../LibCommon \
../LibIms \
../LibIot \
../LibMsrp \
../LibUpperTester \
../LibIms_ConfigAndTrigger \
../LibSip \
../LibDiameter \
../../titan-test-system-framework/ttcn/LibHelpers \
../../titan-test-system-framework/ccsrc/Framework \
../../titan-test-system-framework/ccsrc/Helpers \
../../titan-test-system-framework/ccsrc/loggers \
../../titan-test-system-framework/ccsrc/Protocols/ETH \
../../titan-test-system-framework/ccsrc/Protocols/Tcp \
../../titan-test-system-framework/ccsrc/Protocols/UDP \
../../titan-test-system-framework/ccsrc/Protocols/Xml \
../../ccsrc/Ports/LibSip \
../../ccsrc/Ports/ImsMonitorSipPort \
../../ccsrc/Ports/LibDiameter \
../../ccsrc/Ports/ImsMonitorDiameterPort \
../../ccsrc/Ports/Naptr \
../../ccsrc/Ports/Data \
../../ccsrc/Ports/Sgi \
../../ccsrc/Ports/LibIot \
../../ccsrc/EncDec/LibDiameter \
../../ccsrc/EncDec/LibSip \
../../ccsrc/EncDec/LibMsrp \
../../ccsrc/Protocols/Diameter \
../../ccsrc/Protocols/LowerLayerPPort \
../../ccsrc/Protocols/Sip \
../../ccsrc/Protocols/Naptr \
../../ccsrc/Protocols/UpperTester \
../modules/titan.TestPorts.Common_Components.Abstract_Socket \
module AtsImsIot_TestSystem {
import from LibSip_SIPTypesAndValues all;
import from LibMsrp_TypesAndValues all;
import from AtsImsIot_TypesAndValues all;
import from LibIot_TestInterface all;
import from LibIot_TestInterface all;
import from LibIot_TypesAndValues all;
import from LibDiameter_TypesAndValues all;
/**
* @desc interfaces to the SUT
*/
type component IotSystemInterface {
port DataPort dPort;
port ImsMonitorSipPort sipPort;
port ImsMonitorDiameterPort diameterPort;
port SgiPort sgiPort;
port EquipmentAccessPort eaPort;
port AdapterConfigPort acPort;
}
/**
* @desc
* providing monitoring functionality of involved interfaces. Used as
* PTC.
*/
type component ImsInterfaceMonitor extends InterfaceMonitor {
port DataPort dPort;
port NaptrPort naptrPort;
port ImsMonitorDiameterPort rxPort;
port SgiPort sgiPort;
port ImsCoordinationPort icpPort;
}
type component SipInterfaceMonitor extends InterfaceMonitor {
port ImsMonitorSipPort sipPort;
port ImsCoordinationPort icpPort;
}
type component DiameterInterfaceMonitor extends InterfaceMonitor {
port ImsMonitorDiameterPort diameterPort;
port ImsCoordinationPort icpPort;
}
type component NaptrInterfaceMonitor extends InterfaceMonitor {
port NaptrPort naptrPort;
port ImsCoordinationPort icpPort;
}
type component SgiInterfaceMonitor extends InterfaceMonitor {
port SgiPort sgiPort;
port ImsCoordinationPort icpPort;
}
/**
* @desc
* used to coordinate the behavior of other components. It is in charge
* of controlling the overall execution, manangement of testing phases,
* test verdicts collection and synchronization. Used as MTC.
*/
type component ImsTestCoordinator extends TestCoordinator {
port ImsCoordinationPort icpPort;
var VxLTEMonitorInterfaceList vc_MonIntfList;
var CF_VXLTE_Interfaces vc_vxlte_monitor_components;
}
group portDefinitions {
type port ImsCoordinationPort message {
inout SipMessage, DIAMETER_MSG;
} with {
extension "internal"
}
type port DataPort message {
in Request, Response, SEND_request, RECEIVE_response; // SIP
}
type port ImsMonitorSipPort message { //type port SipPort message
in Request, Response, SEND_request, RECEIVE_response; // SIP
}
type port NaptrPort message {
in NAPTRmessage;
}
type port ImsMonitorDiameterPort message {
in DIAMETER_MSG;
}
type port SgiPort message {
in charstring;
}
}
type record CF_INT_CALL {
ImsInterfaceMonitor gmA,
ImsInterfaceMonitor mxA,
ImsInterfaceMonitor ici,
ImsInterfaceMonitor mxB,
ImsInterfaceMonitor gmB optional,
ImsInterfaceMonitor naptr optional
}
type record CF_INT_AS {
ImsInterfaceMonitor gmA,
ImsInterfaceMonitor ici,
ImsInterfaceMonitor mxA,
ImsInterfaceMonitor gmB,
ImsInterfaceMonitor mxB,
ImsInterfaceMonitor iscA optional,
ImsInterfaceMonitor iscB optional
}
type record CF_ROAM_AS {
ImsInterfaceMonitor gmA,
ImsInterfaceMonitor mxA,
ImsInterfaceMonitor ici,
ImsInterfaceMonitor mxB,
ImsInterfaceMonitor gmB,
ImsInterfaceMonitor iscA optional,
ImsInterfaceMonitor iscB optional
}
type record CF_EPC_CALL {
ImsInterfaceMonitor gmA, // See ETSI TS 103 029 V3.1.1 clause 5.4.1.1
ImsInterfaceMonitor rx, // See ETSI TS 103 029 V3.1.1 clause 5.4.2
ImsInterfaceMonitor mxA,
ImsInterfaceMonitor mw,
ImsInterfaceMonitor sgi,
ImsInterfaceMonitor gmB // See ETSI TS 103 029 V3.1.1 clause 5.4.1.1
}
group g_release15 {
type record CF_VXLTE_Interfaces{
SipInterfaceMonitor gmA optional,
SipInterfaceMonitor gmB optional,
SipInterfaceMonitor mmB_PSAP optional,// MmMx interface at IBCF to PSAP
SipInterfaceMonitor mwEB optional,// Mw interface at E-CSCF/IBCF or E-CSCF/BGCF
SipInterfaceMonitor mlE_LRF optional,// Ml interface at E-CSCF/LRF
SipInterfaceMonitor mwS_PSAP optional,// MwMmMx interface at E,SCSCF to PSAP
SipInterfaceMonitor ic optional,
SipInterfaceMonitor mwPI optional,// Mw interface at P-CSCF/I-CSCF or P-CSCF/S-CSCF if I-CSCF not used
SipInterfaceMonitor mwPS optional,
SipInterfaceMonitor mwPE optional,// Mw interface at P-CSCF to E-CSCF
SipInterfaceMonitor mwIS optional,// Mw interface at I-CSCF/S-CSCF
SipInterfaceMonitor mwIE optional,// Mw interface at I-CSCF/S-CSCF to E-CSCF
SipInterfaceMonitor mwPB optional,// Mw interface at P-CSCF/IBCF
SipInterfaceMonitor mwIB optional,// Mw interface at I-CSCF/IBCF
SipInterfaceMonitor isc optional,
DiameterInterfaceMonitor cxIH optional,
DiameterInterfaceMonitor cxSH optional,
DiameterInterfaceMonitor gx optional,
DiameterInterfaceMonitor rx optional,
DiameterInterfaceMonitor s6a optional,
DiameterInterfaceMonitor s9 optional,
DiameterInterfaceMonitor sh optional
}
type record CF_ATT_old {
ImsInterfaceMonitor gmA,
ImsInterfaceMonitor rx,
ImsInterfaceMonitor s6a,
ImsInterfaceMonitor gx,
ImsInterfaceMonitor mxA,
ImsInterfaceMonitor mwPS, // Mw interface at P-CSCF/I-CSCF or P-CSCF/S-CSCF if I-CSCF not used
ImsInterfaceMonitor mwIS, // Mw interface at I-CSCF/S-CSCF
ImsInterfaceMonitor mwSI, // Mw interface at S-CSCF/IBCF
ImsInterfaceMonitor sgi,
ImsInterfaceMonitor gmB
}
type record CF_ATT {
SipInterfaceMonitor gmA,
DiameterInterfaceMonitor rx,
DiameterInterfaceMonitor s6a,
DiameterInterfaceMonitor gx,
SipInterfaceMonitor mxA,
SipInterfaceMonitor mw,
SgiInterfaceMonitor sgi,
SipInterfaceMonitor gmB
}
} // end of g_release15
}
\ No newline at end of file
sources := \
ttcn/LibCommon_AbstractData.ttcn \
ttcn/LibCommon_TextStrings.ttcn \
ttcn/LibCommon_Time.ttcn \
ttcn/LibCommon_VerdictControl.ttcn \
../patch_lib_common_titan/ttcn/LibCommon_BasicTypesAndValues.ttcn \
../patch_lib_common_titan/ttcn/LibCommon_DataStrings.ttcn \
../patch_lib_common_titan/ttcn/LibCommon_Sync.ttcn \
/**
* @author ETSI
* @version $URL$
* $Id$
* @desc A collection of basic type and value definitions which may be
* useful in the implementation of any TTCN-3 test suite. <br><br>
* @remark End users should be aware that any changes made to the in
* definitions this module may be overwritten in future releases.
* End users are encouraged to contact the distributers of this
* module regarding their modifications or additions so that future
* updates will include your changes.
* @copyright ETSI Copyright Notification
* No part may be reproduced except as authorized by written permission.
* The copyright and the foregoing restriction extend to reproduction in all media.
* All rights reserved.
*
*/
module LibCommon_BasicTypesAndValues {
/**
* @remark Number in subtype name always indicates encoding length
* in _bits_
*/
group unsignedIntegerDefintions {
const integer c_uInt1Max := 1;
const integer c_uInt2Max := 3;
const integer c_uInt3Max := 7;
const integer c_uInt4Max := 15;
const integer c_uInt5Max := 31;
const integer c_uInt6Max := 63;
const integer c_uInt7Max := 127;
const integer c_uInt8Max := 255;
const integer c_uInt9Max := 511;
const integer c_uInt10Max := 1023;
const integer c_uInt11Max := 2047;
const integer c_uInt12Max := 4095;
const integer c_uInt13Max := 8191;
const integer c_uInt14Max := 16383;
const integer c_uInt15Max := 32767;
const integer c_uInt16Max := 65535;
const integer c_uInt17Max := 131071;
const integer c_uInt18Max := 262143;
const integer c_uInt19Max := 524287;
const integer c_uInt20Max := 1048575;
const integer c_uInt21Max := 2097151;
const integer c_uInt22Max := 4194303;
const integer c_uInt23Max := 8388607;
const integer c_uInt24Max := 16777215;
const integer c_uInt25Max := 33554431;
const integer c_uInt26Max := 67108863;
const integer c_uInt27Max := 134217727;
const integer c_uInt28Max := 268435456;
const integer c_uInt29Max := 536870911;
const integer c_uInt30Max := 1073741823;
const integer c_uInt31Max := 2147483647;
const integer c_uInt32Max := 4294967295;
const integer c_uInt36Max := 68719476735;
const integer c_uInt48Max := 281474976710655;
const integer c_uInt52Max := 4503599627370495;
const integer c_uInt64Max := 18446744073709551615;
type integer UInt (0 .. infinity);
type integer UInt1 (0 .. c_uInt1Max) with {variant "unsigned 1 bit"};
type integer UInt2 (0 .. c_uInt2Max) with {variant "unsigned 2 bit"};
type integer UInt3 (0 .. c_uInt3Max) with {variant "unsigned 3 bit"};
type integer UInt4 (0 .. c_uInt4Max) with {variant "unsigned 4 bit"};
type integer UInt5 (0 .. c_uInt5Max) with {variant "unsigned 5 bit"};
type integer UInt6 (0 .. c_uInt6Max) with {variant "unsigned 6 bit"};
type integer UInt7 (0 .. c_uInt7Max) with {variant "unsigned 7 bit"};
type integer UInt8 (0 .. c_uInt8Max) with {variant "unsigned 8 bit"};
type integer UInt9 (0 .. c_uInt9Max) with {variant "unsigned 9 bit"};
type integer UInt10 (0 .. c_uInt10Max) with {variant "unsigned 10 bit"};
type integer UInt11 (0 .. c_uInt11Max) with {variant "unsigned 11 bit"};
type integer UInt12 (0 .. c_uInt12Max) with {variant "unsigned 12 bit"};
type integer UInt13 (0 .. c_uInt13Max) with {variant "unsigned 13 bit"};
type integer UInt14 (0 .. c_uInt14Max) with {variant "unsigned 14 bit"};
type integer UInt15 (0 .. c_uInt15Max) with {variant "unsigned 15 bit"};
type integer UInt16 (0 .. c_uInt16Max) with {variant "unsigned 16 bit"};
type integer UInt17 (0 .. c_uInt17Max) with {variant "unsigned 17 bit"};
type integer UInt18 (0 .. c_uInt18Max) with {variant "unsigned 18 bit"};
type integer UInt19 (0 .. c_uInt19Max) with {variant "unsigned 19 bit"};
type integer UInt20 (0 .. c_uInt20Max) with {variant "unsigned 20 bit"};
type integer UInt21 (0 .. c_uInt21Max) with {variant "unsigned 21 bit"};
type integer UInt22 (0 .. c_uInt22Max) with {variant "unsigned 22 bit"};
type integer UInt23 (0 .. c_uInt23Max) with {variant "unsigned 23 bit"};
type integer UInt24 (0 .. c_uInt24Max) with {variant "unsigned 24 bit"};
type integer UInt25 (0 .. c_uInt25Max) with {variant "unsigned 25 bit"};
type integer UInt26 (0 .. c_uInt26Max) with {variant "unsigned 26 bit"};
type integer UInt27 (0 .. c_uInt27Max) with {variant "unsigned 27 bit"};
type integer UInt28 (0 .. c_uInt28Max) with {variant "unsigned 28 bit"};
type integer UInt29 (0 .. c_uInt29Max) with {variant "unsigned 29 bit"};
type integer UInt30 (0 .. c_uInt30Max) with {variant "unsigned 30 bit"};
type integer UInt31 (0 .. c_uInt31Max) with {variant "unsigned 31 bit"};
type integer UInt32 (0 .. c_uInt32Max) with {variant "unsigned 32 bit"};
type integer UInt36 (0 .. c_uInt36Max) with {variant "unsigned 36 bit"};
type integer UInt48 (0 .. c_uInt48Max) with {variant "unsigned 48 bit"};
type integer UInt52 (0 .. c_uInt52Max) with {variant "unsigned 52 bit"};
type integer UInt64 (0 .. c_uInt64Max) with {variant "unsigned 64 bit"};
} // end group unsignedIntegerDefintions
/**
* @remark Number in subtype name always indicates encoding length
* in _bits_
*/
group signedIntegerDefintions {
const integer c_int1Min := -1;
const integer c_int1Max := 0;
const integer c_int2Min := -2;
const integer c_int2Max := 1;
const integer c_int3Min := -4;
const integer c_int3Max := 3;
const integer c_int4Min := -8;
const integer c_int4Max := 7;
const integer c_int5Min := -16;
const integer c_int5Max := 15;
const integer c_int6Min := -32;
const integer c_int6Max := 31;
const integer c_int7Min := -64;
const integer c_int7Max := 63;
const integer c_int8Min := -128;
const integer c_int8Max := 127;
const integer c_int9Min := -256;
const integer c_int9Max := 255;
const integer c_int10Min := -512;
const integer c_int10Max := 511;
const integer c_int11Min := -1024;
const integer c_int11Max := 1023;
const integer c_int12Min := -2048;
const integer c_int12Max := 2047;
const integer c_int13Min := -4096;
const integer c_int13Max := 4095;
const integer c_int14Min := -8192;
const integer c_int14Max := 8191;
const integer c_int15Min := -16384;
const integer c_int15Max := 16383;
const integer c_int16Min := -32768;
const integer c_int16Max := 32767;
const integer c_int17Min := -65536;
const integer c_int17Max := 65535;
const integer c_int18Min := -131072;
const integer c_int18Max := 131071;
const integer c_int19Min := -262144;
const integer c_int19Max := 262143;
const integer c_int20Min := -524288;
const integer c_int20Max := 524287;
const integer c_int21Min := -1048576;
const integer c_int21Max := 1048575;
const integer c_int22Min := -2097152;
const integer c_int22Max := 2097151;
const integer c_int23Min := -4194304;
const integer c_int23Max := 4194303;
const integer c_int24Min := -8388608;
const integer c_int24Max := 8388607;
const integer c_int25Min := -16777216;
const integer c_int25Max := 16777215;
const integer c_int26Min := -33554432;
const integer c_int26Max := 33554431;
const integer c_int27Min := -67108864;
const integer c_int27Max := 67108863;
const integer c_int28Min := -134217728;
const integer c_int28Max := 134217727;
const integer c_int29Min := -268435456;
const integer c_int29Max := 268435456;
const integer c_int30Min := -536870912;
const integer c_int30Max := 536870911;
const integer c_int31Min := -1073741824;
const integer c_int31Max := 1073741823;
const integer c_int32Min := -2147483648;
const integer c_int32Max := 2147483647;
const integer c_int64Min := -9223372036854775808;
const integer c_int64Max := 9223372036854775807;
type integer Int;
type integer Int1 (c_int1Min .. c_int1Max) with { variant "1 bit"};
type integer Int2 (c_int2Min .. c_int2Max) with { variant "2 bit"};
type integer Int3 (c_int3Min .. c_int3Max) with { variant "3 bit"};
type integer Int4 (c_int4Min .. c_int4Max) with { variant "4 bit"};
type integer Int5 (c_int5Min .. c_int5Max) with { variant "5 bit"};
type integer Int6 (c_int6Min .. c_int6Max) with { variant "6 bit"};
type integer Int7 (c_int7Min .. c_int7Max) with { variant "7 bit"};
type integer Int8 (c_int8Min .. c_int8Max) with { variant "8 bit"};
type integer Int9 (c_int9Min .. c_int9Max) with { variant "9 bit"};
type integer Int10 (c_int10Min .. c_int10Max) with { variant "10 bit"};
type integer Int11 (c_int11Min .. c_int11Max) with { variant "11 bit"};
type integer Int12 (c_int12Min .. c_int12Max) with { variant "12 bit"};
type integer Int13 (c_int13Min .. c_int13Max) with { variant "13 bit"};
type integer Int14 (c_int14Min .. c_int14Max) with { variant "14 bit"};
type integer Int15 (c_int15Min .. c_int15Max) with { variant "15 bit"};
type integer Int16 (c_int16Min .. c_int16Max) with { variant "16 bit"};
type integer Int17 (c_int17Min .. c_int17Max) with { variant "17 bit"};
type integer Int18 (c_int18Min .. c_int18Max) with { variant "18 bit"};
type integer Int19 (c_int19Min .. c_int19Max) with { variant "19 bit"};
type integer Int20 (c_int20Min .. c_int20Max) with { variant "20 bit"};
type integer Int21 (c_int21Min .. c_int21Max) with { variant "21 bit"};
type integer Int22 (c_int22Min .. c_int22Max) with { variant "22 bit"};
type integer Int23 (c_int23Min .. c_int23Max) with { variant "23 bit"};
type integer Int24 (c_int24Min .. c_int24Max) with { variant "24 bit"};
type integer Int25 (c_int25Min .. c_int25Max) with { variant "25 bit"};
type integer Int26 (c_int26Min .. c_int26Max) with { variant "26 bit"};
type integer Int27 (c_int27Min .. c_int27Max) with { variant "27 bit"};
type integer Int28 (c_int28Min .. c_int28Max) with { variant "28 bit"};
type integer Int29 (c_int29Min .. c_int29Max) with { variant "29 bit"};
type integer Int30 (c_int30Min .. c_int30Max) with { variant "30 bit"};
type integer Int31 (c_int31Min .. c_int31Max) with { variant "31 bit"};
type integer Int32 (c_int32Min .. c_int32Max) with { variant "32 bit"};
type integer Int64 (c_int64Min .. c_int64Max) with { variant "64 bit"};
} // end group signedIntegerDefintions
group zeroedIntegers {
const UInt1 c_uInt1Zero := 0;
const UInt2 c_uInt2Zero := 0;
const UInt3 c_uInt3Zero := 0;
const UInt4 c_uInt4Zero := 0;
const UInt5 c_uInt5Zero := 0;
const UInt6 c_uInt6Zero := 0;
const UInt7 c_uInt7Zero := 0;
const UInt8 c_uInt8Zero := 0;
const UInt10 c_uInt10Zero := 0;
const UInt12 c_uInt12Zero := 0;
const UInt14 c_uInt14Zero := 0;
const UInt16 c_uInt16Zero := 0;
const UInt24 c_uInt24Zero := 0;
const UInt32 c_uInt32Zero := 0;
const UInt48 c_uInt48Zero := 0;
}//end group zeroedInt
/**
* @remark Number in subtype name always indicates encoding length
* in _bits_
*/
group booleanDefintions {
type boolean Bool1 with { variant "1 bit" };
type boolean Bool2 with { variant "2 bit" };
type boolean Bool3 with { variant "3 bit" };
type boolean Bool4 with { variant "4 bit" };
type boolean Bool5 with { variant "5 bit" };
type boolean Bool6 with { variant "6 bit" };
type boolean Bool7 with { variant "7 bit" };
type boolean Bool8 with { variant "8 bit" };
} // end group booleanDefintions
} // end module LibCommon_BasicTypesAndValues
/**
*
* @author ETSI
* @version $URL$
* $Id$
* @desc A collection of data string type and value definitions which
* may be useful in the implementation of any TTCN-3 test
* suite. "Data string" refers to TTCN-3 hexstring, octetstring
* and bitstring types.
* @remark End users should be aware that any changes made to the in
* definitions this module may be overwritten in future releases.
* End users are encouraged to contact the distributers of this
* module regarding their modifications or additions so that future
* updates will include your changes.
* @copyright ETSI Copyright Notification
* No part may be reproduced except as authorized by written permission.
* The copyright and the foregoing restriction extend to reproduction in all media.
* All rights reserved.
*
*/
module LibCommon_DataStrings {
/**
* @remark Number in name indicates string length in number of
* _bits_
*/
group bitStringSubTypes {
type bitstring Bit1 length(1) with {encode "length(1)"};
type bitstring Bit2 length(2) with {encode "length(2)"};
type bitstring Bit3 length(3) with {encode "length(3)"};
type bitstring Bit4 length(4) with {encode "length(4)"};
type bitstring Bit5 length(5) with {encode "length(5)"};
type bitstring Bit6 length(6) with {encode "length(6)"};
type bitstring Bit7 length(7) with {encode "length(7)"};
type bitstring Bit8 length(8) with {encode "length(8)"};
type bitstring Bit9 length(9) with {encode "length(9)"};
type bitstring Bit10 length(10) with {encode "length(10)"};
type bitstring Bit11 length(11) with {encode "length(11)"};
type bitstring Bit12 length(12) with {encode "length(12)"};
type bitstring Bit13 length(13) with {encode "length(13)"};
type bitstring Bit14 length(14) with {encode "length(14)"};
type bitstring Bit15 length(15) with {encode "length(15)"};
type bitstring Bit16 length(16) with {encode "length(16)"};
type bitstring Bit17 length(17) with {encode "length(17)"};
type bitstring Bit18 length(18) with {encode "length(18)"};
type bitstring Bit19 length(19) with {encode "length(19)"};
type bitstring Bit20 length(20) with {encode "length(20)"};
type bitstring Bit21 length(21) with {encode "length(21)"};
type bitstring Bit22 length(22) with {encode "length(22)"};
type bitstring Bit23 length(23) with {encode "length(23)"};
type bitstring Bit24 length(24) with {encode "length(24)"};
type bitstring Bit25 length(25) with {encode "length(25)"};
type bitstring Bit26 length(26) with {encode "length(26)"};
type bitstring Bit27 length(27) with {encode "length(27)"};
type bitstring Bit28 length(28) with {encode "length(28)"};
type bitstring Bit29 length(29) with {encode "length(29)"};
type bitstring Bit30 length(30) with {encode "length(30)"};
type bitstring Bit31 length(31) with {encode "length(31)"};
type bitstring Bit32 length(32) with {encode "length(32)"};
type bitstring Bit40 length(40) with {encode "length(40)"};
type bitstring Bit48 length(48) with {encode "length(48)"};
type bitstring Bit64 length(64) with {encode "length(64)"};
type bitstring Bit72 length(72) with {encode "length(72)"};
type bitstring Bit128 length(128) with {encode "length(128)"};
type bitstring Bit144 length(144) with {encode "length(144)"};
type bitstring Bit256 length(256) with {encode "length(256)"};
} // end group bitStringSubTypes
group zeroedBits {
const Bit1 c_1ZeroBit := int2bit(0,1);
const Bit2 c_2ZeroBits := int2bit(0,2);
const Bit4 c_4ZeroBits := int2bit(0,4);
const Bit5 c_5ZeroBits := int2bit(0,5);
const Bit6 c_6ZeroBits := int2bit(0,6);
const Bit8 c_8ZeroBits := int2bit(0,8);
const Bit14 c_14ZeroBits := int2bit(0,14);
const Bit64 c_64ZeroBits := int2bit(0,64);
}//end group zeroedBits
/**
* @remark Number in name indicates string length in number of
* _octets_
*/
group octetStringSubTypes {
type octetstring Oct1 length(1) with {encode "length(1)"};
type octetstring Oct2 length(2) with {encode "length(2)"};
type octetstring Oct3 length(3) with {encode "length(3)"};
type octetstring Oct4 length(4) with {encode "length(4)"};
type octetstring Oct5 length(5) with {encode "length(5)"};
type octetstring Oct6 length(6) with {encode "length(6)"};
type octetstring Oct7 length(7) with {encode "length(7)"};
type octetstring Oct8 length(8) with {encode "length(8)"};
type octetstring Oct9 length(9) with {encode "length(9)"};
type octetstring Oct10 length(10) with {encode "length(10)"};
type octetstring Oct11 length(11) with {encode "length(11)"};
type octetstring Oct12 length(12) with {encode "length(12)"};
type octetstring Oct13 length(13) with {encode "length(13)"};
type octetstring Oct14 length(14) with {encode "length(14)"};
type octetstring Oct15 length(15) with {encode "length(15)"};
type octetstring Oct16 length(16) with {encode "length(16)"};
type octetstring Oct20 length(20) with {encode "length(20)"};
type octetstring Oct32 length(32) with {encode "length(32)"};
type octetstring Oct48 length(48) with {encode "length(48)"};
type octetstring Oct64 length(64) with {encode "length(64)"};
type octetstring Oct80 length(80) with {encode "length(80)"};
type octetstring Oct96 length(96) with {encode "length(96)"};
type octetstring Oct128 length(128) with {encode "length(128)"};
type octetstring Oct160 length(160) with {encode "length(160)"};
type octetstring Oct320 length(320) with {encode "length(320)"};
type octetstring Oct640 length(640) with {encode "length(640)"};
type octetstring Oct1280 length(1280) with {encode "length(1280)"};
type octetstring Oct1380 length(1380) with {encode "length(1380)"};
type octetstring Oct0to3 length(0..3) with {encode "length(0..3)"};
type octetstring Oct0to8 length(0..16) with {encode "length(0..8)"};
type octetstring Oct0to12 length(0..12) with {encode "length(0..12)"};
type octetstring Oct0to16 length(0..16) with {encode "length(0..16)"};
type octetstring Oct0to31 length(0..31) with {encode "length(0..31)"};
type octetstring Oct0to20 length(0..20) with {encode "length(0..20)"};
type octetstring Oct0to30 length(0..20) with {encode "length(0..30)"};
type octetstring Oct0to127 length(0..127) with {encode "length(0..127)"};
type octetstring Oct0to255 length(0..255) with {encode "length(0..255)"};
type octetstring Oct1to15 length(1..15) with {encode "length(1..15)"};
type octetstring Oct1to31 length(1..31) with {encode "length(1..31)"};
type octetstring Oct1to128 length(1..128) with {encode "length(1..128)"};
type octetstring Oct1to254 length(1..254) with {encode "length(1..254)"};
type octetstring Oct1to255 length(1..255) with {encode "length(1..255)"};
type octetstring Oct4to16 length(4..16) with {encode "length(4..16)"};
type octetstring Oct6to15 length(6..15) with {encode "length(6..15)"};
} // end group octetStringSubTypes
group zeroedBytes {
const Oct1 c_1ZeroByte := int2oct(0,1);
const Oct2 c_2ZeroBytes := int2oct(0,2);
const Oct4 c_4ZeroBytes := int2oct(0,4);
const Oct6 c_6ZeroBytes := int2oct(0,6);
const Oct8 c_8ZeroBytes := int2oct(0,8);
const Oct9 c_9ZeroBytes := int2oct(0,9);
const Oct12 c_12ZeroBytes := int2oct(0,12);
const Oct16 c_16ZeroBytes := int2oct(0,16);
const Oct20 c_20ZeroBytes := int2oct(0,20);
const Oct80 c_80ZeroBytes := int2oct(0,80);
const Oct160 c_160ZeroBytes := int2oct(0,160);
const Oct320 c_320ZeroBytes := int2oct(0,320);
const Oct640 c_640ZeroBytes := int2oct(0,640);
const Oct1280 c_1280ZeroBytes := int2oct(0,1280);
const Oct1380 c_1380ZeroBytes := int2oct(0,1380);
const octetstring c_256ZeroBytes := int2oct(0,256);
const octetstring c_1KZeroBytes := int2oct(0,1024);
const octetstring c_4KZeroBytes := int2oct(0,4096);
const octetstring c_16KZeroBytes := int2oct(0,16384);
const octetstring c_64KZeroBytes := int2oct(0,65536);
const octetstring c_128KZeroBytes := int2oct(0,131072);
}//end group zeroedBytes
} // end module LibCommon_DataStrings
/**
* @author ETSI
* @version $URL$
* $Id$
* @desc This module implements _one_ generic synchronization mechanism
* for TTCN-3 test cases with one or more test components.
* Key concept is here that one test component acts as a
* synchronization server which listens and triggers one or more
* synchronization clients. It is recomended to use the MTC always as
* the synchronization server but in theory also a PTC can act as such
* a server.<br><br>
* This synchronization is used by calling a function on
* the server test component to wait for a desired amount of clients
* to notify the server that they have reached a specific synchronization
* point. Each client test component must call another
* function to perform this notification.<br><br>
* In the event that a client is not able to reach a synchronization
* point the server sends out a signal to all clients to abort the
* test case. This signal is a STOP message which can be caught by
* a test component default which in turn can then run a proper
* shut down behavior based on the current state of the test
* component. <br><br>
* Note that this synchronization mechanism can also be used
* in a special mode called "self synchronization" when a test case
* only has one test component. Here, the test component in essence
* acts as a server and client at the same time. The main benefit of
* using self synchoronization is that the same shutdown mechanisms
* can also be reused fomr the multi component test cases. <br><br>
* This module contains a lot of TTCN-3 definitions. It has been
* structured into tree main groups to help the user to identify
* quickly relevant TTCN-3 definitions. For rookie users of this
* module basicUserRelevantDefinitions should offer all the needed
* definitions. Advanced users can consider use of definitions in
* advancedUserRelevantDefinitions. Finally, internalDefinitions
* are definitions which are required for the module to work
* properly but do not need to be used in your code. Remember that
* the main motiviation of this sychronization module is to offer
* are _simple_ user interface. Practice has shown that when writing
* actual test component behavior _only a handful_ of functions
* usually wind up being used! Also check the synchronization examples
* module for example uses of this synchronization mechanism.<br><br>
* The invocation of the sync functions is also closely tied
* to the verdict control functions which should also be reviewed
* prior to using this module. <br><br>
* This module has been derived from EtsiCommon_Synchronization
* which was created in ETSIs STF256/276. It has been kept
* intentionally separate to avoid conflicts with future ETSI
* test suite releases.
* @see LibCommon_Sync.basicUserRelevantDefinitions
* @see LibCommon_Sync.advancedUserRelevantDefinitions
* @remark End users should be aware that any changes made to the in
* definitions this module may be overwritten in future releases.
* End users are encouraged to contact the distributers of this
* module regarding their modifications or additions so that future
* updates will include your changes.
* @copyright ETSI Copyright Notification
* No part may be reproduced except as authorized by written permission.
* The copyright and the foregoing restriction extend to reproduction in all media.
* All rights reserved.
*
*/
module LibCommon_Sync {
//Common
import from LibCommon_BasicTypesAndValues { type UInt } ;
import from LibCommon_AbstractData all;
import from LibCommon_VerdictControl all;
group basicUserRelevantDefinitions {
group importantSyncTypeDefinitions {
group compTypeRelated {
/**
* @desc This type is used to be the base of any synchronization
* behavior which is to be executed on a sync server
* component. The test component which acts as a
* sync server in a test case must NOT directly use
* this component type in its runs on clause!
* Note that server synchronization functions may be
* invoked by a test component as long as its
* component type is type compatible to this component
* type definition!
*/
type component BaseSyncComp {
port SyncPort syncPort;
timer tc_sync := PX_TSYNC_TIME_LIMIT;
}
/**
* @desc This type is used to define any synchronization
* behavior which is to be executed on a sync server
* component. The test component which acts as a
* sync server in a test case may - but does
* not have to - directly use this component type its
* runs on clause.
* Note that server synchronization functions may be
* invoked by a test component as long as its
* component type is type compatible to this component
* type definition!
*/
type component ServerSyncComp extends BaseSyncComp {
timer tc_shutDown := PX_TSHUT_DOWN_TIME_LIMIT;
}
/**
* @desc This type is used to define any synchronization
* behavior which is to be executed on a sync client
* component. The test component(s) which act as a
* sync client in a test case may - but do not have
* to - directly use this component type their runs
* on clause.
* Note that server synchronization functions may be
* invoked by a test component as long as its
* component type is type compatible to this component
* type definition!
*/
type component ClientSyncComp extends BaseSyncComp {
var StringStack v_stateStack:= c_initStringStack;
var TestcaseStep vc_testcaseStep := e_preamble;
}
/**
* @desc This type is used to define any synchronization
* behavior which is relevant to non-concurrent test
* cases.
* Note that self synchronization functions may be
* invoked by a test component as long as its
* component type is type compatible to this component
* type definition!
* Note also that this type is type compatible to the
* ClientSyncComp type so that shutdown altsteps from
* concurrent test cases can also be reused in single
* component test cases!
* @see LibCommon_Sync.ClientSyncComp
*/
type component SelfSyncComp extends ClientSyncComp {
port SyncPort syncSendPort;
}
/**
* @desc This port type must be imported into test suites
* when defining test component types which are
* type compatible to a synchronization component
* type
* @see LibCommon_Sync.SelfSyncComp
* @see LibCommon_Sync.ServerSyncComp
* @see LibCommon_Sync.ClientSyncComp
*/
type port SyncPort message {
inout SyncCmd
} with {
extension "internal"
}
/**
* @desc Describes in which step of execution is the testcase
*/
type enumerated TestcaseStep {
e_preamble,
e_testBody,
e_postamble
}
} // end compTypeRelated
group standardSyncPointNames {
const charstring c_prDone := "preambleDone";
const charstring c_poDone := "postambleDone";
const charstring c_tbDone := "testBodyDone";
const charstring c_initDone := "initDone";
}
} // end group importantSyncTypeDefinitions
group syncCompTestConfiguration {
/**
* @desc Calls self connect function if invoking
* component is the MTC or otherwise connects the client
* the server. This function allows to implement preambles
* in a way that they can be used by test components
* in both non-concurrent as well as concurrent test
* cases!
* @remark This function should _not_ be called if the MTC
* acts as a client (and not a server) in a concurrent
* test case. In this case f_connect4ClientSync
* should be used instead.
* @see LibCommon_Sync.f_connect4SelfSync
* @see LibCommon_Sync.f_connect4ClientSync
*/
function f_connect4SelfOrClientSync()
runs on SelfSyncComp {
if ( self == mtc ) {
f_connect4SelfSync();
} else {
f_connect4ClientSync();
}
}
/**
* @desc Calls self connect function if the invoking
* component is the MTC or otherwise disconnects the client
* from the server. This function allows to implement
* postambles in a way that they can be used in both
* non-concurrent as well as concurrent test cases.
* @remark This function should _not_ be called if the MTC
* acts as a client (and not a server) in a concurrent
* test case. In this case f_disconnect4ClientSync
* should be used instead.
* @see LibCommon_Sync.f_disconnect4SelfSync
* @see LibCommon_Sync.f_disconnect4ClientSync
*/
function f_disconnect4SelfOrClientSync()
runs on SelfSyncComp {
if ( self == mtc ) {
f_disconnect4SelfSync();
} else {
f_disconnect4ClientSync();
}
}
} // end group syncCompTestConfiguration
group syncFunctions {
/**
* @desc Implements synchronization of 2 clients from server side
* on one or more synchronization points.
* If problem occurs, then server sends STOP to all clients.
* Waits for PX_TSYNC_TIME_LIMIT to let clients
* finish executing their behavior until this
* synchronization point. After passing all synchronization
* points successfuly the server waits for all clients
* to stop.
* See f_serverSyncClientsTimed for overwriting this
* the timing constraint!
* This function sets the server component verdict.
* @remark The use of this function requires prior connection of
* the server sync ports!
* @see LibCommon_Sync.f_connect4SelfOrClientSync
* @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT
* @see LibCommon_Sync.f_serverSyncClientsTimed
* @see LibCommon_Sync.f_serverWaitForAllClientsToStop
* @param p_syncPointIds list of synchronization point name/ids
*/
function f_serverSync2ClientsAndStop( in SyncPointList p_syncPointIds )
runs on ServerSyncComp {
f_serverSyncNClientsAndStop(2, p_syncPointIds);
}
/**
* @desc Implements synchronization of 3 clients from server side
* on one or more synchronization points.
* If problem occurs, then server sends STOP to all clients.
* Waits for PX_TSYNC_TIME_LIMIT to let clients
* finish executing their behavior until this
* synchronization point. After passing all synchronization
* points successfuly the server waits for all clients
* to stop.
* See f_serverSyncClientsTimed for overwriting this
* the timing constraint!
* This function sets the server component verdict.
* @remark The use of this function requires prior connection of
* the server sync ports!
* @see LibCommon_Sync.f_connect4SelfOrClientSync
* @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT
* @see LibCommon_Sync.f_serverSyncClientsTimed
* @see LibCommon_Sync.f_serverWaitForAllClientsToStop
* @param p_syncPointIds list of synchronization point name/ids
*/
function f_serverSync3ClientsAndStop( in SyncPointList p_syncPointIds )
runs on ServerSyncComp {
f_serverSyncNClientsAndStop(3, p_syncPointIds);
}
/**
* @desc Implements synchronization of 4 clients from server side
* on one or more synchronization points.
* If problem occurs, then server sends STOP to all clients.
* Waits for PX_TSYNC_TIME_LIMIT to let clients
* finish executing their behavior until this
* synchronization point. After passing all synchronization
* points successfuly the server waits for all clients
* to stop.
* See f_serverSyncClientsTimed for overwriting this
* the timing constraint!
* This function sets the server component verdict.
* @remark The use of this function requires prior connection of
* the server sync ports!
* @see LibCommon_Sync.f_connect4SelfOrClientSync
* @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT
* @see LibCommon_Sync.f_serverSyncClientsTimed
* @see LibCommon_Sync.f_serverWaitForAllClientsToStop
* @param p_syncPointIds list of synchronization point name/ids
*/
function f_serverSync4ClientsAndStop( in SyncPointList p_syncPointIds )
runs on ServerSyncComp {
f_serverSyncNClientsAndStop(4, p_syncPointIds);
}
/**
* @desc Implements synchronization of N clients from server side
* on one or more synchronization points.
* If problem occurs, then server sends STOP to all clients.
* Waits for PX_TSYNC_TIME_LIMIT to let clients
* finish executing their behavior until this
* synchronization point. After passing all synchronization
* points successfuly the server waits for all clients
* to stop.
* See f_serverSyncClientsTimed for overwriting this
* the timing constraint!
* This function sets the server component verdict.
* @remark The use of this function requires prior connection of
* the server sync ports!
* @see LibCommon_Sync.f_connect4SelfOrClientSync
* @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT
* @see LibCommon_Sync.f_serverSyncClientsTimed
* @see LibCommon_Sync.f_serverWaitForAllClientsToStop
* @param p_numClients number of synchronization clients
* @param p_syncPointIds list of synchronization point name/ids
*/
function f_serverSyncNClientsAndStop (
in UInt p_numClients,
in SyncPointList p_syncPointIds )
runs on ServerSyncComp {
var integer i, v_noOfSyncIds := sizeof(p_syncPointIds);
for ( i := 0; i < v_noOfSyncIds; i := i+1 ) {
f_serverSyncClientsTimed (
p_numClients,
valueof(p_syncPointIds[i]),
PX_TSYNC_TIME_LIMIT );
}
f_serverWaitForAllClientsToStop();
}
/**
* @desc Implements synchronization of 2 clients and 1 UT from server side
* on one or more synchronization points.
* If problem occurs, then server sends STOP to all clients.
* Waits for PX_TSYNC_TIME_LIMIT to let clients
* finish executing their behavior until this
* synchronization point. After passing all synchronization
* points successfuly the server waits for all clients
* to stop.
* See f_serverSyncClientsTimed for overwriting this
* the timing constraint!
* This function sets the server component verdict.
* @remark The use of this function requires prior connection of
* the server sync ports!
* @see LibCommon_Sync.f_connect4SelfOrClientSync
* @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT
* @see LibCommon_Sync.f_serverSyncClientsTimed
* @see LibCommon_Sync.f_serverWaitForAllClientsToStop
* @param p_syncPointIds list of synchronization point name/ids
*/
function f_serverSync2ClientsUtAndStop( in SyncPointList p_syncPointIds )
runs on ServerSyncComp {
var integer i, v_noOfSyncIds := sizeof(p_syncPointIds);
for ( i := 0; i < v_noOfSyncIds; i := i+1 ) {
f_serverSyncClientsTimed(3,valueof(p_syncPointIds[i]), PX_TSYNC_TIME_LIMIT);
}
f_serverWaitForAllClientsToStop();
}
/**
* @desc Calls either self synchronization function if
* invoking component is the MTC, otherwise
* calls client synchronization. After that it
* sets the verdict based on the specified return code.
* This function allows to implement TTCN-3 functions
* in a way that they can be used in both non-concurrent
* as well as concurrent test cases.
* @remark This function should _not_ be called if the MTC
* acts as a client (and not a server) in a concurrent
* test case. In this case f_clientSyncAndVerdict
* should be used instead.
* @param p_syncPoint Synchronization point name/id
* @param p_ret Current behavior execution status
* @see LibCommon_Sync.f_clientSyncAndVerdict
* @see LibCommon_VerdictControl.f_setVerdict
*/
function f_selfOrClientSyncAndVerdict( in charstring p_syncPoint,
in FncRetCode p_ret)
runs on SelfSyncComp {
if ( self == mtc ) {
// then assume we are running non-conurrent test case
f_selfSyncAndVerdict(p_syncPoint, p_ret);
} else {
f_clientSyncAndVerdict(p_syncPoint, p_ret);
}
}
/**
* @desc Calls either self synchronization function if
* invoking component is the MTC, otherwise
* calls client synchronization. After that it
* sets a preamble specific verdict based on the
* specified return code.
* This function allows to implement TTCN-3 functions
* in a way that they can be used in both non-concurrent
* as well as concurrent test cases.
* @remark This function should _not_ be called if the MTC
* acts as a client (and not a server) in a concurrent
* test case. In this case f_clientSyncAndVerdictPreamble
* should be used instead.
* @param p_syncPoint Synchronization point name/id
* @param p_ret Current behavior execution status
* @see LibCommon_Sync.f_clientSyncAndVerdict
* @see LibCommon_VerdictControl.f_setVerdictPreamble
*/
function f_selfOrClientSyncAndVerdictPreamble( in charstring p_syncPoint,
in FncRetCode p_ret)
runs on SelfSyncComp {
if ( self == mtc ) {
// then assume we are running non-conurrent test case
f_selfSyncAndVerdictPreamble(p_syncPoint, p_ret);
} else {
f_clientSyncAndVerdictPreamble(p_syncPoint, p_ret);
}
}
/**
* @desc Calls either self synchronization function if
* invoking component is the MTC, otherwise
* calls client synchronization. After that it
* sets a preamble specific verdict based on the
* specified return code.
* This function allows to implement TTCN-3 functions
* in a way that they can be used in both non-concurrent
* as well as concurrent test cases.
* @remark This function should _not_ be called if the MTC
* acts as a client (and not a server) in a concurrent
* test case. In this case f_clientSyncAndVerdictTestBody
* should be used instead.
* @param p_syncPoint Synchronization point name/id
* @param p_ret Current behavior execution status
* @see LibCommon_Sync.f_clientSyncAndVerdict
* @see LibCommon_VerdictControl.f_setVerdictPreamble
*/
function f_selfOrClientSyncAndVerdictTestBody( in charstring p_syncPoint,
in FncRetCode p_ret)
runs on SelfSyncComp {
if ( self == mtc ) {
// then assume we are running non-conurrent test case
f_selfSyncAndVerdictTestBody(p_syncPoint, p_ret);
} else {
f_clientSyncAndVerdictTestBody(p_syncPoint, p_ret);
}
}
/**
* @desc Function kept for backward compatibility
* @see f_selfOrClientSyncAndVerdictPreamble
*
*/
function f_selfOrClientSyncAndVerdictPR( in charstring p_syncPoint,
in FncRetCode p_ret)
runs on SelfSyncComp {
f_selfOrClientSyncAndVerdictPreamble(p_syncPoint, p_ret);
}
} // end group syncFunctions
group syncCompStateHandling {
/**
*
* @desc This function updates the state (stack) of a
* sync client or self sync component. This stack is
* key in the shutdown handling of test components.
* It adds the new state name to the top of the
* sync component stack of states.
* The state will only be added in case of a current
* execution status of e_success.
* @param p_newSyncCompState Name of state which was attempted to be reached.
* @param p_ret Current behavior execution status
* @remark If the state of component changes this function must be
* _at least_ called from your test suite prior to f_selfSync
* or f_clientSync which is the only definite place for the
* shutdown default invocation!
* @see LibCommon_Sync.a_dummyShutDown
* @see LibCommon_Sync.f_selfSync
* @see LibCommon_Sync.f_clientSync
*/
function f_addSyncCompState(in charstring p_newSyncCompState,
in FncRetCode p_ret)
runs on ClientSyncComp {
if ( p_ret == e_success ) {
if ( f_isItemOnStringStack(v_stateStack,p_newSyncCompState) ) {
log("**** f_addSyncCompState: WARNING: Attempt to add state which is already on sync state stack! No additition done.****");
} else {
f_pushStringStack(v_stateStack,p_newSyncCompState);
}
}
} // end function f_addSyncCompState
/**
*
* @desc This function returns the top state on the sync
* state stack of a sync client or self sync
* component and removes it from the stack
* This function cna be used, e.g., in a while
* statement within a postamble or shutdown
* implementation
* @param p_state State on top of the state stack.
* @return false if state stack is empty, true otherwise
* @see LibCommon_Sync.a_dummyShutDown
*/
function f_getTopSyncCompState( out charstring p_state )
runs on ClientSyncComp
return boolean {
if ( not f_peekStringStackTop(v_stateStack,p_state) ) {
p_state := "IDLE";
return false;
}
f_popStringStack(v_stateStack);
return true;
} // end function f_getTopSyncCompState
/*
* @desc This function removes the last state on the state stack
* of a sync client or self sync component.
* This stack is key in the shutdown handling of test
* components.
* @see LibCommon_Sync.a_dummyShutDown
*/
function f_popSyncCompState()
runs on ClientSyncComp {
f_popStringStack(v_stateStack);
} // end function f_popSyncCompState
/**
*
* @desc This function returns the top state on the sync state
* stack of a sync client or self sync component. It
* does not remove it from the stack
* This stack is key in the shutdown handling of test
* components.
* @param p_state State on top of the state stack.
* @return false if state stack is empty, true otherwise
* @see LibCommon_Sync.a_dummyShutDown
*/
function f_peekTopSyncCompState(out charstring p_state)
runs on ClientSyncComp
return boolean {
return f_peekStringStackTop(v_stateStack,p_state);
} // end function f_peekTopSyncCompState
/**
* @desc This function checks if the sync state stack
* of a sync client or self sync component is empty.
* This stack is key in the shutdown handling of test
* components.
* @see LibCommon_Sync.a_dummyShutDown
*/
function f_isSyncCompStateStackEmpty()
runs on ClientSyncComp
return boolean {
return f_isStringStackEmpty(v_stateStack);
} // end function f_isSyncCompStateStackEmpty
} // end group syncCompStateHandling
group shutDownAltsteps {
/**
* @desc This is an example of a shutdown altstep which can be
* used as a "template" for a interface specific shutdown
* altstep or possily as a first temporary solution in
* test case development.<br><br>
* This altstep shall be activated as a default as the
* first statement in each test case function which drives
* an interface, i.e., in MTC behavior of single component
* and in each client behavior of multi component test
* cases.<br>
* The required behavior from this altstep is to:<br><br>
* 1) expect the STOP either via the test component
* syncPort<br><br>
* 2) upon its arrival it should shut down the SUT
* gracefully based on the current component state<br><br>
* The current component state should have been
* previously kept uptodate from a test suite via the
* f_addSyncCompState function. This default will then be
* (automatically) invoked either from within f_selfSync
* or f_clientSync.<br>
* Note that shutdown defaults can be written as
* _interface specific_ - they do not need to be test case
* or test component specific! See another example of a
* shutdown altstep in the sync module.
* @see LibCommon_Sync.f_addSyncCompState
* @see LibCommon_Sync.f_selfSync
* @see LibCommon_Sync.f_clientSync
* @see LibCommon_SyncExamples.a_exampleShutDown
* @remark Your application specific shutdown altstep
* implementation(s) should _not_ be defined in this
* module but as part of your test suite or application specific
* modules.
*/
altstep a_dummyShutDown()
runs on SelfSyncComp {
[] syncPort.receive(m_syncServerStop){
var charstring v_state := "";
tc_sync.stop;
log("**** a_dummyShutDown: Test component received STOP signal from sync server - going to IDLE state ****");
while ( f_getTopSyncCompState(v_state) ) {
if ( v_state == "x" ) {
// then do something
} else if ( v_state == "y" ) {
// then do something else
}
} // end while
f_disconnect4SelfOrClientSync();
// unmap/disconnect more if needed
log("**** a_dummyShutDown: -> Test component stopping itself now! ****") ;
stop ;
}
} // end altstep a_dummyShutDown
/**
* @desc Shutdown alstep in case the sync server is requesting shutdown.
*
* @remark User shall stop the component
*/
altstep a_shutdown()
runs on ClientSyncComp {
[] syncPort.receive(m_syncServerStop){
tc_sync.stop ;
log("**** a_shutdown: Test component received STOP signal from MTC **** ");
}
}
} // end group shutDownAltsteps
} // end group basicUserRelevantDefinitions
group advancedUserRelevantDefinitions {
group serverRelated {
/**
* @desc Implements synchronization of "n" clients from server
* side. If a problem occurs, then server sends STOP to
* all clients. Waits for PX_TSYNC_TIME_LIMIT to let
* clients finish executing their behavior until this
* synchronization point. See f_serverSyncClientsTimed for
* overwriting this later timing constraint!
* This function sets the server component verdict.
* @remark The use of this function requires prior connection of
* the server sync port!
* @see LibCommon_Sync.f_connect4SelfOrClientSync
* @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT
* @see LibCommon_Sync.f_serverSyncClientsTimed
* @param p_noOfClients number of clients to be synchronized
* @param p_syncId synchronization point name/id
*/
function f_serverSyncClients( in UInt p_noOfClients, in charstring p_syncId )
runs on ServerSyncComp {
f_serverSyncClientsTimed(p_noOfClients,p_syncId, PX_TSYNC_TIME_LIMIT);
}
/**
* @desc Implements synchronization of "n" clients from server
* side including intermediate synchronization.
* If a problem occurs, then server sends STOP to
* all clients. Waits for PX_TSYNC_TIME_LIMIT to let
* clients finish executing their behavior until this
* synchronization point. See f_serverSyncClientsTimed for
* overwriting this later timing constraint!
* This function sets the server component verdict.
* @remark The use of this function requires prior connection of
* the server sync port!
* @see LibCommon_Sync.f_connect4SelfOrClientSync
* @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT
* @see LibCommon_Sync.f_serverSyncClientsTimed
* @param p_noOfClients number of clients to be synchronized
* @param p_syncId synchronization point name/id
*/
function f_serverSyncClientsIntermediateSync( in UInt p_noOfClients, in charstring p_syncId, in UInt p_NoOfClientIntermediate, in template (present) charstring p_syncIdIntermediate )
runs on ServerSyncComp {
f_serverSyncClientsTimedIntermediateSync(p_noOfClients,p_syncId, p_NoOfClientIntermediate, p_syncIdIntermediate, PX_TSYNC_TIME_LIMIT);
}
/**
* @desc Handles synchronization of clients from server side.
* If problem occurs, then server sends STOP to all clients.
* This function sets the server verdict.
* @remark The use of this function requires prior connection of
* the server sync ports!
* @param p_NoOfClients number of clients to be synchronized
* @param p_syncId synchronization point name/id
* @param p_execTimeLimit time limit given to all clients to finish the execution
* of their behavior up to this synchronization point
* @see LibCommon_Sync.f_connect4SelfOrClientSync
*/
function f_serverSyncClientsTimed(in UInt p_NoOfClients,
in charstring p_syncId,
float p_execTimeLimit )
runs on ServerSyncComp {
f_serverSyncClientsTimedIntermediateSync(p_NoOfClients, p_syncId, 0, ?, p_execTimeLimit )
} // end function f_serverSyncClientsTimed
/** @desc Handles synchronization of clients from server side including
* intermediate synchronization.
* If problem occurs, then server sends STOP to all clients.
* This function sets the server verdict.
* @remark The use of this function requires prior connection of
* the server sync ports!
* @param p_NoOfClients number of clients to be synchronized
* @param p_syncId synchronization point name/id
* @param p_execTimeLimit time limit given to all clients to finish the execution
* of their behavior up to this synchronization point
* @see LibCommon_Sync.f_connect4SelfOrClientSync
* @return execution status
*/
function f_serverSyncClientsTimedIntermediateSync( in UInt p_NoOfClients,
in charstring p_syncId, in UInt p_NoOfClientIntermediate, in template (present) charstring p_syncIdIntermediate,
float p_execTimeLimit )
runs on ServerSyncComp {
var integer v_noOfRecvdSyncMsgs := 0, v_noOfRecvdSyncMsgsIntermediate := 0;
var boolean v_stopClients := false;
var ClientSyncCompList v_clientRefs := {}, v_clientRefsIntermediate := {};
var ClientSyncComp v_clientRef;
if ( p_syncId == c_prDone ) {
log("**** f_serverSyncClientsTimed: Sync server now starting PREAMBLE synchronization ... ****") ;
} else if ( p_syncId == c_tbDone ) {
log("**** f_serverSyncClientsTimed: Sync server now starting TEST BODY synchronization ... ****") ;
} else if ( p_syncId == c_initDone ) {
log("**** f_serverSyncClientsTimed: Sync server now starting UPPER TESTER synchronization ... ****") ;
} else {
log("**** f_serverSyncClientsTimed: Sync server now starting handling of next synchronization point ... ****") ;
}
tc_sync.start(p_execTimeLimit) ;
alt{
[v_noOfRecvdSyncMsgsIntermediate != p_NoOfClientIntermediate] syncPort.receive(m_syncClientReady(p_syncIdIntermediate)) -> sender v_clientRef {
if(not f_isPresentInArray(v_clientRef, v_clientRefsIntermediate)) {
v_clientRefsIntermediate[v_noOfRecvdSyncMsgsIntermediate] := v_clientRef;
v_noOfRecvdSyncMsgsIntermediate := v_noOfRecvdSyncMsgsIntermediate + 1;
if (v_noOfRecvdSyncMsgsIntermediate == p_NoOfClientIntermediate) {
f_serverSendToAllClients(v_clientRefsIntermediate, m_syncServerReady(p_syncIdIntermediate));
}
}
repeat;
}
[] syncPort.receive(m_syncClientReady(p_syncId)) -> sender v_clientRef {
if(not f_isPresentInArray(v_clientRef, v_clientRefs)) {
v_clientRefs[v_noOfRecvdSyncMsgs] := v_clientRef;
v_noOfRecvdSyncMsgs := v_noOfRecvdSyncMsgs + 1;
}
if ( v_noOfRecvdSyncMsgs != p_NoOfClients ) { repeat; }
}
[] syncPort.receive(m_syncClientStop) -> sender v_clientRef {
log("**** f_serverSyncClientsTimed: Sync server received STOP signal from a client - server will wait for all clients to reach their next synchronization point and then stop them! ****") ;
v_stopClients := true;
if(not f_isPresentInArray(v_clientRef, v_clientRefs)) {
v_clientRefs[v_noOfRecvdSyncMsgs] := v_clientRef;
v_noOfRecvdSyncMsgs := v_noOfRecvdSyncMsgs + 1;
}
if ( v_noOfRecvdSyncMsgs != p_NoOfClients ) { repeat; }
}
[] syncPort.receive(m_syncClientReady(?)) -> sender v_clientRef {
log("**** f_serverSyncClientsTimed: Sync server received client sync message with incorrect synchronization point id which is currently not handled - server will stop all clients! ****") ;
v_stopClients := true;
if(not f_isPresentInArray(v_clientRef, v_clientRefs)) {
v_clientRefs[v_noOfRecvdSyncMsgs] := v_clientRef;
}
}
[] syncPort.receive(SyncCmd :? ) {
log("**** f_serverSyncClientsTimed: Sync server received (invalid) sync message from other sync server - server will stop all clients! ****") ;
v_stopClients := true; }
[] any port.receive {
// leave it to be ok to receive anything else
// in case that the user has added any non-sync ports to
// his/her server component type definition!
repeat;
}
[] tc_sync.timeout{
log("**** f_serverSyncClientsTimed: A client is not responding within specified time limit - sync server is sending stop to all clients! ****");
v_stopClients := true; }
} //end alt
if (v_noOfRecvdSyncMsgsIntermediate != p_NoOfClientIntermediate) {
v_stopClients := true;
}
tc_sync.stop ;
if ( v_stopClients ) {
setverdict(inconc);
// then send out STOP sync msg
f_serverSendToAllClients(v_clientRefs, m_syncServerStop);
f_serverWaitForAllClientsToShutDown(); // function will never return!
} else {
setverdict(pass);
// then send out READY sync msg
f_serverSendToAllClients(v_clientRefs, m_syncServerReady(p_syncId));
if ( p_syncId == c_prDone ) {
log("**** f_serverSyncClientsTimed: Sync server successfully passed PREAMBLE synchronization point. ****") ;
} else if ( p_syncId == c_tbDone ) {
log("**** f_serverSyncClientsTimed: Sync server successfully passed TEST BODY synchronization point. ****") ;
} else {
log("**** f_serverSyncClientsTimed: Sync server successfully passed synchronization point. ****") ;
}
}
} // end function f_serverSyncClientsTimedIntermediateSync
/**
* @desc This function is intended only for use on the sync
* server component in concurrent TTCN-3 test cases.
* It waits for all components to finish execution within
* the PX_TSYNC_TIME_LIMIT. If a timeout occurs
* the server will stop all clients.
* This function sets the server component verdict.
*/
function f_serverWaitForAllClientsToStop()
runs on ServerSyncComp {
tc_sync.start;
alt {
[] all component.done {
tc_sync.stop;
log("**** f_serverWaitForAllClientsToStop: All sync clients have finished their execution. Sync server now terminating test case. ****") ;
}
[] tc_sync.timeout {
log("**** f_serverWaitForAllClientsToStop: Not all sync clients have finshed execution within the sync time limit. Sync server will stop test case! ****") ;
stop;
}
} // end alt
setverdict(pass);
} // end function f_serverWaitForAllClientsToStop
} // end group serverRelated
group clientRelated {
/**
* @desc This function creates the connection needed to
* execute client synchronization functions
* @see LibCommon_Sync.f_clientSync
* @see LibCommon_Sync.f_clientSendStop
*/
function f_connect4ClientSync()
runs on ClientSyncComp {
connect(self:syncPort, mtc:syncPort);
}// end function f_connect4ClientSync
/**
* @desc This function removes the connection needed
* to execute client synchronization functions
* @see LibCommon_Sync.f_clientSync
* @see LibCommon_Sync.f_clientSendStop
*/
function f_disconnect4ClientSync()
runs on ClientSyncComp {
disconnect(self:syncPort, mtc:syncPort);
}// end function f_disconnect4ClientSync
/**
* @desc This function combines client verdict setting with its
* synchronization for use,e.g, after or within a
* test body implementation.
* Note that such premables can _not_ be reused in non-
* concurrent test cases. This can be achieved by using
* the f_selfOrClientSyncAndVerdict function instead.
* This function sets the client component verdict.
* @param p_syncId Synchronization point name/id
* @param p_ret Current behavior execution status
* @remark The use of this function requires prior connection
* of the client sync port!
* @see LibCommon_Sync.f_connect4ClientSync
* @see LibCommon_Sync.f_connect4SelfOrClientSync
* @see LibCommon_VerdictControl.f_setVerdict
* @see LibCommon_Sync.f_selfOrClientSyncAndVerdict
*/
function f_clientSyncAndVerdict(in charstring p_syncId,
in FncRetCode p_ret)
runs on ClientSyncComp {
if(vc_testcaseStep == e_preamble) {
f_clientSyncAndVerdictPreamble(p_syncId, p_ret);
} else if(vc_testcaseStep == e_testBody) {
f_clientSyncAndVerdictTestBody(p_syncId, p_ret);
}
else {
f_clientSyncAndVerdictPostamble(p_syncId, p_ret);
}
}
/**
* @desc This function combines client verdict setting with its
* synchronization for use after or within a preamble
* implementation.
* Note that such preambles can _not_ be reused in non-
* concurrent test cases.
* This function sets the client component verdict.
* @remark The use of this function requires prior connection
* of the client sync port!
* @see LibCommon_Sync.f_connect4ClientSync
* @see LibCommon_Sync.f_connect4SelfOrClientSync
* @see LibCommon_VerdictControl.f_setVerdictPreamble
* @param p_syncId Synchronization point name/id
* @param p_ret Current behavior execution status
*/
function f_clientSyncAndVerdictPreamble(in charstring p_syncId ,
FncRetCode p_ret)
runs on ClientSyncComp {
f_setVerdictPreamble(p_ret);
f_clientSync(p_syncId,p_ret);
vc_testcaseStep := e_testBody;
}
/**
* @desc This function combines client verdict setting with its
* synchronization for use,e.g, after or within a
* test body implementation.
* Note that such premables can _not_ be reused in non-
* concurrent test cases. This can be achieved by using
* the f_selfOrClientSyncAndVerdict function instead.
* This function sets the client component verdict.
* @param p_syncId Synchronization point name/id
* @param p_ret Current behavior execution status
* @remark The use of this function requires prior connection
* of the client sync port!
* @see LibCommon_Sync.f_connect4ClientSync
* @see LibCommon_Sync.f_connect4SelfOrClientSync
* @see LibCommon_VerdictControl.f_setVerdict
* @see LibCommon_Sync.f_selfOrClientSyncAndVerdict
*/
function f_clientSyncAndVerdictTestBody(in charstring p_syncId,
in FncRetCode p_ret)
runs on ClientSyncComp {
f_setVerdict(p_ret);
f_clientSync(p_syncId,p_ret);
vc_testcaseStep := e_postamble;
}
/**
* @desc This function combines client verdict setting with its
* synchronization for use after or within a
* postamble implementation.
* Note that such prostambles can _not_ be reused in non-
* concurrent test cases.
* This function sets the client component verdict.
* @remark The use of this function requires prior connection
* of the client sync port!
* @see LibCommon_Sync.f_connect4ClientSync
* @see LibCommon_Sync.f_connect4SelfOrClientSync
* @see LibCommon_VerdictControl.f_setVerdictPostamble
* @param p_syncId Synchronization point name/id
* @param p_ret Current behavior execution status
*/
function f_clientSyncAndVerdictPostamble(in charstring p_syncId ,
in FncRetCode p_ret)
runs on ClientSyncComp {
f_setVerdictPostamble(p_ret);
f_clientSync(p_syncId,p_ret);
}
/**
* @desc This function handles synchronization of a sync client
* with the server. In case of successful execution it sends
* a READY message to the server and waits the READY back.
* The time used for waiting is defined by PX_TSYNC_TIME_LIMIT.
* In case of a non successful execution status it
* sends a STOP message to the server.
* In both cases the receipt of a STOP message or no
* response from the server it will trigger the shutdown
* default (if activated).
* This function will set only the client verdict to INCONC
* (and stop its execution) if no STOP response is received
* from the server within the PX_TSYNC_TIME_LIMIT
* or if no shutdown default is activated. In all other
* cases the client verdict is NOT set.
* @param p_syncId Synchronization point name/id
* @param p_ret Current behavior execution status
* @remark The use of this function requires prior connection
* of the client sync port!
* @see LibCommon_Sync.f_connect4ClientSync
* @see LibCommon_Sync.f_connect4SelfOrClientSync
* @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT
* @see LibCommon_Sync.a_dummyShutDown
* @see LibCommon_Sync.f_clientSendStop
* @return Updated execution status
*/
function f_clientSync( in charstring p_syncId ,
in FncRetCode p_ret )
runs on ClientSyncComp
return FncRetCode{
if (p_ret == e_success){
syncPort.send(m_syncClientReady(p_syncId));
tc_sync.start;
alt{
[] syncPort.receive(m_syncServerReady(p_syncId)){
tc_sync.stop ; }
[] tc_sync.timeout{
log("**** f_clientSync: Sync client did not receive message from sync server within the specified time limit - sync client will ask sync server to stop test case! ****") ;
f_clientSendStop(); } // function will not return!
} //end alt
} //end if
else {
log("**** f_clientSync: Execution status indicates that execution of test component behavior was not successful - sync client will ask sync server to stop test case! ****") ;
f_clientSendStop(); // function will not return!
}
if ( p_syncId == c_prDone ) {
log("**** f_clientSync: Sync client successfully passed PREAMBLE synchronization point. ****") ;
} else if ( p_syncId == c_tbDone ) {
log("**** f_clientSync: Sync client successfully passed TEST BODY synchronization point. ****") ;
} else {
log("**** f_clientSync: Sync client successfully passed synchronization point. ****") ;
}
return e_success ;
} // end function f_clientSync
/**
* @desc This function can be used to request the shutdown a
* multi component test case _prior_ to reaching a
* synchronization point. It sends a STOP message to
* the sync server and awaits then the STOP from the server
* which will trigger the shutdown default (if activated).
* This function will set the server verdict to INCONC (and
* stop the test case) if no shutdown default is activated.
* This function will set only the client verdict to INCONC
* (and stop its execution) if no STOP response is received
* from the server within the PX_TSYNC_TIME_LIMIT
* or if no shutdown default is activated. In all other
* cases the client verdict is NOT set.
* @remark The use of this function requires prior connection
* of the client sync port!
* @see LibCommon_Sync.f_connect4ClientSync
* @see LibCommon_Sync.f_connect4SelfOrClientSync
* @see LibCommon_Sync.PX_TSYNC_TIME_LIMIT
* @see LibCommon_Sync.a_dummyShutDown
*/
function f_clientSendStop()
runs on ClientSyncComp {
log("**** f_clientSendStop: Sync client requesting from server to stop test case (including itself). ****") ;
syncPort.send(m_syncClientStop) ;
tc_sync.start;
alt{
[] tc_sync.timeout{
log("**** f_clientSendStop: Stopping sync client without shutdown - either no shutdown default active or no stop received from server. ****") ;
setverdict(inconc);
stop ;
}
}//end alt
tc_sync.stop;
stop; // stop here if shutdown default does not stop
}
} // end group clientRelated
} // end group advancedUserRelevantDefinitions
group otherSyncModuleDefinitions {
group syncModuleparams {
/**
*
* @desc Default time limit for a sync client to reach a
* synchronization point
*/
modulepar float PX_TSYNC_TIME_LIMIT := 120.0;
/*
* @desc Default time limit for a sync client to finish
* its execution of the shutdown default
*/
modulepar float PX_TSHUT_DOWN_TIME_LIMIT := 120.0;
}
group otherSyncTypes {
type record of charstring SyncPointList;
type record of ClientSyncComp ClientSyncCompList;
} // end group otherSyncTypes
group otherSelfSyncRelatedDefinitions {
/**
* @desc This function creates the connection needed to
* execute self sync functions
* @see LibCommon_Sync.f_selfSync
* @see LibCommon_Sync.f_selfSyncStop
*/
function f_connect4SelfSync()
runs on SelfSyncComp {
connect(self:syncSendPort, self:syncPort);
}// end function f_connect4SelfSync
/**
* @desc This function removes the connection needed
* to execute self sync functions
* @see LibCommon_Sync.f_selfSync
* @see LibCommon_Sync.f_selfSyncStop
*/
function f_disconnect4SelfSync()
runs on SelfSyncComp {
disconnect(self:syncSendPort, self:syncPort);
}// end function f_disconnect4SelfSync
/**
* @desc This function combines MTC verdict setting with self
* synchronization for use in the preamble / test body / postamble
* @param p_syncId Synchronization point name/id
* @param p_ret Current behavior execution status
* @see LibCommon_VerdictControl.f_setVerdict
* @see LibCommon_Sync.f_selfSync
* @see LibCommon_Sync.a_dummyShutDown
*/
function f_selfSyncAndVerdict( in charstring p_syncId,
in FncRetCode p_ret )
runs on SelfSyncComp {
if(vc_testcaseStep == e_preamble) {
f_selfSyncAndVerdictPreamble(p_syncId, p_ret);
} else if(vc_testcaseStep == e_testBody) {
f_selfSyncAndVerdictTestBody(p_syncId, p_ret);
}
else {
f_selfSyncAndVerdictPostamble(p_syncId, p_ret);
}
}
/**
* @desc This function combines MTC verdict setting with self
* synchronization for use after the preamble.
* @param p_syncId Synchronization point name/id
* @param p_ret Current behavior execution status
* @see LibCommon_VerdictControl.f_setVerdictPreamble
* @see LibCommon_Sync.f_selfSync
*/
function f_selfSyncAndVerdictPreamble( in charstring p_syncId,
in FncRetCode p_ret )
runs on SelfSyncComp {
f_setVerdictPreOrPostamble(p_ret);
f_selfSync(p_syncId,p_ret);
vc_testcaseStep := e_testBody;
}
/**
* @desc This function combines MTC verdict setting with self
* synchronization for use after the test body.
* @param p_syncId Synchronization point name/id
* @param p_ret Current behavior execution status
* @see LibCommon_VerdictControl.f_setVerdict
* @see LibCommon_Sync.f_selfSync
*/
function f_selfSyncAndVerdictTestBody( in charstring p_syncId,
in FncRetCode p_ret )
runs on SelfSyncComp {
f_setVerdict(p_ret);
f_selfSync(p_syncId,p_ret);
vc_testcaseStep := e_postamble;
}
/**
* @desc This function combines MTC verdict setting with self
* synchronization for use after the postamble.
* @param p_syncId Synchronization point name/id
* @param p_ret Current behavior execution status
* @see LibCommon_VerdictControl.f_setVerdictPostamble
* @see LibCommon_Sync.f_selfSync
*/
function f_selfSyncAndVerdictPostamble( in charstring p_syncId ,
in FncRetCode p_ret )
runs on SelfSyncComp {
f_setVerdictPreOrPostamble(p_ret);
f_selfSync(p_syncId,p_ret);
}
/**
* @desc This function synchronizes a MTC with itself. In case
* of a non successful execution status it sends a STOP
* message to itself and invokes that way the
* shutdown default (if activated).
* This function will set the server verdict to INCONC (and
* stop the test case) if no shutdown default is activated.
* Otherwise no verdict is set.
* @remark Sync ports should be connected prior to the invocation
* of this function!
* @param p_syncId Synchronization point name/id
* @param p_ret Current behavior execution status
* @return Updated execution status
* @see LibCommon_Sync.f_connect4SelfSync
* @see LibCommon_Sync.a_dummyShutDown
*/
function f_selfSync( in charstring p_syncId ,
in FncRetCode p_ret )
runs on SelfSyncComp
return FncRetCode{
if (p_ret != e_success){
f_selfSyncStop() ; // function will not return!
}
if ( p_syncId == c_prDone ) {
log("**** f_selfSync: Successfully passed PREAMBLE synchronization point. ****") ;
} else if ( p_syncId == c_tbDone ) {
log("**** f_selfSync: Successfully passed TEST BODY synchronization point. ****") ;
} else {
log("**** f_selfSync: Successfully passed synchronization point. ****") ;
}
return e_success ;
}// end function f_selfSync
/**
* @desc This function can be used to shut down a test case _prior_
* to reaching a synchronization point. it sends a STOP
* message to itself and invokes that way the
* shutdown default (if activated).
* This function will set the server verdict to INCONC (and
* stop the test case) if no shutdown default is activated.
* Otherwise no verdict is set.
* @remark Sync ports should be connected prior to the invocation
* of this function!
* @see LibCommon_Sync.f_connect4SelfSync
*/
function f_selfSyncStop()
runs on SelfSyncComp {
log("**** f_selfSyncStop: MTC requests to stop test case (itself). ****") ;
syncSendPort.send(m_syncServerStop) ; // this MUST be _server_ for the default to catch!
tc_sync.start(PX_TSYNC_TIME_LIMIT);
alt{
[] tc_sync.timeout{
log("**** f_selfSyncStop: Stopping MTC without shutdown - either no shutdown default active or missing syncPort connection ****") ;
setverdict(inconc);
stop ;
}
}//end alt
tc_sync.stop;
stop; // if shutdown default is not activated or if it does not stop
} // end function f_selfSyncStop
} // end group otherSelfSyncRelatedDefinitions
/**
*
* @desc The sychronization protocol is conceptually based on
* named synchronization. Each synchronization point
* has it own specific synchronization message. This
* makes each synchronization unique, and allows, e.g., to
* ensure that a server synchronizes only clients which have
* reached the same synchronization point.
*/
group syncProtocolDefinition {
type union SyncCmd {
ClientReady clientReady,
ServerReady serverReady,
ClientStop clientStop,
ServerStop serverStop
}
type record ClientReady {
charstring syncPointId
}
type record ServerReady {
charstring syncPointId
}
type record ClientStop {}
type record ServerStop {}
} // end group syncProtocolDefinition
group syncMessages {
template SyncCmd m_syncClientReady( template (present) charstring p_syncId ) := {
clientReady := { p_syncId }
}
template SyncCmd m_syncServerReady( template (present) charstring p_syncId ) := {
serverReady := { p_syncId }
}
template SyncCmd m_syncClientStop := {
clientStop := {}
}
template SyncCmd m_syncServerStop := {
serverStop := {}
}
} // end group syncMessages
group otherSyncFunctions {
/**
* @desc Makes server send a sync message to all known clients
* @param p_clientRefs List of client references to which the message is to be send
* @param p_syncCmd The actual synchronization message to be sent out
*/
function f_serverSendToAllClients( in ClientSyncCompList p_clientRefs,
in template (value) SyncCmd p_syncCmd)
runs on ServerSyncComp {
var integer i:=0;
for (i:=0; i< sizeof(p_clientRefs); i:=i+1 ){
syncPort.send(p_syncCmd) to valueof(p_clientRefs[i]);
}
} // end function f_serverSendToAllClients
/**
* @desc This function is intended only for use on server in concurrent
* TTCN-3 test cases. It waits for all components to shut down
* within the PX_TSHUT_DOWN_TIME_LIMIT. If a timeout occurs
* it aborts the test case (no matter how far clients got with their
* shutdown).
* This function sets the server verdict.
*/
function f_serverWaitForAllClientsToShutDown()
runs on ServerSyncComp {
tc_shutDown.start(PX_TSHUT_DOWN_TIME_LIMIT);
alt {
[] syncPort.receive {
// clients may still try to send some sync message
}
[] all component.done {
tc_shutDown.stop;
log("**** f_serverWaitForAllClientsToShutDown: All components have properly shut down. Sync server will now terminate the test case. ****") ;
}
[] tc_shutDown.timeout {
log("**** f_serverWaitForAllClientsToShutDown: Not all clients have properly shutdown within the shut down time limit. Sync server will now terminate test case! ****") ;
}
} // end alt
// cover case that shut down default is NOT activated
setverdict(inconc);
//mtc.stop;
syncPort.send(m_syncServerStop) to self; // this MUST be _server_ for the default to catch!
tc_sync.start(PX_TSYNC_TIME_LIMIT);
alt{
[] tc_sync.timeout{
log("**** f_selfSyncStop: Stopping MTC without shutdown - either no shutdown default active or missing syncPort connection ****") ;
setverdict(inconc);
stop ;
}
}//end alt
tc_sync.stop;
stop; // if shutdown default is not activated or if it does not stop
} // end function f_serverWaitForAllClientsToShutDown
function f_isPresentInArray(in ClientSyncComp p_clientRef, in ClientSyncCompList p_clientRefs)
return boolean {
var integer i;
for(i:=0; i < sizeof(p_clientRefs); i:=i+1) {
if(p_clientRefs[i] == p_clientRef) {
return true;
}
}
return false;
}
} // end group otherSyncFunctions
} // end group otherSyncDefinitions
} // end module LibCommon_Sync
sources := LibIms_UpperTester.ttcn
\ No newline at end of file
sources := \
ttcn/LibDiameter_PIXITS.ttcn \
ttcn/LibDiameter_Interface.ttcn \
ttcn/LibDiameter_Steps.ttcn \
../patch_lib_diameter_titan/ttcn/LibDiameter_Types_Base_AVPs.ttcn \
../patch_lib_diameter_titan/ttcn/LibDiameter_Templates.ttcn \
../patch_lib_diameter_titan/ttcn/LibDiameter_Types_S6a_AVPs.ttcn \
../patch_lib_diameter_titan/ttcn/LibDiameter_Types_S9_AVPs.ttcn \
../patch_lib_diameter_titan/ttcn/LibDiameter_Types_RfRo_AVPs.ttcn \
../patch_lib_diameter_titan/ttcn/LibDiameter_Types_ShDh_AVPs.ttcn \
../patch_lib_diameter_titan/ttcn/LibDiameter_TypesAndValues.ttcn \
../patch_lib_diameter_titan/ttcn/LibDiameter_Types_Rx_AVPs.ttcn \
../patch_lib_diameter_titan/ttcn/LibDiameter_Types_CxDx_AVPs.ttcn \
../patch_lib_diameter_titan/ttcn/LibDiameter_Types_Gx_AVPs.ttcn \
../patch_lib_diameter_titan/ttcn/LibDiameter_EncdecDeclarations.ttcn \