X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fvpn%2Fgnunet-daemon-vpn.c;h=cc8001eae412119f28ab7389e5e26f443e17b2b4;hb=83b19539f4d322b43683f5838b72e9ec2c8e6073;hp=1334800abd2ef46c3572fead6c72d03d4ddf0072;hpb=7f26577b88816cd33b6b12114c47ec36cada6bc2;p=oweals%2Fgnunet.git diff --git a/src/vpn/gnunet-daemon-vpn.c b/src/vpn/gnunet-daemon-vpn.c index 1334800ab..cc8001eae 100644 --- a/src/vpn/gnunet-daemon-vpn.c +++ b/src/vpn/gnunet-daemon-vpn.c @@ -20,674 +20,1220 @@ /** * @file vpn/gnunet-daemon-vpn.c - * @brief + * @brief * @author Philipp Toelke */ #include "platform.h" #include "gnunet_getopt_lib.h" #include "gnunet_program_lib.h" -#include "gnunet_os_lib.h" -#include "gnunet-vpn-helper-p.h" #include "gnunet-vpn-packet.h" -#include "gnunet-vpn-pretty-print.h" #include "gnunet_common.h" #include "gnunet_protocols.h" -#include "gnunet_server_lib.h" -#include "gnunet-service-dns-p.h" +#include "gnunet_applications.h" +#include #include "gnunet_client_lib.h" #include "gnunet_container_lib.h" -#include "block_dns.h" +#include "gnunet_constants.h" +#include +#include "gnunet-daemon-vpn-helper.h" +#include "gnunet-daemon-vpn-dns.h" +#include "gnunet-daemon-vpn.h" +#include "gnunet-vpn-checksum.h" + +const struct GNUNET_CONFIGURATION_Handle *cfg; +struct GNUNET_MESH_Handle *mesh_handle; +struct GNUNET_CONTAINER_MultiHashMap *hashmap; +static struct GNUNET_CONTAINER_Heap *heap; + +struct tunnel_notify_queue +{ + struct tunnel_notify_queue *next; + struct tunnel_notify_queue *prev; + size_t len; + void *cls; +}; /** - * Final status code. + * If there are at least this many address-mappings, old ones will be removed */ -static int ret; +static long long unsigned int max_mappings = 200; /** - * The configuration to use + * Final status code. */ -static const struct GNUNET_CONFIGURATION_Handle *cfg; +static int ret; /** - * PipeHandle to receive data from the helper + * This hashmap contains the mapping from peer, service-descriptor, + * source-port and destination-port to a socket */ -static struct GNUNET_DISK_PipeHandle* helper_in; +static struct GNUNET_CONTAINER_MultiHashMap *udp_connections; -/** - * PipeHandle to send data to the helper - */ -static struct GNUNET_DISK_PipeHandle* helper_out; +GNUNET_SCHEDULER_TaskIdentifier conn_task; -/** - * FileHandle to receive data from the helper - */ -static const struct GNUNET_DISK_FileHandle* fh_from_helper; +GNUNET_SCHEDULER_TaskIdentifier shs_task; /** - * FileHandle to send data to the helper + * Function scheduled as very last function, cleans up after us + *{{{ */ -static const struct GNUNET_DISK_FileHandle* fh_to_helper; +static void +cleanup (void *cls + __attribute__ ((unused)), + const struct GNUNET_SCHEDULER_TaskContext *tskctx) +{ + GNUNET_assert (0 != (tskctx->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)); + + /* stop the helper */ + cleanup_helper (helper_handle); + + /* close the connection to the service-dns */ + if (dns_connection != NULL) + { + GNUNET_CLIENT_disconnect (dns_connection, GNUNET_NO); + dns_connection = NULL; + } + + if (mesh_handle != NULL) + { + GNUNET_MESH_disconnect (mesh_handle); + mesh_handle = NULL; + } + if (GNUNET_SCHEDULER_NO_TASK != shs_task) + { + GNUNET_SCHEDULER_cancel (shs_task); + shs_task = GNUNET_SCHEDULER_NO_TASK; + } + if (GNUNET_SCHEDULER_NO_TASK != conn_task) + { + GNUNET_SCHEDULER_cancel (conn_task); + conn_task = GNUNET_SCHEDULER_NO_TASK; + } +} -/** - * The Message-Tokenizer that tokenizes the messages comming from the helper - */ -static struct GNUNET_SERVER_MessageStreamTokenizer* mst; +/*}}}*/ /** - * The connection to the service-dns + * @return the hash of the IP-Address if a mapping exists, NULL otherwise */ -static struct GNUNET_CLIENT_Connection *dns_connection; +GNUNET_HashCode * +address6_mapping_exists (unsigned char addr[]) +{ + GNUNET_HashCode *key = GNUNET_malloc (sizeof (GNUNET_HashCode)); + unsigned char *k = (unsigned char *) key; + + memset (key, 0, sizeof (GNUNET_HashCode)); + unsigned int i; + + for (i = 0; i < 16; i++) + k[15 - i] = addr[i]; + + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (hashmap, key)) + return key; + else + { + GNUNET_free (key); + return NULL; + } +} /** - * A flag to show that the service-dns has to rehijack the outbound dns-packets - * - * This gets set when the helper restarts as the routing-tables are flushed when - * the interface vanishes. + * @return the hash of the IP-Address if a mapping exists, NULL otherwise */ -static unsigned char restart_hijack; +GNUNET_HashCode * +address4_mapping_exists (uint32_t addr) +{ + GNUNET_HashCode *key = GNUNET_malloc (sizeof (GNUNET_HashCode)); + + memset (key, 0, sizeof (GNUNET_HashCode)); + unsigned char *c = (unsigned char *) &addr; + unsigned char *k = (unsigned char *) key; + unsigned int i; + + for (i = 0; i < 4; i++) + k[3 - i] = c[i]; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "a4_m_e: getting with key %08x, addr is %08x, %d.%d.%d.%d\n", + *((uint32_t *) (key)), addr, c[0], c[1], c[2], c[3]); + + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (hashmap, key)) + return key; + else + { + GNUNET_free (key); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Mapping not found!\n"); + return NULL; + } +} -/** - * The process id of the helper - */ -static struct GNUNET_OS_Process *helper_proc; +static void +collect_mappings (void *cls + __attribute__ ((unused)), + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) + return; -/** - * a list of outgoing dns-query-packets - */ -static struct query_packet_list *head; + struct map_entry *me = GNUNET_CONTAINER_heap_remove_root (heap); -/** - * The last element of the list of outgoing dns-query-packets - */ -static struct query_packet_list *tail; + /* This is free()ed memory! */ + me->heap_node = NULL; -/** - * A list of processed dns-responses. - * - * "processed" means that the packet is complete and can be sent out via udp - * directly - */ -static struct answer_packet_list *answer_proc_head; + /* FIXME! GNUNET_MESH_close_tunnel(me->tunnel); */ -/** - * The last element of the list of processed dns-responses. - */ -static struct answer_packet_list *answer_proc_tail; + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (hashmap, &me->hash, me)); -/** - * The hashmap containing the mappings from ipv6-addresses to gnunet-descriptors - */ -static struct GNUNET_CONTAINER_MultiHashMap* hashmap; + GNUNET_free (me); +} -struct map_entry { - struct GNUNET_vpn_service_descriptor desc; - uint16_t namelen; - /** - * In DNS-Format! - */ - char name[1]; -}; +void +send_icmp4_response (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) + return; -static void helper_read(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tsdkctx); -static void dns_answer_handler(void* cls, const struct GNUNET_MessageHeader *msg); + struct ip_icmp *request = cls; -/** - * Callback called by notify_transmit_ready; sends dns-queries or rehijack-messages - * to the service-dns - * {{{ - */ -static size_t -send_query(void* cls, size_t size, void* buf) { - size_t len; - /* - * Send the rehijack-message - */ - if (restart_hijack == 1) - { - restart_hijack = 0; - /* - * The message is just a header - */ - GNUNET_assert(sizeof(struct GNUNET_MessageHeader) <= size); - struct GNUNET_MessageHeader* hdr = buf; - len = sizeof(struct GNUNET_MessageHeader); - hdr->size = htons(len); - hdr->type = htons(GNUNET_MESSAGE_TYPE_REHIJACK); - } - else - { - struct query_packet_list* query = head; - len = ntohs(query->pkt.hdr.size); + struct ip_icmp *response = alloca (ntohs (request->shdr.size)); - GNUNET_assert(len <= size); + GNUNET_assert (response != NULL); + memset (response, 0, ntohs (request->shdr.size)); - memcpy(buf, &query->pkt.hdr, len); + response->shdr.size = request->shdr.size; + response->shdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER); - GNUNET_CONTAINER_DLL_remove (head, tail, query); + response->tun.flags = 0; + response->tun.type = htons (0x0800); - GNUNET_free(query); - } + response->ip_hdr.hdr_lngth = 5; + response->ip_hdr.version = 4; + response->ip_hdr.proto = 0x01; + response->ip_hdr.dadr = request->ip_hdr.sadr; + response->ip_hdr.sadr = request->ip_hdr.dadr; + response->ip_hdr.tot_lngth = request->ip_hdr.tot_lngth; - /* - * Check whether more data is to be sent - */ - if (head != NULL) - { - GNUNET_CLIENT_notify_transmit_ready(dns_connection, ntohs(head->pkt.hdr.size), GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, &send_query, NULL); - } - else if (restart_hijack == 1) - { - GNUNET_CLIENT_notify_transmit_ready(dns_connection, sizeof(struct GNUNET_MessageHeader), GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, &send_query, NULL); - } + response->ip_hdr.chks = + calculate_ip_checksum ((uint16_t *) & response->ip_hdr, 20); + + response->icmp_hdr.code = 0; + response->icmp_hdr.type = 0x0; + + /* Magic, more Magic! */ + response->icmp_hdr.chks = request->icmp_hdr.chks + 0x8; + + /* Copy the rest of the packet */ + memcpy (response + 1, request + 1, + ntohs (request->shdr.size) - sizeof (struct ip_icmp)); + + write_to_helper (response, ntohs (response->shdr.size)); + + GNUNET_free (request); +} + +void +send_icmp6_response (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) + return; - return len; + struct ip6_icmp *request = cls; + + struct ip6_icmp *response = alloca (ntohs (request->shdr.size)); + + GNUNET_assert (response != NULL); + memset (response, 0, ntohs (request->shdr.size)); + + response->shdr.size = request->shdr.size; + response->shdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER); + + response->tun.flags = 0; + response->tun.type = htons (0x86dd); + + response->ip6_hdr.hoplmt = 255; + response->ip6_hdr.paylgth = request->ip6_hdr.paylgth; + response->ip6_hdr.nxthdr = 0x3a; + response->ip6_hdr.version = 6; + memcpy (&response->ip6_hdr.sadr, &request->ip6_hdr.dadr, 16); + memcpy (&response->ip6_hdr.dadr, &request->ip6_hdr.sadr, 16); + + response->icmp_hdr.code = 0; + response->icmp_hdr.type = 0x81; + + /* Magic, more Magic! */ + response->icmp_hdr.chks = request->icmp_hdr.chks - 0x1; + + /* Copy the rest of the packet */ + memcpy (response + 1, request + 1, + ntohs (request->shdr.size) - sizeof (struct ip6_icmp)); + + write_to_helper (response, ntohs (response->shdr.size)); + + GNUNET_free (request); } -/* }}} */ /** - * Function scheduled as very last function, cleans up after us - *{{{ + * cls is the pointer to a GNUNET_MessageHeader that is + * followed by the service-descriptor and the packet that should be sent; */ -static void -cleanup(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tskctx) { - GNUNET_assert (0 != (tskctx->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)); +static size_t +send_pkt_to_peer_notify_callback (void *cls, size_t size, void *buf) +{ + struct GNUNET_MESH_Tunnel **tunnel = cls; + + struct tunnel_state *ts = GNUNET_MESH_tunnel_get_data (*tunnel); + + ts->th = NULL; + + if (NULL != buf) + { + struct GNUNET_MessageHeader *hdr = + (struct GNUNET_MessageHeader *) (tunnel + 1); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "send_pkt_to_peer_notify_callback: buf = %x; size = %u;\n", buf, + size); + GNUNET_assert (size >= ntohs (hdr->size)); + memcpy (buf, hdr, ntohs (hdr->size)); + size = ntohs (hdr->size); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sent!\n"); + } + else + size = 0; + + if (NULL != ts->head) + { + struct tunnel_notify_queue *element = ts->head; + + GNUNET_CONTAINER_DLL_remove (ts->head, ts->tail, element); + + ts->th = + GNUNET_MESH_notify_transmit_ready (*tunnel, GNUNET_NO, 42, + GNUNET_TIME_relative_divide + (GNUNET_CONSTANTS_MAX_CORK_DELAY, 2), + (const struct GNUNET_PeerIdentity *) + NULL, element->len, + send_pkt_to_peer_notify_callback, + element->cls); + + /* save the handle */ + GNUNET_free (element); + } + GNUNET_free (cls); + + return size; +} - /* stop the helper */ - if (helper_proc != NULL) - { - GNUNET_OS_process_kill (helper_proc, SIGTERM); - GNUNET_OS_process_wait (helper_proc); - GNUNET_OS_process_close (helper_proc); - helper_proc = NULL; - } +unsigned int +port_in_ports (uint64_t ports, uint16_t port) +{ + uint16_t *ps = (uint16_t *) & ports; - /* close the connection to the service-dns */ - if (dns_connection != NULL) - { - GNUNET_CLIENT_disconnect (dns_connection, GNUNET_NO); - dns_connection = NULL; - } + return ports == 0 || ps[0] == port || ps[1] == port || ps[2] == port || + ps[3] == port; +} + +void +send_pkt_to_peer (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi + __attribute__ ((unused))) +{ + /* peer == NULL means that all peers in this request are connected */ + if (peer == NULL) + return; + struct GNUNET_MESH_Tunnel **tunnel = cls; + struct GNUNET_MessageHeader *hdr = + (struct GNUNET_MessageHeader *) (tunnel + 1); + + GNUNET_assert (NULL != tunnel); + GNUNET_assert (NULL != *tunnel); + + struct tunnel_state *ts = GNUNET_MESH_tunnel_get_data (*tunnel); + + if (NULL == ts->th) + { + ts->th = + GNUNET_MESH_notify_transmit_ready (*tunnel, GNUNET_NO, 42, + GNUNET_TIME_relative_divide + (GNUNET_CONSTANTS_MAX_CORK_DELAY, 2), + (const struct GNUNET_PeerIdentity *) + NULL, ntohs (hdr->size), + send_pkt_to_peer_notify_callback, + cls); + } + else + { + struct tunnel_notify_queue *element = GNUNET_malloc (sizeof *element); + + element->cls = cls; + element->len = ntohs (hdr->size); + + GNUNET_CONTAINER_DLL_insert_tail (ts->head, ts->tail, element); + } } -/*}}}*/ /** - * Start the helper-process - * {{{ + * Create a new Address from an answer-packet */ -static void -start_helper_and_schedule(void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) { - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - return; +void +new_ip6addr (unsigned char *buf, const GNUNET_HashCode * peer, + const GNUNET_HashCode * service_desc) +{ /* {{{ */ + char *ipv6addr; + unsigned long long ipv6prefix; - helper_in = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO); - helper_out = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_NO, GNUNET_YES); + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, "vpn", "IPV6ADDR", + &ipv6addr)); + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_number (cfg, "vpn", + "IPV6PREFIX", + &ipv6prefix)); + GNUNET_assert (ipv6prefix < 127); + ipv6prefix = (ipv6prefix + 7) / 8; - if (helper_in == NULL || helper_out == NULL) return; + inet_pton (AF_INET6, ipv6addr, buf); + GNUNET_free (ipv6addr); - helper_proc = GNUNET_OS_start_process(helper_in, helper_out, "gnunet-helper-vpn", "gnunet-helper-vpn", NULL); + int peer_length = 16 - ipv6prefix - 6; - fh_from_helper = GNUNET_DISK_pipe_handle (helper_out, GNUNET_DISK_PIPE_END_READ); - fh_to_helper = GNUNET_DISK_pipe_handle (helper_in, GNUNET_DISK_PIPE_END_WRITE); + if (peer_length <= 0) + peer_length = 0; - GNUNET_DISK_pipe_close_end(helper_out, GNUNET_DISK_PIPE_END_WRITE); - GNUNET_DISK_pipe_close_end(helper_in, GNUNET_DISK_PIPE_END_READ); + int service_length = 16 - ipv6prefix - peer_length; - /* Tell the dns-service to rehijack the dns-port - * The routing-table gets flushed if an interface disappears. - */ - restart_hijack = 1; - GNUNET_CLIENT_notify_transmit_ready(dns_connection, sizeof(struct GNUNET_MessageHeader), GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, &send_query, NULL); + if (service_length <= 0) + service_length = 0; - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, fh_from_helper, &helper_read, NULL); + memcpy (buf + ipv6prefix, service_desc, service_length); + memcpy (buf + ipv6prefix + service_length, peer, peer_length); } + /*}}}*/ + /** - * Restart the helper-process - * {{{ + * Create a new Address from an answer-packet */ -static void -restart_helper(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tskctx) { - // Kill the helper - GNUNET_OS_process_kill (helper_proc, SIGKILL); - GNUNET_OS_process_wait (helper_proc); - GNUNET_OS_process_close (helper_proc); - helper_proc = NULL; - - GNUNET_DISK_pipe_close(helper_in); - GNUNET_DISK_pipe_close(helper_out); - - /* Restart the helper */ - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, start_helper_and_schedule, NULL); +void +new_ip6addr_remote (unsigned char *buf, unsigned char *addr, char addrlen) +{ /* {{{ */ + char *ipv6addr; + unsigned long long ipv6prefix; + + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, "vpn", "IPV6ADDR", + &ipv6addr)); + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_number (cfg, "vpn", + "IPV6PREFIX", + &ipv6prefix)); + GNUNET_assert (ipv6prefix < 127); + ipv6prefix = (ipv6prefix + 7) / 8; + + inet_pton (AF_INET6, ipv6addr, buf); + GNUNET_free (ipv6addr); + + int local_length = 16 - ipv6prefix; + + memcpy (buf + ipv6prefix, addr, GNUNET_MIN (addrlen, local_length)); } + /*}}}*/ /** - * Read from the helper-process - * {{{ + * Create a new Address from an answer-packet */ -static void -helper_read(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tsdkctx) { - /* no message can be bigger then 64k */ - char buf[65535]; - - if (tsdkctx->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) - return; +void +new_ip4addr_remote (unsigned char *buf, unsigned char *addr, char addrlen) +{ /* {{{ */ + char *ipv4addr; + char *ipv4mask; + + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, "vpn", "IPV4ADDR", + &ipv4addr)); + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, "vpn", "IPV4MASK", + &ipv4mask)); + uint32_t mask; + + inet_pton (AF_INET, ipv4addr, buf); + int r = inet_pton (AF_INET, ipv4mask, &mask); + + mask = htonl (mask); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "inet_pton: %d; %m; mask: %08x\n", r, + mask); + + GNUNET_free (ipv4addr); + + int c; + + if (mask) + { + mask = (mask ^ (mask - 1)) >> 1; + for (c = 0; mask; c++) + { + mask >>= 1; + } + } + else + { + c = CHAR_BIT * sizeof (mask); + } - int t = GNUNET_DISK_file_read(fh_from_helper, &buf, 65535); + c = 32 - c; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "The mask %s has %d leading 1s.\n", + ipv4mask, c); - /* On read-error, restart the helper */ - if (t<=0) { - GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Read error for header from vpn-helper: %m\n"); - GNUNET_SCHEDULER_add_now(restart_helper, cls); - return; - } + GNUNET_free (ipv4mask); - /* FIXME */ GNUNET_SERVER_mst_receive(mst, NULL, buf, t, 0, 0); + if (c % 8 == 0) + c = c / 8; + else + GNUNET_assert (0); - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, fh_from_helper, &helper_read, NULL); + memcpy (buf + c, addr, GNUNET_MIN (addrlen, 4 - c)); } + /*}}}*/ /** - * Calculate the checksum of an IPv4-Header + * This gets scheduled with cls pointing to an answer_packet and does everything + * needed in order to send it to the helper. + * + * At the moment this means "inventing" and IPv6-Address for .gnunet-services and + * doing nothing for "real" services. */ -static uint16_t -calculate_ip_checksum(uint16_t* hdr, short len) { - uint32_t sum = 0; - for(; len >= 2; len -= 2) - sum += *(hdr++); - if (len == 1) - sum += *((unsigned char*)hdr); +void +process_answer (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) + return; - sum = (sum >> 16) + (sum & 0xFFFF); + struct answer_packet *pkt = cls; + struct answer_packet_list *list; - return ~sum; -} + /* This answer is about a .gnunet-service + * + * It contains an almost complete DNS-Response, we have to fill in the ip + * at the offset pkt->addroffset + */ + if (pkt->subtype == GNUNET_DNS_ANSWER_TYPE_SERVICE) + { + pkt->subtype = GNUNET_DNS_ANSWER_TYPE_IP; -/** - * Send an dns-answer-packet to the helper - */ -static void -helper_write(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tsdkctx) { - if (tsdkctx->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) + GNUNET_HashCode key; + + memset (&key, 0, sizeof (GNUNET_HashCode)); + + unsigned char *c = ((unsigned char *) pkt) + ntohs (pkt->addroffset); + unsigned char *k = (unsigned char *) &key; + + new_ip6addr (c, &pkt->service_descr.peer, + &pkt->service_descr.service_descriptor); + /* + * Copy the newly generated ip-address to the key backwarts (as only the first part is hashed) + */ + unsigned int i; + + for (i = 0; i < 16; i++) + k[15 - i] = c[i]; + + uint16_t namelen = strlen ((char *) pkt->data + 12) + 1; + + struct map_entry *value = + GNUNET_malloc (sizeof (struct map_entry) + namelen); + char *name = (char *) (value + 1); + + value->namelen = namelen; + memcpy (name, pkt->data + 12, namelen); + + memcpy (&value->desc, &pkt->service_descr, + sizeof (struct GNUNET_vpn_service_descriptor)); + + memset (value->additional_ports, 0, 8192); + + memcpy (&value->hash, &key, sizeof (GNUNET_HashCode)); + + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (hashmap, &key)) + { + GNUNET_CONTAINER_multihashmap_put (hashmap, &key, value, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + + value->heap_node = + GNUNET_CONTAINER_heap_insert (heap, value, + GNUNET_TIME_absolute_get ().abs_value); + if (GNUNET_CONTAINER_heap_get_size (heap) > max_mappings) + GNUNET_SCHEDULER_add_now (collect_mappings, NULL); + } + else + GNUNET_free (value); + + + list = + GNUNET_malloc (htons (pkt->hdr.size) + + sizeof (struct answer_packet_list) - + sizeof (struct answer_packet)); + + memcpy (&list->pkt, pkt, htons (pkt->hdr.size)); + + } + else if (pkt->subtype == GNUNET_DNS_ANSWER_TYPE_REV) + { + GNUNET_HashCode key; + + memset (&key, 0, sizeof key); + unsigned char *k = (unsigned char *) &key; + unsigned char *s = pkt->data + 12; + int i = 0; + + /* Whoever designed the reverse IPv6-lookup is batshit insane */ + for (i = 0; i < 16; i++) + { + unsigned char c1 = s[(4 * i) + 1]; + unsigned char c2 = s[(4 * i) + 3]; + + if (c1 <= '9') + k[i] = c1 - '0'; + else + k[i] = c1 - 87; /* 87 is the difference between 'a' and 10 */ + if (c2 <= '9') + k[i] += 16 * (c2 - '0'); + else + k[i] += 16 * (c2 - 87); + } + + struct map_entry *map_entry = + GNUNET_CONTAINER_multihashmap_get (hashmap, &key); + uint16_t offset = ntohs (pkt->addroffset); + + if (map_entry == NULL) + { + GNUNET_free (pkt); return; + } - struct answer_packet_list* ans = answer_proc_head; - size_t len = ntohs(ans->pkt.hdr.size); - - GNUNET_assert(ans->pkt.subtype == GNUNET_DNS_ANSWER_TYPE_IP); - - size_t data_len = len - sizeof(struct answer_packet) + 1; - size_t net_len = sizeof(struct ip_hdr) + sizeof(struct udp_dns) + data_len; - size_t pkt_len = sizeof(struct GNUNET_MessageHeader) + sizeof(struct pkt_tun) + net_len; - - struct ip_udp_dns* pkt = alloca(pkt_len); - memset(pkt, 0, pkt_len); - - /* set the gnunet-header */ - pkt->shdr.size = htons(pkt_len); - pkt->shdr.type = htons(GNUNET_MESSAGE_TYPE_VPN_HELPER); - - /* set the tun-header (no flags and ethertype of IPv4) */ - pkt->tun.flags = 0; - pkt->tun.type = htons(0x0800); - - /* set the ip-header */ - pkt->ip_hdr.version = 4; - pkt->ip_hdr.hdr_lngth = 5; - pkt->ip_hdr.diff_serv = 0; - pkt->ip_hdr.tot_lngth = htons(net_len); - pkt->ip_hdr.ident = 0; - pkt->ip_hdr.flags = 0; - pkt->ip_hdr.frag_off = 0; - pkt->ip_hdr.ttl = 255; - pkt->ip_hdr.proto = 0x11; /* UDP */ - pkt->ip_hdr.chks = 0; /* Will be calculated later*/ - pkt->ip_hdr.sadr = ans->pkt.from; - pkt->ip_hdr.dadr = ans->pkt.to; - - pkt->ip_hdr.chks = calculate_ip_checksum((uint16_t*)&pkt->ip_hdr, 5*4); - - /* set the udp-header */ - pkt->udp_dns.udp_hdr.spt = htons(53); - pkt->udp_dns.udp_hdr.dpt = ans->pkt.dst_port; - pkt->udp_dns.udp_hdr.len = htons(net_len - sizeof(struct ip_hdr)); - pkt->udp_dns.udp_hdr.crc = 0; /* Optional for IPv4 */ - - memcpy(&pkt->udp_dns.data, ans->pkt.data, data_len); - - GNUNET_CONTAINER_DLL_remove (answer_proc_head, answer_proc_tail, ans); - GNUNET_free(ans); - - /* FIXME */ GNUNET_DISK_file_write(fh_to_helper, pkt, pkt_len); - - /* if more packets are available, reschedule */ - if (answer_proc_head != NULL) - GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL, - fh_to_helper, - &helper_write, - NULL); -} + GNUNET_CONTAINER_heap_update_cost (heap, map_entry->heap_node, + GNUNET_TIME_absolute_get ().abs_value); -/** - * @return GNUNET_YES if a mapping exists - */ -static int -address_mapping_exists(unsigned char addr[]) { - GNUNET_HashCode* key = alloca(sizeof(GNUNET_HashCode)); - memset(key, 0, sizeof(GNUNET_HashCode)); - memcpy(key, addr, 16); - return GNUNET_CONTAINER_multihashmap_contains(hashmap, key); -} + unsigned short namelen = htons (map_entry->namelen); + char *name = (char *) (map_entry + 1); -static void -send_icmp_response(void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { - struct ip6_icmp* request = cls; + list = + GNUNET_malloc (sizeof (struct answer_packet_list) - + sizeof (struct answer_packet) + offset + 2 + + ntohs (namelen)); - struct ip6_icmp* response = alloca(ntohs(request->shdr.size)); - memset(response, 0, ntohs(request->shdr.size)); + struct answer_packet *rpkt = &list->pkt; - response->shdr.size = request->shdr.size; - response->shdr.type = htons(GNUNET_MESSAGE_TYPE_VPN_HELPER); + /* The offset points to the first byte belonging to the address */ + memcpy (rpkt, pkt, offset - 1); - response->tun.flags = 0; - response->tun.type = htons(0x86dd); + rpkt->subtype = GNUNET_DNS_ANSWER_TYPE_IP; + rpkt->hdr.size = ntohs (offset + 2 + ntohs (namelen)); - response->ip6_hdr.hoplmt = 255; - response->ip6_hdr.paylgth = request->ip6_hdr.paylgth; - response->ip6_hdr.nxthdr = 0x3a; - response->ip6_hdr.version = 6; - memcpy(&response->ip6_hdr.sadr, &request->ip6_hdr.dadr, 16); - memcpy(&response->ip6_hdr.dadr, &request->ip6_hdr.sadr, 16); + memcpy (((char *) rpkt) + offset, &namelen, 2); + memcpy (((char *) rpkt) + offset + 2, name, ntohs (namelen)); - response->icmp_hdr.code = 0; - response->icmp_hdr.type = 0x81; + } + else if (pkt->subtype == GNUNET_DNS_ANSWER_TYPE_IP) + { + list = + GNUNET_malloc (htons (pkt->hdr.size) + + sizeof (struct answer_packet_list) - + sizeof (struct answer_packet)); + memcpy (&list->pkt, pkt, htons (pkt->hdr.size)); + } + else if (pkt->subtype == GNUNET_DNS_ANSWER_TYPE_REMOTE_AAAA) + { + pkt->subtype = GNUNET_DNS_ANSWER_TYPE_IP; - /* Magic, more Magic! */ - response->icmp_hdr.chks = request->icmp_hdr.chks - 0x1; + GNUNET_HashCode key; - /* Copy the rest of the packet */ - memcpy(response+1, request+1, ntohs(request->shdr.size) - sizeof(struct ip6_icmp)); + memset (&key, 0, sizeof (GNUNET_HashCode)); - /* FIXME */ GNUNET_DISK_file_write(fh_to_helper, response, ntohs(response->shdr.size)); + unsigned char *c = ((unsigned char *) pkt) + ntohs (pkt->addroffset); - GNUNET_free(request); -} + new_ip6addr_remote (c, pkt->addr, pkt->addrsize); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New mapping to %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n", + c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8], c[9], + c[10], c[11], c[12], c[13], c[14], c[15]); + unsigned char *k = (unsigned char *) &key; -/** - * Receive packets from the helper-process - */ -static void -message_token(void *cls, - void *client, - const struct GNUNET_MessageHeader *message) { - GNUNET_assert(ntohs(message->type) == GNUNET_MESSAGE_TYPE_VPN_HELPER); + /* + * Copy the newly generated ip-address to the key backwards (as only the first part is used in the hash-table) + */ + unsigned int i; - struct tun_pkt *pkt_tun = (struct tun_pkt*) message; + for (i = 0; i < 16; i++) + k[15 - i] = c[i]; - /* ethertype is ipv6 */ - if (ntohs(pkt_tun->tun.type) == 0x86dd) - { - struct ip6_pkt *pkt6 = (struct ip6_pkt*) message; - GNUNET_assert(pkt6->ip6_hdr.version == 6); - struct ip6_tcp *pkt6_tcp; - struct ip6_udp *pkt6_udp; - struct ip6_icmp *pkt6_icmp; - - pkt_printf(pkt6); - switch(pkt6->ip6_hdr.nxthdr) - { - case 0x06: - pkt6_tcp = (struct ip6_tcp*)pkt6; - pkt_printf_ip6tcp(pkt6_tcp); - break; - case 0x11: - pkt6_udp = (struct ip6_udp*)pkt6; - pkt_printf_ip6udp(pkt6_udp); - if (ntohs(pkt6_udp->udp_hdr.dpt) == 53) { - pkt_printf_ip6dns((struct ip6_udp_dns*)pkt6_udp); - } - break; - case 0x3a: - /* ICMPv6 */ - pkt6_icmp = (struct ip6_icmp*)pkt6; - /* If this packet is an icmp-echo-request and a mapping exists, answer */ - if (pkt6_icmp->icmp_hdr.type == 0x80 && address_mapping_exists(pkt6->ip6_hdr.dadr)) - { - pkt6_icmp = GNUNET_malloc(ntohs(pkt6->shdr.size)); - memcpy(pkt6_icmp, pkt6, ntohs(pkt6->shdr.size)); - GNUNET_SCHEDULER_add_now(&send_icmp_response, pkt6_icmp); - } - break; - } - } - /* ethertype is ipv4 */ - else if (ntohs(pkt_tun->tun.type) == 0x0800) - { - struct ip_pkt *pkt = (struct ip_pkt*) message; - struct ip_udp *udp = (struct ip_udp*) message; - GNUNET_assert(pkt->ip_hdr.version == 4); - - /* Send dns-packets to the service-dns */ - if (pkt->ip_hdr.proto == 0x11 && ntohs(udp->udp_hdr.dpt) == 53 ) - { - /* 9 = 8 for the udp-header + 1 for the unsigned char data[1]; */ - size_t len = sizeof(struct query_packet) + ntohs(udp->udp_hdr.len) - 9; - - struct query_packet_list* query = GNUNET_malloc(len + 2*sizeof(struct query_packet_list*)); - query->pkt.hdr.type = htons(GNUNET_MESSAGE_TYPE_LOCAL_QUERY_DNS); - query->pkt.hdr.size = htons(len); - query->pkt.orig_to = pkt->ip_hdr.dadr; - query->pkt.orig_from = pkt->ip_hdr.sadr; - query->pkt.src_port = udp->udp_hdr.spt; - memcpy(query->pkt.data, udp->data, ntohs(udp->udp_hdr.len) - 8); - - GNUNET_CONTAINER_DLL_insert_after(head, tail, tail, query); - - if (dns_connection != NULL) - GNUNET_CLIENT_notify_transmit_ready(dns_connection, - len, - GNUNET_TIME_UNIT_FOREVER_REL, - GNUNET_YES, - &send_query, - NULL); - } - } + uint16_t namelen = strlen ((char *) pkt->data + 12) + 1; + + struct map_entry *value = + GNUNET_malloc (sizeof (struct map_entry) + namelen); + char *name = (char *) (value + 1); + + value->namelen = namelen; + memcpy (name, pkt->data + 12, namelen); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Setting addrlen to %d\n", + pkt->addrsize); + value->addrlen = pkt->addrsize; + memcpy (&value->addr, &pkt->addr, pkt->addrsize); + memset (value->additional_ports, 0, 8192); + + memcpy (&value->hash, &key, sizeof (GNUNET_HashCode)); + + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (hashmap, &key)) + { + GNUNET_CONTAINER_multihashmap_put (hashmap, &key, value, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + value->heap_node = + GNUNET_CONTAINER_heap_insert (heap, value, + GNUNET_TIME_absolute_get ().abs_value); + if (GNUNET_CONTAINER_heap_get_size (heap) > max_mappings) + GNUNET_SCHEDULER_add_now (collect_mappings, NULL); + } + else + GNUNET_free (value); + + list = + GNUNET_malloc (htons (pkt->hdr.size) + + sizeof (struct answer_packet_list) - + sizeof (struct answer_packet)); + + memcpy (&list->pkt, pkt, htons (pkt->hdr.size)); + } + else if (pkt->subtype == GNUNET_DNS_ANSWER_TYPE_REMOTE_A) + { + pkt->subtype = GNUNET_DNS_ANSWER_TYPE_IP; + + GNUNET_HashCode key; + + memset (&key, 0, sizeof (GNUNET_HashCode)); + + unsigned char *c = ((unsigned char *) pkt) + ntohs (pkt->addroffset); + + new_ip4addr_remote (c, pkt->addr, pkt->addrsize); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New mapping to %d.%d.%d.%d\n", c[0], + c[1], c[2], c[3]); + unsigned char *k = (unsigned char *) &key; + + /* + * Copy the newly generated ip-address to the key backwards (as only the first part is used in the hash-table) + */ + unsigned int i; + + for (i = 0; i < 4; i++) + k[3 - i] = c[i]; + + uint16_t namelen = strlen ((char *) pkt->data + 12) + 1; + + struct map_entry *value = + GNUNET_malloc (sizeof (struct map_entry) + namelen); + char *name = (char *) (value + 1); + + value->namelen = namelen; + memcpy (name, pkt->data + 12, namelen); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Setting addrlen to %d\n", + pkt->addrsize); + value->addrlen = pkt->addrsize; + memcpy (&value->addr, &pkt->addr, pkt->addrsize); + memset (value->additional_ports, 0, 8192); + + memcpy (&value->hash, &key, sizeof (GNUNET_HashCode)); + + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (hashmap, &key)) + { + GNUNET_CONTAINER_multihashmap_put (hashmap, &key, value, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + value->heap_node = + GNUNET_CONTAINER_heap_insert (heap, value, + GNUNET_TIME_absolute_get ().abs_value); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Mapping is saved in the hashmap with key %08x.\n", + *((uint32_t *) (&key))); + if (GNUNET_CONTAINER_heap_get_size (heap) > max_mappings) + GNUNET_SCHEDULER_add_now (collect_mappings, NULL); + } + else + GNUNET_free (value); + + list = + GNUNET_malloc (htons (pkt->hdr.size) + + sizeof (struct answer_packet_list) - + sizeof (struct answer_packet)); + + memcpy (&list->pkt, pkt, htons (pkt->hdr.size)); + } + else + { + GNUNET_break (0); + GNUNET_free (pkt); + return; + } + + GNUNET_free (pkt); + + GNUNET_CONTAINER_DLL_insert_after (answer_proc_head, answer_proc_tail, + answer_proc_tail, list); + + schedule_helper_write (GNUNET_TIME_UNIT_FOREVER_REL, NULL); + + return; } /** - * Connect to the service-dns + * Sets a bit active in a bitArray. + * + * @param bitArray memory area to set the bit in + * @param bitIdx which bit to set */ -static void -connect_to_service_dns (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) { - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - return; - GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Connecting to service-dns\n"); - GNUNET_assert (dns_connection == NULL); - dns_connection = GNUNET_CLIENT_connect ("dns", cfg); - GNUNET_CLIENT_receive(dns_connection, &dns_answer_handler, NULL, GNUNET_TIME_UNIT_FOREVER_REL); - - /* If a packet is already in the list, schedule to send it */ - if (head != NULL) - GNUNET_CLIENT_notify_transmit_ready(dns_connection, - ntohs(head->pkt.hdr.size), - GNUNET_TIME_UNIT_FOREVER_REL, - GNUNET_YES, - &send_query, - NULL); - else if (restart_hijack == 1) - { - GNUNET_CLIENT_notify_transmit_ready(dns_connection, sizeof(struct GNUNET_MessageHeader), GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, &send_query, NULL); - } +void +setBit (char *bitArray, unsigned int bitIdx) +{ + size_t arraySlot; + unsigned int targetBit; + + arraySlot = bitIdx / 8; + targetBit = (1L << (bitIdx % 8)); + bitArray[arraySlot] |= targetBit; } /** - * Create a new Address from an answer-packet - * {{{ + * Clears a bit from bitArray. + * + * @param bitArray memory area to set the bit in + * @param bitIdx which bit to unset */ void -new_ip6addr(char* buf, struct answer_packet* pkt) { - memcpy(buf, (int[]){htons(0x1234)}, 2); - memcpy(buf+2, &pkt->service_descr.service_descriptor, 6); - memcpy(buf+8, &pkt->service_descr.peer, 8); +clearBit (char *bitArray, unsigned int bitIdx) +{ + size_t slot; + unsigned int targetBit; + + slot = bitIdx / 8; + targetBit = (1L << (bitIdx % 8)); + bitArray[slot] = bitArray[slot] & (~targetBit); } -/*}}}*/ /** - * This gets scheduled with cls pointing to an answer_packet and does everything - * needed in order to send it to the helper. + * Checks if a bit is active in the bitArray * - * At the moment this means "inventing" and IPv6-Address for .gnunet-services and - * doing nothing for "real" services. + * @param bitArray memory area to set the bit in + * @param bitIdx which bit to test + * @return GNUNET_YES if the bit is set, GNUNET_NO if not. + */ +int +testBit (char *bitArray, unsigned int bitIdx) +{ + size_t slot; + unsigned int targetBit; + + slot = bitIdx / 8; + targetBit = (1L << (bitIdx % 8)); + if (bitArray[slot] & targetBit) + return GNUNET_YES; + else + return GNUNET_NO; +} + +/** + * @brief Add the port to the list of additional ports in the map_entry + * + * @param me the map_entry + * @param port the port in host-byte-order */ static void -process_answer(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tc) { - struct answer_packet* pkt = cls; - struct answer_packet_list* list; - - /* This answer is about a .gnunet-service - * - * It contains an almost complete DNS-Response, we have to fill in the ip - * at the offset pkt->addroffset - */ - //FIXME htons? - if (pkt->subtype == GNUNET_DNS_ANSWER_TYPE_SERVICE) +add_additional_port (struct map_entry *me, uint16_t port) +{ + setBit (me->additional_ports, port); +} + +static int +receive_udp_back (void *cls + __attribute__ ((unused)), struct GNUNET_MESH_Tunnel *tunnel, + void **tunnel_ctx, const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi + __attribute__ ((unused))) +{ + GNUNET_HashCode *desc = (GNUNET_HashCode *) (message + 1); + struct remote_addr *s = (struct remote_addr *) desc; + struct udp_pkt *pkt = (struct udp_pkt *) (desc + 1); + const struct GNUNET_PeerIdentity *other = sender; + struct tunnel_state *ts = *tunnel_ctx; + + if (16 == ts->addrlen) + { + size_t size = + sizeof (struct ip6_udp) + ntohs (pkt->len) - 1 - + sizeof (struct udp_pkt); + + struct ip6_udp *pkt6 = alloca (size); + + GNUNET_assert (pkt6 != NULL); + + if (ntohs (message->type) == GNUNET_MESSAGE_TYPE_VPN_SERVICE_UDP_BACK) + new_ip6addr (pkt6->ip6_hdr.sadr, &other->hashPubKey, desc); + else + new_ip6addr_remote (pkt6->ip6_hdr.sadr, s->addr, s->addrlen); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Relaying calc:%d gnu:%d udp:%d bytes!\n", size, + ntohs (message->size), ntohs (pkt->len)); + + pkt6->shdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER); + pkt6->shdr.size = htons (size); + + pkt6->tun.flags = 0; + pkt6->tun.type = htons (0x86dd); + + pkt6->ip6_hdr.version = 6; + pkt6->ip6_hdr.tclass_h = 0; + pkt6->ip6_hdr.tclass_l = 0; + pkt6->ip6_hdr.flowlbl = 0; + pkt6->ip6_hdr.paylgth = pkt->len; + pkt6->ip6_hdr.nxthdr = IPPROTO_UDP; + pkt6->ip6_hdr.hoplmt = 0xff; + + { + char *ipv6addr; + + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, "vpn", + "IPV6ADDR", + &ipv6addr)); + inet_pton (AF_INET6, ipv6addr, pkt6->ip6_hdr.dadr); + GNUNET_free (ipv6addr); + } + memcpy (&pkt6->udp_hdr, pkt, ntohs (pkt->len)); + + GNUNET_HashCode *key = address6_mapping_exists (pkt6->ip6_hdr.sadr); + + GNUNET_assert (key != NULL); + + struct map_entry *me = GNUNET_CONTAINER_multihashmap_get (hashmap, key); + + GNUNET_CONTAINER_heap_update_cost (heap, me->heap_node, + GNUNET_TIME_absolute_get ().abs_value); + + GNUNET_free (key); + + GNUNET_assert (me != NULL); + if (ntohs (message->type) == GNUNET_MESSAGE_TYPE_VPN_SERVICE_UDP_BACK) + { + GNUNET_assert (me->desc. + service_type & htonl (GNUNET_DNS_SERVICE_TYPE_UDP)); + if (!port_in_ports (me->desc.ports, pkt6->udp_hdr.spt) && + !testBit (me->additional_ports, ntohs (pkt6->udp_hdr.spt))) { - pkt->subtype = GNUNET_DNS_ANSWER_TYPE_IP; + add_additional_port (me, ntohs (pkt6->udp_hdr.spt)); + } + } - GNUNET_HashCode key; - memset(&key, 0, sizeof(GNUNET_HashCode)); - new_ip6addr((char*)&key, pkt); + pkt6->udp_hdr.crc = 0; + uint32_t sum = 0; - uint16_t namelen = strlen((char*)pkt->data+12)+1; + sum = + calculate_checksum_update (sum, (uint16_t *) & pkt6->ip6_hdr.sadr, 16); + sum = + calculate_checksum_update (sum, (uint16_t *) & pkt6->ip6_hdr.dadr, 16); + uint32_t tmp = (pkt6->udp_hdr.len & 0xffff); + + sum = calculate_checksum_update (sum, (uint16_t *) & tmp, 4); + tmp = htons (((pkt6->ip6_hdr.nxthdr & 0x00ff))); + sum = calculate_checksum_update (sum, (uint16_t *) & tmp, 4); + + sum = + calculate_checksum_update (sum, (uint16_t *) & pkt6->udp_hdr, + ntohs (pkt->len)); + pkt6->udp_hdr.crc = calculate_checksum_end (sum); + + write_to_helper (pkt6, size); + } + else + { + size_t size = + sizeof (struct ip_udp) + ntohs (pkt->len) - 1 - sizeof (struct udp_pkt); + + struct ip_udp *pkt4 = alloca (size); + + GNUNET_assert (pkt4 != NULL); + + GNUNET_assert (ntohs (message->type) == + GNUNET_MESSAGE_TYPE_VPN_REMOTE_UDP_BACK); + uint32_t sadr; + + new_ip4addr_remote ((unsigned char *) &sadr, s->addr, s->addrlen); + pkt4->ip_hdr.sadr = sadr; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Relaying calc:%d gnu:%d udp:%d bytes!\n", size, + ntohs (message->size), ntohs (pkt->len)); + + pkt4->shdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER); + pkt4->shdr.size = htons (size); + + pkt4->tun.flags = 0; + pkt4->tun.type = htons (0x0800); + + pkt4->ip_hdr.version = 4; + pkt4->ip_hdr.hdr_lngth = 5; + pkt4->ip_hdr.diff_serv = 0; + pkt4->ip_hdr.tot_lngth = htons (20 + ntohs (pkt->len)); + pkt4->ip_hdr.ident = 0; + pkt4->ip_hdr.flags = 0; + pkt4->ip_hdr.frag_off = 0; + pkt4->ip_hdr.ttl = 255; + pkt4->ip_hdr.proto = IPPROTO_UDP; + pkt4->ip_hdr.chks = 0; /* Will be calculated later */ + + { + char *ipv4addr; + uint32_t dadr; + + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, "vpn", + "IPV4ADDR", + &ipv4addr)); + inet_pton (AF_INET, ipv4addr, &dadr); + GNUNET_free (ipv4addr); + pkt4->ip_hdr.dadr = dadr; + } + memcpy (&pkt4->udp_hdr, pkt, ntohs (pkt->len)); - struct map_entry* value = GNUNET_malloc(sizeof(struct GNUNET_vpn_service_descriptor) + 2 + namelen); + GNUNET_HashCode *key = address4_mapping_exists (pkt4->ip_hdr.sadr); - value->namelen = namelen; - memcpy(value->name, pkt->data+12, namelen); + GNUNET_assert (key != NULL); - memcpy(&value->desc, &pkt->service_descr, sizeof(struct GNUNET_vpn_service_descriptor)); + struct map_entry *me = GNUNET_CONTAINER_multihashmap_get (hashmap, key); - if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(hashmap, - &key, - value, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) - { - GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Could not store to hashmap\n"); - } + GNUNET_CONTAINER_heap_update_cost (heap, me->heap_node, + GNUNET_TIME_absolute_get ().abs_value); - memcpy(((char*)pkt)+ntohs(pkt->addroffset), &key, 16); + GNUNET_free (key); - list = GNUNET_malloc(htons(pkt->hdr.size) + 2*sizeof(struct answer_packet_list*)); + GNUNET_assert (me != NULL); - memcpy(&list->pkt, pkt, htons(pkt->hdr.size)); + pkt4->udp_hdr.crc = 0; /* Optional for IPv4 */ - } - else if (pkt->subtype == GNUNET_DNS_ANSWER_TYPE_REV) - { - GNUNET_HashCode key; - memset(&key, 0, sizeof key); - unsigned char* k = (unsigned char*)&key; - unsigned char* s = pkt->data+12; - int i = 0; - /* Whoever designed the reverse IPv6-lookup is batshit insane */ - for (i = 0; i < 16; i++) - { - unsigned char c1 = s[(4*i)+1]; - unsigned char c2 = s[(4*i)+3]; - if (c1 <= '9') - k[15-i] = c1 - '0'; - else - k[15-i] = c1 - 87; /* 87 is the difference between 'a' and 10 */ - if (c2 <= '9') - k[15-i] += 16*(c2 - '0'); - else - k[15-i] += 16*(c2 - 87); - } - - struct map_entry* map_entry = GNUNET_CONTAINER_multihashmap_get(hashmap, &key); - unsigned short offset = ntohs(pkt->addroffset); - - if (map_entry == NULL) - { - GNUNET_free(pkt); - return; - } - - unsigned short namelen = htons(map_entry->namelen); - char* name = map_entry->name; - - list = GNUNET_malloc(2*sizeof(struct answer_packet_list*) + offset + 2 + ntohs(namelen)); - - struct answer_packet* rpkt = &list->pkt; - - memcpy(rpkt, pkt, offset); - - rpkt->subtype = GNUNET_DNS_ANSWER_TYPE_IP; - rpkt->hdr.size = ntohs(offset + 2 + ntohs(namelen)); - - memcpy(((char*)rpkt)+offset, &namelen, 2); - memcpy(((char*)rpkt)+offset+2, name, ntohs(namelen)); + pkt4->ip_hdr.chks = + calculate_ip_checksum ((uint16_t *) & pkt4->ip_hdr, 5 * 4); - } - else if (pkt->subtype == GNUNET_DNS_ANSWER_TYPE_IP) - { - list = GNUNET_malloc(htons(pkt->hdr.size) + 2*sizeof(struct answer_packet_list*)); - memcpy(&list->pkt, pkt, htons(pkt->hdr.size)); - } + write_to_helper (pkt4, size); + } + + return GNUNET_OK; +} + +static int +receive_tcp_back (void *cls + __attribute__ ((unused)), struct GNUNET_MESH_Tunnel *tunnel, + void **tunnel_ctx, const struct GNUNET_PeerIdentity *sender + __attribute__ ((unused)), + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi + __attribute__ ((unused))) +{ + GNUNET_HashCode *desc = (GNUNET_HashCode *) (message + 1); + struct remote_addr *s = (struct remote_addr *) desc; + struct tcp_pkt *pkt = (struct tcp_pkt *) (desc + 1); + const struct GNUNET_PeerIdentity *other = sender; + struct tunnel_state *ts = *tunnel_ctx; + + size_t pktlen = + ntohs (message->size) - sizeof (struct GNUNET_MessageHeader) - + sizeof (GNUNET_HashCode); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received TCP-Packet back, addrlen = %d\n", s->addrlen); + + if (ntohs (message->type) == GNUNET_MESSAGE_TYPE_VPN_SERVICE_TCP_BACK || + ts->addrlen == 16) + { + size_t size = pktlen + sizeof (struct ip6_tcp) - 1; + + struct ip6_tcp *pkt6 = alloca (size); + + memset (pkt6, 0, size); + + GNUNET_assert (pkt6 != NULL); + + if (ntohs (message->type) == GNUNET_MESSAGE_TYPE_VPN_SERVICE_TCP_BACK) + new_ip6addr (pkt6->ip6_hdr.sadr, &other->hashPubKey, desc); else - { - GNUNET_break(0); - GNUNET_free(pkt); - return; - } + new_ip6addr_remote (pkt6->ip6_hdr.sadr, s->addr, s->addrlen); + + pkt6->shdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER); + pkt6->shdr.size = htons (size); + + pkt6->tun.flags = 0; + pkt6->tun.type = htons (0x86dd); + + pkt6->ip6_hdr.version = 6; + pkt6->ip6_hdr.tclass_h = 0; + pkt6->ip6_hdr.tclass_l = 0; + pkt6->ip6_hdr.flowlbl = 0; + pkt6->ip6_hdr.paylgth = htons (pktlen); + pkt6->ip6_hdr.nxthdr = IPPROTO_TCP; + pkt6->ip6_hdr.hoplmt = 0xff; + + { + char *ipv6addr; + + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, "vpn", + "IPV6ADDR", + &ipv6addr)); + inet_pton (AF_INET6, ipv6addr, pkt6->ip6_hdr.dadr); + GNUNET_free (ipv6addr); + } + memcpy (&pkt6->tcp_hdr, pkt, pktlen); - GNUNET_free(pkt); + GNUNET_HashCode *key = address6_mapping_exists (pkt6->ip6_hdr.sadr); - GNUNET_CONTAINER_DLL_insert_after(answer_proc_head, answer_proc_tail, answer_proc_tail, list); + GNUNET_assert (key != NULL); - GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL, fh_to_helper, &helper_write, NULL); + struct map_entry *me = GNUNET_CONTAINER_multihashmap_get (hashmap, key); - return; -} + GNUNET_CONTAINER_heap_update_cost (heap, me->heap_node, + GNUNET_TIME_absolute_get ().abs_value); -/** - * This receives packets from the service-dns and schedules process_answer to - * handle it - */ -static void -dns_answer_handler(void* cls, const struct GNUNET_MessageHeader *msg) { - /* the service disconnected, reconnect after short wait */ - if (msg == NULL) - { - GNUNET_CLIENT_disconnect(dns_connection, GNUNET_NO); - dns_connection = NULL; - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &connect_to_service_dns, - NULL); - return; - } + GNUNET_free (key); - /* the service did something strange, reconnect immediately */ - if (msg->type != htons(GNUNET_MESSAGE_TYPE_LOCAL_RESPONSE_DNS)) - { - GNUNET_break (0); - GNUNET_CLIENT_disconnect(dns_connection, GNUNET_NO); - dns_connection = NULL; - GNUNET_SCHEDULER_add_now (&connect_to_service_dns, - NULL); - return; - } - void *pkt = GNUNET_malloc(ntohs(msg->size)); + GNUNET_assert (me != NULL); + if (ntohs (message->type) == GNUNET_MESSAGE_TYPE_VPN_SERVICE_UDP_BACK) + GNUNET_assert (me->desc. + service_type & htonl (GNUNET_DNS_SERVICE_TYPE_TCP)); + + pkt6->tcp_hdr.crc = 0; + uint32_t sum = 0; + uint32_t tmp; + + sum = + calculate_checksum_update (sum, (uint16_t *) & pkt6->ip6_hdr.sadr, 16); + sum = + calculate_checksum_update (sum, (uint16_t *) & pkt6->ip6_hdr.dadr, 16); + tmp = htonl (pktlen); + sum = calculate_checksum_update (sum, (uint16_t *) & tmp, 4); + tmp = htonl (((pkt6->ip6_hdr.nxthdr & 0x000000ff))); + sum = calculate_checksum_update (sum, (uint16_t *) & tmp, 4); + + sum = + calculate_checksum_update (sum, (uint16_t *) & pkt6->tcp_hdr, + ntohs (pkt6->ip6_hdr.paylgth)); + pkt6->tcp_hdr.crc = calculate_checksum_end (sum); + + write_to_helper (pkt6, size); + } + else + { + size_t size = pktlen + sizeof (struct ip_tcp) - 1; + + struct ip_tcp *pkt4 = alloca (size); + + GNUNET_assert (pkt4 != NULL); + memset (pkt4, 0, size); + + GNUNET_assert (ntohs (message->type) == + GNUNET_MESSAGE_TYPE_VPN_REMOTE_TCP_BACK); + uint32_t sadr; + + new_ip4addr_remote ((unsigned char *) &sadr, s->addr, s->addrlen); + pkt4->ip_hdr.sadr = sadr; + + pkt4->shdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER); + pkt4->shdr.size = htons (size); + + pkt4->tun.flags = 0; + pkt4->tun.type = htons (0x0800); + + pkt4->ip_hdr.version = 4; + pkt4->ip_hdr.hdr_lngth = 5; + pkt4->ip_hdr.diff_serv = 0; + pkt4->ip_hdr.tot_lngth = htons (20 + pktlen); + pkt4->ip_hdr.ident = 0; + pkt4->ip_hdr.flags = 0; + pkt4->ip_hdr.frag_off = 0; + pkt4->ip_hdr.ttl = 255; + pkt4->ip_hdr.proto = IPPROTO_TCP; + pkt4->ip_hdr.chks = 0; /* Will be calculated later */ + + { + char *ipv4addr; + uint32_t dadr; + + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, "vpn", + "IPV4ADDR", + &ipv4addr)); + inet_pton (AF_INET, ipv4addr, &dadr); + GNUNET_free (ipv4addr); + pkt4->ip_hdr.dadr = dadr; + } + + memcpy (&pkt4->tcp_hdr, pkt, pktlen); + + GNUNET_HashCode *key = address4_mapping_exists (pkt4->ip_hdr.sadr); + + GNUNET_assert (key != NULL); - memcpy(pkt, msg, ntohs(msg->size)); + struct map_entry *me = GNUNET_CONTAINER_multihashmap_get (hashmap, key); - GNUNET_SCHEDULER_add_now(process_answer, pkt); - GNUNET_CLIENT_receive(dns_connection, &dns_answer_handler, NULL, GNUNET_TIME_UNIT_FOREVER_REL); + GNUNET_CONTAINER_heap_update_cost (heap, me->heap_node, + GNUNET_TIME_absolute_get ().abs_value); + + GNUNET_free (key); + + GNUNET_assert (me != NULL); + pkt4->tcp_hdr.crc = 0; + uint32_t sum = 0; + uint32_t tmp; + + tmp = pkt4->ip_hdr.sadr; + sum = calculate_checksum_update (sum, (uint16_t *) & tmp, 4); + tmp = pkt4->ip_hdr.dadr; + sum = calculate_checksum_update (sum, (uint16_t *) & tmp, 4); + + tmp = (0x06 << 16) | (0xffff & pktlen); // 0x06 for TCP? + + tmp = htonl (tmp); + + sum = calculate_checksum_update (sum, (uint16_t *) & tmp, 4); + + sum = calculate_checksum_update (sum, (uint16_t *) & pkt4->tcp_hdr, pktlen); + pkt4->tcp_hdr.crc = calculate_checksum_end (sum); + + pkt4->ip_hdr.chks = + calculate_ip_checksum ((uint16_t *) & pkt4->ip_hdr, 5 * 4); + + write_to_helper (pkt4, size); + } + + return GNUNET_OK; +} + +static void * +new_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel, + const struct GNUNET_PeerIdentity *initiator, + const struct GNUNET_ATS_Information *atsi) +{ + /* Why should anyone open an inbound tunnel to vpn? */ + GNUNET_break (0); + return NULL; +} + +static void +cleaner (void *cls, const struct GNUNET_MESH_Tunnel *tunnel, void *tunnel_ctx) +{ + /* Why should anyone open an inbound tunnel to vpn? */ + GNUNET_break (0); } /** @@ -696,20 +1242,38 @@ dns_answer_handler(void* cls, const struct GNUNET_MessageHeader *msg) { * @param cls closure * @param args remaining command-line arguments * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param cfg configuration + * @param cfg_ configuration */ static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg_) { - mst = GNUNET_SERVER_mst_create(&message_token, NULL); - cfg = cfg_; - restart_hijack = 0; - hashmap = GNUNET_CONTAINER_multihashmap_create(65536); - GNUNET_SCHEDULER_add_now (connect_to_service_dns, NULL); - GNUNET_SCHEDULER_add_now (start_helper_and_schedule, NULL); - GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_FOREVER_REL, &cleanup, cls); +run (void *cls, char *const *args __attribute__ ((unused)), const char *cfgfilep + __attribute__ ((unused)), const struct GNUNET_CONFIGURATION_Handle *cfg_) +{ + static const struct GNUNET_MESH_MessageHandler handlers[] = { + {receive_udp_back, GNUNET_MESSAGE_TYPE_VPN_SERVICE_UDP_BACK, 0}, + {receive_tcp_back, GNUNET_MESSAGE_TYPE_VPN_SERVICE_TCP_BACK, 0}, + {receive_udp_back, GNUNET_MESSAGE_TYPE_VPN_REMOTE_UDP_BACK, 0}, + {receive_tcp_back, GNUNET_MESSAGE_TYPE_VPN_REMOTE_TCP_BACK, 0}, + {NULL, 0, 0} + }; + + static const GNUNET_MESH_ApplicationType types[] = { + GNUNET_APPLICATION_TYPE_END + }; + + mesh_handle = + GNUNET_MESH_connect (cfg_, 42, NULL, new_tunnel, cleaner, handlers, + types); + cfg = cfg_; + restart_hijack = 0; + hashmap = GNUNET_CONTAINER_multihashmap_create (65536); + heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); + GNUNET_CONFIGURATION_get_value_number (cfg, "vpn", "MAX_MAPPINGg", + &max_mappings); + udp_connections = GNUNET_CONTAINER_multihashmap_create (65536); + conn_task = GNUNET_SCHEDULER_add_now (connect_to_service_dns, NULL); + shs_task = + GNUNET_SCHEDULER_add_after (conn_task, start_helper_and_schedule, NULL); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup, cls); } /** @@ -720,17 +1284,15 @@ run (void *cls, * @return 0 ok, 1 on error */ int -main (int argc, char *const *argv) { - static const struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - - return (GNUNET_OK == - GNUNET_PROGRAM_run (argc, - argv, - "gnunet-daemon-vpn", - gettext_noop ("help text"), - options, &run, NULL)) ? ret : 1; +main (int argc, char *const *argv) +{ + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + return (GNUNET_OK == + GNUNET_PROGRAM_run (argc, argv, "vpn", gettext_noop ("help text"), + options, &run, NULL)) ? ret : 1; } /* end of gnunet-daemon-vpn.c */