void CheckAndHandleUdpUnicastSocket() { int nReceived_size; int nRemainingBytes; int nReplyLen; EipUint8 *rxp; struct sockaddr_in stFrom; unsigned long nFromLen; /* see if this is an unsolicited inbound UDP message */ if (true == checkSocketSet(g_network_status.udp_unicast_listener)) { nFromLen = sizeof(stFrom); OPENER_TRACE_STATE( "networkhandler: unsolicited UDP message on EIP broadcast socket\n"); /*Handle udp broadcast messages */ nReceived_size = recvfrom(g_network_status.udp_unicast_listener, g_ethernet_communciation_buffer, PC_OPENER_ETHERNET_BUFFER_SIZE, 0, (struct sockaddr *) &stFrom, &nFromLen); if (nReceived_size <= 0) { /* got error */ OPENER_TRACE_ERR( "networkhandler: error on recvfrom udp broadcast port: %s\n", strerror(errno)); return; } OPENER_TRACE_INFO("Data received on udp:\n"); rxp = &g_ethernet_communciation_buffer[0]; do { nReplyLen = HandleReceivedExplictUdpData( g_network_status.udp_unicast_listener, &stFrom, rxp, nReceived_size, &nRemainingBytes, true); rxp += nReceived_size - nRemainingBytes; nReceived_size = nRemainingBytes; if (nReplyLen > 0) { OPENER_TRACE_INFO("reply sent:\n"); /* if the active fd matches a registered UDP callback, handle a UDP packet */ if (sendto(g_network_status.udp_unicast_listener, (char *)g_ethernet_communciation_buffer, nReplyLen, 0, (struct sockaddr *) &stFrom, sizeof(stFrom)) != nReplyLen) { OPENER_TRACE_INFO( "networkhandler: UDP response was not fully sent\n"); } } } while (nRemainingBytes > 0); } }
void CheckAndHandleTcpListenerSocket() { int newfd; /* see if this is a connection request to the TCP listener*/ if (true == checkSocketSet(g_network_status.tcp_listener)) { OPENER_TRACE_INFO("networkhandler: new TCP connection\n"); newfd = accept(g_network_status.tcp_listener, NULL, NULL); if (newfd == -1) { OPENER_TRACE_ERR("networkhandler: error on accept: %s\n", strerror(errno)); return; } FD_SET(newfd, &master); /* add newfd to master set */ if (newfd > fdmax) { fdmax = newfd; } OPENER_TRACE_STATE( "networkhandler: opened new TCP connection on fd %d\n", newfd); } }
void CloseSocket(int pa_nSockFd) { OPENER_TRACE_INFO("networkhandler: closing socket %d\n", pa_nSockFd); if (kEipInvalidSocket != pa_nSockFd) { FD_CLR(pa_nSockFd, &master); #ifdef WIN32 closesocket(pa_nSockFd); #else shutdown(pa_nSockFd, SHUT_RDWR); close(pa_nSockFd); #endif } }
CipBool checkSocketSet(int pa_nSocket) { CipBool nRetVal = false; if (FD_ISSET(pa_nSocket, &read_fds)) { if (FD_ISSET(pa_nSocket, &master)) { nRetVal = true; } else { OPENER_TRACE_INFO("socket: %d closed with pending message\n", pa_nSocket); } FD_CLR(pa_nSocket, &read_fds); /* remove it from the read set so that later checks will not find it */ } return nRetVal; }
void ConfigureDomainName() { // This was a parameter! int interface_index = 0; CipDword dwSize = 0; int i = 0; // Set the flags to pass to GetAdaptersAddresses CipUdint flags = GAA_FLAG_INCLUDE_PREFIX; CipDword dwRetVal = 0; // default to unspecified address family (both) CipUdint family = AF_UNSPEC; LPVOID lpMsgBuf = NULL; PIP_ADAPTER_ADDRESSES pAddresses = NULL; PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = NULL; CipUdint outBufLen = 0; CipUdint tries = 0; family = AF_INET; // Allocate a 15 KB buffer to start with. outBufLen = WORKING_BUFFER_SIZE; do { pAddresses = (IP_ADAPTER_ADDRESSES *)CipCalloc(1,outBufLen); if (pAddresses == NULL) { printf ("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n"); exit(1); } dwRetVal = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen); if (dwRetVal == ERROR_BUFFER_OVERFLOW) { CipFree(pAddresses); pAddresses = NULL; } else { break; } tries++; } while ( (dwRetVal == ERROR_BUFFER_OVERFLOW) && (tries < MAX_TRIES) ); if (dwRetVal == NO_ERROR) { // If successful, output some information from the data we received pCurrAddresses = pAddresses; while (pCurrAddresses) { if (interface_index == pCurrAddresses->IfIndex) { pDnServer = pCurrAddresses->FirstDnsServerAddress; if (pDnServer) { for (i = 0; pDnServer != NULL; i++) { pDnServer = pDnServer->Next; } } char pStringBuf[INET_ADDRSTRLEN]; if (i != 0) { if (NULL != interface_configuration_.domain_name.string) { /* if the string is already set to a value we have to free the resources * before we can set the new value in order to avoid memory leaks. */ CipFree(interface_configuration_.domain_name.string); } interface_configuration_.domain_name.length = strlen( pCurrAddresses->DnsSuffix); if (interface_configuration_.domain_name.length) { interface_configuration_.domain_name.string = (CipByte *)CipCalloc( interface_configuration_.domain_name.length + 1, sizeof(CipUsint) ); strcpy(interface_configuration_.domain_name.string, pCurrAddresses->DnsSuffix); } else { interface_configuration_.domain_name.string = NULL; } /* inet_ntop(AF_INET, pCurrAddresses->FirstDnsServerAddress->Address.lpSockaddr->sa_data + 2, interface_configuration_.name_server, sizeof(interface_configuration_.name_server) ); inet_ntop(AF_INET, pCurrAddresses->FirstDnsServerAddress->Next->Address.lpSockaddr->sa_data + 2, interface_configuration_.name_server_2, sizeof(interface_configuration_.name_server_2) ); */ } else{ interface_configuration_.domain_name.length = 0;} } pCurrAddresses = pCurrAddresses->Next; } } else { OPENER_TRACE_INFO("Call to GetAdaptersAddresses failed with error: %d\n", dwRetVal); if (dwRetVal == ERROR_NO_DATA) { OPENER_TRACE_INFO( "\tNo addresses were found for the requested parameters\n"); } else { if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwRetVal, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR)&lpMsgBuf, 0, NULL) ) { OPENER_TRACE_INFO("\tError: %s", lpMsgBuf); CipFree(lpMsgBuf); if (pAddresses) { CipFree(pAddresses); } exit(1); } } } if (pAddresses) { CipFree(pAddresses); } }
EIP_STATUS setAssemblyAttributeSingle(S_CIP_Instance * pa_pstInstance, S_CIP_MR_Request * pa_pstMRRequest, S_CIP_MR_Response * pa_pstMRResponse) { EIP_UINT8 * acReqData; S_CIP_attribute_struct * p; OPENER_TRACE_INFO(" setAttribute %d\n", pa_pstMRRequest->RequestPath.AttributNr); acReqData = pa_pstMRRequest->Data; pa_pstMRResponse->DataLength = 0; pa_pstMRResponse->ReplyService = (0x80 | pa_pstMRRequest->Service); pa_pstMRResponse->GeneralStatus = CIP_ERROR_ATTRIBUTE_NOT_SUPPORTED; pa_pstMRResponse->SizeofAdditionalStatus = 0; p = getAttribute(pa_pstInstance, pa_pstMRRequest->RequestPath.AttributNr); if((p != 0) && (3 == pa_pstMRRequest->RequestPath.AttributNr)) { if(p->pt2data != 0) { S_CIP_Byte_Array * pacData = (S_CIP_Byte_Array *) p->pt2data; //TODO: check for ATTRIBUTE_SET/GETABLE MASK if(true == isConnectedOutputAssembly(pa_pstInstance->nInstanceNr)) { OPENER_TRACE_WARN("Assembly AssemblyAttributeSingle: received data for connected output assembly\n\r"); pa_pstMRResponse->GeneralStatus = CIP_ERROR_ATTRIBUTE_NOT_SETTABLE; } else { if(pa_pstMRRequest->DataLength < pacData->len) { OPENER_TRACE_INFO("Assembly setAssemblyAttributeSingle: not enough data received.\r\n"); pa_pstMRResponse->GeneralStatus = CIP_ERROR_NOT_ENOUGH_DATA; } else { if(pa_pstMRRequest->DataLength > pacData->len) { OPENER_TRACE_INFO("Assembly setAssemblyAttributeSingle: too much data received.\r\n"); pa_pstMRResponse->GeneralStatus = CIP_ERROR_TOO_MUCH_DATA; } else { memcpy(pacData->Data, acReqData, pacData->len); if(IApp_AfterAssemblyDataReceived(pa_pstInstance) != EIP_OK) { /* punt early without updating the status... though I don't know * how much this helps us here, as the attribute's data has already * been overwritten. * * however this is the task of the application side which will * take the data. In addition we have to inform the sender that the * data was not ok. */ pa_pstMRResponse->GeneralStatus = CIP_ERROR_INVALID_ATTRIBUTE_VALUE; } else { pa_pstMRResponse->GeneralStatus = CIP_ERROR_SUCCESS; } } } } } else { /* the attribute was zero we are a heartbeat assembly */ pa_pstMRResponse->GeneralStatus = CIP_ERROR_TOO_MUCH_DATA; } } return EIP_OK_SEND; }
/* create a new UDP socket for the connection manager returns the fd if successful, else -1 */ int CreateUdpSocket(int pa_nDirection, struct sockaddr_in *pa_pstAddr) { struct sockaddr_in stPeerAdr; int newfd; #ifdef WIN32 unsigned long nPeerAddrLen; #else socklen_t nPeerAddrLen; #endif nPeerAddrLen = sizeof(struct sockaddr_in); /* create a new UDP socket */ if ((newfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { OPENER_TRACE_ERR("networkhandler: cannot create UDP socket: %s\n", strerror(errno)); return kEipInvalidSocket; } OPENER_TRACE_INFO("networkhandler: UDP socket %d\n", newfd); /* check if it is sending or receiving */ if (pa_nDirection == kUdpCommuncationDirectionConsuming) { int nOptVal = 1; if (setsockopt(newfd, SOL_SOCKET, SO_REUSEADDR, (char *)&nOptVal, sizeof(nOptVal)) == -1) { OPENER_TRACE_ERR("error setting socket option SO_REUSEADDR on consuming udp socket\n"); return kEipStatusError; } /* bind is only for consuming necessary */ if ((bind(newfd, (struct sockaddr *) pa_pstAddr, sizeof(struct sockaddr))) == -1) { OPENER_TRACE_ERR("error on bind udp: %s\n", strerror(errno)); return kEipInvalidSocket; } OPENER_TRACE_INFO("networkhandler: bind UDP socket %d\n", newfd); } else { /* we have a producing udp socket */ if (pa_pstAddr->sin_addr.s_addr == g_multicast_configuration.starting_multicast_address) { if (1 != g_time_to_live_value) { /* we need to set a TTL value for the socket */ if (setsockopt(newfd, IPPROTO_IP, IP_MULTICAST_TTL, &g_time_to_live_value, sizeof(g_time_to_live_value) < 0)) { OPENER_TRACE_ERR("networkhandler: could not set the TTL to: %d, error: %s\n", g_time_to_live_value, strerror(errno)); return kEipInvalidSocket; } } } } if ((pa_nDirection == kUdpCommuncationDirectionConsuming) || (0 == pa_pstAddr->sin_addr.s_addr)) { /* we have a peer to peer producer or a consuming connection*/ if (getpeername(g_current_active_tcp_socket, (struct sockaddr *) &stPeerAdr, &nPeerAddrLen) < 0) { OPENER_TRACE_ERR("networkhandler: could not get peername: %s\n", strerror(errno)); return kEipInvalidSocket; } /* store the originators address */ pa_pstAddr->sin_addr.s_addr = stPeerAdr.sin_addr.s_addr; } /* add new fd to the master list */ FD_SET(newfd, &master); if (newfd > fdmax) { fdmax = newfd; } return newfd; }
EipStatus handleDataOnTCPSocket(int pa_nSocket) { EipUint8 *rxp; long nCheckVal; size_t unDataSize; long nDataSent; int nRemainingBytes = 0; /* We will handle just one EIP packet here the rest is done by the select * method which will inform us if more data is available in the socket because of the current implementation of the main loop this may not be the fastest way and a loop here with a non blocking socket would better fit*/ /*Check how many data is here -- read the first four bytes from the connection */ nCheckVal = recv(pa_nSocket, g_ethernet_communciation_buffer, 4, 0); /*TODO we may have to set the socket to a non blocking socket */ if (nCheckVal == 0) { OPENER_TRACE_ERR("networkhandler: connection closed by client: %s\n", strerror(errno)); return kEipStatusError; } if (nCheckVal < 0) { OPENER_TRACE_ERR("networkhandler: error on recv: %s\n", strerror(errno)); return kEipStatusError; } rxp = &g_ethernet_communciation_buffer[2]; /* at this place EIP stores the data length */ unDataSize = GetIntFromMessage(&rxp) + ENCAPSULATION_HEADER_LENGTH - 4; /* -4 is for the 4 bytes we have already read*/ /* (NOTE this advances the buffer pointer) */ if (PC_OPENER_ETHERNET_BUFFER_SIZE - 4 < unDataSize) { /*TODO can this be handled in a better way?*/ OPENER_TRACE_ERR("too large packet received will be ignored, will drop the data\n"); /* Currently we will drop the whole packet */ nDataSent = PC_OPENER_ETHERNET_BUFFER_SIZE; do { nCheckVal = recv(pa_nSocket, g_ethernet_communciation_buffer, nDataSent, 0); if (nCheckVal == 0) /* got error or connection closed by client */ { OPENER_TRACE_ERR("networkhandler: connection closed by client: %s\n", strerror(errno)); return kEipStatusError; } if (nCheckVal < 0) { OPENER_TRACE_ERR("networkhandler: error on recv: %s\n", strerror(errno)); return kEipStatusError; } unDataSize -= nCheckVal; if ((unDataSize < PC_OPENER_ETHERNET_BUFFER_SIZE) && (unDataSize != 0)) { nDataSent = unDataSize; } } while (0 != unDataSize); /*TODO fragile end statement */ return kEipStatusOk; } nCheckVal = recv(pa_nSocket, &g_ethernet_communciation_buffer[4], unDataSize, 0); if (nCheckVal == 0) /* got error or connection closed by client */ { OPENER_TRACE_ERR("networkhandler: connection closed by client: %s\n", strerror(errno)); return kEipStatusError; } if (nCheckVal < 0) { OPENER_TRACE_ERR("networkhandler: error on recv: %s\n", strerror(errno)); return kEipStatusError; } if ((unsigned) nCheckVal == unDataSize) { /*we got the right amount of data */ unDataSize += 4; /*TODO handle partial packets*/ OPENER_TRACE_INFO("Data received on tcp:\n"); g_current_active_tcp_socket = pa_nSocket; nCheckVal = HandleReceivedExplictTcpData(pa_nSocket, g_ethernet_communciation_buffer, unDataSize, &nRemainingBytes); g_current_active_tcp_socket = -1; if (nRemainingBytes != 0) { OPENER_TRACE_WARN("Warning: received packet was to long: %d Bytes left!\n", nRemainingBytes); } if (nCheckVal > 0) { OPENER_TRACE_INFO("reply sent:\n"); nDataSent = send(pa_nSocket, (char *) g_ethernet_communciation_buffer, nCheckVal, 0); if (nDataSent != nCheckVal) { OPENER_TRACE_WARN("TCP response was not fully sent\n"); } } return kEipStatusOk; } else { /* we got a fragmented packet currently we cannot handle this will * for this we would need a network buffer per TCP socket * * However with typical packet sizes of EIP this should't be a big issue. */ /*TODO handle fragmented packets */ } return kEipStatusError; }