/* ****************************************************************************** * SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipike_channel.c,v $ $Revision: 1.6.2.6 $ * $Source: /home/interpeak/CVSRoot/ipike/src/ipike_channel.c,v $ * $Author: zan $ $Date: 2013-08-02 07:52:29 $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Erik Dahlberg ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* A message channel framework */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include "ipike_config.h" #ifdef IPIKE_USE_EVENT_LOG /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #include "ipike_h.h" #include "ipike_channel.h" /* **************************************************************************** * 4 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 5 DEFINES **************************************************************************** */ #define IPIKE_CHANNEL_ADDRESS_MAX_LEN 32 #define IPIKE_CHANNEL_USE_TCP #define for_each_channel_in_server(_chan, _server) \ for (_chan = (Ipike_channel *) ipcom_list_first(&_server->channel_list);\ _chan != IP_NULL; \ _chan = (Ipike_channel *) ipcom_list_next(&_chan->list) ) /* **************************************************************************** * 6 TYPES **************************************************************************** */ typedef enum Ipike_channel_state_enum { IPIKE_CHANNEL_STATE_CLOSED = 0, IPIKE_CHANNEL_STATE_LISTEN = 1, IPIKE_CHANNEL_STATE_OPEN = 2, } Ipike_channel_state; struct Ipike_channel_struct { Ipcom_list list; Ip_fd fd; Ipike_channel_state state; Ipcom_mutex mutex; }; struct Ipike_channel_server_struct { Ipcom_list channel_list; Ipike_channel_server_cb on_open_cb; Ipike_channel_server_cb on_close_cb; Ipike_channel_server_receive_cb on_receive_cb; }; enum Ipike_channel_msg_type_enum { IPIKE_CHANNEL_REGISTER = 0, IPIKE_CHANNEL_UNREGISTER = 1, IPIKE_CHANNEL_MESSAGE = 2, }; #define MSG_HDR(_msg) ((Ipike_channel_msg_header *)_msg - 1); struct Ipike_channel_addr_struct { union Ip_sockaddr_union addr; }; /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 LOCAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipike_channel_set_state *=========================================================================== */ IP_INLINE void ipike_channel_set_state( Ipike_channel * channel, Ipike_channel_state state) { channel->state = state; } /* *=========================================================================== * ipike_channel_is_open *=========================================================================== */ IP_INLINE Ip_bool ipike_channel_is_open( const Ipike_channel * channel) { return (channel->state == IPIKE_CHANNEL_STATE_OPEN); } /* *=========================================================================== * ipike_channel_lock *=========================================================================== */ IP_INLINE void ipike_channel_lock( const Ipike_channel * channel) { IPCOM_LOG1(DEBUG2, "locking channel %p", channel); ipcom_mutex_lock(channel->mutex); } /* *=========================================================================== * ipike_channel_unlock *=========================================================================== */ IP_INLINE void ipike_channel_unlock( const Ipike_channel * channel) { IPCOM_LOG1(DEBUG2, "Unlocking channel %p", channel); ipcom_mutex_unlock(channel->mutex); } /* *=========================================================================== * ipike_channel_destroy *=========================================================================== */ IP_PUBLIC void ipike_channel_destroy( Ipike_channel * channel) { ip_assert(channel != IP_NULL); IPCOM_LOG1(DEBUG2, "Destroying channel %p", channel); ipike_channel_close(channel); (void)ipcom_mutex_delete(&channel->mutex); ipcom_free(channel); } /* *=========================================================================== * ipike_channel_server_destroy *=========================================================================== */ IP_GLOBAL void ipike_channel_server_destroy( Ipike_channel_server * cserver) { Ipike_channel * channel; ip_assert(cserver != IP_NULL); IPCOM_LOG1(DEBUG2, "Destroying channel server %p", cserver); for ( channel = (Ipike_channel *) ipcom_list_first(&cserver->channel_list); channel != IP_NULL; channel = (Ipike_channel *) ipcom_list_first(&cserver->channel_list)) { ipcom_list_remove(&channel->list); ipike_channel_destroy(channel); } ipcom_free(cserver); } /* *=========================================================================== * ipike_channel_create *=========================================================================== */ IP_STATIC Ipike_channel * ipike_channel_create() { Ipike_channel *channel; channel = ipcom_malloc(sizeof(Ipike_channel)); if ( IP_NULL == channel ) return IP_NULL; ipcom_memset(channel, 0, sizeof(Ipike_channel)); channel->fd = IP_INVALID_SOCKET; ipike_channel_set_state(channel, IPIKE_CHANNEL_STATE_CLOSED); /* Create channel mutex */ if ( ipcom_mutex_create(&channel->mutex) != IPCOM_SUCCESS ) { ipcom_free(channel); return IP_NULL; } return channel; } /* *=========================================================================== * ipike_channel_server_accept_new_channel *=========================================================================== */ IP_STATIC Ipike_channel_status ipike_channel_server_accept_new_channel( Ipike_channel_server * cserver, Ipike_channel * channel) { Ipike_channel * new_channel; Ip_fd fd; ip_assert(channel->state == IPIKE_CHANNEL_STATE_LISTEN); IPCOM_LOG0(DEBUG2, "ipike_channel_server_accept_new_channel() :: "\ "received new connection attempt"); fd = ipcom_accept(channel->fd, IP_NULL, 0 ); if ( fd < 0 ) { IPCOM_LOG1(ERR, "ipike_channel_server_accept_new_channel() :: "\ "failed to accept new connection (%s)", ipcom_strerror(ipcom_errno)); return IPIKE_CHANNEL_ERROR; } /* Create a new channel */ new_channel = ipike_channel_create(); if ( new_channel == NULL ) { IP_PANIC(); return IPIKE_CHANNEL_ERROR; } /* Set channel state and add it to the channel list */ new_channel->fd = fd; ipike_channel_set_state(new_channel, IPIKE_CHANNEL_STATE_OPEN); ipcom_list_insert_last(&cserver->channel_list, &new_channel->list); /* Notify user with the new channel */ if ( cserver->on_open_cb ) cserver->on_open_cb(new_channel); IPCOM_LOG0(DEBUG, "ipike_channel_server_accept_new_channel() :: "\ "new channel established"); return IPIKE_CHANNEL_OK; } /* **************************************************************************** * 10 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipike_channel_addr_create *=========================================================================== */ IP_GLOBAL Ipike_channel_addr * ipike_channel_addr_create( const char * caddr_str) { Ipike_channel_addr * caddr; char tmp[IPIKE_CHANNEL_ADDRESS_MAX_LEN]; char * ip_str; char * port_str; Ip_u16 port; caddr = ipcom_malloc(sizeof(Ipike_channel_addr)); if ( caddr == IP_NULL ) return IP_NULL; ipcom_memset(caddr, 0, sizeof(Ipike_channel_addr)); ipcom_memset(tmp, 0, sizeof(tmp)); ipcom_strncpy(tmp, caddr_str, sizeof(tmp) - 1); port_str = ipcom_strchr(tmp, '@'); if ( port_str == IP_NULL ) { ipcom_free(caddr); return IP_NULL; } ip_str = tmp; *port_str = '\0'; port_str++; port = (Ip_u16)ipcom_atoi(port_str); #ifdef IPCOM_USE_INET if ( 1 == ipcom_inet_pton(IP_AF_INET, ip_str, &caddr->addr.sin.sin_addr)) { caddr->addr.sa.sa_family = IP_AF_INET; caddr->addr.sin.sin_port = ip_htons(port); IPCOM_SA_LEN_SET(&caddr->addr.sa, sizeof(struct Ip_sockaddr_in)); return caddr; } #endif #ifdef IPCOM_USE_INET6 if ( 1 == ipcom_inet_pton(IP_AF_INET6, ip_str, &caddr->addr.sin6.sin6_addr) ) { caddr->addr.sa.sa_family = IP_AF_INET6; caddr->addr.sin6.sin6_port = ip_htons(port); IPCOM_SA_LEN_SET(&caddr->addr.sa, sizeof(struct Ip_sockaddr_in6)); return caddr; } #endif return caddr; } /* *=========================================================================== * ipike_channel_addr_destroy *=========================================================================== */ IP_GLOBAL void ipike_channel_addr_destroy( Ipike_channel_addr * caddr) { ipcom_free(caddr); } /* *=========================================================================== * ipike_channel_msg_create *=========================================================================== */ IP_GLOBAL Ipike_channel_msg * ipike_channel_msg_create(Ip_u16 len) { Ipike_channel_msg * msg; Ip_size_t msg_len; msg_len = sizeof(Ipike_channel_msg) + len; msg = ipcom_malloc(msg_len); if ( msg == IP_NULL ) return IP_NULL; ipcom_memset(msg, 0, msg_len); msg->type = IPIKE_CHANNEL_MESSAGE; msg->len = (Ip_u16)msg_len; return msg; } /* *=========================================================================== * ike_channel_msg_destroy( *=========================================================================== */ IP_GLOBAL void ipike_channel_msg_destroy( Ipike_channel_msg *msg) { ip_assert(msg); ipcom_free(msg); } /* *=========================================================================== * ipike_channel_server_add_fds *=========================================================================== */ IP_GLOBAL int ipike_channel_server_add_fds( Ipike_channel_server * cserver, Ip_fd_set * read_fds, Ip_fd_set * write_fds) { Ipike_channel * channel; int max = -1; IPCOM_UNUSED_ARG(write_fds); for_each_channel_in_server(channel, cserver) { switch (channel->state) { case IPIKE_CHANNEL_STATE_LISTEN: case IPIKE_CHANNEL_STATE_OPEN: IP_FD_SET(channel->fd, read_fds); break; default: IPCOM_LOG1(ERR, "ipike_channel_server_add_fds() :: "\ "invalid fd state (%u)", channel->state); break; } max = IP_MAX(channel->fd, max); } return max; } /* *=========================================================================== * ipike_channel_server_broadcast_msg *=========================================================================== */ IP_GLOBAL void ipike_channel_server_broadcast_msg( Ipike_channel_server * channel_server, Ipike_channel_msg * msg) { Ipike_channel *channel; /* Broadcast message on all open channels */ for_each_channel_in_server(channel, channel_server) { if ( ipike_channel_is_open(channel) ) { ipike_channel_send_msg(channel, msg); } } } /* *=========================================================================== * ipike_channel_server_create *=========================================================================== */ IP_GLOBAL Ipike_channel_server * ipike_channel_server_create( Ipike_channel_addr * caddr, Ipike_channel_server_cb on_open_cb, Ipike_channel_server_cb on_close_cb, Ipike_channel_server_receive_cb on_receive_cb) { Ipike_channel_server * cserver; Ipike_channel * channel; cserver = ipcom_malloc(sizeof(Ipike_channel_server)); if ( IP_NULL == cserver ) return IP_NULL; ipcom_memset(cserver, 0, sizeof(Ipike_channel_server)); ipcom_list_init(&cserver->channel_list); /* Set user callbacks */ cserver->on_open_cb = on_open_cb; cserver->on_close_cb = on_close_cb; cserver->on_receive_cb = on_receive_cb; channel = ipike_channel_create(); if ( channel == IP_NULL ) goto errout; ipcom_list_insert_last(&cserver->channel_list, &channel->list); /* Open socket */ #if defined(IPCOM_USE_INET) channel->fd = ipcom_socket(IP_AF_INET, IP_SOCK_STREAM, IP_IPPROTO_TCP); #elif defined(IPCOM_USE_INET6) channel->fd = ipcom_socket(IP_AF_INET6, IP_SOCK_STREAM, IP_IPPROTO_TCP); #endif if ( channel->fd < 0 ) goto errout; /* Bind channel server */ if ( ipcom_bind(channel->fd, (struct Ip_sockaddr *) &caddr->addr, IPCOM_SA_LEN_GET(&caddr->addr)) < 0 ) goto errout; /* Start listen for new connections */ if (ipcom_listen(channel->fd, 1) != 0) { IPCOM_LOG1(WARNING, "ipike_event_channel_bind() :: failed to set socket to"\ " listen state (%s)", ipcom_strerror(ipcom_errno)); goto errout; } /* The channel is listen state */ ipike_channel_set_state(channel, IPIKE_CHANNEL_STATE_LISTEN); return cserver; errout: if ( cserver ) ipike_channel_server_destroy(cserver); return IP_NULL; } /* *=========================================================================== * ipike_channel_open *=========================================================================== */ IP_GLOBAL Ipike_channel * ipike_channel_open( Ipike_channel_addr * caddr) { Ipike_channel * channel; /* Create channel */ channel = ipike_channel_create(); if ( channel == IP_NULL ) goto errout; /* Open socket */ #if defined(IPCOM_USE_INET) channel->fd = ipcom_socket(IP_AF_INET, IP_SOCK_STREAM, IP_IPPROTO_TCP); #elif defined(IPCOM_USE_INET6) channel->fd = ipcom_socket(IP_AF_INET6, IP_SOCK_STREAM, IP_IPPROTO_TCP); #endif if ( channel->fd < 0 ) goto errout; /* Connect to IKE Daemon */ if ( ipcom_connect(channel->fd, (struct Ip_sockaddr *) &caddr->addr, IPCOM_SA_LEN_GET(&caddr->addr)) < 0 ) goto errout; /* The channel is established */ ipike_channel_set_state(channel, IPIKE_CHANNEL_STATE_OPEN); return channel; errout: if (channel) ipike_channel_destroy(channel); return IP_NULL; } /* *=========================================================================== * ipike_channel_close *=========================================================================== */ IP_GLOBAL void ipike_channel_close( Ipike_channel * channel) { ipike_channel_lock(channel); /* Close channel socket */ if ( channel->fd != IP_INVALID_SOCKET ) { (void)ipcom_socketclose(channel->fd); channel->fd = IP_INVALID_SOCKET; } ipike_channel_set_state(channel, IPIKE_CHANNEL_STATE_CLOSED); ipike_channel_unlock(channel); } /* *=========================================================================== * ipike_channel_receive_msg *=========================================================================== */ IP_GLOBAL Ipike_channel_status ipike_channel_receive_msg( Ipike_channel * channel, Ipike_channel_msg ** msg) { Ipike_channel_msg hdr; Ipike_channel_msg * msg_out = IP_NULL; Ipike_channel_status err = IPIKE_CHANNEL_ERROR; Ip_ssize_t ret = 0; Ip_size_t len; Ip_fd fd; ipike_channel_lock(channel); fd = channel->fd; ipike_channel_unlock(channel); /* Read header information */ ret = ipcom_recv(fd, &hdr, sizeof(hdr), 0); /* Receive error */ if ( ret < 0 ) { IPCOM_LOG2(DEBUG, "ipike_channel_receive_msg() :: "\ "failed to receive (%ld) (%s)", ret, ipcom_strerror(ipcom_errno)); goto errout; } /* Socket closed */ if ( ret == 0 ) { IPCOM_LOG0(DEBUG, "ipike_channel_receive_msg() :: channel closed"); err = IPIKE_CHANNEL_CLOSED; goto errout; } /* Expect the same size as specified in header */ if ( (Ip_ssize_t) ret != sizeof(hdr) ) { IPCOM_LOG2(DEBUG, "ipike_channel_receive_msg() :: "\ "bad message, got %ld , expected %ld", hdr.len, sizeof(hdr)); goto errout; } IPCOM_LOG1(DEBUG2, "ipike_channel_receive_msg() :: "\ "received message header of length %d\n", hdr.len); /* If no message buffer was provided, create one dynamically */ if ( *msg == IP_NULL ) { /* Allocate message space */ /* packet is sent by target itself, it is not vulnerable to attack */ /* coverity[tainted_data] : INTENTIONAL */ msg_out = ipike_channel_msg_create(hdr.len); if ( msg_out == IP_NULL ) { IP_PANIC(); goto errout; } *msg = msg_out; } else { /* Make sure the provided buffer has enough space for this message */ if ( (*msg)->len < hdr.len ) { IPCOM_LOG0(DEBUG, "ipike_channel_receive_msg() :: " "not enough memory in provided message"); goto errout; } } /* Set message length to the size of the received message */ (*msg)->len = hdr.len; /* Read the rest of the message */ len = (*msg)->len - sizeof(Ipike_channel_msg); ret = ipcom_recv(fd, (*msg) + 1, len , 0); if ( ret == 0 ) { IPCOM_LOG0(DEBUG, "ipike_channel_receive_msg() :: channel closed"); err = IPIKE_CHANNEL_CLOSED; goto errout; } if ( ret != (Ip_ssize_t) len ) { IPCOM_LOG2(DEBUG, "ipike_channel_receive_msg() :: "\ "failed to receive message (%ld) (%s)", ret, ipcom_strerror(ipcom_errno)); goto errout; } IPCOM_LOG1(DEBUG, "ipike_channel_receive_msg() :: "\ "Successfully received message (%d)", hdr.len); return IPIKE_CHANNEL_OK; errout: if ( msg_out ) ipike_channel_msg_destroy(msg_out); return err; } /* *=========================================================================== * ipike_channel_send_msg *=========================================================================== */ IP_GLOBAL Ipike_channel_status ipike_channel_send_msg( Ipike_channel * channel, Ipike_channel_msg * msg) { ip_assert(channel); ip_assert(msg); /* We need an open channel */ if ( !ipike_channel_is_open(channel) ) { IPCOM_LOG0(DEBUG, "ipike_channel_send_msg() :: "\ "channel is not open"); return IPIKE_CHANNEL_CLOSED; } /* Send message */ if ( ipcom_send(channel->fd, (void *) msg, msg->len, 0) != msg->len ) { IPCOM_LOG1(ERR, "ipike_channel_send_msg() :: "\ "failed to send message (%s)", ipcom_strerror(ipcom_errno)); return IPIKE_CHANNEL_ERROR; } /* Message has been sent */ return IPIKE_CHANNEL_OK; } /* *=========================================================================== * ipike_channel_server_process *=========================================================================== */ IP_GLOBAL int ipike_channel_server_process( Ipike_channel_server * cserver, Ip_fd_set * read_fds, Ip_fd_set * write_fds) { Ipike_channel * channel; Ipike_channel * next; int num_processed = 0; IPCOM_UNUSED_ARG(write_fds); /* Get first channel */ channel = (Ipike_channel * )ipcom_list_first(&cserver->channel_list); if (channel==IP_NULL) { return 0; } /* This is the master channel where new connection can arrive on */ if ( IP_FD_ISSET(channel->fd, read_fds) ) { /* Accept new channel connection */ (void)ipike_channel_server_accept_new_channel(cserver, channel); num_processed++; } /* Continue to check if there are messages on other channels */ channel = (Ipike_channel *) ipcom_list_next(&channel->list); while ( channel ) { /* Receive data */ Ipike_channel_msg * msg = IP_NULL; /* Store next pointer so we can remote the channel object if is needed */ next = (Ipike_channel *) ipcom_list_next(&channel->list); if ( IP_FD_ISSET(channel->fd, read_fds) ) { /* Receive message */ if ( ipike_channel_receive_msg(channel, &msg) != IPIKE_CHANNEL_OK ) { IPCOM_LOG0(DEBUG, "ipike_channel_server_process():: "\ "closing channel"); ipcom_list_remove(&channel->list); /* Notify user that channel is closed */ if ( cserver->on_close_cb ) cserver->on_close_cb(channel); /* Destroy the channel on errors */ ipike_channel_destroy(channel); } else { /* Send message to user receive callback */ if (cserver->on_receive_cb) cserver->on_receive_cb(channel, msg); /* Destroy message */ ipike_channel_msg_destroy(msg); } num_processed++; } channel = next; } return num_processed; } #else int ipike_channel_empty_file = 0; #endif /* IPIKE_USE_EVENT_LOG */ /* **************************************************************************** * END OF FILE **************************************************************************** */