/****************************************************************************** * Copyright (c) 2000-2021 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: * Peter Dimitrov- initial implementation and initial documentation * Adam Delic * Eduard Czimbalmos * Endre Kulcsar * Gabor Bettesch * Gabor Szalai * Tamas Buti * Zoltan Medve ******************************************************************************/ // // File: SCTPasp_PT.cc // Description: SCTPasp test port source // Rev: <RnXnn> // Prodnr: CNL 113 469 // #include "SCTPasp_PT.hh" #include <sys/types.h> #include <arpa/inet.h> #include <unistd.h> #include <netdb.h> #include <stdarg.h> #include <memory.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include <limits.h> #define BUFLEN 1024 #define MAP_LENGTH 10 #ifdef SCTP_ADAPTION_LAYER #ifdef LKSCTP_1_0_7 #undef LKSCTP_1_0_7 #error LKSCTP_1_0_7 defined but the lksctp older than 1.0.7. Use only -DUSE_SCTP, version is automatically selected #endif #ifdef LKSCTP_1_0_9 #error LKSCTP_1_0_9 defined but the lksctp older than 1.0.7. Use only -DUSE_SCTP, version is automatically selected #undef LKSCTP_1_0_9 #endif #else // 1.0.7 or newer #ifdef SCTP_AUTH_CHUNK // 1.0.9 or newer #ifdef LKSCTP_1_0_7 #undef LKSCTP_1_0_7 #error LKSCTP_1_0_7 defined but the lksctp newer than 1.0.7. Use only -DUSE_SCTP, version is automatically selected #endif #ifndef LKSCTP_1_0_9 #define LKSCTP_1_0_9 #endif #else // 1.0.7 #ifdef LKSCTP_1_0_9 #undef LKSCTP_1_0_9 #error LKSCTP_1_0_9 defined but the lksctp older than 1.0.9. Use only -DUSE_SCTP, version is automatically selected #endif #ifndef LKSCTP_1_0_7 #define LKSCTP_1_0_7 #endif #endif #endif namespace SCTPasp__PortType { /* is any of the bytes from offset .. u8_size in 'u8' non-zero? return offset or -1 if all zero */ static int byte_nonzero(const uint8_t *u8, unsigned int offset, unsigned int u8_size) { int j; for (j = offset; j < u8_size; j++) { if (u8[j] != 0) return j; } return -1; } static int sctp_sockopt_event_subscribe_size = 0; static int determine_sctp_sockopt_event_subscribe_size(void) { uint8_t buf[256]; socklen_t buf_len = sizeof(buf); int sd, rc; /* only do this once */ if (sctp_sockopt_event_subscribe_size != 0) return 0; sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP); if (sd < 0) return sd; rc = getsockopt(sd, IPPROTO_SCTP, SCTP_EVENTS, buf, &buf_len); close(sd); if (rc < 0) return rc; sctp_sockopt_event_subscribe_size = buf_len; //LOGP(DLINP, LOGL_INFO, "sizes of 'struct sctp_event_subscribe': compile-time %zu, kernel: %u\n", // sizeof(struct sctp_event_subscribe), sctp_sockopt_event_subscribe_size); return 0; } /* Attempt to work around Linux kernel ABI breakage * * The Linux kernel ABI for the SCTP_EVENTS socket option has been broken repeatedly. * - until commit 35ea82d611da59f8bea44a37996b3b11bb1d3fd7 ( kernel < 4.11), the size is 10 bytes * - in 4.11 it is 11 bytes * - in 4.12 .. 5.4 it is 13 bytes * - in kernels >= 5.5 it is 14 bytes * * This wouldn't be a problem if the kernel didn't have a "stupid" assumption that the structure * size passed by userspace will match 1:1 the length of the structure at kernel compile time. In * an ideal world, it would just use the known first bytes and assume the remainder is all zero. * But as it doesn't do that, let's try to work around this */ static int sctp_setsockopt_events_linux_workaround(int fd, const struct sctp_event_subscribe *event) { const unsigned int compiletime_size = sizeof(*event); int rc; if (determine_sctp_sockopt_event_subscribe_size() < 0) { //LOGP(DLINP, LOGL_ERROR, "Cannot determine SCTP_EVENTS socket option size\n"); return -1; } if (compiletime_size == sctp_sockopt_event_subscribe_size) { /* no kernel workaround needed */ return setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, event, compiletime_size); } else if (compiletime_size < sctp_sockopt_event_subscribe_size) { /* we are using an older userspace with a more modern kernel and hence need * to pad the data */ uint8_t buf[sctp_sockopt_event_subscribe_size]; memcpy(buf, event, compiletime_size); memset(buf + sizeof(*event), 0, sctp_sockopt_event_subscribe_size - compiletime_size); return setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, buf, sctp_sockopt_event_subscribe_size); } else /* if (compiletime_size > sctp_sockopt_event_subscribe_size) */ { /* we are using a newer userspace with an older kernel and hence need to truncate * the data - but only if the caller didn't try to enable any of the events of the * truncated portion */ rc = byte_nonzero((const uint8_t *)event, sctp_sockopt_event_subscribe_size, compiletime_size); if (rc >= 0) { //LOGP(DLINP, LOGL_ERROR, "Kernel only supports sctp_event_subscribe of %u bytes, " // "but caller tried to enable more modern event at offset %u\n", // sctp_sockopt_event_subscribe_size, rc); return -1; } return setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, event, sctp_sockopt_event_subscribe_size); } } struct SCTPasp__PT_PROVIDER::fd_map_item { // used by map operations int fd; // socket descriptor boolean erased; boolean processing_message; // if true only part of the message is received boolean einprogress; // connection establishment is in progress void * buf; // buffer ssize_t buflen; // length of the buffer ssize_t nr; // number of received bytes struct sockaddr_storage sin; // storing remote address socklen_t saLen; }; struct SCTPasp__PT_PROVIDER::fd_map_server_item // server item { // used by map operations int fd; // socket descriptor boolean erased; CHARSTRING* local_IP_address; unsigned short local_port; }; SCTPasp__PT_PROVIDER::SCTPasp__PT_PROVIDER(const char *par_port_name) : PORT(par_port_name) { simple_mode = FALSE; reconnect = FALSE; reconnect_max_attempts = 6; server_mode = FALSE; debug = FALSE; server_backlog = 1; local_IP_address = "0.0.0.0"; (void) memset(&initmsg, 0, sizeof(struct sctp_initmsg)); initmsg.sinit_num_ostreams = 64; initmsg.sinit_max_instreams = 64; initmsg.sinit_max_attempts = 0; initmsg.sinit_max_init_timeo = 0; (void) memset(&events, 0, sizeof (events)); events.sctp_data_io_event = TRUE; events.sctp_association_event = TRUE; events.sctp_address_event = TRUE; events.sctp_send_failure_event = TRUE; events.sctp_peer_error_event = TRUE; events.sctp_shutdown_event = TRUE; events.sctp_partial_delivery_event = TRUE; #if defined(LKSCTP_1_0_7) || defined(LKSCTP_1_0_9) events.sctp_adaptation_layer_event = TRUE; #else events.sctp_adaption_layer_event = TRUE; #endif local_port_is_present = FALSE; peer_IP_address_is_present = FALSE; peer_port_is_present = FALSE; fd_map=NULL; list_len=0; fd_map_server=NULL; list_len_server=0; fd = -1; local_port=-1; peer_port=-1; receiving_fd=-1; } SCTPasp__PT_PROVIDER::~SCTPasp__PT_PROVIDER() { for(int i=0;i<list_len;i++) map_delete_item(i); Free(fd_map); if(!simple_mode) { for(int i=0;i<list_len_server;i++) map_delete_item_server(i); Free(fd_map_server); } } void SCTPasp__PT_PROVIDER::set_parameter(const char *parameter_name, const char *parameter_value) { if(strcmp(parameter_name, "simple_mode") == 0) { if (strcasecmp(parameter_value,"yes") == 0) simple_mode = TRUE; else if(strcasecmp(parameter_value,"no") == 0) simple_mode = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. Only yes and no can be used!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "reconnect") == 0) { if (strcasecmp(parameter_value,"yes") == 0) reconnect = TRUE; else if(strcasecmp(parameter_value,"no") == 0) reconnect = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. Only yes and no can be used!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "reconnect_max_attempts") == 0) { int value; if ( (sscanf(parameter_value, "%d", &value) == 1) && (value>=0) ) reconnect_max_attempts = value; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "server_mode") == 0) { if (strcasecmp(parameter_value,"yes") == 0) server_mode = TRUE; else if(strcasecmp(parameter_value,"no") == 0) server_mode = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. Only yes and no can be used!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "debug") == 0) { if (strcasecmp(parameter_value,"yes") == 0) debug = TRUE; else if(strcasecmp(parameter_value,"no") == 0) debug = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. Only yes and no can be used!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "server_backlog") == 0) { int value; if ( (sscanf(parameter_value, "%d", &value) == 1) && (value>=0) ) server_backlog = value; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "local_IP_address") == 0) { local_IP_address = parameter_value; } else if(strcmp(parameter_name, "local_port") == 0) { unsigned short value; if (sscanf(parameter_value, "%hu", &value) == 1) { local_port = value; local_port_is_present = TRUE; } else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "peer_IP_address") == 0) { peer_IP_address = parameter_value; peer_IP_address_is_present = TRUE; } else if(strcmp(parameter_name, "peer_port") == 0) { unsigned short value; if (sscanf(parameter_value, "%hu", &value) == 1) { peer_port = value; peer_port_is_present = TRUE; } else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sinit_num_ostreams") == 0) { long value; if ( (sscanf(parameter_value, "%ld", &value) == 1) && (value>=0) ) initmsg.sinit_num_ostreams = value; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sinit_max_instreams") == 0) { long value; if ( (sscanf(parameter_value, "%ld", &value) == 1) && (value>=0) ) initmsg.sinit_max_instreams = value; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sinit_max_attempts") == 0) { long value; if ( (sscanf(parameter_value, "%ld", &value) == 1) && (value>=0) ) initmsg.sinit_max_attempts = value; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sinit_max_init_timeo") == 0) { long value; if ( (sscanf(parameter_value, "%ld", &value) == 1) && (value>=0) ) initmsg.sinit_max_init_timeo = value; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be positive integer!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sctp_association_event") == 0) { if (strcasecmp(parameter_value,"enabled") == 0) events.sctp_association_event = TRUE; else if(strcasecmp(parameter_value,"disabled") == 0) events.sctp_association_event = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be enabled or disabled!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sctp_address_event") == 0) { if (strcasecmp(parameter_value,"enabled") == 0) events.sctp_address_event = TRUE; else if(strcasecmp(parameter_value,"disabled") == 0) events.sctp_address_event = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be enabled or disabled!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sctp_send_failure_event") == 0) { if (strcasecmp(parameter_value,"enabled") == 0) events.sctp_send_failure_event = TRUE; else if(strcasecmp(parameter_value,"disabled") == 0) events.sctp_send_failure_event = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be enabled or disabled!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sctp_peer_error_event") == 0) { if (strcasecmp(parameter_value,"enabled") == 0) events.sctp_peer_error_event = TRUE; else if(strcasecmp(parameter_value,"disabled") == 0) events.sctp_peer_error_event = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be enabled or disabled!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sctp_shutdown_event") == 0) { if (strcasecmp(parameter_value,"enabled") == 0) events.sctp_shutdown_event = TRUE; else if(strcasecmp(parameter_value,"disabled") == 0) events.sctp_shutdown_event = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be enabled or disabled!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sctp_partial_delivery_event") == 0) { if (strcasecmp(parameter_value,"enabled") == 0) events.sctp_partial_delivery_event = TRUE; else if(strcasecmp(parameter_value,"disabled") == 0) events.sctp_partial_delivery_event = FALSE; else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be enabled or disabled!" , parameter_value, parameter_name); } else if(strcmp(parameter_name, "sctp_adaption_layer_event") == 0) { if (strcasecmp(parameter_value,"enabled") == 0) #if defined(LKSCTP_1_0_7) || defined(LKSCTP_1_0_9) events.sctp_adaptation_layer_event = TRUE; #else events.sctp_adaption_layer_event = TRUE; #endif else if(strcasecmp(parameter_value,"disabled") == 0) #if defined(LKSCTP_1_0_7) || defined(LKSCTP_1_0_9) events.sctp_adaptation_layer_event = FALSE; #else events.sctp_adaption_layer_event = FALSE; #endif else error("set_parameter(): Invalid parameter value: %s for parameter %s. It should be enabled or disabled!" , parameter_value, parameter_name); } else TTCN_warning("%s: unknown & unhandled parameter: %s", get_name(), parameter_name); errno = 0; } void SCTPasp__PT_PROVIDER::Handle_Fd_Event_Writable(int my_fd){ int i= map_get_item(my_fd); if(i!=-1 && !simple_mode && !fd_map[i].erased && fd_map[i].einprogress ) { if (connect(fd_map[i].fd, (struct sockaddr *)&fd_map[i].sin, fd_map[i].saLen) == -1) { Handler_Remove_Fd_Write(fd_map[i].fd); if(errno == EISCONN) { SCTPasp__Types::ASP__SCTP__RESULT asp_sctp_result; asp_sctp_result.client__id() = fd_map[i].fd; asp_sctp_result.error__status() = FALSE; asp_sctp_result.error__message() = OMIT_VALUE; incoming_message(asp_sctp_result); fd_map[i].einprogress = FALSE; Handler_Add_Fd_Read(fd_map[i].fd); log("Connection successfully established to (%s):(%d)",(const char*)peer_IP_address, peer_port); } else { close(fd_map[i].fd); fd = -1; TTCN_warning("Connect error!"); SCTPasp__Types::ASP__SCTP__RESULT asp_sctp_result; asp_sctp_result.client__id() = fd_map[i].fd; asp_sctp_result.error__status() = TRUE; asp_sctp_result.error__message() = strerror(errno); incoming_message(asp_sctp_result); map_delete_item_fd(fd_map[i].fd); errno = 0; log("Connection establishment to (%s):(%d) failed !", (const char*)peer_IP_address, peer_port); } } } } void SCTPasp__PT_PROVIDER::Handle_Fd_Event_Error(int my_fd){ Handle_Fd_Event_Readable(my_fd); } CHARSTRING get_ip(struct sockaddr_storage *sa){ char ret_val[INET6_ADDRSTRLEN]; ret_val[0]='\0'; if(sa->ss_family == AF_INET){ struct sockaddr_in* sa4=(struct sockaddr_in*)sa; inet_ntop(AF_INET,&(sa4->sin_addr),ret_val,INET6_ADDRSTRLEN); } else if(sa->ss_family == AF_INET6) { struct sockaddr_in6* sa6=(struct sockaddr_in6*)sa; inet_ntop(AF_INET6,&(sa6->sin6_addr),ret_val,INET6_ADDRSTRLEN); } return CHARSTRING(ret_val); } int get_port(struct sockaddr_storage *sa){ if(sa->ss_family == AF_INET){ struct sockaddr_in* sa4=(struct sockaddr_in*)sa; return ntohs(sa4->sin_port); } else if(sa->ss_family == AF_INET6) { struct sockaddr_in6* sa6=(struct sockaddr_in6*)sa; return ntohs(sa6->sin6_port); } return 0; } void SCTPasp__PT_PROVIDER::Handle_Fd_Event_Readable(int my_fd){ // Accepting new client if(!simple_mode) { for(int i=0;i<list_len_server;i++) { if(!fd_map_server[i].erased && fd_map_server[i].fd==my_fd) { int newclient_fd; struct sockaddr_storage peer_address; socklen_t addrlen = sizeof(peer_address); if ((newclient_fd = accept(fd_map_server[i].fd, (struct sockaddr *)&peer_address, &addrlen)) == -1) error("Event handler: accept error (server mode)!"); else { map_put_item(newclient_fd); setNonBlocking(newclient_fd); Handler_Add_Fd_Read(newclient_fd); incoming_message(SCTPasp__Types::ASP__SCTP__Connected( INTEGER(newclient_fd), *fd_map_server[i].local_IP_address, INTEGER(fd_map_server[i].local_port), get_ip(&peer_address), get_port(&peer_address))); } } } } else { if(server_mode && fd==my_fd) { int newclient_fd; struct sockaddr_storage peer_address; socklen_t addrlen = sizeof(peer_address); if ((newclient_fd = accept(fd, (struct sockaddr *)&peer_address, &addrlen)) == -1) error("Event handler: accept error (server mode)!"); else { map_put_item(newclient_fd); setNonBlocking(newclient_fd); Handler_Add_Fd_Read(newclient_fd); } } } // Receiving data int i= map_get_item(my_fd); if(i!=-1) // valid fd { log("Calling Event_Handler."); receiving_fd = fd_map[i].fd; struct cmsghdr *cmsg; struct sctp_sndrcvinfo *sri; char cbuf[sizeof (*cmsg) + sizeof (*sri)]; struct msghdr msg; struct iovec iov; size_t cmsglen = sizeof (*cmsg) + sizeof (*sri); if ( !fd_map[i].processing_message ) { fd_map[i].buf = Malloc(BUFLEN); fd_map[i].buflen = BUFLEN; iov.iov_base = fd_map[i].buf; iov.iov_len = fd_map[i].buflen; } else { // Set the next read offset log("Event_Handler: setting the next read offset."); iov.iov_base = (char *)fd_map[i].buf + fd_map[i].nr; iov.iov_len = fd_map[i].buflen - fd_map[i].nr; } // Set up the msghdr structure for receiving memset(&msg, 0, sizeof (msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = cbuf; msg.msg_controllen = cmsglen; memset(cbuf, 0, sizeof (*cmsg) + sizeof (*sri)); cmsg = (struct cmsghdr *)cbuf; sri = (struct sctp_sndrcvinfo *)(cmsg + 1); return_value_t value = getmsg(receiving_fd, &msg); switch(value) { case WHOLE_MESSAGE_RECEIVED: fd_map[i].processing_message = FALSE; // Intercept notifications here if (msg.msg_flags & MSG_NOTIFICATION) { log("Calling event_handler for an incoming notification."); handle_event(fd_map[i].buf); } else { log("Incoming data."); unsigned int ui = ntohl(sri->sinfo_ppid); INTEGER i_ppid; if (ui <= (unsigned long)INT_MAX) i_ppid = ui; else { char sbuf[16]; sprintf(sbuf, "%u", ui); i_ppid = INTEGER(sbuf); } incoming_message(SCTPasp__Types::ASP__SCTP( INTEGER(receiving_fd), INTEGER(sri->sinfo_stream), i_ppid, OCTETSTRING(fd_map[i].nr,(const unsigned char *)fd_map[i].buf))); } Free(fd_map[i].buf); fd_map[i].buf = NULL; break; case PARTIAL_RECEIVE: fd_map[i].processing_message = TRUE; break; case EOF_OR_ERROR: if (!server_mode) fd = -1; // setting closed socket to -1 in client mode (and reconnect mode) map_delete_item(i); if (events.sctp_association_event) incoming_message(SCTPasp__Types::ASP__SCTP__ASSOC__CHANGE( INTEGER(receiving_fd), SCTPasp__Types::SAC__STATE(SCTP_COMM_LOST))); log("getmsg() returned with NULL. Socket is closed."); if (reconnect) forced_reconnect(reconnect_max_attempts); break; }//endswitch }// endif } void SCTPasp__PT_PROVIDER::user_map(const char *system_port) { log("Calling user_map(%s).",system_port); if(simple_mode) { if ( server_mode && reconnect ) { error("user_map(): server mode and reconnect mode are mutually exclusive!"); } if ( server_mode && !local_port_is_present ) { error("user_map(): in server mode local_port must be defined!"); } // Server mode: turns on listening if (server_mode) { log("Running in SERVER_MODE."); struct sockaddr_storage sa; socklen_t saLen=sizeof(sa); int sock_type=fill_addr_struct(local_IP_address,local_port,&sa,saLen); fd=create_socket(sock_type); if(bind(fd,(const struct sockaddr *)&sa,saLen)!=0){ error("bind failed: %d, %s", errno, strerror(errno)); } if (listen(fd, server_backlog) == -1) error("Listen error!"); log("Listening @ (%s):(%d)", (const char*)local_IP_address, local_port); Handler_Add_Fd_Read(fd); } else if (reconnect) { log("Running in RECONNECT MODE."); forced_reconnect(reconnect_max_attempts+1); } else { log("Running in CLIENT MODE."); } } else { log("Running in NORMAL MODE."); } log("Leaving user_map()."); } void SCTPasp__PT_PROVIDER::user_unmap(const char *system_port) { log("Calling user_unmap(%s).",system_port); if(!simple_mode) { for(int i=0;i<list_len;i++) map_delete_item(i); for(int i=0;i<list_len_server;i++) map_delete_item_server(i); } else { for(int i=0;i<list_len;i++) map_delete_item(i); if(server_mode && fd != -1) { close(fd); Handler_Remove_Fd(fd, EVENT_ALL); } } log("Leaving user_unmap()."); } void SCTPasp__PT_PROVIDER::user_start() { log("Calling user_start()."); log("Leaving user_start()."); } void SCTPasp__PT_PROVIDER::user_stop() { log("Calling user_stop()."); log("Leaving user_stop()."); } void SCTPasp__PT_PROVIDER::outgoing_send(const SCTPasp__Types::ASP__SCTP__Connect& send_par) { log("Calling outgoing_send (ASP_SCTP_CONNECT)."); if(simple_mode) { if (server_mode) error("ASP_SCTP_CONNECT is not allowed in server mode!"); if(fd != -1) error("ASP_SCTP_CONNECT called during active connection."); } if( !peer_IP_address_is_present && !send_par.peer__hostname().ispresent() ) error("Peer IP address should be defined!"); if( !peer_port_is_present && !send_par.peer__portnumber().ispresent() ) error("Peer port should be defined!"); if(send_par.peer__hostname().ispresent()) { peer_IP_address = send_par.peer__hostname()(); } if(send_par.peer__portnumber().ispresent()){ peer_port = (int) send_par.peer__portnumber()(); } struct sockaddr_storage sa; socklen_t saLen; int sock_type=fill_addr_struct(peer_IP_address,peer_port,&sa,saLen); fd=create_socket(sock_type); if(simple_mode && local_port_is_present){ // we should bind struct sockaddr_storage loc_sa; socklen_t loc_saLen; int loc_sock_type=fill_addr_struct(local_IP_address,local_port,&loc_sa,loc_saLen); if(sock_type!=loc_sock_type) error("The local and peer IP addreses are different type: %s %i %s %i", (const char*)peer_IP_address,sock_type,(const char*)local_IP_address,loc_sock_type); if(bind(fd,(const struct sockaddr *)&loc_sa,loc_saLen)!=0){ error("bind failed %d %s",errno, strerror(errno)); } } log("Connecting to (%s):(%d)", (const char*)peer_IP_address, peer_port); // setting non-blocking mode if(!simple_mode) setNonBlocking(fd); if (connect(fd, (const struct sockaddr *)&sa, saLen) == -1) { if(errno == EINPROGRESS && !simple_mode) { map_put_item(fd); int i = map_get_item(fd); fd_map[i].einprogress = TRUE; fd_map[i].sin = sa; fd_map[i].saLen = saLen; Handler_Add_Fd_Write(fd); log("Connection in progress to (%s):(%d)",(const char*)peer_IP_address, peer_port); } else { close(fd); fd = -1; TTCN_warning("Connect error!"); SCTPasp__Types::ASP__SCTP__RESULT asp_sctp_result; asp_sctp_result.client__id() = OMIT_VALUE; asp_sctp_result.error__status() = TRUE; asp_sctp_result.error__message() = strerror(errno); incoming_message(asp_sctp_result); } errno = 0; } else { SCTPasp__Types::ASP__SCTP__RESULT asp_sctp_result; asp_sctp_result.client__id() = fd; asp_sctp_result.error__status() = FALSE; asp_sctp_result.error__message() = OMIT_VALUE; incoming_message(asp_sctp_result); map_put_item(fd); if(simple_mode) setNonBlocking(fd); Handler_Add_Fd_Read(fd); log("Connection successfully established to (%s):(%d)", (const char*)peer_IP_address, peer_port); } log("Leaving outgoing_send (ASP_SCTP_CONNECT)."); } void SCTPasp__PT_PROVIDER::outgoing_send(const SCTPasp__Types::ASP__SCTP__ConnectFrom& send_par) { log("Calling outgoing_send (ASP_SCTP_CONNECTFROM)."); if(!simple_mode) { if( !peer_IP_address_is_present && !send_par.peer__hostname().ispresent() ) error("Peer IP address should be defined!"); if( !peer_port_is_present && !send_par.peer__portnumber().ispresent() ) error("Peer port should be defined!"); // work around for create_socket() if(send_par.peer__hostname().ispresent()) { peer_IP_address = send_par.peer__hostname()(); } if(send_par.peer__portnumber().ispresent()){ peer_port = (int) send_par.peer__portnumber()(); } struct sockaddr_storage sa; socklen_t saLen; int sock_type=fill_addr_struct(peer_IP_address,peer_port,&sa,saLen); struct sockaddr_storage loc_sa; socklen_t loc_saLen; int loc_sock_type=fill_addr_struct(send_par.local__hostname().ispresent()?send_par.local__hostname()():local_IP_address, (int) send_par.local__portnumber(),&loc_sa,loc_saLen); if(sock_type!=loc_sock_type) error("The local and peer IP addreses are different type: %s %i %s %i", (const char*)peer_IP_address,sock_type,(const char*)local_IP_address,loc_sock_type); fd=create_socket(sock_type); if(bind(fd,(const struct sockaddr *)&loc_sa,loc_saLen)!=0){ error("bind failed %d %s",errno, strerror(errno)); } log("Connecting to (%s):(%d)", (const char*)peer_IP_address, peer_port); // setting non-blocking mode setNonBlocking(fd); if (connect(fd, (struct sockaddr *)&sa, saLen) == -1) { if(errno == EINPROGRESS) { map_put_item(fd); int i = map_get_item(fd); fd_map[i].einprogress = TRUE; fd_map[i].sin = sa; fd_map[i].saLen = saLen; Handler_Add_Fd_Write(fd); log("Connection in progress to (%s):(%d)",(const char*)peer_IP_address, peer_port); } else { close(fd); fd = -1; TTCN_warning("Connect error!"); SCTPasp__Types::ASP__SCTP__RESULT asp_sctp_result; asp_sctp_result.client__id() = OMIT_VALUE; asp_sctp_result.error__status() = TRUE; asp_sctp_result.error__message() = strerror(errno); incoming_message(asp_sctp_result); } errno = 0; } else { SCTPasp__Types::ASP__SCTP__RESULT asp_sctp_result; asp_sctp_result.client__id() = fd; asp_sctp_result.error__status() = FALSE; asp_sctp_result.error__message() = OMIT_VALUE; incoming_message(asp_sctp_result); map_put_item(fd); Handler_Add_Fd_Read(fd); log("Connection successfully established to (%s):(%d)", (const char*)peer_IP_address, peer_port); } } log("Leaving outgoing_send (ASP_SCTP_CONNECTFROM)."); } void SCTPasp__PT_PROVIDER::outgoing_send(const SCTPasp__Types::ASP__SCTP__Listen& send_par) { log("Calling outgoing_send (ASP_SCTP_LISTEN)."); if(!simple_mode) { const CHARSTRING& loc_name=send_par.local__hostname().ispresent()?send_par.local__hostname()():local_IP_address; struct sockaddr_storage loc_sa; socklen_t loc_saLen; int loc_sock_type=fill_addr_struct(loc_name,(int) send_par.local__portnumber(),&loc_sa,loc_saLen); fd=create_socket(loc_sock_type); if(bind(fd,(const struct sockaddr *)&loc_sa,loc_saLen)!=0){ error("bind failed %d %s",errno, strerror(errno)); } if (listen(fd, server_backlog) == -1) error("Listen error!"); map_put_item_server(fd, loc_name, (int) send_par.local__portnumber()); log("Listening @ (%s):(%d)", (const char *)loc_name, (int) send_par.local__portnumber()); Handler_Add_Fd_Read(fd); #ifdef SCTP_REPORT_LISTEN_RESULT SCTPasp__Types::ASP__SCTP__RESULT asp_sctp_result; asp_sctp_result.client__id() = fd; asp_sctp_result.error__status() = FALSE; asp_sctp_result.error__message() = OMIT_VALUE; incoming_message(asp_sctp_result); #endif } log("Leaving outgoing_send (ASP_SCTP_LISTEN)."); } void SCTPasp__PT_PROVIDER::outgoing_send(const SCTPasp__Types::ASP__SCTP__SetSocketOptions& send_par) { log("Calling outgoing_send (ASP_SCTP_SETSOCKETOPTIONS)."); switch (send_par.get_selection()) { case SCTPasp__Types::ASP__SCTP__SetSocketOptions::ALT_Sctp__init: { (void) memset(&initmsg, 0, sizeof(struct sctp_initmsg)); const SCTPasp__Types::SCTP__INIT& init = send_par.Sctp__init(); initmsg.sinit_num_ostreams = (int) init.sinit__num__ostreams(); initmsg.sinit_max_instreams = (int) init.sinit__max__instreams(); initmsg.sinit_max_attempts = (int) init.sinit__max__attempts(); initmsg.sinit_max_init_timeo = (int) init.sinit__max__init__timeo(); log("Setting SCTP socket options (initmsg)."); if (setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(struct sctp_initmsg)) < 0) { TTCN_warning("Setsockopt error!"); SCTPasp__Types::ASP__SCTP__RESULT asp_sctp_result; asp_sctp_result.client__id() = fd; asp_sctp_result.error__status() = TRUE; asp_sctp_result.error__message() = strerror(errno); incoming_message(asp_sctp_result); errno = 0; } else { SCTPasp__Types::ASP__SCTP__RESULT asp_sctp_result; asp_sctp_result.client__id() = fd; asp_sctp_result.error__status() = FALSE; asp_sctp_result.error__message() = OMIT_VALUE; incoming_message(asp_sctp_result); } break; } case SCTPasp__Types::ASP__SCTP__SetSocketOptions::ALT_Sctp__events: { const SCTPasp__Types::SCTP__EVENTS& event = send_par.Sctp__events(); events.sctp_data_io_event = (boolean) event.sctp__data__io__event(); events.sctp_association_event = (boolean) event.sctp__association__event(); events.sctp_address_event = (boolean) event.sctp__address__event(); events.sctp_send_failure_event = (boolean) event.sctp__send__failure__event(); events.sctp_peer_error_event = (boolean) event.sctp__peer__error__event(); events.sctp_shutdown_event = (boolean) event.sctp__shutdown__event(); events.sctp_partial_delivery_event = (boolean) event.sctp__partial__delivery__event(); #if defined(LKSCTP_1_0_7) || defined(LKSCTP_1_0_9) events.sctp_adaptation_layer_event = (boolean) event.sctp__adaption__layer__event(); #else events.sctp_adaption_layer_event = (boolean) event.sctp__adaption__layer__event(); #endif break; } case SCTPasp__Types::ASP__SCTP__SetSocketOptions::ALT_So__linger: { struct linger so_linger; (void) memset(&so_linger, 0, sizeof (so_linger)); const SCTPasp__Types::SO__LINGER& so = send_par.So__linger(); so_linger.l_onoff = (int) so.l__onoff(); so_linger.l_linger = (int) so.l__linger(); // Setting a socket level option log("Setting SCTP socket options (so_linger)."); if (setsockopt(fd, SOL_SOCKET, SCTP_EVENTS, &so_linger, sizeof (so_linger)) < 0) { TTCN_warning("Setsockopt error!"); SCTPasp__Types::ASP__SCTP__RESULT asp_sctp_result; asp_sctp_result.client__id() = fd; asp_sctp_result.error__status() = TRUE; asp_sctp_result.error__message() = strerror(errno); incoming_message(asp_sctp_result); errno = 0; } else { SCTPasp__Types::ASP__SCTP__RESULT asp_sctp_result; asp_sctp_result.client__id() = fd; asp_sctp_result.error__status() = FALSE; asp_sctp_result.error__message() = OMIT_VALUE; incoming_message(asp_sctp_result); } break; } case SCTPasp__Types::ASP__SCTP__SetSocketOptions::ALT_Sctp__rtoinfo: { struct sctp_rtoinfo sctp_rtoinfo; (void) memset(&sctp_rtoinfo, 0, sizeof (sctp_rtoinfo)); const SCTPasp__Types::SCTP__RTOINFO& rto = send_par.Sctp__rtoinfo(); int local_fd = (int) rto.client__id(); sctp_rtoinfo.srto_initial = (int) rto.srto__initial(); sctp_rtoinfo.srto_max = (int) rto.srto__max(); sctp_rtoinfo.srto_min = (int) rto.srto__min(); // Setting a SCTP level socket option log("Setting SCTP socket options (sctp_rtoinfo)."); if (setsockopt(local_fd, IPPROTO_SCTP, SCTP_RTOINFO, &sctp_rtoinfo, sizeof (sctp_rtoinfo)) < 0) { TTCN_warning("Setsockopt error!"); SCTPasp__Types::ASP__SCTP__RESULT asp_sctp_result; asp_sctp_result.client__id() = local_fd; asp_sctp_result.error__status() = TRUE; asp_sctp_result.error__message() = strerror(errno); incoming_message(asp_sctp_result); errno = 0; } else { SCTPasp__Types::ASP__SCTP__RESULT asp_sctp_result; asp_sctp_result.client__id() = local_fd; asp_sctp_result.error__status() = FALSE; asp_sctp_result.error__message() = OMIT_VALUE; incoming_message(asp_sctp_result); } break; } default: error("Setsocketoptions error: UNBOUND value!"); break; } log("Leaving outgoing_send (ASP_SCTP_SETSOCKETOPTIONS)."); } void SCTPasp__PT_PROVIDER::outgoing_send(const SCTPasp__Types::ASP__SCTP__Close& send_par) { log("Calling outgoing_send (ASP_SCTP_CLOSE)."); if(!simple_mode) { if(send_par.client__id().ispresent()) { int local_fd = (int) (const INTEGER&) send_par.client__id(); log("NORMAL MODE: closing client/server socket (fd = %d).", local_fd); map_delete_item_fd(local_fd); map_delete_item_fd_server(local_fd); } else { // if OMIT is given then all sockets will be closed log("NORMAL MODE: closing all sockets."); for(int i=0;i<list_len;i++) map_delete_item(i); for(int i=0;i<list_len_server;i++) map_delete_item_server(i); } } else { if (server_mode) { // closing the given connection if(send_par.client__id().ispresent()) { int local_fd = (int) (const INTEGER&) send_par.client__id(); log("SERVER MODE: closing client socket (fd = %d).", local_fd); map_delete_item_fd(local_fd); } else { // if OMIT is given in server mode then all clients will be closed log("SERVER MODE: closing all client sockets."); for(int i=0;i<list_len;i++) map_delete_item(i); } } else { // closing the connection to the server if ( send_par.client__id().ispresent() ) error("In client mode the client_id field of ASP_SCTP_Close should be set to OMIT!"); log("CLIENT MODE: closing socket (fd = %d).", fd); map_delete_item_fd(fd); fd=-1; } } log("Leaving outgoing_send (ASP_SCTP_CLOSE)."); } void SCTPasp__PT_PROVIDER::outgoing_send(const SCTPasp__Types::ASP__SCTP& send_par) { log("Calling outgoing_send (ASP_SCTP)."); struct cmsghdr *cmsg; struct sctp_sndrcvinfo *sri; char cbuf[sizeof (*cmsg) + sizeof (*sri)]; struct msghdr msg; struct iovec iov; const unsigned char *buf; buf = (const unsigned char *)send_par.data(); iov.iov_len = send_par.data().lengthof(); memset(&msg, 0, sizeof (msg)); iov.iov_base = (char *)buf; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = cbuf; msg.msg_controllen = sizeof (*cmsg) + sizeof (*sri); memset(cbuf, 0, sizeof (*cmsg) + sizeof (*sri)); cmsg = (struct cmsghdr *)cbuf; sri = (struct sctp_sndrcvinfo *)(cmsg + 1); cmsg->cmsg_len = sizeof (*cmsg) + sizeof (*sri); cmsg->cmsg_level = IPPROTO_SCTP; cmsg->cmsg_type = SCTP_SNDRCV; sri->sinfo_stream = (int) send_par.sinfo__stream(); int target; if(!simple_mode) { if (!send_par.client__id().ispresent()) error("In NORMAL mode the client_id field of ASP_SCTP should be set to a valid value and not to omit!"); target = (int) (const INTEGER&) send_par.client__id(); if ( (map_get_item(target)==-1) && (map_get_item_server(target)==-1)) error("Bad client id! %d",target); } else { if (server_mode) { if (!send_par.client__id().ispresent()) error("In server mode the client_id field of ASP_SCTP should be set to a valid value and not to omit!"); } else // client mode { if (send_par.client__id().ispresent()) error("In client mode the client_id field of ASP_SCTP should be set to OMIT!"); } target = fd; if (server_mode) target = (int) (const INTEGER&) send_par.client__id(); if (map_get_item(target)==-1) error("Bad client id! %d",target); } uint32_t ui; if (send_par.sinfo__ppid().get_val().is_native() && send_par.sinfo__ppid() > 0) ui = (int)send_par.sinfo__ppid(); else { OCTETSTRING os = int2oct(send_par.sinfo__ppid(), 4); unsigned char* p = (unsigned char*)&ui; *(p++) = os[3].get_octet(); *(p++) = os[2].get_octet(); *(p++) = os[1].get_octet(); *(p++) = os[0].get_octet(); } sri->sinfo_ppid = htonl(ui); log("Sending SCTP message to file descriptor %d.", target); if (sendmsg(target, &msg, 0) < 0) { SCTPasp__Types::ASP__SCTP__SENDMSG__ERROR asp_sctp_sendmsg_error; if (server_mode) asp_sctp_sendmsg_error.client__id() = target; else asp_sctp_sendmsg_error.client__id() = OMIT_VALUE; asp_sctp_sendmsg_error.sinfo__stream() = send_par.sinfo__stream(); asp_sctp_sendmsg_error.sinfo__ppid() = send_par.sinfo__ppid(); asp_sctp_sendmsg_error.data() = send_par.data(); incoming_message(asp_sctp_sendmsg_error); TTCN_warning("Sendmsg error! Strerror=%s", strerror(errno)); errno = 0; } log("Leaving outgoing_send (ASP_SCTP)."); } SCTPasp__PT_PROVIDER::return_value_t SCTPasp__PT_PROVIDER::getmsg(int fd, struct msghdr *msg) { log("Calling getmsg()."); int index = map_get_item(fd); if ( !fd_map[index].processing_message ) fd_map[index].nr = 0; ssize_t value = recvmsg(fd, msg, 0); if (value <= 0) // EOF or error { log("Leaving getmsg(): EOF or error."); errno = 0; return EOF_OR_ERROR; } fd_map[index].nr += value; log("getmsg(): [%d] bytes received. Receiving buffer now has [%d] bytes.", value, fd_map[index].nr); // Whole message is received, return it. if (msg->msg_flags & MSG_EOR) { log("Leaving getmsg(): whole message is received."); return WHOLE_MESSAGE_RECEIVED; } // Maybe we need a bigger buffer, do realloc(). if (fd_map[index].buflen == fd_map[index].nr) { log("getmsg(): resizing receiving buffer: [%d] bytes -> [%d] bytes", fd_map[index].buflen, (fd_map[index].buflen * 2)); fd_map[index].buf = Realloc(fd_map[index].buf, fd_map[index].buflen * 2); fd_map[index].buflen *= 2; } log("Leaving getmsg(): part of the message is received."); return PARTIAL_RECEIVE; } void SCTPasp__PT_PROVIDER::handle_event(void *buf) { union sctp_notification *snp; snp = (sctp_notification *)buf; switch (snp->sn_header.sn_type) { case SCTP_ASSOC_CHANGE: { log("incoming SCTP_ASSOC_CHANGE event."); struct sctp_assoc_change *sac; sac = &snp->sn_assoc_change; // #ifdef LKSCTP_1_0_7 SCTPasp__Types::SAC__STATE sac_state_ttcn; switch(sac->sac_state) { case SCTP_COMM_UP: sac_state_ttcn = SCTPasp__Types::SAC__STATE::SCTP__COMM__UP; break; case SCTP_COMM_LOST: sac_state_ttcn = SCTPasp__Types::SAC__STATE::SCTP__COMM__LOST; break; case SCTP_RESTART: sac_state_ttcn = SCTPasp__Types::SAC__STATE::SCTP__RESTART; break; case SCTP_SHUTDOWN_COMP: sac_state_ttcn = SCTPasp__Types::SAC__STATE::SCTP__SHUTDOWN__COMP; break; case SCTP_CANT_STR_ASSOC: sac_state_ttcn = SCTPasp__Types::SAC__STATE::SCTP__CANT__STR__ASSOC; break; default: sac_state_ttcn = SCTPasp__Types::SAC__STATE::SCTP__UNKNOWN__SAC__STATE; TTCN_warning("Unexpected sac_state value received %d", sac->sac_state); break; } // #endif if(sac->sac_state == SCTP_COMM_LOST) { if(simple_mode) { if (!server_mode) fd = -1; // setting closed socket to -1 in client mode (and reconnect mode) map_delete_item_fd(receiving_fd); } else { map_delete_item_fd(receiving_fd); map_delete_item_fd_server(receiving_fd); } } if (events.sctp_association_event) incoming_message(SCTPasp__Types::ASP__SCTP__ASSOC__CHANGE( INTEGER(receiving_fd), sac_state_ttcn )); if(simple_mode) { if (reconnect && (sac->sac_state == SCTP_COMM_LOST) ) forced_reconnect(reconnect_max_attempts); } break; } case SCTP_PEER_ADDR_CHANGE:{ log("incoming SCTP_PEER_ADDR_CHANGE event."); struct sctp_paddr_change *spc; spc = &snp->sn_paddr_change; // #ifdef LKSCTP_1_0_7 SCTPasp__Types::SPC__STATE spc_state_ttcn; switch(spc->spc_state) { case SCTP_ADDR_AVAILABLE: spc_state_ttcn = SCTPasp__Types::SPC__STATE::SCTP__ADDR__AVAILABLE; break; case SCTP_ADDR_UNREACHABLE: spc_state_ttcn = SCTPasp__Types::SPC__STATE::SCTP__ADDR__UNREACHABLE; break; case SCTP_ADDR_REMOVED: spc_state_ttcn = SCTPasp__Types::SPC__STATE::SCTP__ADDR__REMOVED; break; case SCTP_ADDR_ADDED: spc_state_ttcn = SCTPasp__Types::SPC__STATE::SCTP__ADDR__ADDED; break; case SCTP_ADDR_MADE_PRIM: spc_state_ttcn = SCTPasp__Types::SPC__STATE::SCTP__ADDR__MADE__PRIM; break; #if defined(LKSCTP_1_0_7) || defined(LKSCTP_1_0_9) case SCTP_ADDR_CONFIRMED: spc_state_ttcn = SCTPasp__Types::SPC__STATE::SCTP__ADDR__CONFIRMED; break; #endif default: spc_state_ttcn = SCTPasp__Types::SPC__STATE::SCTP__UNKNOWN__SPC__STATE; TTCN_warning("Unexpected spc_state value received %d", spc->spc_state); break; } // #endif if (events.sctp_address_event) incoming_message(SCTPasp__Types::ASP__SCTP__PEER__ADDR__CHANGE( INTEGER(receiving_fd), spc_state_ttcn )); break; } case SCTP_REMOTE_ERROR: log("incoming SCTP_REMOTE_ERROR event."); //struct sctp_remote_error *sre; //sre = &snp->sn_remote_error; if (events.sctp_peer_error_event) incoming_message(SCTPasp__Types::ASP__SCTP__REMOTE__ERROR(INTEGER(receiving_fd))); break; case SCTP_SEND_FAILED: log("incoming SCTP_SEND_FAILED event."); //struct sctp_send_failed *ssf; //ssf = &snp->sn_send_failed; if (events.sctp_send_failure_event) incoming_message(SCTPasp__Types::ASP__SCTP__SEND__FAILED(INTEGER(receiving_fd))); break; case SCTP_SHUTDOWN_EVENT: log("incoming SCTP_SHUTDOWN_EVENT event."); //struct sctp_shutdown_event *sse; //sse = &snp->sn_shutdown_event; if (events.sctp_shutdown_event) incoming_message(SCTPasp__Types::ASP__SCTP__SHUTDOWN__EVENT(INTEGER(receiving_fd))); break; #if defined(LKSCTP_1_0_7) || defined(LKSCTP_1_0_9) case SCTP_ADAPTATION_INDICATION: log("incoming SCTP_ADAPTION_INDICATION event."); //struct sctp_adaptation_event *sai; //sai = &snp->sn_adaptation_event; if (events.sctp_adaptation_layer_event) incoming_message(SCTPasp__Types::ASP__SCTP__ADAPTION__INDICATION(INTEGER(receiving_fd))); break; #else case SCTP_ADAPTION_INDICATION: log("incoming SCTP_ADAPTION_INDICATION event."); //struct sctp_adaption_event *sai; //sai = &snp->sn_adaption_event; if (events.sctp_adaption_layer_event) incoming_message(SCTPasp__Types::ASP__SCTP__ADAPTION__INDICATION(INTEGER(receiving_fd))); break; #endif case SCTP_PARTIAL_DELIVERY_EVENT: log("incoming SCTP_PARTIAL_DELIVERY_EVENT event."); //struct sctp_pdapi_event *pdapi; //pdapi = &snp->sn_pdapi_event; if (events.sctp_partial_delivery_event) incoming_message(SCTPasp__Types::ASP__SCTP__PARTIAL__DELIVERY__EVENT(INTEGER(receiving_fd))); break; default: TTCN_warning("Unknown notification type!"); break; } } void SCTPasp__PT_PROVIDER::log(const char *fmt, ...) { if(debug) { va_list ap; va_start(ap, fmt); TTCN_Logger::begin_event(TTCN_DEBUG); TTCN_Logger::log_event("SCTPasp Test Port (%s): ", get_name()); TTCN_Logger::log_event_va_list(fmt, ap); TTCN_Logger::end_event(); va_end(ap); } } void SCTPasp__PT_PROVIDER::error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); TTCN_Logger::begin_event(TTCN_ERROR); TTCN_Logger::log_event("SCTPasp Test Port (%s): ", get_name()); TTCN_Logger::log_event_va_list(fmt, ap); TTCN_Logger::end_event(); va_end(ap); TTCN_error("Fatal error in SCTPasp Test Port %s (see above).", get_name()); } void SCTPasp__PT_PROVIDER::forced_reconnect(int attempts) { struct sockaddr_storage sa; socklen_t saLen; int sock_type=fill_addr_struct(peer_IP_address,peer_port,&sa,saLen); log("[reconnect] Connecting to (%s):(%d)", (const char*)peer_IP_address, peer_port); unsigned int sleep_interval = 1; int i; for(i = 0; i < attempts; i++) { fd=create_socket(sock_type); if (connect(fd, (struct sockaddr *)&sa, saLen) == -1) { close(fd); fd = -1; TTCN_warning("Connect error!"); errno = 0; if( ((i % 2 ) == 0) && (i != 0)) sleep_interval *= 2; sleep(sleep_interval); } else { map_put_item(fd); setNonBlocking(fd); Handler_Add_Fd_Read(fd); log("[reconnect] Connection successfully established to (%s):(%d)", (const char *)peer_IP_address, peer_port); break; } } if (i == attempts) error("Forced reconnect failed! Remote end is unreachable!"); } void SCTPasp__PT_PROVIDER::map_put_item(int fd) { int i=0; while((i<list_len) && !fd_map[i].erased) i++; // searching for the free item if(i==list_len) { // list is full add new elemnts list_len+=MAP_LENGTH; fd_map=(fd_map_item *)Realloc(fd_map,(list_len)*sizeof(fd_map_item)); for(int k=i;k<list_len;k++) { // init new elements fd_map[k].fd=-1; fd_map[k].erased=TRUE; fd_map[k].einprogress=FALSE; fd_map[k].buf=NULL; fd_map[k].buflen=0; fd_map[k].processing_message=FALSE; fd_map[k].nr=0; fd_map[k].saLen=0; memset(&fd_map[k].sin,0,sizeof(struct sockaddr_storage)); } } fd_map[i].fd=fd; // adding new connection fd_map[i].erased=FALSE; } int SCTPasp__PT_PROVIDER::map_get_item(int fd) { for(int i = 0; i < list_len; i++) if( !(fd_map[i].erased) && (fd_map[i].fd == fd) ) return i; return(-1); } void SCTPasp__PT_PROVIDER::map_delete_item_fd(int fd) { if(fd!=-1) for(int i = 0; i < list_len; i++) if( fd_map[i].fd == fd ) { map_delete_item(i); break; } } void SCTPasp__PT_PROVIDER::map_delete_item(int index) { if((index>=list_len) || (index<0)) error("map_delete_item: index out of range (0-%d): %d",list_len-1,index); if(fd_map[index].fd!=-1) {close(fd_map[index].fd);Handler_Remove_Fd(fd_map[index].fd, EVENT_ALL);} fd_map[index].fd=-1; fd_map[index].erased=TRUE; fd_map[index].einprogress=FALSE; if(fd_map[index].buf) Free(fd_map[index].buf); fd_map[index].buf=NULL; fd_map[index].buflen=0; fd_map[index].processing_message=FALSE; fd_map[index].nr=0; fd_map[index].saLen=0; memset(&fd_map[index].sin,0,sizeof(struct sockaddr_storage)); } void SCTPasp__PT_PROVIDER::map_put_item_server(int fd, const CHARSTRING& local_IP_address, unsigned short local_port) { int i=0; while((i<list_len_server) && !fd_map_server[i].erased) i++; // searching for the free item if(i==list_len_server) { // list is full add new elemnts list_len_server+=MAP_LENGTH; fd_map_server=(fd_map_server_item *)Realloc(fd_map_server,(list_len_server)*sizeof(fd_map_server_item)); for(int k=i;k<list_len_server;k++) { // init new elements fd_map_server[k].fd=-1; fd_map_server[k].erased=TRUE; fd_map_server[k].local_IP_address = NULL; fd_map_server[k].local_port = 0; } } fd_map_server[i].fd=fd; // adding new connection fd_map_server[i].erased=FALSE; if(fd_map_server[i].local_IP_address == NULL){ fd_map_server[i].local_IP_address = new CHARSTRING(); } *fd_map_server[i].local_IP_address = local_IP_address; fd_map_server[i].local_port = local_port; } int SCTPasp__PT_PROVIDER::map_get_item_server(int fd) { for(int i = 0; i < list_len_server; i++) if( !(fd_map_server[i].erased) && (fd_map_server[i].fd == fd) ) return i; return(-1); } void SCTPasp__PT_PROVIDER::map_delete_item_fd_server(int fd) { if(fd!=-1) for(int i = 0; i < list_len_server; i++) if( fd_map_server[i].fd == fd ) { map_delete_item_server(i); break; } } void SCTPasp__PT_PROVIDER::map_delete_item_server(int index) { if((index>=list_len_server) || (index<0)) error("map_delete_item: index out of range (0-%d): %d",list_len_server-1,index); if(fd_map_server[index].fd!=-1) {close(fd_map_server[index].fd);Handler_Remove_Fd(fd_map_server[index].fd, EVENT_ALL);} fd_map_server[index].fd=-1; fd_map_server[index].erased=TRUE; if(fd_map_server[index].local_IP_address != NULL){ delete fd_map_server[index].local_IP_address;} fd_map_server[index].local_IP_address = NULL; fd_map_server[index].local_port = 0; } int SCTPasp__PT_PROVIDER::fill_addr_struct(const char* name, int port, struct sockaddr_storage* sa, socklen_t& saLen){ //int err = 0; int addrtype = -1; struct sockaddr_in saddr; struct sockaddr_in6 saddr6; memset(sa,0,sizeof(struct sockaddr_storage)); struct sockaddr_in* v4=(sockaddr_in*)sa; struct sockaddr_in6* v6=(sockaddr_in6*)sa; if(inet_pton(AF_INET, name, &(saddr.sin_addr))) { saLen = sizeof(*v4); v4->sin_family = AF_INET; v4->sin_port = htons(port); memcpy(&(v4->sin_addr), &(saddr.sin_addr), sizeof(saddr.sin_addr)); addrtype = AF_INET; } else if(inet_pton(AF_INET6, name, &(saddr6.sin6_addr))) { saLen = sizeof(*v6); v6->sin6_family = AF_INET6; v6->sin6_port = htons(port); memcpy(&(v6->sin6_addr), &(saddr6.sin6_addr), sizeof(saddr6.sin6_addr)); addrtype = AF_INET6; } else { struct addrinfo myaddr, *res; memset(&myaddr,0,sizeof(myaddr)); myaddr.ai_flags = AI_ADDRCONFIG|AI_PASSIVE; myaddr.ai_socktype = SOCK_STREAM; myaddr.ai_protocol = 0; // if ((err = getaddrinfo(name, NULL, &myaddr, &res)) != 0) { if (getaddrinfo(name, NULL, &myaddr, &res) != 0) { //printf("SetSockAddr: getaddrinfo error: %i, %s", err, gai_strerror(err)); error("Can't resolve host name: %s",name); } if (res->ai_addr->sa_family == AF_INET) { // IPv4 struct sockaddr_in *saddr = (struct sockaddr_in *) res->ai_addr; saLen = sizeof(*v4); v4->sin_family = AF_INET; v4->sin_port = htons(port); memcpy(&(v4->sin_addr), &(saddr->sin_addr), sizeof(saddr->sin_addr)); addrtype = AF_INET; } else if (res->ai_addr->sa_family == AF_INET6){ // IPv6 struct sockaddr_in6 *saddr = (struct sockaddr_in6 *) res->ai_addr; saLen = sizeof(*v6); memcpy(v6,saddr,saLen); v6->sin6_port = htons(port); addrtype = AF_INET6; } freeaddrinfo(res); } return addrtype; } int SCTPasp__PT_PROVIDER::create_socket(int addr_family) { int local_fd; log("Creating SCTP socket."); usleep(200000); if ((local_fd = socket(addr_family, SOCK_STREAM, IPPROTO_SCTP)) == -1) error("Socket error: cannot create socket! %d %s %d %d",errno, strerror(errno),addr_family,AF_INET); log("Setting SCTP socket options (initmsg)."); if (setsockopt(local_fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(struct sctp_initmsg)) < 0) { TTCN_warning("Setsockopt error!"); errno = 0; } log("Setting SCTP socket options (events)."); if (sctp_setsockopt_events_linux_workaround(local_fd, &events) < 0) { TTCN_warning("Setsockopt error!"); errno = 0; } return local_fd; } void SCTPasp__PT_PROVIDER::setNonBlocking(int fd) { int flags = fcntl(fd, F_GETFL); flags |= O_NONBLOCK; int result = fcntl(fd, F_SETFL, flags); if (result==-1) error("SCTPasp__PT::setNonBlocking(): Fcntl() error!"); } }