| /******************************************************************************* |
| * 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; |
| } |