You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
388 lines
9.2 KiB
C
388 lines
9.2 KiB
C
![]()
11 years ago
|
/*
|
||
|
* iso_server.c
|
||
|
*
|
||
|
* Copyright 2013, 2014 Michael Zillgith
|
||
|
*
|
||
|
* This file is part of libIEC61850.
|
||
|
*
|
||
|
* libIEC61850 is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation, either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* libIEC61850 is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*
|
||
|
* See COPYING file for the complete license text.
|
||
|
*/
|
||
|
|
||
|
#include "libiec61850_platform_includes.h"
|
||
|
|
||
|
#include "stack_config.h"
|
||
|
|
||
|
#ifndef DEBUG_ISO_SERVER
|
||
|
#ifdef DEBUG
|
||
|
#define DEBUG_ISO_SERVER 1
|
||
|
#else
|
||
|
#define DEBUG_ISO_SERVER 0
|
||
|
#endif /*DEBUG */
|
||
|
#endif /* DEBUG_ISO_SERVER */
|
||
|
|
||
|
#include "mms_server_connection.h"
|
||
|
|
||
|
#include "thread.h"
|
||
|
|
||
|
#include "iso_server.h"
|
||
|
|
||
|
#include "iso_server_private.h"
|
||
|
|
||
|
#ifndef CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS
|
||
|
#define CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS 5
|
||
|
#endif
|
||
|
|
||
|
#define TCP_PORT 102
|
||
|
#define SECURE_TCP_PORT 3782
|
||
|
#define BACKLOG 10
|
||
|
|
||
|
struct sIsoServer {
|
||
|
IsoServerState state;
|
||
|
ConnectionIndicationHandler connectionHandler;
|
||
|
void* connectionHandlerParameter;
|
||
|
|
||
|
AcseAuthenticator authenticator;
|
||
|
void* authenticatorParameter;
|
||
|
|
||
|
Thread serverThread;
|
||
|
Socket serverSocket;
|
||
|
int tcpPort;
|
||
|
char* localIpAddress;
|
||
|
|
||
|
#if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1)
|
||
|
LinkedList openClientConnections;
|
||
|
#else
|
||
|
IsoConnection* openClientConnections;
|
||
|
#endif
|
||
|
|
||
|
Semaphore userLock;
|
||
|
|
||
|
Semaphore connectionCounterMutex;
|
||
|
int connectionCounter;
|
||
|
};
|
||
|
|
||
|
static void
|
||
|
addClientConnection(IsoServer self, IsoConnection connection)
|
||
|
{
|
||
|
|
||
|
#if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1)
|
||
|
LinkedList_add(self->openClientConnections, connection);
|
||
|
#else
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS; i++) {
|
||
|
if (self->openClientConnections[i] == NULL) {
|
||
|
self->openClientConnections[i] = connection;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
removeClientConnection(IsoServer self, IsoConnection connection)
|
||
|
{
|
||
|
#if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1)
|
||
|
LinkedList_remove(self->openClientConnections, connection);
|
||
|
#else
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS; i++) {
|
||
|
if (self->openClientConnections[i] == connection) {
|
||
|
self->openClientConnections[i] = NULL;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
closeAllOpenClientConnections(IsoServer self)
|
||
|
{
|
||
|
|
||
|
#if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1)
|
||
|
LinkedList openConnection = LinkedList_getNext(self->openClientConnections);
|
||
|
while (openConnection != NULL) {
|
||
|
IsoConnection isoConnection = (IsoConnection) openConnection->data;
|
||
|
|
||
|
IsoConnection_close(isoConnection);
|
||
|
|
||
|
openConnection = LinkedList_getNext(openConnection);
|
||
|
}
|
||
|
#else
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS; i++) {
|
||
|
if (self->openClientConnections[i] != NULL) {
|
||
|
|
||
|
IsoConnection_close(self->openClientConnections[i]);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
isoServerThread(void* isoServerParam)
|
||
|
{
|
||
|
IsoServer self = (IsoServer) isoServerParam;
|
||
|
|
||
|
if (DEBUG_ISO_SERVER)
|
||
|
printf("ISO_SERVER: isoServerThread %p started\n", &isoServerParam);
|
||
|
|
||
|
Socket connectionSocket;
|
||
|
|
||
|
self->serverSocket = (Socket) TcpServerSocket_create(self->localIpAddress, self->tcpPort);
|
||
|
|
||
|
if (self->serverSocket == NULL) {
|
||
|
self->state = ISO_SVR_STATE_ERROR;
|
||
|
goto cleanUp;
|
||
|
}
|
||
|
|
||
|
ServerSocket_setBacklog((ServerSocket) self->serverSocket, BACKLOG);
|
||
|
|
||
|
ServerSocket_listen((ServerSocket) self->serverSocket);
|
||
|
|
||
|
self->state = ISO_SVR_STATE_RUNNING;
|
||
|
|
||
|
while (self->state == ISO_SVR_STATE_RUNNING)
|
||
|
{
|
||
|
|
||
|
if ((connectionSocket = ServerSocket_accept((ServerSocket) self->serverSocket)) == NULL) {
|
||
|
break;
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
#if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS != -1)
|
||
|
if (private_IsoServer_getConnectionCounter(self) >= CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS) {
|
||
|
if (DEBUG_ISO_SERVER)
|
||
|
printf("ISO_SERVER: maximum number of connections reached -> reject connection attempt.\n");
|
||
|
|
||
|
Socket_destroy(connectionSocket);
|
||
|
continue;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
IsoConnection isoConnection = IsoConnection_create(connectionSocket, self);
|
||
|
|
||
|
private_IsoServer_increaseConnectionCounter(self);
|
||
|
|
||
|
addClientConnection(self, isoConnection);
|
||
|
|
||
|
|
||
|
self->connectionHandler(ISO_CONNECTION_OPENED, self->connectionHandlerParameter,
|
||
|
isoConnection);
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
self->state = ISO_SVR_STATE_STOPPED;
|
||
|
|
||
|
cleanUp:
|
||
|
self->serverSocket = NULL;
|
||
|
|
||
|
if (DEBUG_ISO_SERVER)
|
||
|
printf("ISO_SERVER: isoServerThread %p stopped\n", &isoServerParam);
|
||
|
|
||
|
}
|
||
|
|
||
|
IsoServer
|
||
|
IsoServer_create()
|
||
|
{
|
||
|
IsoServer self = (IsoServer) calloc(1, sizeof(struct sIsoServer));
|
||
|
|
||
|
self->state = ISO_SVR_STATE_IDLE;
|
||
|
self->tcpPort = TCP_PORT;
|
||
|
|
||
|
#if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1)
|
||
|
self->openClientConnections = LinkedList_create();
|
||
|
#else
|
||
|
self->openClientConnections = (IsoConnection*)
|
||
|
calloc(CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS, sizeof(IsoConnection));
|
||
|
#endif
|
||
|
|
||
|
self->connectionCounterMutex = Semaphore_create(1);
|
||
|
self->connectionCounter = 0;
|
||
|
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
IsoServer_setTcpPort(IsoServer self, int port)
|
||
|
{
|
||
|
self->tcpPort = port;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
IsoServer_setLocalIpAddress(IsoServer self, char* ipAddress)
|
||
|
{
|
||
|
self->localIpAddress = ipAddress;
|
||
|
}
|
||
|
|
||
|
IsoServerState
|
||
|
IsoServer_getState(IsoServer self)
|
||
|
{
|
||
|
return self->state;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
IsoServer_setAuthenticator(IsoServer self, AcseAuthenticator authenticator, void* authenticatorParameter)
|
||
|
{
|
||
|
self->authenticator = authenticator;
|
||
|
self->authenticatorParameter = authenticatorParameter;
|
||
|
}
|
||
|
|
||
|
AcseAuthenticator
|
||
|
IsoServer_getAuthenticator(IsoServer self)
|
||
|
{
|
||
|
return self->authenticator;
|
||
|
}
|
||
|
|
||
|
void*
|
||
|
IsoServer_getAuthenticatorParameter(IsoServer self)
|
||
|
{
|
||
|
return self->authenticatorParameter;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
IsoServer_startListening(IsoServer self)
|
||
|
{
|
||
|
self->serverThread = Thread_create((ThreadExecutionFunction) isoServerThread, self, false);
|
||
|
|
||
|
Thread_start(self->serverThread);
|
||
|
|
||
|
while (self->state == ISO_SVR_STATE_IDLE)
|
||
|
Thread_sleep(1);
|
||
|
|
||
|
if (DEBUG_ISO_SERVER)
|
||
|
printf("ISO_SERVER: new iso server thread started\n");
|
||
|
}
|
||
|
|
||
|
void
|
||
|
IsoServer_stopListening(IsoServer self)
|
||
|
{
|
||
|
self->state = ISO_SVR_STATE_STOPPED;
|
||
|
|
||
|
if (self->serverSocket != NULL) {
|
||
|
ServerSocket_destroy((ServerSocket) self->serverSocket);
|
||
|
self->serverSocket = NULL;
|
||
|
}
|
||
|
|
||
|
if (self->serverThread != NULL)
|
||
|
Thread_destroy(self->serverThread);
|
||
|
|
||
|
closeAllOpenClientConnections(self);
|
||
|
|
||
|
/* Wait for connection threads to finish */
|
||
|
while (private_IsoServer_getConnectionCounter(self) > 0)
|
||
|
Thread_sleep(10);
|
||
|
|
||
|
if (DEBUG_ISO_SERVER)
|
||
|
printf("ISO_SERVER: IsoServer_stopListening finished!\n");
|
||
|
}
|
||
|
|
||
|
void
|
||
|
IsoServer_closeConnection(IsoServer self, IsoConnection isoConnection)
|
||
|
{
|
||
|
if (self->state != ISO_SVR_STATE_IDLE) {
|
||
|
self->connectionHandler(ISO_CONNECTION_CLOSED, self->connectionHandlerParameter,
|
||
|
isoConnection);
|
||
|
}
|
||
|
|
||
|
removeClientConnection(self, isoConnection);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
IsoServer_setConnectionHandler(IsoServer self, ConnectionIndicationHandler handler,
|
||
|
void* parameter)
|
||
|
{
|
||
|
self->connectionHandler = handler;
|
||
|
self->connectionHandlerParameter = parameter;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
IsoServer_destroy(IsoServer self)
|
||
|
{
|
||
|
if (self->state == ISO_SVR_STATE_RUNNING)
|
||
|
IsoServer_stopListening(self);
|
||
|
|
||
|
#if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1)
|
||
|
LinkedList_destroy(self->openClientConnections);
|
||
|
#else
|
||
|
free(self->openClientConnections);
|
||
|
#endif
|
||
|
|
||
|
Semaphore_destroy(self->connectionCounterMutex);
|
||
|
|
||
|
free(self);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
private_IsoServer_increaseConnectionCounter(IsoServer self)
|
||
|
{
|
||
|
Semaphore_wait(self->connectionCounterMutex);
|
||
|
self->connectionCounter++;
|
||
|
if (DEBUG_ISO_SERVER)
|
||
|
printf("IsoServer: increase connection counter to %i!\n", self->connectionCounter);
|
||
|
Semaphore_post(self->connectionCounterMutex);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
private_IsoServer_decreaseConnectionCounter(IsoServer self)
|
||
|
{
|
||
|
|
||
|
Semaphore_wait(self->connectionCounterMutex);
|
||
|
self->connectionCounter--;
|
||
|
|
||
|
if (DEBUG_ISO_SERVER)
|
||
|
printf("IsoServer: decrease connection counter to %i!\n", self->connectionCounter);
|
||
|
Semaphore_post(self->connectionCounterMutex);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
private_IsoServer_getConnectionCounter(IsoServer self)
|
||
|
{
|
||
|
int connectionCounter;
|
||
|
|
||
|
Semaphore_wait(self->connectionCounterMutex);
|
||
|
connectionCounter = self->connectionCounter;
|
||
|
Semaphore_post(self->connectionCounterMutex);
|
||
|
|
||
|
return connectionCounter;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
IsoServer_setUserLock(IsoServer self, Semaphore userLock)
|
||
|
{
|
||
|
self->userLock = userLock;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
IsoServer_userLock(IsoServer self)
|
||
|
{
|
||
|
if (self->userLock != NULL)
|
||
|
Semaphore_wait(self->userLock);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
IsoServer_userUnlock(IsoServer self)
|
||
|
{
|
||
|
if (self->userLock != NULL)
|
||
|
Semaphore_post(self->userLock);
|
||
|
}
|