/* * Copyright (C) Ambroz Bizjak * Contributions: * Transparent DNS: Copyright (C) Kerem Hadimli * * 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 #include #include #include #include #include #include static int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2); static int conaddr_comparator (void *unused, struct UdpGwClient_conaddr *v1, struct UdpGwClient_conaddr *v2); static void free_server (UdpGwClient *o); static void decoder_handler_error (UdpGwClient *o); static void recv_interface_handler_send (UdpGwClient *o, uint8_t *data, int data_len); static void send_monitor_handler (UdpGwClient *o); static void keepalive_if_handler_done (UdpGwClient *o); static struct UdpGwClient_connection * find_connection_by_conaddr (UdpGwClient *o, struct UdpGwClient_conaddr conaddr); static struct UdpGwClient_connection * find_connection_by_conid (UdpGwClient *o, uint16_t conid); static uint16_t find_unused_conid (UdpGwClient *o); static void connection_init (UdpGwClient *o, struct UdpGwClient_conaddr conaddr, uint8_t flags, const uint8_t *data, int data_len); static void connection_free (struct UdpGwClient_connection *con); static void connection_first_job_handler (struct UdpGwClient_connection *con); static void connection_send (struct UdpGwClient_connection *con, uint8_t flags, const uint8_t *data, int data_len); static struct UdpGwClient_connection * reuse_connection (UdpGwClient *o, struct UdpGwClient_conaddr conaddr); static int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2) { return B_COMPARE(*v1, *v2); } static int conaddr_comparator (void *unused, struct UdpGwClient_conaddr *v1, struct UdpGwClient_conaddr *v2) { int r = BAddr_CompareOrder(&v1->remote_addr, &v2->remote_addr); if (r) { return r; } return BAddr_CompareOrder(&v1->local_addr, &v2->local_addr); } static void free_server (UdpGwClient *o) { // disconnect send connector PacketPassConnector_DisconnectOutput(&o->send_connector); // free send sender PacketStreamSender_Free(&o->send_sender); // free receive decoder PacketProtoDecoder_Free(&o->recv_decoder); // free receive interface PacketPassInterface_Free(&o->recv_if); } static void decoder_handler_error (UdpGwClient *o) { DebugObject_Access(&o->d_obj); ASSERT(o->have_server) BLog(BLOG_ERROR, "decoder error"); // report error o->handler_servererror(o->user); return; } static void recv_interface_handler_send (UdpGwClient *o, uint8_t *data, int data_len) { DebugObject_Access(&o->d_obj); ASSERT(o->have_server) ASSERT(data_len >= 0) ASSERT(data_len <= o->udpgw_mtu) // accept packet PacketPassInterface_Done(&o->recv_if); // check header if (data_len < sizeof(struct udpgw_header)) { BLog(BLOG_ERROR, "missing header"); return; } struct udpgw_header header; memcpy(&header, data, sizeof(header)); data += sizeof(header); data_len -= sizeof(header); uint8_t flags = ltoh8(header.flags); uint16_t conid = ltoh16(header.conid); // parse address BAddr remote_addr; if ((flags & UDPGW_CLIENT_FLAG_IPV6)) { if (data_len < sizeof(struct udpgw_addr_ipv6)) { BLog(BLOG_ERROR, "missing ipv6 address"); return; } struct udpgw_addr_ipv6 addr_ipv6; memcpy(&addr_ipv6, data, sizeof(addr_ipv6)); data += sizeof(addr_ipv6); data_len -= sizeof(addr_ipv6); BAddr_InitIPv6(&remote_addr, addr_ipv6.addr_ip, addr_ipv6.addr_port); } else { if (data_len < sizeof(struct udpgw_addr_ipv4)) { BLog(BLOG_ERROR, "missing ipv4 address"); return; } struct udpgw_addr_ipv4 addr_ipv4; memcpy(&addr_ipv4, data, sizeof(addr_ipv4)); data += sizeof(addr_ipv4); data_len -= sizeof(addr_ipv4); BAddr_InitIPv4(&remote_addr, addr_ipv4.addr_ip, addr_ipv4.addr_port); } // check remaining data if (data_len > o->udp_mtu) { BLog(BLOG_ERROR, "too much data"); return; } // find connection struct UdpGwClient_connection *con = find_connection_by_conid(o, conid); if (!con) { BLog(BLOG_ERROR, "unknown conid"); return; } // check remote address if (BAddr_CompareOrder(&con->conaddr.remote_addr, &remote_addr) != 0) { BLog(BLOG_ERROR, "wrong remote address"); return; } // move connection to front of the list LinkedList1_Remove(&o->connections_list, &con->connections_list_node); LinkedList1_Append(&o->connections_list, &con->connections_list_node); // pass packet to user o->handler_received(o->user, con->conaddr.local_addr, con->conaddr.remote_addr, data, data_len); return; } static void send_monitor_handler (UdpGwClient *o) { DebugObject_Access(&o->d_obj); if (o->keepalive_sending) { return; } BLog(BLOG_INFO, "keepalive"); // send keepalive PacketPassInterface_Sender_Send(o->keepalive_if, (uint8_t *)&o->keepalive_packet, sizeof(o->keepalive_packet)); // set sending keep-alive o->keepalive_sending = 1; } static void keepalive_if_handler_done (UdpGwClient *o) { DebugObject_Access(&o->d_obj); ASSERT(o->keepalive_sending) // set not sending keepalive o->keepalive_sending = 0; } static struct UdpGwClient_connection * find_connection_by_conaddr (UdpGwClient *o, struct UdpGwClient_conaddr conaddr) { BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree_by_conaddr, &conaddr); if (!tree_node) { return NULL; } return UPPER_OBJECT(tree_node, struct UdpGwClient_connection, connections_tree_by_conaddr_node); } static struct UdpGwClient_connection * find_connection_by_conid (UdpGwClient *o, uint16_t conid) { BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree_by_conid, &conid); if (!tree_node) { return NULL; } return UPPER_OBJECT(tree_node, struct UdpGwClient_connection, connections_tree_by_conid_node); } static uint16_t find_unused_conid (UdpGwClient *o) { ASSERT(o->num_connections < o->max_connections) while (1) { if (!find_connection_by_conid(o, o->next_conid)) { return o->next_conid; } if (o->next_conid == o->max_connections - 1) { o->next_conid = 0; } else { o->next_conid++; } } } static void connection_init (UdpGwClient *o, struct UdpGwClient_conaddr conaddr, uint8_t flags, const uint8_t *data, int data_len) { ASSERT(o->num_connections < o->max_connections) ASSERT(!find_connection_by_conaddr(o, conaddr)) ASSERT(data_len >= 0) ASSERT(data_len <= o->udp_mtu) // allocate structure struct UdpGwClient_connection *con = (struct UdpGwClient_connection *)malloc(sizeof(*con)); if (!con) { BLog(BLOG_ERROR, "malloc failed"); goto fail0; } // init arguments con->client = o; con->conaddr = conaddr; con->first_flags = flags; con->first_data = data; con->first_data_len = data_len; // allocate conid con->conid = find_unused_conid(o); // init first job BPending_Init(&con->first_job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_first_job_handler, con); BPending_Set(&con->first_job); // init queue flow PacketPassFairQueueFlow_Init(&con->send_qflow, &o->send_queue); // init PacketProtoFlow if (!PacketProtoFlow_Init(&con->send_ppflow, o->udpgw_mtu, o->send_buffer_size, PacketPassFairQueueFlow_GetInput(&con->send_qflow), BReactor_PendingGroup(o->reactor))) { BLog(BLOG_ERROR, "PacketProtoFlow_Init failed"); goto fail1; } con->send_if = PacketProtoFlow_GetInput(&con->send_ppflow); // insert to connections tree by conaddr ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node, NULL)) // insert to connections tree by conid ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conid, &con->connections_tree_by_conid_node, NULL)) // insert to connections list LinkedList1_Append(&o->connections_list, &con->connections_list_node); // increment number of connections o->num_connections++; return; fail1: PacketPassFairQueueFlow_Free(&con->send_qflow); BPending_Free(&con->first_job); free(con); fail0: return; } static void connection_free (struct UdpGwClient_connection *con) { UdpGwClient *o = con->client; PacketPassFairQueueFlow_AssertFree(&con->send_qflow); // decrement number of connections o->num_connections--; // remove from connections list LinkedList1_Remove(&o->connections_list, &con->connections_list_node); // remove from connections tree by conid BAVL_Remove(&o->connections_tree_by_conid, &con->connections_tree_by_conid_node); // remove from connections tree by conaddr BAVL_Remove(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node); // free PacketProtoFlow PacketProtoFlow_Free(&con->send_ppflow); // free queue flow PacketPassFairQueueFlow_Free(&con->send_qflow); // free first job BPending_Free(&con->first_job); // free structure free(con); } static void connection_first_job_handler (struct UdpGwClient_connection *con) { connection_send(con, UDPGW_CLIENT_FLAG_REBIND|con->first_flags, con->first_data, con->first_data_len); } static void connection_send (struct UdpGwClient_connection *con, uint8_t flags, const uint8_t *data, int data_len) { UdpGwClient *o = con->client; B_USE(o) ASSERT(data_len >= 0) ASSERT(data_len <= o->udp_mtu) // get buffer location uint8_t *out; if (!BufferWriter_StartPacket(con->send_if, &out)) { BLog(BLOG_ERROR, "out of buffer"); return; } int out_pos = 0; if (con->conaddr.remote_addr.type == BADDR_TYPE_IPV6) { flags |= UDPGW_CLIENT_FLAG_IPV6; } // write header struct udpgw_header header; header.flags = ltoh8(flags); header.conid = ltoh16(con->conid); memcpy(out + out_pos, &header, sizeof(header)); out_pos += sizeof(header); // write address switch (con->conaddr.remote_addr.type) { case BADDR_TYPE_IPV4: { struct udpgw_addr_ipv4 addr_ipv4; addr_ipv4.addr_ip = con->conaddr.remote_addr.ipv4.ip; addr_ipv4.addr_port = con->conaddr.remote_addr.ipv4.port; memcpy(out + out_pos, &addr_ipv4, sizeof(addr_ipv4)); out_pos += sizeof(addr_ipv4); } break; case BADDR_TYPE_IPV6: { struct udpgw_addr_ipv6 addr_ipv6; memcpy(addr_ipv6.addr_ip, con->conaddr.remote_addr.ipv6.ip, sizeof(addr_ipv6.addr_ip)); addr_ipv6.addr_port = con->conaddr.remote_addr.ipv6.port; memcpy(out + out_pos, &addr_ipv6, sizeof(addr_ipv6)); out_pos += sizeof(addr_ipv6); } break; } // write packet to buffer memcpy(out + out_pos, data, data_len); out_pos += data_len; // submit packet to buffer BufferWriter_EndPacket(con->send_if, out_pos); } static struct UdpGwClient_connection * reuse_connection (UdpGwClient *o, struct UdpGwClient_conaddr conaddr) { ASSERT(!find_connection_by_conaddr(o, conaddr)) ASSERT(o->num_connections > 0) // get least recently used connection struct UdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), struct UdpGwClient_connection, connections_list_node); // remove from connections tree by conaddr BAVL_Remove(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node); // set new conaddr con->conaddr = conaddr; // insert to connections tree by conaddr ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node, NULL)) return con; } int UdpGwClient_Init (UdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, BReactor *reactor, void *user, UdpGwClient_handler_servererror handler_servererror, UdpGwClient_handler_received handler_received) { ASSERT(udp_mtu >= 0) ASSERT(udpgw_compute_mtu(udp_mtu) >= 0) ASSERT(udpgw_compute_mtu(udp_mtu) <= PACKETPROTO_MAXPAYLOAD) ASSERT(max_connections > 0) ASSERT(send_buffer_size > 0) // init arguments o->udp_mtu = udp_mtu; o->max_connections = max_connections; o->send_buffer_size = send_buffer_size; o->keepalive_time = keepalive_time; o->reactor = reactor; o->user = user; o->handler_servererror = handler_servererror; o->handler_received = handler_received; // limit max connections to number of conid's if (o->max_connections > UINT16_MAX + 1) { o->max_connections = UINT16_MAX + 1; } // compute MTUs o->udpgw_mtu = udpgw_compute_mtu(o->udp_mtu); o->pp_mtu = o->udpgw_mtu + sizeof(struct packetproto_header); // init connections tree by conaddr BAVL_Init(&o->connections_tree_by_conaddr, OFFSET_DIFF(struct UdpGwClient_connection, conaddr, connections_tree_by_conaddr_node), (BAVL_comparator)conaddr_comparator, NULL); // init connections tree by conid BAVL_Init(&o->connections_tree_by_conid, OFFSET_DIFF(struct UdpGwClient_connection, conid, connections_tree_by_conid_node), (BAVL_comparator)uint16_comparator, NULL); // init connections list LinkedList1_Init(&o->connections_list); // set zero connections o->num_connections = 0; // set next conid o->next_conid = 0; // init send connector PacketPassConnector_Init(&o->send_connector, o->pp_mtu, BReactor_PendingGroup(o->reactor)); // init send monitor PacketPassInactivityMonitor_Init(&o->send_monitor, PacketPassConnector_GetInput(&o->send_connector), o->reactor, o->keepalive_time, (PacketPassInactivityMonitor_handler)send_monitor_handler, o); // init send queue if (!PacketPassFairQueue_Init(&o->send_queue, PacketPassInactivityMonitor_GetInput(&o->send_monitor), BReactor_PendingGroup(o->reactor), 0, 1)) { goto fail0; } // construct keepalive packet o->keepalive_packet.pp.len = sizeof(o->keepalive_packet.udpgw); memset(&o->keepalive_packet.udpgw, 0, sizeof(o->keepalive_packet.udpgw)); o->keepalive_packet.udpgw.flags = UDPGW_CLIENT_FLAG_KEEPALIVE; // init keepalive queue flow PacketPassFairQueueFlow_Init(&o->keepalive_qflow, &o->send_queue); o->keepalive_if = PacketPassFairQueueFlow_GetInput(&o->keepalive_qflow); // init keepalive output PacketPassInterface_Sender_Init(o->keepalive_if, (PacketPassInterface_handler_done)keepalive_if_handler_done, o); // set not sending keepalive o->keepalive_sending = 0; // set have no server o->have_server = 0; DebugObject_Init(&o->d_obj); return 1; fail0: PacketPassInactivityMonitor_Free(&o->send_monitor); PacketPassConnector_Free(&o->send_connector); return 0; } void UdpGwClient_Free (UdpGwClient *o) { DebugObject_Free(&o->d_obj); // allow freeing send queue flows PacketPassFairQueue_PrepareFree(&o->send_queue); // free connections while (!LinkedList1_IsEmpty(&o->connections_list)) { struct UdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), struct UdpGwClient_connection, connections_list_node); connection_free(con); } // free server if (o->have_server) { free_server(o); } // free keepalive queue flow PacketPassFairQueueFlow_Free(&o->keepalive_qflow); // free send queue PacketPassFairQueue_Free(&o->send_queue); // free send PacketPassInactivityMonitor_Free(&o->send_monitor); // free send connector PacketPassConnector_Free(&o->send_connector); } void UdpGwClient_SubmitPacket (UdpGwClient *o, BAddr local_addr, BAddr remote_addr, int is_dns, const uint8_t *data, int data_len) { DebugObject_Access(&o->d_obj); ASSERT(local_addr.type == BADDR_TYPE_IPV4 || local_addr.type == BADDR_TYPE_IPV6) ASSERT(remote_addr.type == BADDR_TYPE_IPV4 || remote_addr.type == BADDR_TYPE_IPV6) ASSERT(data_len >= 0) ASSERT(data_len <= o->udp_mtu) // build conaddr struct UdpGwClient_conaddr conaddr; conaddr.local_addr = local_addr; conaddr.remote_addr = remote_addr; // lookup connection struct UdpGwClient_connection *con = find_connection_by_conaddr(o, conaddr); uint8_t flags = 0; if (is_dns) { // route to remote DNS server instead of provided address flags |= UDPGW_CLIENT_FLAG_DNS; } // if no connection and can't create a new one, reuse the least recently used une if (!con && o->num_connections == o->max_connections) { con = reuse_connection(o, conaddr); flags |= UDPGW_CLIENT_FLAG_REBIND; } if (!con) { // create new connection connection_init(o, conaddr, flags, data, data_len); } else { // move connection to front of the list LinkedList1_Remove(&o->connections_list, &con->connections_list_node); LinkedList1_Append(&o->connections_list, &con->connections_list_node); // send packet to existing connection connection_send(con, flags, data, data_len); } } int UdpGwClient_ConnectServer (UdpGwClient *o, StreamPassInterface *send_if, StreamRecvInterface *recv_if) { DebugObject_Access(&o->d_obj); ASSERT(!o->have_server) // init receive interface PacketPassInterface_Init(&o->recv_if, o->udpgw_mtu, (PacketPassInterface_handler_send)recv_interface_handler_send, o, BReactor_PendingGroup(o->reactor)); // init receive decoder if (!PacketProtoDecoder_Init(&o->recv_decoder, recv_if, &o->recv_if, BReactor_PendingGroup(o->reactor), o, (PacketProtoDecoder_handler_error)decoder_handler_error)) { BLog(BLOG_ERROR, "PacketProtoDecoder_Init failed"); goto fail1; } // init send sender PacketStreamSender_Init(&o->send_sender, send_if, o->pp_mtu, BReactor_PendingGroup(o->reactor)); // connect send connector PacketPassConnector_ConnectOutput(&o->send_connector, PacketStreamSender_GetInput(&o->send_sender)); // set have server o->have_server = 1; return 1; fail1: PacketPassInterface_Free(&o->recv_if); return 0; } void UdpGwClient_DisconnectServer (UdpGwClient *o) { DebugObject_Access(&o->d_obj); ASSERT(o->have_server) // free server free_server(o); // set have no server o->have_server = 0; }