From 0f617a81c282e9897b8695612e57ca6cc70f60b7 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 7 Jan 2012 21:45:43 +0000 Subject: -finishing first implementation of VPN client library --- src/include/gnunet_vpn_service.h | 4 +- src/vpn/vpn.h | 9 +- src/vpn/vpn_api.c | 343 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 345 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/include/gnunet_vpn_service.h b/src/include/gnunet_vpn_service.h index 76befaae0..ecf6cf5f0 100644 --- a/src/include/gnunet_vpn_service.h +++ b/src/include/gnunet_vpn_service.h @@ -93,7 +93,7 @@ GNUNET_VPN_cancel_request (struct GNUNET_VPN_RedirectionRequest *rr); * anyway) */ struct GNUNET_VPN_RedirectionRequest * -GNUNET_VPN_redirect_to_peer (struct GNUNET_VPN_Handle *rh, +GNUNET_VPN_redirect_to_peer (struct GNUNET_VPN_Handle *vh, int result_af, uint8_t protocol, const struct GNUNET_PeerIdentity *peer, @@ -131,7 +131,7 @@ GNUNET_VPN_redirect_to_peer (struct GNUNET_VPN_Handle *rh, * anyway) */ struct GNUNET_VPN_RedirectionRequest * -GNUNET_VPN_redirect_to_ip (struct GNUNET_VPN_Handle *rh, +GNUNET_VPN_redirect_to_ip (struct GNUNET_VPN_Handle *vh, int result_af, int addr_af, const void *addr, diff --git a/src/vpn/vpn.h b/src/vpn/vpn.h index 90bcdaa61..4a80e1812 100644 --- a/src/vpn/vpn.h +++ b/src/vpn/vpn.h @@ -113,7 +113,7 @@ struct RedirectToServiceRequestMessage /** * Service descriptor identifying the service. */ - struct GNUNET_PeerIdentity desc; + GNUNET_HashCode service_descriptor; /** * Unique ID to match a future response to this request. @@ -132,7 +132,7 @@ struct RedirectToIpResponseMessage { /** - * Type is GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_IP + * Type is GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP */ struct GNUNET_MessageHeader header; @@ -142,6 +142,11 @@ struct RedirectToIpResponseMessage */ int32_t result_af; + /** + * Unique ID to match the response to a request. + */ + uint64_t request_id; + /* followed by destination address ('struct in_addr' or 'struct in6_addr') */ }; diff --git a/src/vpn/vpn_api.c b/src/vpn/vpn_api.c index b6fab37bf..f7759a3bc 100644 --- a/src/vpn/vpn_api.c +++ b/src/vpn/vpn_api.c @@ -43,6 +43,11 @@ struct GNUNET_VPN_Handle */ struct GNUNET_CLIENT_Connection *client; + /** + * Active transmission request. + */ + struct GNUNET_CLIENT_TransmitHandle *th; + /** * Head of list of active redirection requests. */ @@ -52,6 +57,17 @@ struct GNUNET_VPN_Handle * Tail of list of active redirection requests. */ struct GNUNET_VPN_RedirectionRequest *rr_tail; + + /** + * Identifier of a reconnect task. + */ + GNUNET_SCHEDULER_TaskIdentifier rt; + + /** + * ID of the last request that was submitted to the service. + */ + uint64_t request_id_gen; + }; @@ -106,6 +122,11 @@ struct GNUNET_VPN_RedirectionRequest */ struct GNUNET_TIME_Absolute expiration_time; + /** + * non-zero if this request has been sent to the service. + */ + uint64_t request_id; + /** * Desired address family for the result. */ @@ -130,6 +151,255 @@ struct GNUNET_VPN_RedirectionRequest }; +/** + * Disconnect from the service (communication error) and reconnect later. + * + * @param vh handle to reconnect. + */ +static void +reconnect (struct GNUNET_VPN_Handle *vh); + + +/** + * Function called when we receive a message from the VPN service. + * + * @param cls the 'struct GNUNET_VPN_Handle' + * @param msg message received, NULL on timeout or fatal error + */ +static void +receive_response (void *cls, + const struct GNUNET_MessageHeader* msg) +{ + struct GNUNET_VPN_Handle *vh = cls; + const struct RedirectToIpResponseMessage *rm; + struct GNUNET_VPN_RedirectionRequest *rr; + size_t msize; + size_t alen; + int af; + + if (NULL == msg) + { + reconnect (vh); + return; + } + if ( (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP) || + (sizeof (struct RedirectToIpResponseMessage) > (msize = ntohs (msg->size))) ) + { + GNUNET_break (0); + reconnect (vh); + return; + } + rm = (const struct RedirectToIpResponseMessage *) msg; + af = (int) ntohl (rm->result_af); + switch (af) + { + case AF_UNSPEC: + alen = 0; + break; + case AF_INET: + alen = sizeof (struct in_addr); + break; + case AF_INET6: + alen = sizeof (struct in6_addr); + break; + default: + GNUNET_break (0); + reconnect (vh); + return; + } + if ( (msize != alen + sizeof (struct RedirectToIpResponseMessage)) || + (0 == rm->request_id) ) + { + GNUNET_break (0); + reconnect (vh); + return; + } + for (rr = vh->rr_head; NULL != rr; rr = rr->next) + { + if (rr->request_id == rm->request_id) + { + GNUNET_CONTAINER_DLL_remove (vh->rr_head, + vh->rr_tail, + rr); + rr->cb (rr->cb_cls, + af, + (af == AF_UNSPEC) ? NULL : &rm[1]); + GNUNET_free (rr); + break; + } + } + GNUNET_CLIENT_receive (vh->client, + &receive_response, vh, + GNUNET_TIME_UNIT_FOREVER_REL); +} + + +/** + * We're ready to transmit a request to the VPN service. Do it. + * + * @param cls the 'struct GNUNET_VPN_Handle*' + * @param size number of bytes available in buf + * @param buf where to copy the request + * @return number of bytes copied to 'buf' + */ +static size_t +transmit_request (void *cls, + size_t size, + void *buf) +{ + struct GNUNET_VPN_Handle *vh = cls; + struct GNUNET_VPN_RedirectionRequest *rr; + struct RedirectToIpRequestMessage rip; + struct RedirectToServiceRequestMessage rs; + char *cbuf; + size_t alen; + size_t ret; + + vh->th = NULL; + /* find a pending request */ + rr = vh->rr_head; + while ( (NULL != rr) && + (0 != rr->request_id) ) + rr = rr->next; + if (NULL == rr) + return 0; + + /* if first request, start receive loop */ + if (0 == vh->request_id_gen) + GNUNET_CLIENT_receive (vh->client, + &receive_response, vh, + GNUNET_TIME_UNIT_FOREVER_REL); + if (NULL == rr->addr) + { + ret = sizeof (struct RedirectToServiceRequestMessage); + rs.header.size = htons ((uint16_t) ret); + rs.header.type = htons (GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_SERVICE); + rs.nac = htonl (rr->nac); + rs.expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time); + rs.protocol = htonl (rr->protocol); + rs.result_af = htonl (rr->result_af); + rs.target = rr->peer; + rs.service_descriptor = rr->serv; + rs.request_id = rr->request_id = ++vh->request_id_gen; + memcpy (buf, &rs, sizeof (struct RedirectToServiceRequestMessage)); + } + else + { + switch (rr->addr_af) + { + case AF_INET: + alen = sizeof (struct in_addr); + break; + case AF_INET6: + alen = sizeof (struct in6_addr); + break; + default: + GNUNET_assert (0); + return 0; + } + ret = alen + sizeof (struct RedirectToIpRequestMessage); + rip.header.size = htons ((uint16_t) ret); + rip.header.type = htons (GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_IP); + rip.nac = htonl (rr->nac); + rip.expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time); + rip.result_af = htonl (rr->result_af); + rip.addr_af = htonl (rr->addr_af); + rip.request_id = rr->request_id = ++vh->request_id_gen; + cbuf = buf; + memcpy (cbuf, &rip, sizeof (struct RedirectToIpRequestMessage)); + memcpy (&cbuf[sizeof (struct RedirectToIpRequestMessage)], rr->addr, alen); + } + /* test if there are more pending requests */ + while ( (NULL != rr) && + (0 != rr->request_id) ) + rr = rr->next; + if (NULL != rr) + vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client, + sizeof (struct RedirectToServiceRequestMessage), + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_NO, + &transmit_request, + vh); + return ret; +} + + +/** + * Add a request to our request queue and transmit it. + * + * @param rr request to queue and transmit. + */ +static void +queue_request (struct GNUNET_VPN_RedirectionRequest *rr) +{ + struct GNUNET_VPN_Handle *vh; + + vh = rr->vh; + GNUNET_CONTAINER_DLL_insert_tail (vh->rr_head, + vh->rr_tail, + rr); + if ( (NULL == vh->th) && + (NULL != vh->client) ) + vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client, + sizeof (struct RedirectToServiceRequestMessage), + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_NO, + &transmit_request, + vh); +} + + +/** + * Connect to the VPN service and start again to transmit our requests. + * + * @param cls the 'struct GNUNET_VPN_Handle *' + * @param tc scheduler context + */ +static void +connect_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_VPN_Handle *vh = cls; + + vh->client = GNUNET_CLIENT_connect ("vpn", vh->cfg); + GNUNET_assert (NULL != vh->client); + GNUNET_assert (NULL != vh->th); + if (NULL != vh->rr_head) + vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client, + sizeof (struct RedirectToServiceRequestMessage), + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_NO, + &transmit_request, + vh); +} + + +/** + * Disconnect from the service (communication error) and reconnect later. + * + * @param vh handle to reconnect. + */ +static void +reconnect (struct GNUNET_VPN_Handle *vh) +{ + struct GNUNET_VPN_RedirectionRequest *rr; + + if (NULL != vh->th) + { + GNUNET_CLIENT_notify_transmit_ready_cancel (vh->th); + vh->th = NULL; + } + GNUNET_CLIENT_disconnect (vh->client, GNUNET_NO); + vh->client = NULL; + vh->request_id_gen = 0; + for (rr = vh->rr_head; NULL != rr; rr = rr->next) + rr->request_id = 0; + vh->rt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &connect_task, + vh); +} + + /** * Cancel redirection request with the service. * @@ -157,7 +427,8 @@ GNUNET_VPN_cancel_request (struct GNUNET_VPN_RedirectionRequest *rr) * limitations, the longest inactive mappings will be destroyed. * * @param vh VPN handle - * @param af address family, AF_INET or AF_INET6 + * @param result_af desired address family for the returned allocation + * can also be AF_UNSPEC * @param protocol protocol, IPPROTO_UDP or IPPROTO_TCP * @param peer target peer for the redirection * @param serv service descriptor to give to the peer @@ -173,8 +444,8 @@ GNUNET_VPN_cancel_request (struct GNUNET_VPN_RedirectionRequest *rr) * anyway) */ struct GNUNET_VPN_RedirectionRequest * -GNUNET_VPN_redirect_to_peer (struct GNUNET_VPN_Handle *rh, - int af, +GNUNET_VPN_redirect_to_peer (struct GNUNET_VPN_Handle *vh, + int result_af, uint8_t protocol, const struct GNUNET_PeerIdentity *peer, const GNUNET_HashCode *serv, @@ -183,7 +454,20 @@ GNUNET_VPN_redirect_to_peer (struct GNUNET_VPN_Handle *rh, GNUNET_VPN_AllocationCallback cb, void *cb_cls) { - return NULL; // FIXME + struct GNUNET_VPN_RedirectionRequest *rr; + + rr = GNUNET_malloc (sizeof (struct GNUNET_VPN_RedirectionRequest)); + rr->vh = vh; + rr->cb = cb; + rr->cb_cls = cb_cls; + rr->peer = *peer; + rr->serv = *serv; + rr->expiration_time = expiration_time; + rr->result_af = result_af; + rr->nac = nac; + rr->protocol = protocol; + queue_request (rr); + return rr; } @@ -213,7 +497,7 @@ GNUNET_VPN_redirect_to_peer (struct GNUNET_VPN_Handle *rh, * anyway) */ struct GNUNET_VPN_RedirectionRequest * -GNUNET_VPN_redirect_to_ip (struct GNUNET_VPN_Handle *rh, +GNUNET_VPN_redirect_to_ip (struct GNUNET_VPN_Handle *vh, int result_af, int addr_af, const void *addr, @@ -222,7 +506,33 @@ GNUNET_VPN_redirect_to_ip (struct GNUNET_VPN_Handle *rh, GNUNET_VPN_AllocationCallback cb, void *cb_cls) { - return NULL; // FIXME + struct GNUNET_VPN_RedirectionRequest *rr; + size_t alen; + + switch (addr_af) + { + case AF_INET: + alen = sizeof (struct in_addr); + break; + case AF_INET6: + alen = sizeof (struct in6_addr); + break; + default: + GNUNET_break (0); + return NULL; + } + rr = GNUNET_malloc (sizeof (struct GNUNET_VPN_RedirectionRequest) + alen); + rr->vh = vh; + rr->addr = &rr[1]; + rr->cb = cb; + rr->cb_cls = cb_cls; + rr->expiration_time = expiration_time; + rr->result_af = result_af; + rr->addr_af = addr_af; + rr->nac = nac; + memcpy (&rr[1], addr, alen); + queue_request (rr); + return rr; } @@ -240,6 +550,11 @@ GNUNET_VPN_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) vh = GNUNET_malloc (sizeof (struct GNUNET_VPN_Handle)); vh->cfg = cfg; vh->client = GNUNET_CLIENT_connect ("vpn", cfg); + if (NULL == vh->client) + { + GNUNET_free (vh); + return NULL; + } return vh; } @@ -253,7 +568,21 @@ void GNUNET_VPN_disconnect (struct GNUNET_VPN_Handle *vh) { GNUNET_assert (NULL == vh->rr_head); - GNUNET_CLIENT_disconnect (vh->client, GNUNET_NO); + if (NULL != vh->th) + { + GNUNET_CLIENT_notify_transmit_ready_cancel (vh->th); + vh->th = NULL; + } + if (NULL != vh->client) + { + GNUNET_CLIENT_disconnect (vh->client, GNUNET_NO); + vh->client = NULL; + } + if (GNUNET_SCHEDULER_NO_TASK != vh->rt) + { + GNUNET_SCHEDULER_cancel (vh->rt); + vh->rt = GNUNET_SCHEDULER_NO_TASK; + } GNUNET_free (vh); } -- cgit v1.2.3