/*- * Copyright (C) 2012-2013 Michael Tuexen * Copyright (C) 2012-2013 Irene Ruengeler * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: rtcweb.c,v 1.26 2012-07-17 13:50:02 tuexen Exp $ */ /* * gcc -Wall -std=c99 -pedantic -o rtcweb rtcweb.c -lusrsctp */ #ifdef _WIN32 #define _CRT_SECURE_NO_WARNINGS #endif #include #include #include #include #include #include #ifndef _WIN32 #include #include #include #include #include #include #include #else #include #include #include #endif #include #include "programs_helper.h" #define LINE_LENGTH (1024) #define BUFFER_SIZE (1<<16) #define NUMBER_OF_CHANNELS (100) #define NUMBER_OF_STREAMS (100) #define DATA_CHANNEL_PPID_CONTROL 50 #define DATA_CHANNEL_PPID_DOMSTRING 51 #define DATA_CHANNEL_PPID_BINARY 52 #define DATA_CHANNEL_CLOSED 0 #define DATA_CHANNEL_CONNECTING 1 #define DATA_CHANNEL_OPEN 2 #define DATA_CHANNEL_CLOSING 3 #define DATA_CHANNEL_FLAGS_SEND_REQ 0x00000001 #define DATA_CHANNEL_FLAGS_SEND_RSP 0x00000002 #define DATA_CHANNEL_FLAGS_SEND_ACK 0x00000004 struct channel { uint32_t id; uint32_t pr_value; uint16_t pr_policy; uint16_t i_stream; uint16_t o_stream; uint8_t unordered; uint8_t state; uint32_t flags; }; struct peer_connection { struct channel channels[NUMBER_OF_CHANNELS]; struct channel *i_stream_channel[NUMBER_OF_STREAMS]; struct channel *o_stream_channel[NUMBER_OF_STREAMS]; uint16_t o_stream_buffer[NUMBER_OF_STREAMS]; uint32_t o_stream_buffer_counter; #ifdef _WIN32 CRITICAL_SECTION mutex; #else pthread_mutex_t mutex; #endif struct socket *sock; } peer_connection; #define DATA_CHANNEL_OPEN_REQUEST 0 #define DATA_CHANNEL_OPEN_RESPONSE 1 #define DATA_CHANNEL_ACK 2 #define DATA_CHANNEL_RELIABLE 0 #define DATA_CHANNEL_RELIABLE_STREAM 1 #define DATA_CHANNEL_UNRELIABLE 2 #define DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT 3 #define DATA_CHANNEL_PARTIAL_RELIABLE_TIMED 4 #define DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED 0x0001 #ifndef _WIN32 #define SCTP_PACKED __attribute__((packed)) #else #pragma pack (push, 1) #define SCTP_PACKED #endif #if defined(_WIN32) && !defined(__MINGW32__) #pragma warning( push ) #pragma warning( disable : 4200 ) #endif /* defined(_WIN32) && !defined(__MINGW32__) */ struct rtcweb_datachannel_open_request { uint8_t msg_type; /* DATA_CHANNEL_OPEN_REQUEST */ uint8_t channel_type; uint16_t flags; uint16_t reliability_params; int16_t priority; char label[]; } SCTP_PACKED; #if defined(_WIN32) && !defined(__MINGW32__) #pragma warning( pop ) #endif /* defined(_WIN32) && !defined(__MINGW32__) */ struct rtcweb_datachannel_open_response { uint8_t msg_type; /* DATA_CHANNEL_OPEN_RESPONSE */ uint8_t error; uint16_t flags; uint16_t reverse_stream; } SCTP_PACKED; struct rtcweb_datachannel_ack { uint8_t msg_type; /* DATA_CHANNEL_ACK */ } SCTP_PACKED; #ifdef _WIN32 #pragma pack(pop) #endif #undef SCTP_PACKED static void lock_peer_connection(struct peer_connection *); static void unlock_peer_connection(struct peer_connection *); static void init_peer_connection(struct peer_connection *pc) { uint32_t i; struct channel *channel; #ifdef _WIN32 InitializeCriticalSection(&(pc->mutex)); #else pthread_mutex_init(&pc->mutex, NULL); #endif lock_peer_connection(pc); for (i = 0; i < NUMBER_OF_CHANNELS; i++) { channel = &(pc->channels[i]); channel->id = i; channel->state = DATA_CHANNEL_CLOSED; channel->pr_policy = SCTP_PR_SCTP_NONE; channel->pr_value = 0; channel->i_stream = 0; channel->o_stream = 0; channel->unordered = 0; channel->flags = 0; } for (i = 0; i < NUMBER_OF_STREAMS; i++) { pc->i_stream_channel[i] = NULL; pc->o_stream_channel[i] = NULL; pc->o_stream_buffer[i] = 0; } pc->o_stream_buffer_counter = 0; pc->sock = NULL; unlock_peer_connection(pc); } static void lock_peer_connection(struct peer_connection *pc) { #ifdef _WIN32 EnterCriticalSection(&(pc->mutex)); #else pthread_mutex_lock(&pc->mutex); #endif } static void unlock_peer_connection(struct peer_connection *pc) { #ifdef _WIN32 LeaveCriticalSection(&(pc->mutex)); #else pthread_mutex_unlock(&pc->mutex); #endif } static struct channel * find_channel_by_i_stream(struct peer_connection *pc, uint16_t i_stream) { if (i_stream < NUMBER_OF_STREAMS) { return (pc->i_stream_channel[i_stream]); } else { return (NULL); } } static struct channel * find_channel_by_o_stream(struct peer_connection *pc, uint16_t o_stream) { if (o_stream < NUMBER_OF_STREAMS) { return (pc->o_stream_channel[o_stream]); } else { return (NULL); } } static struct channel * find_free_channel(struct peer_connection *pc) { uint32_t i; for (i = 0; i < NUMBER_OF_CHANNELS; i++) { if (pc->channels[i].state == DATA_CHANNEL_CLOSED) { break; } } if (i == NUMBER_OF_CHANNELS) { return (NULL); } else { return (&(pc->channels[i])); } } static uint16_t find_free_o_stream(struct peer_connection *pc) { struct sctp_status status; uint32_t i, limit; socklen_t len; len = (socklen_t)sizeof(struct sctp_status); if (usrsctp_getsockopt(pc->sock, IPPROTO_SCTP, SCTP_STATUS, &status, &len) < 0) { perror("getsockopt"); return (0); } if (status.sstat_outstrms < NUMBER_OF_STREAMS) { limit = status.sstat_outstrms; } else { limit = NUMBER_OF_STREAMS; } /* stream id 0 is reserved */ for (i = 1; i < limit; i++) { if (pc->o_stream_channel[i] == NULL) { break; } } if (i == limit) { return (0); } else { return ((uint16_t)i); } } static void request_more_o_streams(struct peer_connection *pc) { struct sctp_status status; struct sctp_add_streams sas; uint32_t i, o_streams_needed; socklen_t len; o_streams_needed = 0; for (i = 0; i < NUMBER_OF_CHANNELS; i++) { if ((pc->channels[i].state == DATA_CHANNEL_CONNECTING) && (pc->channels[i].o_stream == 0)) { o_streams_needed++; } } len = (socklen_t)sizeof(struct sctp_status); if (usrsctp_getsockopt(pc->sock, IPPROTO_SCTP, SCTP_STATUS, &status, &len) < 0) { perror("getsockopt"); return; } if (status.sstat_outstrms + o_streams_needed > NUMBER_OF_STREAMS) { o_streams_needed = NUMBER_OF_STREAMS - status.sstat_outstrms; } if (o_streams_needed == 0) { return; } memset(&sas, 0, sizeof(struct sctp_add_streams)); sas.sas_instrms = 0; sas.sas_outstrms = (uint16_t)o_streams_needed; /* XXX error handling */ if (usrsctp_setsockopt(pc->sock, IPPROTO_SCTP, SCTP_ADD_STREAMS, &sas, (socklen_t)sizeof(struct sctp_add_streams)) < 0) { perror("setsockopt"); } return; } static int send_open_request_message(struct socket *sock, uint16_t o_stream, uint8_t unordered, uint16_t pr_policy, uint32_t pr_value) { /* XXX: This should be encoded in a better way */ struct rtcweb_datachannel_open_request req; struct sctp_sndinfo sndinfo; memset(&req, 0, sizeof(struct rtcweb_datachannel_open_request)); req.msg_type = DATA_CHANNEL_OPEN_REQUEST; switch (pr_policy) { case SCTP_PR_SCTP_NONE: /* XXX: What about DATA_CHANNEL_RELIABLE_STREAM */ req.channel_type = DATA_CHANNEL_RELIABLE; break; case SCTP_PR_SCTP_TTL: /* XXX: What about DATA_CHANNEL_UNRELIABLE */ req.channel_type = DATA_CHANNEL_PARTIAL_RELIABLE_TIMED; break; case SCTP_PR_SCTP_RTX: req.channel_type = DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT; break; default: return (0); } req.flags = htons(0); if (unordered) { req.flags |= htons(DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED); } req.reliability_params = htons((uint16_t)pr_value); /* XXX Why 16-bit */ req.priority = htons(0); /* XXX: add support */ memset(&sndinfo, 0, sizeof(struct sctp_sndinfo)); sndinfo.snd_sid = o_stream; sndinfo.snd_flags = SCTP_EOR; sndinfo.snd_ppid = htonl(DATA_CHANNEL_PPID_CONTROL); if (usrsctp_sendv(sock, &req, sizeof(struct rtcweb_datachannel_open_request), NULL, 0, &sndinfo, (socklen_t)sizeof(struct sctp_sndinfo), SCTP_SENDV_SNDINFO, 0) < 0) { perror("sctp_sendv"); return (0); } else { return (1); } } static int send_open_response_message(struct socket *sock, uint16_t o_stream, uint16_t i_stream) { /* XXX: This should be encoded in a better way */ struct rtcweb_datachannel_open_response rsp; struct sctp_sndinfo sndinfo; memset(&rsp, 0, sizeof(struct rtcweb_datachannel_open_response)); rsp.msg_type = DATA_CHANNEL_OPEN_RESPONSE; rsp.error = 0; rsp.flags = htons(0); rsp.reverse_stream = htons(i_stream); memset(&sndinfo, 0, sizeof(struct sctp_sndinfo)); sndinfo.snd_sid = o_stream; sndinfo.snd_flags = SCTP_EOR; sndinfo.snd_ppid = htonl(DATA_CHANNEL_PPID_CONTROL); if (usrsctp_sendv(sock, &rsp, sizeof(struct rtcweb_datachannel_open_response), NULL, 0, &sndinfo, (socklen_t)sizeof(struct sctp_sndinfo), SCTP_SENDV_SNDINFO, 0) < 0) { perror("sctp_sendv"); return (0); } else { return (1); } } static int send_open_ack_message(struct socket *sock, uint16_t o_stream) { /* XXX: This should be encoded in a better way */ struct rtcweb_datachannel_ack ack; struct sctp_sndinfo sndinfo; memset(&ack, 0, sizeof(struct rtcweb_datachannel_ack)); ack.msg_type = DATA_CHANNEL_ACK; memset(&sndinfo, 0, sizeof(struct sctp_sndinfo)); sndinfo.snd_sid = o_stream; sndinfo.snd_flags = SCTP_EOR; sndinfo.snd_ppid = htonl(DATA_CHANNEL_PPID_CONTROL); if (usrsctp_sendv(sock, &ack, sizeof(struct rtcweb_datachannel_ack), NULL, 0, &sndinfo, (socklen_t)sizeof(struct sctp_sndinfo), SCTP_SENDV_SNDINFO, 0) < 0) { perror("sctp_sendv"); return (0); } else { return (1); } } static void send_deferred_messages(struct peer_connection *pc) { uint32_t i; struct channel *channel; for (i = 0; i < NUMBER_OF_CHANNELS; i++) { channel = &(pc->channels[i]); if (channel->flags & DATA_CHANNEL_FLAGS_SEND_REQ) { if (send_open_request_message(pc->sock, channel->o_stream, channel->unordered, channel->pr_policy, channel->pr_value)) { channel->flags &= ~DATA_CHANNEL_FLAGS_SEND_REQ; } else { if (errno != EAGAIN) { /* XXX: error handling */ } } } if (channel->flags & DATA_CHANNEL_FLAGS_SEND_RSP) { if (send_open_response_message(pc->sock, channel->o_stream, channel->i_stream)) { channel->flags &= ~DATA_CHANNEL_FLAGS_SEND_RSP; } else { if (errno != EAGAIN) { /* XXX: error handling */ } } } if (channel->flags & DATA_CHANNEL_FLAGS_SEND_ACK) { if (send_open_ack_message(pc->sock, channel->o_stream)) { channel->flags &= ~DATA_CHANNEL_FLAGS_SEND_ACK; } else { if (errno != EAGAIN) { /* XXX: error handling */ } } } } return; } static struct channel * open_channel(struct peer_connection *pc, uint8_t unordered, uint16_t pr_policy, uint32_t pr_value) { struct channel *channel; uint16_t o_stream; if ((pr_policy != SCTP_PR_SCTP_NONE) && (pr_policy != SCTP_PR_SCTP_TTL) && (pr_policy != SCTP_PR_SCTP_RTX)) { return (NULL); } if ((unordered != 0) && (unordered != 1)) { return (NULL); } if ((pr_policy == SCTP_PR_SCTP_NONE) && (pr_value != 0)) { return (NULL); } if ((channel = find_free_channel(pc)) == NULL) { return (NULL); } o_stream = find_free_o_stream(pc); channel->state = DATA_CHANNEL_CONNECTING; channel->unordered = unordered; channel->pr_policy = pr_policy; channel->pr_value = pr_value; channel->o_stream = o_stream; channel->flags = 0; if (o_stream == 0) { request_more_o_streams(pc); } else { if (send_open_request_message(pc->sock, o_stream, unordered, pr_policy, pr_value)) { pc->o_stream_channel[o_stream] = channel; } else { if (errno == EAGAIN) { pc->o_stream_channel[o_stream] = channel; channel->flags |= DATA_CHANNEL_FLAGS_SEND_REQ; } else { channel->state = DATA_CHANNEL_CLOSED; channel->unordered = 0; channel->pr_policy = 0; channel->pr_value = 0; channel->o_stream = 0; channel->flags = 0; channel = NULL; } } } return (channel); } static int send_user_message(struct peer_connection *pc, struct channel *channel, char *message, size_t length) { struct sctp_sendv_spa spa; if (channel == NULL) { return (0); } if ((channel->state != DATA_CHANNEL_OPEN) && (channel->state != DATA_CHANNEL_CONNECTING)) { /* XXX: What to do in other states */ return (0); } memset(&spa, 0, sizeof(struct sctp_sendv_spa)); spa.sendv_sndinfo.snd_sid = channel->o_stream; if ((channel->state == DATA_CHANNEL_OPEN) && (channel->unordered)) { spa.sendv_sndinfo.snd_flags = SCTP_EOR | SCTP_UNORDERED; } else { spa.sendv_sndinfo.snd_flags = SCTP_EOR; } spa.sendv_sndinfo.snd_ppid = htonl(DATA_CHANNEL_PPID_DOMSTRING); spa.sendv_flags = SCTP_SEND_SNDINFO_VALID; if ((channel->pr_policy == SCTP_PR_SCTP_TTL) || (channel->pr_policy == SCTP_PR_SCTP_RTX)) { spa.sendv_prinfo.pr_policy = channel->pr_policy; spa.sendv_prinfo.pr_value = channel->pr_value; spa.sendv_flags |= SCTP_SEND_PRINFO_VALID; } if (usrsctp_sendv(pc->sock, message, length, NULL, 0, &spa, (socklen_t)sizeof(struct sctp_sendv_spa), SCTP_SENDV_SPA, 0) < 0) { perror("sctp_sendv"); return (0); } else { return (1); } } static void reset_outgoing_stream(struct peer_connection *pc, uint16_t o_stream) { uint32_t i; for (i = 0; i < pc->o_stream_buffer_counter; i++) { if (pc->o_stream_buffer[i] == o_stream) { return; } } pc->o_stream_buffer[pc->o_stream_buffer_counter++] = o_stream; return; } static void send_outgoing_stream_reset(struct peer_connection *pc) { struct sctp_reset_streams *srs; uint32_t i; size_t len; if (pc->o_stream_buffer_counter == 0) { return; } len = sizeof(sctp_assoc_t) + (2 + pc->o_stream_buffer_counter) * sizeof(uint16_t); srs = (struct sctp_reset_streams *)malloc(len); if (srs == NULL) { return; } memset(srs, 0, len); srs->srs_flags = SCTP_STREAM_RESET_OUTGOING; srs->srs_number_streams = pc->o_stream_buffer_counter; for (i = 0; i < pc->o_stream_buffer_counter; i++) { srs->srs_stream_list[i] = pc->o_stream_buffer[i]; } if (usrsctp_setsockopt(pc->sock, IPPROTO_SCTP, SCTP_RESET_STREAMS, srs, (socklen_t)len) < 0) { perror("setsockopt"); } else { for (i = 0; i < pc->o_stream_buffer_counter; i++) { srs->srs_stream_list[i] = 0; } pc->o_stream_buffer_counter = 0; } free(srs); return; } static void close_channel(struct peer_connection *pc, struct channel *channel) { if (channel == NULL) { return; } if (channel->state != DATA_CHANNEL_OPEN) { return; } reset_outgoing_stream(pc, channel->o_stream); send_outgoing_stream_reset(pc); channel->state = DATA_CHANNEL_CLOSING; return; } static void handle_open_request_message(struct peer_connection *pc, struct rtcweb_datachannel_open_request *req, size_t length, uint16_t i_stream) { struct channel *channel; uint32_t pr_value; uint16_t pr_policy; uint16_t o_stream; uint8_t unordered; if ((channel = find_channel_by_i_stream(pc, i_stream))) { printf("handle_open_request_message: channel %u is in state %u instead of CLOSED.\n", channel->id, channel->state); /* XXX: some error handling */ return; } if ((channel = find_free_channel(pc)) == NULL) { /* XXX: some error handling */ return; } switch (req->channel_type) { case DATA_CHANNEL_RELIABLE: pr_policy = SCTP_PR_SCTP_NONE; break; /* XXX Doesn't make sense */ case DATA_CHANNEL_RELIABLE_STREAM: pr_policy = SCTP_PR_SCTP_NONE; break; /* XXX Doesn't make sense */ case DATA_CHANNEL_UNRELIABLE: pr_policy = SCTP_PR_SCTP_TTL; break; case DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT: pr_policy = SCTP_PR_SCTP_RTX; break; case DATA_CHANNEL_PARTIAL_RELIABLE_TIMED: pr_policy = SCTP_PR_SCTP_TTL; break; default: pr_policy = SCTP_PR_SCTP_NONE; /* XXX error handling */ break; } pr_value = ntohs(req->reliability_params); if (ntohs(req->flags) & DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED) { unordered = 1; } else { unordered = 0; } o_stream = find_free_o_stream(pc); channel->state = DATA_CHANNEL_CONNECTING; channel->unordered = unordered; channel->pr_policy = pr_policy; channel->pr_value = pr_value; channel->i_stream = i_stream; channel->o_stream = o_stream; channel->flags = 0; pc->i_stream_channel[i_stream] = channel; if (o_stream == 0) { request_more_o_streams(pc); } else { if (send_open_response_message(pc->sock, o_stream, i_stream)) { pc->o_stream_channel[o_stream] = channel; } else { if (errno == EAGAIN) { channel->flags |= DATA_CHANNEL_FLAGS_SEND_RSP; pc->o_stream_channel[o_stream] = channel; } else { /* XXX: Signal error to the other end. */ pc->i_stream_channel[i_stream] = NULL; channel->state = DATA_CHANNEL_CLOSED; channel->unordered = 0; channel->pr_policy = 0; channel->pr_value = 0; channel->i_stream = 0; channel->o_stream = 0; channel->flags = 0; } } } } static void handle_open_response_message(struct peer_connection *pc, struct rtcweb_datachannel_open_response *rsp, size_t length, uint16_t i_stream) { uint16_t o_stream; struct channel *channel; o_stream = ntohs(rsp->reverse_stream); channel = find_channel_by_o_stream(pc, o_stream); if (channel == NULL) { /* XXX: improve error handling */ printf("handle_open_response_message: Can't find channel for outgoing steam %d.\n", o_stream); return; } if (channel->state != DATA_CHANNEL_CONNECTING) { /* XXX: improve error handling */ printf("handle_open_response_message: Channel with id %u for outgoing steam %u is in state %u.\n", channel->id, o_stream, channel->state); return; } if (find_channel_by_i_stream(pc, i_stream)) { /* XXX: improve error handling */ printf("handle_open_response_message: Channel collision for channel with id %u and streams (in/out) = (%u/%u).\n", channel->id, i_stream, o_stream); return; } channel->i_stream = i_stream; channel->state = DATA_CHANNEL_OPEN; pc->i_stream_channel[i_stream] = channel; if (send_open_ack_message(pc->sock, o_stream)) { channel->flags = 0; } else { channel->flags |= DATA_CHANNEL_FLAGS_SEND_ACK; } return; } static void handle_open_ack_message(struct peer_connection *pc, struct rtcweb_datachannel_ack *ack, size_t length, uint16_t i_stream) { struct channel *channel; channel = find_channel_by_i_stream(pc, i_stream); if (channel == NULL) { /* XXX: some error handling */ return; } if (channel->state == DATA_CHANNEL_OPEN) { return; } if (channel->state != DATA_CHANNEL_CONNECTING) { /* XXX: error handling */ return; } channel->state = DATA_CHANNEL_OPEN; return; } static void handle_unknown_message(char *msg, size_t length, uint16_t i_stream) { /* XXX: Send an error message */ return; } static void handle_data_message(struct peer_connection *pc, char *buffer, size_t length, uint16_t i_stream) { struct channel *channel; channel = find_channel_by_i_stream(pc, i_stream); if (channel == NULL) { /* XXX: Some error handling */ return; } if (channel->state == DATA_CHANNEL_CONNECTING) { /* Implicit ACK */ channel->state = DATA_CHANNEL_OPEN; } if (channel->state != DATA_CHANNEL_OPEN) { /* XXX: What about other states? */ /* XXX: Some error handling */ return; } else { /* Assuming DATA_CHANNEL_PPID_DOMSTRING */ /* XXX: Protect for non 0 terminated buffer */ printf("Message received of length %zu on channel with id %u: %.*s\n", length, channel->id, (int)length, buffer); } return; } static void handle_message(struct peer_connection *pc, char *buffer, size_t length, uint32_t ppid, uint16_t i_stream) { struct rtcweb_datachannel_open_request *req; struct rtcweb_datachannel_open_response *rsp; struct rtcweb_datachannel_ack *ack, *msg; switch (ppid) { case DATA_CHANNEL_PPID_CONTROL: if (length < sizeof(struct rtcweb_datachannel_ack)) { return; } msg = (struct rtcweb_datachannel_ack *)buffer; switch (msg->msg_type) { case DATA_CHANNEL_OPEN_REQUEST: if (length < sizeof(struct rtcweb_datachannel_open_request)) { /* XXX: error handling? */ return; } req = (struct rtcweb_datachannel_open_request *)buffer; handle_open_request_message(pc, req, length, i_stream); break; case DATA_CHANNEL_OPEN_RESPONSE: if (length < sizeof(struct rtcweb_datachannel_open_response)) { /* XXX: error handling? */ return; } rsp = (struct rtcweb_datachannel_open_response *)buffer; handle_open_response_message(pc, rsp, length, i_stream); break; case DATA_CHANNEL_ACK: if (length < sizeof(struct rtcweb_datachannel_ack)) { /* XXX: error handling? */ return; } ack = (struct rtcweb_datachannel_ack *)buffer; handle_open_ack_message(pc, ack, length, i_stream); break; default: handle_unknown_message(buffer, length, i_stream); break; } break; case DATA_CHANNEL_PPID_DOMSTRING: case DATA_CHANNEL_PPID_BINARY: handle_data_message(pc, buffer, length, i_stream); break; default: printf("Message of length %zu, PPID %u on stream %u received.\n", length, ppid, i_stream); break; } } static void handle_association_change_event(struct sctp_assoc_change *sac) { unsigned int i, n; printf("Association change "); switch (sac->sac_state) { case SCTP_COMM_UP: printf("SCTP_COMM_UP"); break; case SCTP_COMM_LOST: printf("SCTP_COMM_LOST"); break; case SCTP_RESTART: printf("SCTP_RESTART"); break; case SCTP_SHUTDOWN_COMP: printf("SCTP_SHUTDOWN_COMP"); break; case SCTP_CANT_STR_ASSOC: printf("SCTP_CANT_STR_ASSOC"); break; default: printf("UNKNOWN"); break; } printf(", streams (in/out) = (%u/%u)", sac->sac_inbound_streams, sac->sac_outbound_streams); n = sac->sac_length - sizeof(struct sctp_assoc_change); if (((sac->sac_state == SCTP_COMM_UP) || (sac->sac_state == SCTP_RESTART)) && (n > 0)) { printf(", supports"); for (i = 0; i < n; i++) { switch (sac->sac_info[i]) { case SCTP_ASSOC_SUPPORTS_PR: printf(" PR"); break; case SCTP_ASSOC_SUPPORTS_AUTH: printf(" AUTH"); break; case SCTP_ASSOC_SUPPORTS_ASCONF: printf(" ASCONF"); break; case SCTP_ASSOC_SUPPORTS_MULTIBUF: printf(" MULTIBUF"); break; case SCTP_ASSOC_SUPPORTS_RE_CONFIG: printf(" RE-CONFIG"); break; case SCTP_ASSOC_SUPPORTS_INTERLEAVING: printf(" INTERLEAVING"); break; default: printf(" UNKNOWN(0x%02x)", sac->sac_info[i]); break; } } } else if (((sac->sac_state == SCTP_COMM_LOST) || (sac->sac_state == SCTP_CANT_STR_ASSOC)) && (n > 0)) { printf(", ABORT ="); for (i = 0; i < n; i++) { printf(" 0x%02x", sac->sac_info[i]); } } printf(".\n"); if ((sac->sac_state == SCTP_CANT_STR_ASSOC) || (sac->sac_state == SCTP_SHUTDOWN_COMP) || (sac->sac_state == SCTP_COMM_LOST)) { exit(0); } return; } static void handle_peer_address_change_event(struct sctp_paddr_change *spc) { char addr_buf[INET6_ADDRSTRLEN]; const char *addr; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; switch (spc->spc_aaddr.ss_family) { case AF_INET: sin = (struct sockaddr_in *)&spc->spc_aaddr; addr = inet_ntop(AF_INET, &sin->sin_addr, addr_buf, INET_ADDRSTRLEN); break; case AF_INET6: sin6 = (struct sockaddr_in6 *)&spc->spc_aaddr; addr = inet_ntop(AF_INET6, &sin6->sin6_addr, addr_buf, INET6_ADDRSTRLEN); break; default: #ifdef _WIN32 if (_snprintf(addr_buf, INET6_ADDRSTRLEN, "Unknown family %d", spc->spc_aaddr.ss_family) < 0) { #else if (snprintf(addr_buf, INET6_ADDRSTRLEN, "Unknown family %d", spc->spc_aaddr.ss_family) < 0) { #endif addr_buf[0] = '\0'; } addr = addr_buf; break; } printf("Peer address %s is now ", addr); switch (spc->spc_state) { case SCTP_ADDR_AVAILABLE: printf("SCTP_ADDR_AVAILABLE"); break; case SCTP_ADDR_UNREACHABLE: printf("SCTP_ADDR_UNREACHABLE"); break; case SCTP_ADDR_REMOVED: printf("SCTP_ADDR_REMOVED"); break; case SCTP_ADDR_ADDED: printf("SCTP_ADDR_ADDED"); break; case SCTP_ADDR_MADE_PRIM: printf("SCTP_ADDR_MADE_PRIM"); break; case SCTP_ADDR_CONFIRMED: printf("SCTP_ADDR_CONFIRMED"); break; default: printf("UNKNOWN"); break; } printf(" (error = 0x%08x).\n", spc->spc_error); return; } static void handle_adaptation_indication(struct sctp_adaptation_event *sai) { printf("Adaptation indication: %x.\n", sai-> sai_adaptation_ind); return; } static void handle_shutdown_event(struct sctp_shutdown_event *sse) { printf("Shutdown event.\n"); /* XXX: notify all channels. */ return; } static void handle_stream_reset_event(struct peer_connection *pc, struct sctp_stream_reset_event *strrst) { uint32_t n, i; struct channel *channel; n = (strrst->strreset_length - sizeof(struct sctp_stream_reset_event)) / sizeof(uint16_t); printf("Stream reset event: flags = %x, ", strrst->strreset_flags); if (strrst->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) { if (strrst->strreset_flags & SCTP_STREAM_RESET_OUTGOING_SSN) { printf("incoming/"); } printf("incoming "); } if (strrst->strreset_flags & SCTP_STREAM_RESET_OUTGOING_SSN) { printf("outgoing "); } printf("stream ids = "); for (i = 0; i < n; i++) { if (i > 0) { printf(", "); } printf("%d", strrst->strreset_stream_list[i]); } printf(".\n"); if (!(strrst->strreset_flags & SCTP_STREAM_RESET_DENIED) && !(strrst->strreset_flags & SCTP_STREAM_RESET_FAILED)) { for (i = 0; i < n; i++) { if (strrst->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) { channel = find_channel_by_i_stream(pc, strrst->strreset_stream_list[i]); if (channel != NULL) { pc->i_stream_channel[channel->i_stream] = NULL; channel->i_stream = 0; if (channel->o_stream == 0) { channel->pr_policy = SCTP_PR_SCTP_NONE; channel->pr_value = 0; channel->unordered = 0; channel->flags = 0; channel->state = DATA_CHANNEL_CLOSED; } else { if (channel->state == DATA_CHANNEL_OPEN) { reset_outgoing_stream(pc, channel->o_stream); channel->state = DATA_CHANNEL_CLOSING; } else { /* XXX: What to do? */ } } } } if (strrst->strreset_flags & SCTP_STREAM_RESET_OUTGOING_SSN) { channel = find_channel_by_o_stream(pc, strrst->strreset_stream_list[i]); if (channel != NULL) { pc->o_stream_channel[channel->o_stream] = NULL; channel->o_stream = 0; if (channel->i_stream == 0) { channel->pr_policy = SCTP_PR_SCTP_NONE; channel->pr_value = 0; channel->unordered = 0; channel->flags = 0; channel->state = DATA_CHANNEL_CLOSED; } } } } } return; } static void handle_stream_change_event(struct peer_connection *pc, struct sctp_stream_change_event *strchg) { uint16_t o_stream; uint32_t i; struct channel *channel; printf("Stream change event: streams (in/out) = (%u/%u), flags = %x.\n", strchg->strchange_instrms, strchg->strchange_outstrms, strchg->strchange_flags); for (i = 0; i < NUMBER_OF_CHANNELS; i++) { channel = &(pc->channels[i]); if ((channel->state == DATA_CHANNEL_CONNECTING) && (channel->o_stream == 0)) { if ((strchg->strchange_flags & SCTP_STREAM_CHANGE_DENIED) || (strchg->strchange_flags & SCTP_STREAM_CHANGE_FAILED)) { /* XXX: Signal to the other end. */ if (channel->i_stream != 0) { pc->i_stream_channel[channel->i_stream] = NULL; } channel->unordered = 0; channel->pr_policy = SCTP_PR_SCTP_NONE; channel->pr_value = 0; channel->i_stream = 0; channel->o_stream = 0; channel->flags = 0; channel->state = DATA_CHANNEL_CLOSED; } else { o_stream = find_free_o_stream(pc); if (o_stream != 0) { channel->o_stream = o_stream; pc->o_stream_channel[o_stream] = channel; if (channel->i_stream == 0) { channel->flags |= DATA_CHANNEL_FLAGS_SEND_REQ; } else { channel->flags |= DATA_CHANNEL_FLAGS_SEND_RSP; } } else { /* We will not find more ... */ break; } } } } return; } static void handle_remote_error_event(struct sctp_remote_error *sre) { size_t i, n; n = sre->sre_length - sizeof(struct sctp_remote_error); printf("Remote Error (error = 0x%04x): ", sre->sre_error); for (i = 0; i < n; i++) { printf(" 0x%02x", sre-> sre_data[i]); } printf(".\n"); return; } static void handle_send_failed_event(struct sctp_send_failed_event *ssfe) { size_t i, n; if (ssfe->ssfe_flags & SCTP_DATA_UNSENT) { printf("Unsent "); } if (ssfe->ssfe_flags & SCTP_DATA_SENT) { printf("Sent "); } if (ssfe->ssfe_flags & ~(SCTP_DATA_SENT | SCTP_DATA_UNSENT)) { printf("(flags = %x) ", ssfe->ssfe_flags); } printf("message with PPID = %u, SID = %u, flags: 0x%04x due to error = 0x%08x", (uint32_t)ntohl(ssfe->ssfe_info.snd_ppid), ssfe->ssfe_info.snd_sid, ssfe->ssfe_info.snd_flags, ssfe->ssfe_error); n = ssfe->ssfe_length - sizeof(struct sctp_send_failed_event); for (i = 0; i < n; i++) { printf(" 0x%02x", ssfe->ssfe_data[i]); } printf(".\n"); return; } static void handle_notification_rtcweb(struct peer_connection *pc, union sctp_notification *notif, size_t n) { if (notif->sn_header.sn_length != (uint32_t)n) { return; } switch (notif->sn_header.sn_type) { case SCTP_ASSOC_CHANGE: handle_association_change_event(&(notif->sn_assoc_change)); break; case SCTP_PEER_ADDR_CHANGE: handle_peer_address_change_event(&(notif->sn_paddr_change)); break; case SCTP_REMOTE_ERROR: handle_remote_error_event(&(notif->sn_remote_error)); break; case SCTP_SHUTDOWN_EVENT: handle_shutdown_event(&(notif->sn_shutdown_event)); break; case SCTP_ADAPTATION_INDICATION: handle_adaptation_indication(&(notif->sn_adaptation_event)); break; case SCTP_PARTIAL_DELIVERY_EVENT: break; case SCTP_AUTHENTICATION_EVENT: break; case SCTP_SENDER_DRY_EVENT: break; case SCTP_NOTIFICATIONS_STOPPED_EVENT: break; case SCTP_SEND_FAILED_EVENT: handle_send_failed_event(&(notif->sn_send_failed_event)); break; case SCTP_STREAM_RESET_EVENT: handle_stream_reset_event(pc, &(notif->sn_strreset_event)); send_deferred_messages(pc); send_outgoing_stream_reset(pc); request_more_o_streams(pc); break; case SCTP_ASSOC_RESET_EVENT: break; case SCTP_STREAM_CHANGE_EVENT: handle_stream_change_event(pc, &(notif->sn_strchange_event)); send_deferred_messages(pc); send_outgoing_stream_reset(pc); request_more_o_streams(pc); break; default: break; } } static void print_status(struct peer_connection *pc) { struct sctp_status status; socklen_t len; uint32_t i; struct channel *channel; len = (socklen_t)sizeof(struct sctp_status); if (usrsctp_getsockopt(pc->sock, IPPROTO_SCTP, SCTP_STATUS, &status, &len) < 0) { perror("getsockopt"); return; } printf("Association state: "); switch (status.sstat_state) { case SCTP_CLOSED: printf("CLOSED\n"); break; case SCTP_BOUND: printf("BOUND\n"); break; case SCTP_LISTEN: printf("LISTEN\n"); break; case SCTP_COOKIE_WAIT: printf("COOKIE_WAIT\n"); break; case SCTP_COOKIE_ECHOED: printf("COOKIE_ECHOED\n"); break; case SCTP_ESTABLISHED: printf("ESTABLISHED\n"); break; case SCTP_SHUTDOWN_PENDING: printf("SHUTDOWN_PENDING\n"); break; case SCTP_SHUTDOWN_SENT: printf("SHUTDOWN_SENT\n"); break; case SCTP_SHUTDOWN_RECEIVED: printf("SHUTDOWN_RECEIVED\n"); break; case SCTP_SHUTDOWN_ACK_SENT: printf("SHUTDOWN_ACK_SENT\n"); break; default: printf("UNKNOWN\n"); break; } printf("Number of streams (i/o) = (%u/%u)\n", status.sstat_instrms, status.sstat_outstrms); for (i = 0; i < NUMBER_OF_CHANNELS; i++) { channel = &(pc->channels[i]); if (channel->state == DATA_CHANNEL_CLOSED) { continue; } printf("Channel with id = %u: state ", channel->id); switch (channel->state) { case DATA_CHANNEL_CLOSED: printf("CLOSED"); break; case DATA_CHANNEL_CONNECTING: printf("CONNECTING"); break; case DATA_CHANNEL_OPEN: printf("OPEN"); break; case DATA_CHANNEL_CLOSING: printf("CLOSING"); break; default: printf("UNKNOWN(%d)", channel->state); break; } printf(", flags = 0x%08x, stream id (in/out): (%u/%u), ", channel->flags, channel->i_stream, channel->o_stream); if (channel->unordered) { printf("unordered, "); } else { printf("ordered, "); } switch (channel->pr_policy) { case SCTP_PR_SCTP_NONE: printf("reliable.\n"); break; case SCTP_PR_SCTP_TTL: printf("unreliable (timeout %ums).\n", channel->pr_value); break; case SCTP_PR_SCTP_RTX: printf("unreliable (max. %u rtx).\n", channel->pr_value); break; default: printf("unknown policy %u.\n", channel->pr_policy); break; } } } static int receive_cb(struct socket *sock, union sctp_sockstore addr, void *data, size_t datalen, struct sctp_rcvinfo rcv, int flags, void *ulp_info) { struct peer_connection *pc; pc = (struct peer_connection *)ulp_info; if (data) { lock_peer_connection(pc); if (flags & MSG_NOTIFICATION) { handle_notification_rtcweb(pc, (union sctp_notification *)data, datalen); } else { handle_message(pc, data, datalen, ntohl(rcv.rcv_ppid), rcv.rcv_sid); } unlock_peer_connection(pc); } return (1); } int main(int argc, char *argv[]) { struct socket *sock; struct sockaddr_in addr; socklen_t addr_len; char line[LINE_LENGTH + 1]; unsigned int unordered, policy, value, id, seconds; unsigned int i; struct channel *channel; const int on = 1; struct sctp_assoc_value av; struct sctp_event event; struct sctp_udpencaps encaps; struct sctp_initmsg initmsg; uint16_t event_types[] = {SCTP_ASSOC_CHANGE, SCTP_PEER_ADDR_CHANGE, SCTP_REMOTE_ERROR, SCTP_SHUTDOWN_EVENT, SCTP_ADAPTATION_INDICATION, SCTP_SEND_FAILED_EVENT, SCTP_STREAM_RESET_EVENT, SCTP_STREAM_CHANGE_EVENT}; char addrbuf[INET_ADDRSTRLEN]; if (argc > 1) { usrsctp_init(atoi(argv[1]), NULL, debug_printf_stack); } else { usrsctp_init(9899, NULL, debug_printf_stack); } #ifdef SCTP_DEBUG usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_NONE); #endif usrsctp_sysctl_set_sctp_blackhole(2); usrsctp_sysctl_set_sctp_no_csum_on_loopback(0); if ((sock = usrsctp_socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP, receive_cb, NULL, 0, &peer_connection)) == NULL) { perror("socket"); } init_peer_connection(&peer_connection); if (argc > 2) { memset(&encaps, 0, sizeof(struct sctp_udpencaps)); encaps.sue_address.ss_family = AF_INET6; encaps.sue_port = htons(atoi(argv[2])); if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_REMOTE_UDP_ENCAPS_PORT, (const void*)&encaps, (socklen_t)sizeof(struct sctp_udpencaps)) < 0) { perror("setsockopt"); } } if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on, sizeof(int)) < 0) { perror("setsockopt SCTP_RECVRCVINFO"); } if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &on, sizeof(int)) < 0) { perror("setsockopt SCTP_EXPLICIT_EOR"); } /* Allow resetting streams. */ av.assoc_id = SCTP_ALL_ASSOC; av.assoc_value = SCTP_ENABLE_RESET_STREAM_REQ | SCTP_ENABLE_CHANGE_ASSOC_REQ; if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET, &av, sizeof(struct sctp_assoc_value)) < 0) { perror("setsockopt SCTP_ENABLE_STREAM_RESET"); } /* Enable the events of interest. */ memset(&event, 0, sizeof(event)); event.se_assoc_id = SCTP_ALL_ASSOC; event.se_on = 1; for (i = 0; i < sizeof(event_types)/sizeof(uint16_t); i++) { event.se_type = event_types[i]; if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_EVENT, &event, sizeof(event)) < 0) { perror("setsockopt SCTP_EVENT"); } } memset(&initmsg, 0, sizeof(struct sctp_initmsg)); initmsg.sinit_num_ostreams = 5; initmsg.sinit_max_instreams = 65535; if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(struct sctp_initmsg)) < 0) { perror("setsockopt SCTP_INITMSG"); } if (argc == 5) { /* operating as client */ memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; #ifdef HAVE_SIN_LEN addr.sin_len = sizeof(struct sockaddr_in); #endif if (!inet_pton(AF_INET, argv[3], &addr.sin_addr.s_addr)){ printf("error: invalid address\n"); exit(1); } addr.sin_port = htons(atoi(argv[4])); if (usrsctp_connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { perror("connect"); } printf("Connected to %s:%d.\n", inet_ntop(AF_INET, &(addr.sin_addr), addrbuf, INET_ADDRSTRLEN), ntohs(addr.sin_port)); } else if (argc == 4) { struct socket *conn_sock; /* operating as server */ memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; #ifdef HAVE_SIN_LEN addr.sin_len = sizeof(struct sockaddr_in); #endif addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(atoi(argv[3])); if (usrsctp_bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { perror("bind"); } if (usrsctp_listen(sock, 1) < 0) { perror("listen"); } addr_len = (socklen_t)sizeof(struct sockaddr_in); memset(&addr, 0, sizeof(struct sockaddr_in)); if ((conn_sock = usrsctp_accept(sock, (struct sockaddr *)&addr, &addr_len)) == NULL) { perror("accept"); } usrsctp_close(sock); sock = conn_sock; printf("Connected to %s:%d.\n", inet_ntop(AF_INET, &(addr.sin_addr), addrbuf, INET_ADDRSTRLEN), ntohs(addr.sin_port)); } else { printf("Usage: %s local_udp_port remote_udp_port local_port when operating as server\n" " %s local_udp_port remote_udp_port remote_addr remote_port when operating as client\n", argv[0], argv[0]); return (0); } lock_peer_connection(&peer_connection); peer_connection.sock = sock; unlock_peer_connection(&peer_connection); for (;;) { #if defined(_WIN32) && !defined(__MINGW32__) if (gets_s(line, LINE_LENGTH) == NULL) { #else if (fgets(line, LINE_LENGTH, stdin) == NULL) { #endif if (usrsctp_shutdown(sock, SHUT_WR) < 0) { perror("usrsctp_shutdown"); } while (usrsctp_finish() != 0) { #ifdef _WIN32 Sleep(1000); #else sleep(1); #endif } break; } if (strncmp(line, "?", strlen("?")) == 0 || strncmp(line, "help", strlen("help")) == 0) { printf("Commands:\n" "open unordered pr_policy pr_value - opens a channel\n" "close channel - closes the channel\n" "send channel:string - sends string using channel\n" "status - prints the status\n" "sleep n - sleep for n seconds\n" "help - this message\n"); } else if (strncmp(line, "status", strlen("status")) == 0) { lock_peer_connection(&peer_connection); print_status(&peer_connection); unlock_peer_connection(&peer_connection); } else if (strncmp(line, "quit", strlen("quit")) == 0) { if (usrsctp_shutdown(sock, SHUT_WR) < 0) { perror("usrsctp_shutdown"); } while (usrsctp_finish() != 0) { #ifdef _WIN32 Sleep(1000); #else sleep(1); #endif } break; } else if (sscanf(line, "open %u %u %u", &unordered, &policy, &value) == 3) { lock_peer_connection(&peer_connection); channel = open_channel(&peer_connection, (uint8_t)unordered, (uint16_t)policy, (uint32_t)value); unlock_peer_connection(&peer_connection); if (channel == NULL) { printf("Creating channel failed.\n"); } else { printf("Channel with id %u created.\n", channel->id); } } else if (sscanf(line, "close %u", &id) == 1) { if (id < NUMBER_OF_CHANNELS) { lock_peer_connection(&peer_connection); close_channel(&peer_connection, &peer_connection.channels[id]); unlock_peer_connection(&peer_connection); } } else if (sscanf(line, "send %u", &id) == 1) { if (id < NUMBER_OF_CHANNELS) { char *msg; msg = strstr(line, ":"); if (msg) { msg++; lock_peer_connection(&peer_connection); #ifdef _WIN32 if (send_user_message(&peer_connection, &peer_connection.channels[id], msg, strlen(msg))) { #else if (send_user_message(&peer_connection, &peer_connection.channels[id], msg, strlen(msg) - 1)) { #endif printf("Message sent.\n"); } else { printf("Message sending failed.\n"); } unlock_peer_connection(&peer_connection); } } } else if (sscanf(line, "sleep %u", &seconds) == 1) { #ifdef _WIN32 Sleep(seconds * 1000); #else sleep(seconds); #endif } else { printf("Unknown command: %s", line); } } return (0); }