/** * @file DatagramPeerIO.c * @author Ambroz Bizjak * * @section LICENSE * * 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 author 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 COPYRIGHT HOLDERS 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 AUTHOR 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. */ #include #include #define DATAGRAMPEERIO_MODE_NONE 0 #define DATAGRAMPEERIO_MODE_CONNECT 1 #define DATAGRAMPEERIO_MODE_BIND 2 #define PeerLog(_o, ...) BLog_LogViaFunc((_o)->logfunc, (_o)->user, BLOG_CURRENT_CHANNEL, __VA_ARGS__) static void init_io (DatagramPeerIO *o); static void free_io (DatagramPeerIO *o); static void dgram_handler (DatagramPeerIO *o, int event); static void reset_mode (DatagramPeerIO *o); static void recv_decoder_notifier_handler (DatagramPeerIO *o, uint8_t *data, int data_len); void init_io (DatagramPeerIO *o) { // init dgram recv interface BDatagram_RecvAsync_Init(&o->dgram, o->effective_socket_mtu); // connect source PacketRecvConnector_ConnectInput(&o->recv_connector, BDatagram_RecvAsync_GetIf(&o->dgram)); // init dgram send interface BDatagram_SendAsync_Init(&o->dgram, o->effective_socket_mtu); // connect sink PacketPassConnector_ConnectOutput(&o->send_connector, BDatagram_SendAsync_GetIf(&o->dgram)); } void free_io (DatagramPeerIO *o) { // disconnect sink PacketPassConnector_DisconnectOutput(&o->send_connector); // free dgram send interface BDatagram_SendAsync_Free(&o->dgram); // disconnect source PacketRecvConnector_DisconnectInput(&o->recv_connector); // free dgram recv interface BDatagram_RecvAsync_Free(&o->dgram); } void dgram_handler (DatagramPeerIO *o, int event) { DebugObject_Access(&o->d_obj); ASSERT(o->mode == DATAGRAMPEERIO_MODE_CONNECT || o->mode == DATAGRAMPEERIO_MODE_BIND) PeerLog(o, BLOG_NOTICE, "error"); // reset mode reset_mode(o); // report error if (o->handler_error) { o->handler_error(o->user); return; } } void reset_mode (DatagramPeerIO *o) { ASSERT(o->mode == DATAGRAMPEERIO_MODE_NONE || o->mode == DATAGRAMPEERIO_MODE_CONNECT || o->mode == DATAGRAMPEERIO_MODE_BIND) if (o->mode == DATAGRAMPEERIO_MODE_NONE) { return; } // remove recv notifier handler PacketPassNotifier_SetHandler(&o->recv_notifier, NULL, NULL); // free I/O free_io(o); // free datagram object BDatagram_Free(&o->dgram); // set mode o->mode = DATAGRAMPEERIO_MODE_NONE; } void recv_decoder_notifier_handler (DatagramPeerIO *o, uint8_t *data, int data_len) { ASSERT(o->mode == DATAGRAMPEERIO_MODE_BIND) DebugObject_Access(&o->d_obj); // obtain addresses from last received packet BAddr addr; BIPAddr local_addr; ASSERT_EXECUTE(BDatagram_GetLastReceiveAddrs(&o->dgram, &addr, &local_addr)) // check address family just in case if (!BDatagram_AddressFamilySupported(addr.type)) { PeerLog(o, BLOG_ERROR, "unsupported receive address"); return; } // update addresses BDatagram_SetSendAddrs(&o->dgram, addr, local_addr); } int DatagramPeerIO_Init ( DatagramPeerIO *o, BReactor *reactor, int payload_mtu, int socket_mtu, struct spproto_security_params sp_params, btime_t latency, int num_frames, PacketPassInterface *recv_userif, int otp_warning_count, BThreadWorkDispatcher *twd, void *user, BLog_logfunc logfunc, DatagramPeerIO_handler_error handler_error, DatagramPeerIO_handler_otp_warning handler_otp_warning, DatagramPeerIO_handler_otp_ready handler_otp_ready ) { ASSERT(payload_mtu >= 0) ASSERT(socket_mtu >= 0) spproto_assert_security_params(sp_params); ASSERT(num_frames > 0) ASSERT(PacketPassInterface_GetMTU(recv_userif) >= payload_mtu) if (SPPROTO_HAVE_OTP(sp_params)) { ASSERT(otp_warning_count > 0) ASSERT(otp_warning_count <= sp_params.otp_num) } // set parameters o->reactor = reactor; o->payload_mtu = payload_mtu; o->sp_params = sp_params; o->user = user; o->logfunc = logfunc; o->handler_error = handler_error; // check num frames (for FragmentProtoAssembler) if (num_frames >= FPA_MAX_TIME) { PeerLog(o, BLOG_ERROR, "num_frames is too big"); goto fail0; } // check payload MTU (for FragmentProto) if (o->payload_mtu > UINT16_MAX) { PeerLog(o, BLOG_ERROR, "payload MTU is too big"); goto fail0; } // calculate SPProto payload MTU if ((o->spproto_payload_mtu = spproto_payload_mtu_for_carrier_mtu(o->sp_params, socket_mtu)) <= (int)sizeof(struct fragmentproto_chunk_header)) { PeerLog(o, BLOG_ERROR, "socket MTU is too small"); goto fail0; } // calculate effective socket MTU if ((o->effective_socket_mtu = spproto_carrier_mtu_for_payload_mtu(o->sp_params, o->spproto_payload_mtu)) < 0) { PeerLog(o, BLOG_ERROR, "spproto_carrier_mtu_for_payload_mtu failed !?"); goto fail0; } // init receiving // init assembler if (!FragmentProtoAssembler_Init(&o->recv_assembler, o->spproto_payload_mtu, recv_userif, num_frames, fragmentproto_max_chunks_for_frame(o->spproto_payload_mtu, o->payload_mtu), BReactor_PendingGroup(o->reactor), o->user, o->logfunc )) { PeerLog(o, BLOG_ERROR, "FragmentProtoAssembler_Init failed"); goto fail0; } // init notifier PacketPassNotifier_Init(&o->recv_notifier, FragmentProtoAssembler_GetInput(&o->recv_assembler), BReactor_PendingGroup(o->reactor)); // init decoder if (!SPProtoDecoder_Init(&o->recv_decoder, PacketPassNotifier_GetInput(&o->recv_notifier), o->sp_params, 2, BReactor_PendingGroup(o->reactor), twd, o->user, o->logfunc)) { PeerLog(o, BLOG_ERROR, "SPProtoDecoder_Init failed"); goto fail1; } SPProtoDecoder_SetHandlers(&o->recv_decoder, handler_otp_ready, user); // init connector PacketRecvConnector_Init(&o->recv_connector, o->effective_socket_mtu, BReactor_PendingGroup(o->reactor)); // init buffer if (!SinglePacketBuffer_Init(&o->recv_buffer, PacketRecvConnector_GetOutput(&o->recv_connector), SPProtoDecoder_GetInput(&o->recv_decoder), BReactor_PendingGroup(o->reactor))) { PeerLog(o, BLOG_ERROR, "SinglePacketBuffer_Init failed"); goto fail2; } // init sending base // init disassembler FragmentProtoDisassembler_Init(&o->send_disassembler, o->reactor, o->payload_mtu, o->spproto_payload_mtu, -1, latency); // init encoder if (!SPProtoEncoder_Init(&o->send_encoder, FragmentProtoDisassembler_GetOutput(&o->send_disassembler), o->sp_params, otp_warning_count, BReactor_PendingGroup(o->reactor), twd)) { PeerLog(o, BLOG_ERROR, "SPProtoEncoder_Init failed"); goto fail3; } SPProtoEncoder_SetHandlers(&o->send_encoder, handler_otp_warning, user); // init connector PacketPassConnector_Init(&o->send_connector, o->effective_socket_mtu, BReactor_PendingGroup(o->reactor)); // init buffer if (!SinglePacketBuffer_Init(&o->send_buffer, SPProtoEncoder_GetOutput(&o->send_encoder), PacketPassConnector_GetInput(&o->send_connector), BReactor_PendingGroup(o->reactor))) { PeerLog(o, BLOG_ERROR, "SinglePacketBuffer_Init failed"); goto fail4; } // set mode o->mode = DATAGRAMPEERIO_MODE_NONE; DebugObject_Init(&o->d_obj); return 1; fail4: PacketPassConnector_Free(&o->send_connector); SPProtoEncoder_Free(&o->send_encoder); fail3: FragmentProtoDisassembler_Free(&o->send_disassembler); SinglePacketBuffer_Free(&o->recv_buffer); fail2: PacketRecvConnector_Free(&o->recv_connector); SPProtoDecoder_Free(&o->recv_decoder); fail1: PacketPassNotifier_Free(&o->recv_notifier); FragmentProtoAssembler_Free(&o->recv_assembler); fail0: return 0; } void DatagramPeerIO_Free (DatagramPeerIO *o) { DebugObject_Free(&o->d_obj); // reset mode reset_mode(o); // free sending base SinglePacketBuffer_Free(&o->send_buffer); PacketPassConnector_Free(&o->send_connector); SPProtoEncoder_Free(&o->send_encoder); FragmentProtoDisassembler_Free(&o->send_disassembler); // free receiving SinglePacketBuffer_Free(&o->recv_buffer); PacketRecvConnector_Free(&o->recv_connector); SPProtoDecoder_Free(&o->recv_decoder); PacketPassNotifier_Free(&o->recv_notifier); FragmentProtoAssembler_Free(&o->recv_assembler); } PacketPassInterface * DatagramPeerIO_GetSendInput (DatagramPeerIO *o) { DebugObject_Access(&o->d_obj); return FragmentProtoDisassembler_GetInput(&o->send_disassembler); } int DatagramPeerIO_Connect (DatagramPeerIO *o, BAddr addr) { DebugObject_Access(&o->d_obj); // reset mode reset_mode(o); // check address if (!BDatagram_AddressFamilySupported(addr.type)) { PeerLog(o, BLOG_ERROR, "BDatagram_AddressFamilySupported failed"); goto fail0; } // init dgram if (!BDatagram_Init(&o->dgram, addr.type, o->reactor, o, (BDatagram_handler)dgram_handler)) { PeerLog(o, BLOG_ERROR, "BDatagram_Init failed"); goto fail0; } // set send address BIPAddr local_addr; BIPAddr_InitInvalid(&local_addr); BDatagram_SetSendAddrs(&o->dgram, addr, local_addr); // init I/O init_io(o); // set mode o->mode = DATAGRAMPEERIO_MODE_CONNECT; return 1; fail0: return 0; } int DatagramPeerIO_Bind (DatagramPeerIO *o, BAddr addr) { DebugObject_Access(&o->d_obj); ASSERT(BDatagram_AddressFamilySupported(addr.type)) // reset mode reset_mode(o); // init dgram if (!BDatagram_Init(&o->dgram, addr.type, o->reactor, o, (BDatagram_handler)dgram_handler)) { PeerLog(o, BLOG_ERROR, "BDatagram_Init failed"); goto fail0; } // bind dgram if (!BDatagram_Bind(&o->dgram, addr)) { PeerLog(o, BLOG_INFO, "BDatagram_Bind failed"); goto fail1; } // init I/O init_io(o); // set recv notifier handler PacketPassNotifier_SetHandler(&o->recv_notifier, (PacketPassNotifier_handler_notify)recv_decoder_notifier_handler, o); // set mode o->mode = DATAGRAMPEERIO_MODE_BIND; return 1; fail1: BDatagram_Free(&o->dgram); fail0: return 0; } void DatagramPeerIO_SetEncryptionKey (DatagramPeerIO *o, uint8_t *encryption_key) { ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params)) DebugObject_Access(&o->d_obj); // set sending key SPProtoEncoder_SetEncryptionKey(&o->send_encoder, encryption_key); // set receiving key SPProtoDecoder_SetEncryptionKey(&o->recv_decoder, encryption_key); } void DatagramPeerIO_RemoveEncryptionKey (DatagramPeerIO *o) { ASSERT(SPPROTO_HAVE_ENCRYPTION(o->sp_params)) DebugObject_Access(&o->d_obj); // remove sending key SPProtoEncoder_RemoveEncryptionKey(&o->send_encoder); // remove receiving key SPProtoDecoder_RemoveEncryptionKey(&o->recv_decoder); } void DatagramPeerIO_SetOTPSendSeed (DatagramPeerIO *o, uint16_t seed_id, uint8_t *key, uint8_t *iv) { ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) DebugObject_Access(&o->d_obj); // set sending seed SPProtoEncoder_SetOTPSeed(&o->send_encoder, seed_id, key, iv); } void DatagramPeerIO_RemoveOTPSendSeed (DatagramPeerIO *o) { ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) DebugObject_Access(&o->d_obj); // remove sending seed SPProtoEncoder_RemoveOTPSeed(&o->send_encoder); } void DatagramPeerIO_AddOTPRecvSeed (DatagramPeerIO *o, uint16_t seed_id, uint8_t *key, uint8_t *iv) { ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) DebugObject_Access(&o->d_obj); // add receiving seed SPProtoDecoder_AddOTPSeed(&o->recv_decoder, seed_id, key, iv); } void DatagramPeerIO_RemoveOTPRecvSeeds (DatagramPeerIO *o) { ASSERT(SPPROTO_HAVE_OTP(o->sp_params)) DebugObject_Access(&o->d_obj); // remove receiving seeds SPProtoDecoder_RemoveOTPSeeds(&o->recv_decoder); }