blob: a12214af33d3612e90e51c7a3e68fa1c30b287a6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2023 ACIN, Profactor GmbH, AIT, fortiss GmbH, OFFIS e.V.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Alois Zoitl, Ingo Hegny, Gerhard Ebenhofer, Thomas Strasser
* - initial API and implementation and/or initial documentation
* Jörg Walter
* - improve multicast support
*******************************************************************************/
#include <sockhand.h> //needs to be first pulls in the platform specific includes
#include "bsdsocketinterf.h"
#include "devlog.h"
#include <string.h>
void CBSDSocketInterface::closeSocket(TSocketDescriptor paSockD){
#if defined(NET_OS)
closesocket(paSockD);
#else
close(paSockD);
#endif
}
CBSDSocketInterface::TSocketDescriptor CBSDSocketInterface::openTCPServerConnection(
const char *const paIPAddr, unsigned short paPort){
TSocketDescriptor nRetVal = -1;
#ifndef LOGINFO
(void)paIPAddr;
#else
DEVLOG_INFO("CBSDSocketInterface: Opening TCP-Server connection at: %s:%d\n", paIPAddr, paPort);
#endif
if(TSocketDescriptor nSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);-1 != nSocket) {
struct sockaddr_in stSockAddr;
memset(&(stSockAddr), '\0', sizeof(sockaddr_in));
stSockAddr.sin_family = AF_INET;
#if VXWORKS
stSockAddr.sin_port = static_cast<unsigned short>(htons(paPort));
#else
stSockAddr.sin_port = htons(paPort);
#endif
stSockAddr.sin_addr.s_addr = htonl(INADDR_ANY );
if (int nOptVal = 1;setsockopt(nSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&nOptVal, sizeof(nOptVal)) == -1) {
DEVLOG_ERROR("CBSDSocketInterface: could not set socket option SO_REUSEADDR: %s\n", strerror(errno));
}
if(0 == bind(nSocket, (struct sockaddr *) &stSockAddr, sizeof(struct sockaddr))){
if(-1 == listen(nSocket, 1)){ // for the classic IEC 61499 server only one connection at the same time is accepted TODO mayb make this adjustable for future extensions
DEVLOG_ERROR("CBSDSocketInterface: listen() failed: %s\n", strerror(errno));
}
else{
nRetVal = nSocket;
}
}
else{
DEVLOG_ERROR("CBSDSocketInterface: bind() failed: %s\n", strerror(errno));
}
if(-1 == nRetVal){
close(nSocket);
}
}
else{
DEVLOG_ERROR("CBSDSocketInterface: Couldn't create socket: %s\n", strerror(errno));
}
return nRetVal;
}
CBSDSocketInterface::TSocketDescriptor CBSDSocketInterface::openTCPClientConnection(
char *paIPAddr, unsigned short paPort){
TSocketDescriptor nRetVal = -1;
DEVLOG_INFO("CBSDSocketInterface: Opening TCP-Client connection at: %s:%d\n", paIPAddr, paPort);
if(TSocketDescriptor nSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);-1 != nSocket) {
struct sockaddr_in stSockAddr;
stSockAddr.sin_family = AF_INET;
#if VXWORKS
stSockAddr.sin_port = static_cast<unsigned short>(htons(paPort));
#else
stSockAddr.sin_port = htons(paPort);
#endif
stSockAddr.sin_addr.s_addr = inet_addr(paIPAddr);
#ifndef __ZEPHYR__
memset(&(stSockAddr.sin_zero), '\0', sizeof(stSockAddr.sin_zero));
#endif
if(-1 == connect(nSocket, (struct sockaddr *) &stSockAddr, sizeof(struct sockaddr))){
close(nSocket);
DEVLOG_ERROR("CBSDSocketInterface: connect() failed: %s\n", strerror(errno));
}
else{
nRetVal = nSocket;
}
}
else{
DEVLOG_ERROR("CBSDSocketInterface: Couldn't create socket: %s\n", strerror(errno));
}
return nRetVal;
}
CBSDSocketInterface::TSocketDescriptor CBSDSocketInterface::acceptTCPConnection(
TSocketDescriptor paListeningSockD){
struct sockaddr client_addr;
int sin_size = sizeof(struct sockaddr);
TSocketDescriptor nRetVal;
#if defined(NET_OS) || defined (VXWORKS)
nRetVal = accept(paListeningSockD, &client_addr, &sin_size);
#else
nRetVal = accept(paListeningSockD, &client_addr, (socklen_t*) &sin_size);
#endif
return nRetVal;
}
int CBSDSocketInterface::sendDataOnTCP(TSocketDescriptor paSockD, const char* paData,
unsigned int paSize) {
// This function sends all data in the buffer before it returns!
int nToSend = paSize;
int nRetVal = 0;
while(0 < nToSend){
//TODO: check if open connection (socket might be closed by peer)
nRetVal = static_cast<int>(send(paSockD, paData, nToSend, 0));
if(nRetVal <= 0){
DEVLOG_ERROR("TCP-Socket Send failed: %s\n", strerror(errno));
break;
}
nToSend -= nRetVal;
paData += nRetVal;
}
return nRetVal;
}
int CBSDSocketInterface::receiveDataFromTCP(TSocketDescriptor paSockD, char* paData,
unsigned int paBufSize){
int nRetVal;
do{
nRetVal = static_cast<int>(recv(paSockD, paData, paBufSize, 0));
} while((-1 == nRetVal) && (EINTR == errno)); // recv got interrupt / recieving again
if(nRetVal == -1){
DEVLOG_ERROR("CBSDSocketInterface: TCP-Socket recv() failed: %s\n", strerror(errno));
}
return nRetVal;
}
CBSDSocketInterface::TSocketDescriptor CBSDSocketInterface::openUDPSendPort(char *paIPAddr,
unsigned short paPort, TUDPDestAddr *mDestAddr, const char *paMCInterface){
DEVLOG_INFO("CBSDSocketInterface: Opening UDP sending connection at: %s:%d\n", paIPAddr, paPort);
TSocketDescriptor nRetVal;
nRetVal = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(-1 != nRetVal){
mDestAddr->sin_family = AF_INET;
#if VXWORKS
mDestAddr->sin_port = static_cast<unsigned short>(htons(paPort));
#else
mDestAddr->sin_port = htons(paPort);
#endif
mDestAddr->sin_addr.s_addr = inet_addr(paIPAddr);
#ifndef __ZEPHYR__
memset(&(mDestAddr->sin_zero), '\0', sizeof(mDestAddr->sin_zero));
if (paMCInterface) {
struct in_addr ifaddr;
ifaddr.s_addr = inet_addr(paMCInterface);
if (setsockopt(nRetVal, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, sizeof(ifaddr)) != 0) {
DEVLOG_WARNING("CBSDSocketInterface: setsockopt(IP_MULTICAST_IF) failed: %s\n", strerror(errno));
}
}
#endif
#ifdef NET_OS
/* following is typedef void TM_fAR * in treck/include/trsocket.h */
int nIfCount;
int nRunner = 0;
unsigned long nLocalInAddr;
ttUserInterface *pIfList;
ttUserInterface stInterfaceHandle; /* gets recast as above */
/*get interfaces */
pIfList = tfGetInterfaceList(&nIfCount);
while (nIfCount--){
stInterfaceHandle = pIfList[nRunner++];
if (tfGetIpAddress(stInterfaceHandle, &nLocalInAddr, 0) == 0){ //get IPv4 Address of interface
/* bind socket to all available IPv4 addresses for multicast */
setsockopt (nRetVal, IPPROTO_IP, IP_MULTICAST_IF, (char *) &nLocalInAddr, sizeof (nLocalInAddr));
}
}
#endif
}
return nRetVal;
}
CBSDSocketInterface::TSocketDescriptor CBSDSocketInterface::openUDPReceivePort(char *paIPAddr,
unsigned short paPort, const char *paMCInterface){
DEVLOG_INFO("CBSDSocketInterface: Opening UDP receiving connection at: %s:%d\n", paIPAddr, paPort);
TSocketDescriptor nRetVal = -1;
TSocketDescriptor nSocket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(-1 != nSocket){
int nReuseAddrVal = 1;
if(0
<= setsockopt(nSocket, SOL_SOCKET, SO_REUSEADDR, (char *) &nReuseAddrVal, sizeof(nReuseAddrVal))){
#ifdef __APPLE__
if(0 > setsockopt(nSocket, SOL_SOCKET, SO_REUSEPORT, (char *) &nReuseAddrVal, sizeof(nReuseAddrVal))){
DEVLOG_ERROR("CBSDSocketInterface: setsockopt(SO_REUSEPORT) failed: %s\n", strerror(errno));
return nRetVal;
}
#endif
struct sockaddr_in stSockAddr;
stSockAddr.sin_family = AF_INET;
#if VXWORKS
stSockAddr.sin_port = static_cast<unsigned short>(htons(paPort));
#else
stSockAddr.sin_port = htons(paPort);
#endif
stSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
#ifndef __ZEPHYR__
memset(&(stSockAddr.sin_zero), '\0', sizeof(stSockAddr.sin_zero));
#endif
if(0 == bind(nSocket, (struct sockaddr *) &stSockAddr, sizeof(struct sockaddr))){
#ifndef __ZEPHYR__
// setting up multicast group
struct ip_mreq stMReq;
stMReq.imr_multiaddr.s_addr = inet_addr(paIPAddr);
stMReq.imr_interface.s_addr = inet_addr(paMCInterface);
if(0 > setsockopt(nSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &stMReq, sizeof(stMReq))){
//if this fails we may have given a non multicasting addr. For now we accept this. May need to be changed in the future.
DEVLOG_WARNING("CBSDSocketInterface: setsockopt(IP_ADD_MEMBERSHIP) failed: %s\n", strerror(errno));
}
#endif
nRetVal = nSocket;
}
else{
DEVLOG_ERROR("CBSDSocketInterface: bind() failed: %s\n", strerror(errno));
}
}
else{
DEVLOG_ERROR("CBSDSocketInterface: setsockopt(SO_REUSEADDR) failed: %s\n", strerror(errno));
}
}
else{
DEVLOG_ERROR("CBSDSocketInterface: Couldn't create socket: %s\n", strerror(errno));
}
return nRetVal;
}
int CBSDSocketInterface::sendDataOnUDP(TSocketDescriptor paSockD, TUDPDestAddr *paDestAddr,
char* paData, unsigned int paSize){
// This function sends all data in the buffer before it returns!
int nToSend = paSize;
int nRetVal = 0;
while(0 < nToSend){
//TODO: check if open connection (socket might be closed by peer)
nRetVal = static_cast<int>(
sendto(paSockD, paData, nToSend, 0, (struct sockaddr *) paDestAddr, sizeof(struct sockaddr)));
if(nRetVal <= 0){
DEVLOG_ERROR("CBSDSocketInterface: UDP-Socket Send failed: %s\n", strerror(errno));
break;
}
nToSend -= nRetVal;
paData += nRetVal;
}
return nRetVal;
}
int CBSDSocketInterface::receiveDataFromUDP(TSocketDescriptor paSockD, char* paData,
unsigned int paBufSize){
int nRetVal;
do{
nRetVal = static_cast<int>(recvfrom(paSockD, paData, paBufSize, 0, nullptr, nullptr));
} while((-1 == nRetVal) && (EINTR == errno)); // recv got interrupt / recieving again
if(nRetVal == -1){ //
DEVLOG_ERROR("CBSDSocketInterface: UDP-Socket recvfrom() failed: %s\n", strerror(errno));
}
return nRetVal;
}