X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftransport%2Fgnunet-service-transport.c;h=efe53e0215388c90c214f377540e225ca77e5ed1;hb=cf45b8dff29c366d51aa2e6ea6a64b99b514b9c9;hp=f689b54c01a38d75a561fae6eb94bda015bec6c1;hpb=d5e4648c1a755af9d9b47d446e750d228ce66f99;p=oweals%2Fgnunet.git diff --git a/src/transport/gnunet-service-transport.c b/src/transport/gnunet-service-transport.c index f689b54c0..efe53e021 100644 --- a/src/transport/gnunet-service-transport.c +++ b/src/transport/gnunet-service-transport.c @@ -67,8 +67,13 @@ /** * How long until a HELLO verification attempt should time out? + * Must be rather small, otherwise a partially successful HELLO + * validation (some addresses working) might not be available + * before a client's request for a connection fails for good. + * Besides, if a single request to an address takes a long time, + * then the peer is unlikely worthwhile anyway. */ -#define HELLO_VERIFICATION_TIMEOUT GNUNET_TIME_UNIT_MINUTES +#define HELLO_VERIFICATION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3) /** * How often do we re-add (cheaper) plugins to our list of plugins @@ -214,6 +219,11 @@ struct MessageQueue */ int internal_msg; + /** + * How important is the message? + */ + unsigned int priority; + }; @@ -355,12 +365,6 @@ struct NeighbourList */ uint32_t quota_in; - /** - * What is the latest version of our HELLO that we have - * sent to this neighbour? - */ - unsigned int hello_version_sent; - /** * How often has the other peer (recently) violated the * inbound traffic limit? Incremented by 10 per violation, @@ -437,70 +441,6 @@ struct TransportClient }; -/** - * Message used to ask a peer to validate receipt (to check an address - * from a HELLO). Followed by the address used. Note that the - * recipients response does not affirm that he has this address, - * only that he got the challenge message. - */ -struct ValidationChallengeMessage -{ - - /** - * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PING - */ - struct GNUNET_MessageHeader header; - - /** - * What are we signing and why? - */ - struct GNUNET_CRYPTO_RsaSignaturePurpose purpose; - - /** - * Random challenge number (in network byte order). - */ - uint32_t challenge GNUNET_PACKED; - - /** - * Who is the intended recipient? - */ - struct GNUNET_PeerIdentity target; -}; - - -/** - * Message used to validate a HELLO. If this was - * the right recipient, the response is a signature - * of the original validation request. The - * challenge is included in the confirmation to make - * matching of replies to requests possible. - */ -struct ValidationChallengeResponse -{ - - /** - * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PONG - */ - struct GNUNET_MessageHeader header; - - /** - * Random challenge number (in network byte order). - */ - uint32_t challenge GNUNET_PACKED; - - /** - * Who signed this message? - */ - struct GNUNET_PeerIdentity sender; - - /** - * Signature. - */ - struct GNUNET_CRYPTO_RsaSignature signature; - -}; - - /** * For each HELLO, we may have to validate multiple addresses; * each address gets its own request entry. @@ -512,12 +452,6 @@ struct ValidationAddress */ struct ValidationAddress *next; - /** - * Our challenge message. Points to after this - * struct, so this field should not be freed. - */ - struct ValidationChallengeMessage *msg; - /** * Name of the transport. */ @@ -533,6 +467,11 @@ struct ValidationAddress */ size_t addr_len; + /** + * Challenge number we used. + */ + uint32_t challenge; + /** * Set to GNUNET_YES if the challenge was met, * GNUNET_SYSERR if we know it failed, GNUNET_NO @@ -613,7 +552,7 @@ struct GNUNET_SCHEDULER_Handle *sched; /** * Our configuration. */ -struct GNUNET_CONFIGURATION_Handle *cfg; +const struct GNUNET_CONFIGURATION_Handle *cfg; /** * Linked list of all clients to this service. @@ -733,7 +672,7 @@ transmit_to_client_callback (void *cls, size_t size, void *buf) uint16_t msize; size_t tsize; const struct GNUNET_MessageHeader *msg; - struct GNUNET_NETWORK_TransmitHandle *th; + struct GNUNET_CONNECTION_TransmitHandle *th; char *cbuf; if (buf == NULL) @@ -758,6 +697,11 @@ transmit_to_client_callback (void *cls, size_t size, void *buf) msize = ntohs (msg->size); if (msize + tsize > size) break; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting message of type %u to client.\n", + ntohs (msg->type)); +#endif client->message_queue_head = q->next; if (q->next == NULL) client->message_queue_tail = NULL; @@ -766,9 +710,9 @@ transmit_to_client_callback (void *cls, size_t size, void *buf) GNUNET_free (q); client->message_count--; } - GNUNET_assert (tsize > 0); if (NULL != q) { + GNUNET_assert (msize >= sizeof (struct GNUNET_MessageHeader)); th = GNUNET_SERVER_notify_transmit_ready (client->client, msize, GNUNET_TIME_UNIT_FOREVER_REL, @@ -796,7 +740,7 @@ transmit_to_client (struct TransportClient *client, { struct ClientMessageQueueEntry *q; uint16_t msize; - struct GNUNET_NETWORK_TransmitHandle *th; + struct GNUNET_CONNECTION_TransmitHandle *th; if ((client->message_count >= MAX_PENDING) && (GNUNET_YES == may_drop)) { @@ -809,6 +753,7 @@ transmit_to_client (struct TransportClient *client, } client->message_count++; msize = ntohs (msg->size); + GNUNET_assert (msize >= sizeof (struct GNUNET_MessageHeader)); q = GNUNET_malloc (sizeof (struct ClientMessageQueueEntry) + msize); memcpy (&q[1], msg, msize); /* append to message queue */ @@ -863,6 +808,24 @@ try_alternative_plugins (struct NeighbourList *neighbour) } +/** + * The peer specified by the given neighbour has timed-out or a plugin + * has disconnected. We may either need to do nothing (other plugins + * still up), or trigger a full disconnect and clean up. This + * function updates our state and do the necessary notifications. + * Also notifies our clients that the neighbour is now officially + * gone. + * + * @param n the neighbour list entry for the peer + * @param check should we just check if all plugins + * disconnected or must we ask all plugins to + * disconnect? + */ +static void +disconnect_neighbour (struct NeighbourList *n, + int check); + + /** * Check the ready list for the given neighbour and * if a plugin is ready for transmission (and if we @@ -870,7 +833,8 @@ try_alternative_plugins (struct NeighbourList *neighbour) * * @param neighbour target peer for which to check the plugins */ -static void try_transmission_to_peer (struct NeighbourList *neighbour); +static void +try_transmission_to_peer (struct NeighbourList *neighbour); /** @@ -920,13 +884,19 @@ transmit_send_continuation (void *cls, else { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transmission failed, marking connection as down.\n"); + "Transmission to peer `%s' failed, marking connection as down.\n", + GNUNET_i2s(target)); rl->connected = GNUNET_NO; + rl->plugin_handle = NULL; } if (!mq->internal_msg) rl->transmit_ready = GNUNET_YES; if (mq->client != NULL) { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Notifying client %p about failed transission to peer `%4s'.\n", + mq->client, + GNUNET_i2s(target)); send_ok_msg.header.size = htons (sizeof (send_ok_msg)); send_ok_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK); send_ok_msg.success = htonl (result); @@ -937,115 +907,10 @@ transmit_send_continuation (void *cls, GNUNET_free (mq); /* one plugin just became ready again, try transmitting another message (if available) */ - try_transmission_to_peer (n); -} - - - - -/** - * We could not use an existing (or validated) connection to - * talk to a peer. Try addresses that have not yet been - * validated. - * - * @param n neighbour we want to communicate with - * @return plugin ready to talk, or NULL if none is available - */ -static struct ReadyList * -try_unvalidated_addresses (struct NeighbourList *n) -{ - struct ValidationList *vl; - struct ValidationAddress *va; - struct GNUNET_PeerIdentity id; - struct GNUNET_TIME_Absolute now; - unsigned int total; - unsigned int cnt; - struct ReadyList *rl; - struct TransportPlugin *plugin; - -#if DEBUG_TRANSPORT - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Trying to connect to `%4s' using unvalidated addresses\n", - GNUNET_i2s (&n->id)); -#endif - /* NOTE: this function needs to not only identify the - plugin but also setup "plugin_handle", binding it to the - right address using the plugin's "send_to" API */ - now = GNUNET_TIME_absolute_get (); - vl = pending_validations; - while (vl != NULL) - { - GNUNET_CRYPTO_hash (&vl->publicKey, - sizeof (struct - GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), - &id.hashPubKey); - if (0 == memcmp (&id, &n->id, sizeof (struct GNUNET_PeerIdentity))) - break; - vl = vl->next; - } - if (vl == NULL) - { -#if DEBUG_TRANSPORT - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "No unvalidated address found for peer `%4s'\n", - GNUNET_i2s (&n->id)); -#endif - return NULL; - } - total = 0; - cnt = 0; - va = vl->addresses; - while (va != NULL) - { - cnt++; - if (va->expiration.value > now.value) - total++; - va = va->next; - } - if (total == 0) - { -#if DEBUG_TRANSPORT - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "All %u unvalidated addresses for peer have expired\n", - cnt); -#endif - return NULL; - } - total = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total); - for (va = vl->addresses; va != NULL; va = va->next) - { - if (va->expiration.value <= now.value) - continue; - if (total > 0) - { - total--; - continue; - } -#if DEBUG_TRANSPORT - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, - "Trying unvalidated address of `%s' transport\n", - va->transport_name); -#endif - plugin = find_transport (va->transport_name); - if (plugin == NULL) - { - GNUNET_break (0); - break; - } - rl = GNUNET_malloc (sizeof (struct ReadyList)); - rl->next = n->plugins; - n->plugins = rl; - rl->plugin = plugin; - rl->plugin_handle = plugin->api->send_to (plugin->api->cls, - &n->id, - NULL, - NULL, - GNUNET_TIME_UNIT_ZERO, - &va->msg[1], va->addr_len); - rl->transmit_ready = GNUNET_YES; - return rl; - } - return NULL; + if (result == GNUNET_OK) + try_transmission_to_peer (n); + else + disconnect_neighbour (n, GNUNET_YES); } @@ -1093,8 +958,6 @@ try_transmission_to_peer (struct NeighbourList *neighbour) } pos = pos->next; } - if (rl == NULL) - rl = try_unvalidated_addresses (neighbour); if (rl == NULL) { #if DEBUG_TRANSPORT @@ -1107,6 +970,11 @@ try_transmission_to_peer (struct NeighbourList *neighbour) { rl->connect_attempts++; rl->connected = GNUNET_YES; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Establishing fresh connection with `%4s' via plugin `%s'\n", + GNUNET_i2s (&neighbour->id), rl->plugin->short_name); +#endif } neighbour->messages = mq->next; mq->plugin = rl->plugin; @@ -1123,6 +991,7 @@ try_transmission_to_peer (struct NeighbourList *neighbour) rl->plugin_handle, rl, &neighbour->id, + mq->priority, mq->message, GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, &transmit_send_continuation, mq); @@ -1133,12 +1002,14 @@ try_transmission_to_peer (struct NeighbourList *neighbour) * Send the specified message to the specified peer. * * @param client source of the transmission request (can be NULL) + * @param priority how important is the message * @param msg message to send * @param is_internal is this an internal message * @param neighbour handle to the neighbour for transmission */ static void transmit_to_peer (struct TransportClient *client, + unsigned int priority, const struct GNUNET_MessageHeader *msg, int is_internal, struct NeighbourList *neighbour) { @@ -1174,6 +1045,7 @@ transmit_to_peer (struct TransportClient *client, mq->message = m; mq->neighbour = neighbour; mq->internal_msg = is_internal; + mq->priority = priority; /* find tail */ mqe = neighbour->messages; @@ -1194,6 +1066,9 @@ transmit_to_peer (struct TransportClient *client, } +/** + * FIXME: document. + */ struct GeneratorContext { struct TransportPlugin *plug_pos; @@ -1202,6 +1077,9 @@ struct GeneratorContext }; +/** + * FIXME: document. + */ static size_t address_generator (void *cls, size_t max, void *buf) { @@ -1238,7 +1116,8 @@ refresh_hello () #if DEBUG_TRANSPORT GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, - "Refreshing my HELLO\n"); + "Refreshing my `%s'\n", + "HELLO"); #endif gc.plug_pos = plugins; gc.addr_pos = plugins != NULL ? plugins->addresses : NULL; @@ -1259,7 +1138,13 @@ refresh_hello () npos = neighbours; while (npos != NULL) { - transmit_to_peer (NULL, +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + "Transmitting updated `%s' to neighbour `%4s'\n", + "HELLO", + GNUNET_i2s(&npos->id)); +#endif + transmit_to_peer (NULL, 0, (const struct GNUNET_MessageHeader *) our_hello, GNUNET_YES, npos); npos = npos->next; @@ -1298,9 +1183,9 @@ update_addresses (struct TransportPlugin *plugin, int fresh) struct AddressList *next; int expired; - if (plugin->address_update_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + if (plugin->address_update_task != GNUNET_SCHEDULER_NO_TASK) GNUNET_SCHEDULER_cancel (plugin->env.sched, plugin->address_update_task); - plugin->address_update_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + plugin->address_update_task = GNUNET_SCHEDULER_NO_TASK; now = GNUNET_TIME_absolute_get (); min_remaining = GNUNET_TIME_UNIT_FOREVER_REL; expired = GNUNET_NO; @@ -1333,9 +1218,6 @@ update_addresses (struct TransportPlugin *plugin, int fresh) if (min_remaining.value < GNUNET_TIME_UNIT_FOREVER_REL.value) plugin->address_update_task = GNUNET_SCHEDULER_add_delayed (plugin->env.sched, - GNUNET_NO, - GNUNET_SCHEDULER_PRIORITY_IDLE, - GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, min_remaining, &expire_address_task, plugin); @@ -1352,7 +1234,7 @@ static void expire_address_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct TransportPlugin *plugin = cls; - plugin->address_update_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + plugin->address_update_task = GNUNET_SCHEDULER_NO_TASK; update_addresses (plugin, GNUNET_NO); } @@ -1380,11 +1262,6 @@ plugin_env_notify_address (void *cls, struct AddressList *al; struct GNUNET_TIME_Absolute abex; -#if DEBUG_TRANSPORT - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Plugin `%s' informs us about a new address `%s'\n", name, - GNUNET_a2s(addr, addrlen)); -#endif abex = GNUNET_TIME_relative_to_absolute (expires); GNUNET_assert (p == find_transport (name)); @@ -1399,6 +1276,11 @@ plugin_env_notify_address (void *cls, } al = al->next; } +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Plugin `%s' informs us about a new address `%s'\n", name, + GNUNET_a2s(addr, addrlen)); +#endif al = GNUNET_malloc (sizeof (struct AddressList) + addrlen); al->addr = &al[1]; al->next = p->addresses; @@ -1410,6 +1292,9 @@ plugin_env_notify_address (void *cls, } +/** + * FIXME: document. + */ struct LookupHelloContext { GNUNET_TRANSPORT_AddressCallback iterator; @@ -1418,6 +1303,9 @@ struct LookupHelloContext }; +/** + * FIXME: document. + */ static int lookup_address_callback (void *cls, const char *tname, @@ -1430,6 +1318,9 @@ lookup_address_callback (void *cls, } +/** + * FIXME: document. + */ static void lookup_hello_callback (void *cls, const struct GNUNET_PeerIdentity *peer, @@ -1552,7 +1443,7 @@ list_validated_addresses (void *cls, size_t max, void *buf) return 0; ret = GNUNET_HELLO_add_address ((*va)->transport_name, (*va)->expiration, - &(*va)->msg[1], (*va)->addr_len, buf, max); + &(*va)[1], (*va)->addr_len, buf, max); *va = (*va)->next; return ret; } @@ -1568,8 +1459,10 @@ cleanup_validation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) struct ValidationList *pos; struct ValidationList *prev; struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Absolute first; struct GNUNET_HELLO_Message *hello; struct GNUNET_PeerIdentity pid; + struct NeighbourList *n; #if DEBUG_TRANSPORT GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, @@ -1599,6 +1492,9 @@ cleanup_validation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) "HELLO", GNUNET_i2s (&pid)); #endif GNUNET_PEERINFO_add_peer (cfg, sched, &pid, hello); + n = find_neighbour (&pid); + if (NULL != n) + try_transmission_to_peer (n); GNUNET_free (hello); while (NULL != (va = pos->addresses)) { @@ -1619,16 +1515,132 @@ cleanup_validation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) /* finally, reschedule cleanup if needed; list is ordered by timeout, so we need the last element... */ + if (NULL != pending_validations) + { + first = pending_validations->timeout; + pos = pending_validations; + while (pos != NULL) + { + first = GNUNET_TIME_absolute_min (first, pos->timeout); + pos = pos->next; + } + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_TIME_absolute_get_remaining (first), + &cleanup_validation, NULL); + } +} + + + + +/** + * Function that will be called if we receive a validation + * of an address challenge that we transmitted to another + * peer. Note that the validation should only be considered + * acceptable if the challenge matches AND if the sender + * address is at least a plausible address for this peer + * (otherwise we may be seeing a MiM attack). + * + * @param cls closure + * @param name name of the transport that generated the address + * @param peer who responded to our challenge + * @param challenge the challenge number we presumably used + * @param sender_addr string describing our sender address (as observed + * by the other peer in human-readable format) + */ +static void +plugin_env_notify_validation (void *cls, + const char *name, + const struct GNUNET_PeerIdentity *peer, + uint32_t challenge, + const char *sender_addr) +{ + unsigned int not_done; + int matched; + struct ValidationList *pos; + struct ValidationAddress *va; + struct GNUNET_PeerIdentity id; + pos = pending_validations; - while ((pos != NULL) && (pos->next != NULL)) - pos = pos->next; - if (NULL != pos) - GNUNET_SCHEDULER_add_delayed (sched, - GNUNET_NO, - GNUNET_SCHEDULER_PRIORITY_IDLE, - GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, - GNUNET_TIME_absolute_get_remaining - (pos->timeout), &cleanup_validation, NULL); + while (pos != NULL) + { + GNUNET_CRYPTO_hash (&pos->publicKey, + sizeof (struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &id.hashPubKey); + if (0 == + memcmp (peer, &id, sizeof (struct GNUNET_PeerIdentity))) + break; + pos = pos->next; + } + if (pos == NULL) + { + /* TODO: call statistics (unmatched PONG) */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Received validation response but have no record of any validation request for `%4s'. Ignoring.\n"), + GNUNET_i2s(peer)); + return; + } + not_done = 0; + matched = GNUNET_NO; + va = pos->addresses; + while (va != NULL) + { + if (va->challenge == challenge) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Confirmed validity of address, peer `%4s' has address `%s'.\n", + GNUNET_i2s (peer), + GNUNET_a2s ((const struct sockaddr*) &va[1], + va->addr_len)); +#endif + GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK, + _("Another peer saw us using the address `%s' via `%s'. If this is not plausible, this address should be listed in the configuration as implausible to avoid MiM attacks.\n"), + sender_addr, + name); + va->ok = GNUNET_YES; + va->expiration = + GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION); + matched = GNUNET_YES; + } + if (va->ok != GNUNET_YES) + not_done++; + va = va->next; + } + if (GNUNET_NO == matched) + { + /* TODO: call statistics (unmatched PONG) */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Received `%s' message but have no record of a matching `%s' message. Ignoring.\n"), + "PONG", "PING"); + } + if (0 == not_done) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "All addresses validated, will now construct `%s' for `%4s'.\n", + "HELLO", + GNUNET_i2s (peer)); +#endif + pos->timeout.value = 0; + GNUNET_SCHEDULER_add_with_priority (sched, + GNUNET_SCHEDULER_PRIORITY_IDLE, + &cleanup_validation, NULL); + } + else + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Still waiting for %u additional `%s' messages before constructing `%s' for `%4s'.\n", + not_done, + "PONG", + "HELLO", + GNUNET_i2s (peer)); +#endif + } } @@ -1665,7 +1677,6 @@ run_validation (void *cls, struct ValidationList *e = cls; struct TransportPlugin *tp; struct ValidationAddress *va; - struct ValidationChallengeMessage *vcm; struct GNUNET_PeerIdentity id; tp = find_transport (tname); @@ -1688,25 +1699,14 @@ run_validation (void *cls, tname, GNUNET_i2s(&id)); - va = GNUNET_malloc (sizeof (struct ValidationAddress) + - sizeof (struct ValidationChallengeMessage) + addrlen); + va = GNUNET_malloc (sizeof (struct ValidationAddress) + addrlen); va->next = e->addresses; e->addresses = va; - vcm = (struct ValidationChallengeMessage *) &va[1]; - va->msg = vcm; va->transport_name = GNUNET_strdup (tname); va->addr_len = addrlen; - vcm->header.size = - htons (sizeof (struct ValidationChallengeMessage) + addrlen); - vcm->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PING); - vcm->purpose.size = - htonl (sizeof (struct ValidationChallengeMessage) + addrlen - - sizeof (struct GNUNET_MessageHeader)); - vcm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO); - vcm->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, - (unsigned int) -1); - vcm->target = id; - memcpy (&vcm[1], addr, addrlen); + va->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + (unsigned int) -1); + memcpy (&va[1], addr, addrlen); return GNUNET_OK; } @@ -1719,12 +1719,14 @@ run_validation (void *cls, static void check_hello_validated (void *cls, const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_HELLO_Message *h, uint32_t trust) + const struct GNUNET_HELLO_Message *h, + uint32_t trust) { struct CheckHelloValidatedContext *chvc = cls; struct ValidationAddress *va; struct TransportPlugin *tp; int first_call; + struct GNUNET_PeerIdentity apeer; first_call = GNUNET_NO; if (chvc->e == NULL) @@ -1755,35 +1757,41 @@ check_hello_validated (void *cls, if (h != NULL) return; /* wait for next call */ /* finally, transmit validation attempts */ + GNUNET_assert (GNUNET_OK == + GNUNET_HELLO_get_id (chvc->hello, + &apeer)); +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Ready to validate addresses from `%s' message for peer `%4s'\n", + "HELLO", GNUNET_i2s (&apeer)); +#endif va = chvc->e->addresses; while (va != NULL) { #if DEBUG_TRANSPORT GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Establishing `%s' connection to validate `%s' of `%4s' (sending our `%s')\n", + "Establishing `%s' connection to validate `%s' address `%s' of `%4s'\n", va->transport_name, - "HELLO", GNUNET_i2s (&va->msg->target), "HELLO"); + "HELLO", + GNUNET_a2s ((const struct sockaddr*) &va[1], + va->addr_len), + GNUNET_i2s (&apeer)); #endif tp = find_transport (va->transport_name); GNUNET_assert (tp != NULL); - if (NULL == - tp->api->send_to (tp->api->cls, - &va->msg->target, - (const struct GNUNET_MessageHeader *) our_hello, - &va->msg->header, - HELLO_VERIFICATION_TIMEOUT, - &va->msg[1], va->addr_len)) + if (GNUNET_OK != + tp->api->validate (tp->api->cls, + &apeer, + va->challenge, + HELLO_VERIFICATION_TIMEOUT, + &va[1], + va->addr_len)) va->ok = GNUNET_SYSERR; va = va->next; } - if (chvc->e->next == NULL) - GNUNET_SCHEDULER_add_delayed (sched, - GNUNET_NO, - GNUNET_SCHEDULER_PRIORITY_IDLE, - GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, - GNUNET_TIME_absolute_get_remaining - (chvc->e->timeout), &cleanup_validation, - NULL); + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_TIME_absolute_get_remaining (chvc->e->timeout), + &cleanup_validation, NULL); GNUNET_free (chvc); } @@ -1869,204 +1877,42 @@ process_hello (struct TransportPlugin *plugin, /** - * Handle PING-message. If the plugin that gave us the message is - * able to queue the PONG immediately, we only queue one PONG. - * Otherwise we send at most TWO PONG messages, one via an unconfirmed - * transport and one via a confirmed transport. Both addresses are - * selected randomly among those available. - * - * @param plugin plugin that gave us the message - * @param sender claimed sender of the PING - * @param plugin_context context that might be used to send response - * @param message the actual message - */ -static void -process_ping (struct TransportPlugin *plugin, - const struct GNUNET_PeerIdentity *sender, - void *plugin_context, - const struct GNUNET_MessageHeader *message) -{ - const struct ValidationChallengeMessage *vcm; - struct ValidationChallengeResponse vcr; - uint16_t msize; - struct NeighbourList *n; - -#if DEBUG_TRANSPORT - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, - "Processing PING\n"); -#endif - msize = ntohs (message->size); - if (msize < sizeof (struct ValidationChallengeMessage)) - { - GNUNET_break_op (0); - return; - } - vcm = (const struct ValidationChallengeMessage *) message; - if (0 != memcmp (&vcm->target, - &my_identity, sizeof (struct GNUNET_PeerIdentity))) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _("Received `%s' message not destined for me!\n"), "PING"); - /* TODO: call statistics */ - return; - } - if ((ntohl (vcm->purpose.size) != - msize - sizeof (struct GNUNET_MessageHeader)) - || (ntohl (vcm->purpose.purpose) != - GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO)) - { - GNUNET_break_op (0); - return; - } - msize -= sizeof (struct ValidationChallengeMessage); - if (GNUNET_OK != - plugin->api->address_suggested (plugin->api->cls, &vcm[1], msize)) - { - GNUNET_break_op (0); - return; - } - vcr.header.size = htons (sizeof (struct ValidationChallengeResponse)); - vcr.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG); - vcr.challenge = vcm->challenge; - vcr.sender = my_identity; - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_rsa_sign (my_private_key, - &vcm->purpose, &vcr.signature)); -#if EXTRA_CHECKS - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_rsa_verify - (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO, &vcm->purpose, - &vcr.signature, &my_public_key)); -#endif -#if DEBUG_TRANSPORT - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, - "Trying to transmit PONG using inbound connection\n"); -#endif - n = find_neighbour (sender); - if (n == NULL) - { - GNUNET_break (0); - return; - } - transmit_to_peer (NULL, &vcr.header, GNUNET_YES, n); -} - - -/** - * Handle PONG-message. - * - * @param message the actual message - */ -static void -process_pong (struct TransportPlugin *plugin, - const struct GNUNET_MessageHeader *message) -{ - const struct ValidationChallengeResponse *vcr; - struct ValidationList *pos; - struct GNUNET_PeerIdentity peer; - struct ValidationAddress *va; - int all_done; - int matched; - -#if DEBUG_TRANSPORT - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, - "Processing PONG\n"); -#endif - vcr = (const struct ValidationChallengeResponse *) message; - pos = pending_validations; - while (pos != NULL) - { - GNUNET_CRYPTO_hash (&pos->publicKey, - sizeof (struct - GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), - &peer.hashPubKey); - if (0 == - memcmp (&peer, &vcr->sender, sizeof (struct GNUNET_PeerIdentity))) - break; - pos = pos->next; - } - if (pos == NULL) - { - /* TODO: call statistics (unmatched PONG) */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ - ("Received `%s' message but have no record of a matching `%s' message. Ignoring.\n"), - "PONG", "PING"); - return; - } - all_done = GNUNET_YES; - matched = GNUNET_NO; - va = pos->addresses; - while (va != NULL) - { - if (va->msg->challenge == vcr->challenge) - { - if (GNUNET_OK != - GNUNET_CRYPTO_rsa_verify - (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO, &va->msg->purpose, - &vcr->signature, &pos->publicKey)) - { - /* this could rarely happen if we used the same - challenge number for the peer for two different - transports / addresses, but the likelihood is - very small... */ - GNUNET_break_op (0); - } - else - { -#if DEBUG_TRANSPORT - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Confirmed validity of peer address.\n"); -#endif - va->ok = GNUNET_YES; - va->expiration = - GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION); - matched = GNUNET_YES; - } - } - if (va->ok != GNUNET_YES) - all_done = GNUNET_NO; - va = va->next; - } - if (GNUNET_NO == matched) - { - /* TODO: call statistics (unmatched PONG) */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ - ("Received `%s' message but have no record of a matching `%s' message. Ignoring.\n"), - "PONG", "PING"); - } - if (GNUNET_YES == all_done) - { - pos->timeout.value = 0; - GNUNET_SCHEDULER_add_delayed (sched, - GNUNET_NO, - GNUNET_SCHEDULER_PRIORITY_IDLE, - GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, - GNUNET_TIME_UNIT_ZERO, - &cleanup_validation, NULL); - } -} - - -/** - * The peer specified by the given neighbour has timed-out. Update - * our state and do the necessary notifications. Also notifies - * our clients that the neighbour is now officially gone. + * The peer specified by the given neighbour has timed-out or a plugin + * has disconnected. We may either need to do nothing (other plugins + * still up), or trigger a full disconnect and clean up. This + * function updates our state and do the necessary notifications. + * Also notifies our clients that the neighbour is now officially + * gone. * * @param n the neighbour list entry for the peer + * @param check should we just check if all plugins + * disconnected or must we ask all plugins to + * disconnect? */ static void -disconnect_neighbour (struct NeighbourList *n) +disconnect_neighbour (struct NeighbourList *n, + int check) { struct ReadyList *rpos; struct NeighbourList *npos; struct NeighbourList *nprev; struct MessageQueue *mq; + + if (GNUNET_YES == check) + { + rpos = n->plugins; + while (NULL != rpos) + { + if (GNUNET_YES == rpos->connected) + return; /* still connected */ + rpos = rpos->next; + } + } #if DEBUG_TRANSPORT GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, - "Disconnecting from neighbour\n"); + "Disconnecting from `%4s'\n", + GNUNET_i2s(&n->id)); #endif /* remove n from neighbours list */ nprev = NULL; @@ -2085,13 +1931,16 @@ disconnect_neighbour (struct NeighbourList *n) /* notify all clients about disconnect */ notify_clients_disconnect (&n->id); - /* clean up all plugins, cancel connections & pending transmissions */ + /* clean up all plugins, cancel connections and pending transmissions */ while (NULL != (rpos = n->plugins)) { n->plugins = rpos->next; GNUNET_assert (rpos->neighbour == n); - rpos->plugin->api->cancel (rpos->plugin->api->cls, - rpos->plugin_handle, rpos, &n->id); + if (GNUNET_YES == rpos->connected) + rpos->plugin->api->cancel (rpos->plugin->api->cls, + rpos->plugin_handle, + rpos, + &n->id); GNUNET_free (rpos); } @@ -2102,7 +1951,9 @@ disconnect_neighbour (struct NeighbourList *n) GNUNET_assert (mq->neighbour == n); GNUNET_free (mq); } - + if (n->timeout_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel (sched, + n->timeout_task); /* finally, free n itself */ GNUNET_free (n); } @@ -2148,14 +1999,14 @@ neighbour_timeout_task (void *cls, #if DEBUG_TRANSPORT GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, - "Neighbour has timed out!\n"); + "Neighbour `%4s' has timed out!\n", + GNUNET_i2s(&n->id)); #endif - n->timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; - disconnect_neighbour (n); + n->timeout_task = GNUNET_SCHEDULER_NO_TASK; + disconnect_neighbour (n, GNUNET_NO); } - /** * Create a fresh entry in our neighbour list for the given peer. * Will try to transmit our current HELLO to the new neighbour. Also @@ -2184,14 +2035,10 @@ setup_new_neighbour (const struct GNUNET_PeerIdentity *peer) GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); n->quota_in = (GNUNET_CONSTANTS_DEFAULT_BPM_IN_OUT + 59999) / (60 * 1000); add_plugins (n); - n->hello_version_sent = our_hello_version; n->timeout_task = GNUNET_SCHEDULER_add_delayed (sched, - GNUNET_NO, - GNUNET_SCHEDULER_PRIORITY_IDLE, - GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, &neighbour_timeout_task, n); - transmit_to_peer (NULL, + transmit_to_peer (NULL, 0, (const struct GNUNET_MessageHeader *) our_hello, GNUNET_YES, n); notify_clients_connect (peer, GNUNET_TIME_UNIT_FOREVER_REL); @@ -2205,6 +2052,7 @@ setup_new_neighbour (const struct GNUNET_PeerIdentity *peer) * reducing the rate at which they read from the socket * and generally forward to our receive callback. * + * @param cls the "struct TransportPlugin *" we gave to the plugin * @param plugin_context value to pass to this plugin * to respond to the given peer (use is optional, * but may speed up processing) @@ -2259,13 +2107,19 @@ plugin_env_receive (void *cls, } if (message == NULL) { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + "Receive failed from `%4s', triggering disconnect\n", + GNUNET_i2s(&n->id)); +#endif + /* TODO: call stats */ if ((service_context != NULL) && (service_context->plugin_handle == plugin_context)) { service_context->connected = GNUNET_NO; service_context->plugin_handle = NULL; } - /* TODO: call stats */ + disconnect_neighbour (n, GNUNET_YES); return NULL; } #if DEBUG_TRANSPORT @@ -2293,9 +2147,7 @@ plugin_env_receive (void *cls, n->peer_timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); n->timeout_task = - GNUNET_SCHEDULER_add_delayed (sched, GNUNET_NO, - GNUNET_SCHEDULER_PRIORITY_IDLE, - GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_SCHEDULER_add_delayed (sched, GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, &neighbour_timeout_task, n); update_quota (n); @@ -2307,7 +2159,8 @@ plugin_env_receive (void *cls, _ ("Dropping incoming message due to repeated bandwidth quota violations.\n")); /* TODO: call stats */ - GNUNET_assert (NULL != service_context->neighbour); + GNUNET_assert ( (service_context == NULL) || + (NULL != service_context->neighbour) ); return service_context; } switch (ntohs (message->type)) @@ -2315,20 +2168,16 @@ plugin_env_receive (void *cls, case GNUNET_MESSAGE_TYPE_HELLO: #if DEBUG_TRANSPORT GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Receiving `%s' message from other peer.\n", "HELLO"); + "Receiving `%s' message from `%4s'.\n", "HELLO", + GNUNET_i2s(peer)); #endif process_hello (plugin, message); #if DEBUG_TRANSPORT GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending `%s' message to connecting peer.\n", "ACK"); + "Sending `%s' message to connecting peer `%4s'.\n", "ACK", + GNUNET_i2s(peer)); #endif - transmit_to_peer (NULL, &ack, GNUNET_YES, n); - break; - case GNUNET_MESSAGE_TYPE_TRANSPORT_PING: - process_ping (plugin, peer, plugin_context, message); - break; - case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG: - process_pong (plugin, message); + transmit_to_peer (NULL, 0, &ack, GNUNET_YES, n); break; case GNUNET_MESSAGE_TYPE_TRANSPORT_ACK: n->saw_ack = GNUNET_YES; @@ -2336,8 +2185,9 @@ plugin_env_receive (void *cls, default: #if DEBUG_TRANSPORT GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received message of type %u from other peer, sending to all clients.\n", - ntohs (message->type)); + "Received message of type %u from `%4s', sending to all clients.\n", + ntohs (message->type), + GNUNET_i2s(peer)); #endif /* transmit message to all clients */ im = GNUNET_malloc (sizeof (struct InboundMessage) + msize); @@ -2355,7 +2205,8 @@ plugin_env_receive (void *cls, } GNUNET_free (im); } - GNUNET_assert (NULL != service_context->neighbour); + GNUNET_assert ( (service_context == NULL) || + (NULL != service_context->neighbour) ); return service_context; } @@ -2403,7 +2254,8 @@ handle_start (void *cls, { #if DEBUG_TRANSPORT GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending our own HELLO to new client\n"); + "Sending our own `%s' to new client\n", + "HELLO"); #endif transmit_to_client (c, (const struct GNUNET_MessageHeader *) our_hello, @@ -2515,7 +2367,7 @@ handle_send (void *cls, ntohs (obmm->size), ntohs (obmm->type), GNUNET_i2s (&obm->peer)); #endif - transmit_to_peer (tc, obmm, GNUNET_NO, n); + transmit_to_peer (tc, ntohl(obm->priority), obmm, GNUNET_NO, n); GNUNET_SERVER_receive_done (client, GNUNET_OK); } @@ -2582,11 +2434,20 @@ handle_try_connect (void *cls, tcm = (const struct TryConnectMessage *) message; #if DEBUG_TRANSPORT GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received `%s' request from client asking to connect to `%4s'\n", - "TRY_CONNECT", GNUNET_i2s (&tcm->peer)); + "Received `%s' request from client %p asking to connect to `%4s'\n", + "TRY_CONNECT", + client, + GNUNET_i2s (&tcm->peer)); #endif if (NULL == find_neighbour (&tcm->peer)) setup_new_neighbour (&tcm->peer); +#if DEBUG_TRANSPORT + else + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Client asked to connect to `%4s', but connection already exists\n", + "TRY_CONNECT", + GNUNET_i2s (&tcm->peer)); +#endif GNUNET_SERVER_receive_done (client, GNUNET_OK); } @@ -2620,10 +2481,13 @@ create_environment (struct TransportPlugin *plug) plug->env.cfg = cfg; plug->env.sched = sched; plug->env.my_public_key = &my_public_key; + plug->env.my_private_key = my_private_key; + plug->env.my_identity = &my_identity; plug->env.cls = plug; plug->env.receive = &plugin_env_receive; plug->env.lookup = &plugin_env_lookup_address; plug->env.notify_address = &plugin_env_notify_address; + plug->env.notify_validation = &plugin_env_notify_validation; plug->env.default_quota_in = (GNUNET_CONSTANTS_DEFAULT_BPM_IN_OUT + 59999) / (60 * 1000); plug->env.max_connections = max_connect_per_transport; } @@ -2707,6 +2571,42 @@ client_disconnect_notification (void *cls, } +/** + * Function called when the service shuts down. Unloads our plugins. + * + * @param cls closure, unused + * @param tc task context (unused) + */ +static void +unload_plugins (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct TransportPlugin *plug; + struct AddressList *al; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transport service is unloading plugins...\n"); +#endif + while (NULL != (plug = plugins)) + { + plugins = plug->next; + GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api)); + GNUNET_free (plug->lib_name); + GNUNET_free (plug->short_name); + while (NULL != (al = plug->addresses)) + { + plug->addresses = al->next; + GNUNET_free (al); + } + GNUNET_free (plug); + } + if (my_private_key != NULL) + GNUNET_CRYPTO_rsa_key_free (my_private_key); + GNUNET_free_non_null (our_hello); +} + + /** * Initiate transport service. * @@ -2718,7 +2618,8 @@ client_disconnect_notification (void *cls, static void run (void *cls, struct GNUNET_SCHEDULER_Handle *s, - struct GNUNET_SERVER_Handle *serv, struct GNUNET_CONFIGURATION_Handle *c) + struct GNUNET_SERVER_Handle *serv, + const struct GNUNET_CONFIGURATION_Handle *c) { char *plugs; char *pos; @@ -2780,6 +2681,9 @@ run (void *cls, } GNUNET_free (plugs); } + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_TIME_UNIT_FOREVER_REL, + &unload_plugins, NULL); if (no_transports) refresh_hello (); #if DEBUG_TRANSPORT @@ -2791,41 +2695,6 @@ run (void *cls, } -/** - * Function called when the service shuts - * down. Unloads our plugins. - * - * @param cls closure - * @param cfg configuration to use - */ -static void -unload_plugins (void *cls, struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct TransportPlugin *plug; - struct AddressList *al; - -#if DEBUG_TRANSPORT - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transport service is unloading plugins...\n"); -#endif - while (NULL != (plug = plugins)) - { - plugins = plug->next; - GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api)); - GNUNET_free (plug->lib_name); - GNUNET_free (plug->short_name); - while (NULL != (al = plug->addresses)) - { - plug->addresses = al->next; - GNUNET_free (al); - } - GNUNET_free (plug); - } - if (my_private_key != NULL) - GNUNET_CRYPTO_rsa_key_free (my_private_key); -} - - /** * The main function for the transport service. * @@ -2837,10 +2706,10 @@ int main (int argc, char *const *argv) { return (GNUNET_OK == - GNUNET_SERVICE_run (argc, - argv, - "transport", - &run, NULL, &unload_plugins, NULL)) ? 0 : 1; + GNUNET_SERVICE_run (argc, + argv, + "transport", + &run, NULL)) ? 0 : 1; } /* end of gnunet-service-transport.c */