📄 transport.c
字号:
/* This file is part of GNUnet (C) 2001, 2002, 2004, 2005, 2006, 2007 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*//** * @file applications/transport/transport.c * @brief Methods to access the transport layer. * @author Christian Grothoff */#include "platform.h"#include "gnunet_util.h"#include "gnunet_protocols.h"#include "gnunet_core.h"#include "gnunet_identity_service.h"#include "gnunet_transport_service.h"#define DEBUG_TRANSPORT GNUNET_NOstatic GNUNET_CoreAPIForTransport ctapi;static GNUNET_CoreAPIForPlugins *coreAPI;static GNUNET_Identity_ServiceAPI *identity;/** * Note that this array MUST not be modified * (in size/NULLs) after gnunetd has started * to go multi-threaded! */static GNUNET_TransportAPI **tapis = NULL;static unsigned int tapis_count = 0;static unsigned long long hello_live;static struct GNUNET_Mutex *tapis_lock;static struct GNUNET_Mutex *lock;static struct GNUNET_GE_Context *ectx;#define HELLO_RECREATE_FREQ (5 * GNUNET_CRON_MINUTES)#define CHECK_IT GNUNET_NO#if CHECK_IT#include "check.c"#else#define CHECK(s) do {} while(0)#endif/** * Close the session with the remote node. * @return GNUNET_OK on success, GNUNET_SYSERR on error */static intassertAssociated (GNUNET_TSession * tsession, const char *token){ int i; if (tsession == NULL) { GNUNET_GE_BREAK (ectx, 0); return GNUNET_SYSERR; } GNUNET_mutex_lock (lock); for (i = 0; i < tsession->token_count; i++) { if (0 == strcmp (tsession->tokens[i], token)) { i = -1; break; } } if (i != -1) { GNUNET_GE_BREAK (NULL, 0); GNUNET_mutex_unlock (lock); return GNUNET_SYSERR; } GNUNET_mutex_unlock (lock); return GNUNET_OK;}/** * Create signed hello for this transport and put it into * the cache tapi->hello. */static voidcreateSignedhello (void *cls){ GNUNET_TransportAPI *tapi = cls; GNUNET_mutex_lock (tapis_lock); GNUNET_free_non_null (tapi->hello); tapi->hello = tapi->hello_create (); if (NULL == tapi->hello) { GNUNET_mutex_unlock (tapis_lock); return; } memcpy (&tapi->hello->publicKey, identity->getPublicPrivateKey (), sizeof (GNUNET_RSA_PublicKey)); memcpy (&tapi->hello->senderIdentity, coreAPI->my_identity, sizeof (GNUNET_PeerIdentity)); tapi->hello->expiration_time = htonl (GNUNET_get_time_int32 (NULL) + hello_live); tapi->hello->header.type = htons (GNUNET_P2P_PROTO_HELLO); tapi->hello->header.size = htons (GNUNET_sizeof_hello (tapi->hello)); if (GNUNET_SYSERR == identity->signData (&(tapi->hello)->senderIdentity, GNUNET_sizeof_hello (tapi->hello) - sizeof (GNUNET_RSA_Signature) - sizeof (GNUNET_RSA_PublicKey) - sizeof (GNUNET_MessageHeader), &tapi->hello->signature)) { GNUNET_free (tapi->hello); tapi->hello = NULL; GNUNET_GE_BREAK (ectx, 0); } GNUNET_mutex_unlock (tapis_lock);}/** * Is this transport mechanism available (for sending)? * @return GNUNET_YES or GNUNET_NO */static intisTransportAvailable (unsigned short ttype){ if (ttype >= tapis_count) return GNUNET_NO; if (NULL == tapis[ttype]) return GNUNET_NO; return GNUNET_YES;}/** * Add an implementation of a transport protocol. */static intaddTransport (GNUNET_TransportAPI * tapi){ if (tapi->protocol_number >= tapis_count) GNUNET_array_grow (tapis, tapis_count, tapi->protocol_number + 1); if (tapis[tapi->protocol_number] != NULL) { GNUNET_GE_BREAK (ectx, 0); return GNUNET_SYSERR; } tapis[tapi->protocol_number] = tapi; tapi->hello = NULL; GNUNET_cron_add_job (coreAPI->cron, &createSignedhello, HELLO_RECREATE_FREQ, HELLO_RECREATE_FREQ, tapi); return GNUNET_OK;}/** * Convert hello to string. */static inthelloToAddress (const GNUNET_MessageHello * hello, void **sa, unsigned int *sa_len){ unsigned short prot; prot = ntohs (hello->protocol); if ((prot >= tapis_count) || (tapis[prot] == NULL)) { GNUNET_GE_LOG (ectx, GNUNET_GE_INFO | GNUNET_GE_REQUEST | GNUNET_GE_USER, _ ("Converting peer address to string failed, transport type %d not supported\n"), ntohs (hello->protocol)); return GNUNET_SYSERR; } return tapis[prot]->hello_to_address (hello, sa, sa_len);}/** * Iterate over all available transport mechanisms. * @param callback the method to call on each transport API implementation * @param data second argument to callback */static intforEachTransport (GNUNET_TransportCallback callback, void *data){ int i; int ret; ret = 0; for (i = 0; i < tapis_count; i++) { if (tapis[i] != NULL) { ret++; if (callback != NULL) callback (tapis[i], data); } } return ret;}/** * Connect to a remote host using the advertised * transport layer. This may fail if the appropriate * transport mechanism is not available. * * @param hello the hello of the target node * @param may_reuse can an existing connection be * re-used? * @return session on success, NULL on error */static GNUNET_TSession *transportConnect (const GNUNET_MessageHello * hello, const char *token, int may_reuse){ unsigned short prot; GNUNET_TSession *tsession; prot = ntohs (hello->protocol); if ((prot >= tapis_count) || (tapis[prot] == NULL)) { GNUNET_GE_LOG (ectx, GNUNET_GE_INFO | GNUNET_GE_REQUEST | GNUNET_GE_USER | GNUNET_GE_ADMIN, _ ("Transport connection attempt failed, transport type %d not supported\n"), prot); return NULL; } tsession = NULL; if (GNUNET_OK != tapis[prot]->connect (hello, &tsession, may_reuse)) return NULL; tsession->ttype = prot; GNUNET_mutex_lock (lock); GNUNET_array_append (tsession->tokens, tsession->token_count, token); CHECK (tsession); GNUNET_mutex_unlock (lock); GNUNET_GE_BREAK (NULL, GNUNET_OK == assertAssociated (tsession, token)); return tsession;}static GNUNET_TSession *transportConnectFreely (const GNUNET_PeerIdentity * peer, int useTempList, const char *token){ int i; GNUNET_MessageHello *hello; unsigned int *perm; GNUNET_TSession *ret; unsigned int hc;#if DEBUG_TRANSPORT GNUNET_EncName enc;#endif hc = 0; ret = NULL; perm = GNUNET_permute (GNUNET_RANDOM_QUALITY_WEAK, tapis_count); for (i = 0; i < tapis_count; i++) { if (tapis[perm[i]] == NULL) continue; hello = identity->identity2Hello (peer, perm[i], useTempList); if (hello == NULL) continue; hc++; ret = transportConnect (hello, token, GNUNET_YES); GNUNET_free (hello); if (ret != NULL) break; } GNUNET_free (perm); if (ret == NULL) {#if DEBUG_TRANSPORT GNUNET_hash_to_enc (&peer->hashPubKey, &enc); GNUNET_GE_LOG (ectx, GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_ADMIN, _ ("Transport failed to connect to peer `%s' (%u HELLOs known, none worked)\n"), &enc, hc);#endif } return ret;}/** * A (core) Session is to be associated with a transport session. The * transport service may want to know in order to call back on the * core if the connection is being closed. * * @param tsession the session handle passed along * from the call to receive that was made by the transport * layer * @return GNUNET_OK if the session could be associated, * GNUNET_SYSERR if not. */static inttransportAssociate (GNUNET_TSession * tsession, const char *token){ int ret; if ((tsession == NULL) || (tsession->ttype >= tapis_count) || (tapis[tsession->ttype] == NULL)) return GNUNET_SYSERR; ret = tapis[tsession->ttype]->associate (tsession); GNUNET_mutex_lock (lock); if (ret == GNUNET_OK) GNUNET_array_append (tsession->tokens, tsession->token_count, token); CHECK (tsession); GNUNET_mutex_unlock (lock); if (ret == GNUNET_OK) GNUNET_GE_BREAK (NULL, GNUNET_OK == assertAssociated (tsession, token)); return ret;}/** * Get the cost of a message in for the given transport mechanism. */static unsigned inttransportGetCost (int ttype){ if ((ttype >= tapis_count) || (tapis[ttype] == NULL)) return GNUNET_SYSERR; /* -1 = INFTY */ return tapis[ttype]->cost;}/** * Send a message. * @param tsession the transport session identifying the connection * @param msg the message to send * @param size the size of the message * @param important * @return GNUNET_OK on success, GNUNET_SYSERR on persistent error, GNUNET_NO on * temporary error */static inttransportSend (GNUNET_TSession * tsession, const void *msg, unsigned int size, int important){ if (tsession == NULL) { GNUNET_GE_LOG (ectx, GNUNET_GE_DEBUG | GNUNET_GE_DEVELOPER | GNUNET_GE_BULK, "Transmission attempted on uni-directional pipe, failing.\n"); return GNUNET_SYSERR; /* can't do that, can happen for unidirectional pipes that call core with GNUNET_TSession being NULL. */ } GNUNET_mutex_lock (lock); CHECK (tsession); GNUNET_mutex_unlock (lock); if ((tsession->ttype >= tapis_count) || (tapis[tsession->ttype] == NULL)) { GNUNET_GE_LOG (ectx, GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER, _ ("Transmission attempt failed, transport type %d unknown.\n"), tsession->ttype); return GNUNET_SYSERR; } return tapis[tsession->ttype]->send (tsession, msg, size, important);}/** * Close the session with the remote node. * @return GNUNET_OK on success, GNUNET_SYSERR on error */static inttransportDisconnect (GNUNET_TSession * tsession, const char *token){ int i; if (tsession == NULL) { GNUNET_GE_BREAK (ectx, 0); return GNUNET_SYSERR; } if ((tsession->ttype >= tapis_count) || (tapis[tsession->ttype] == NULL)) { GNUNET_GE_BREAK (ectx, 0); return GNUNET_SYSERR; } GNUNET_mutex_lock (lock); CHECK (tsession); for (i = 0; i < tsession->token_count; i++) { if (0 == strcmp (tsession->tokens[i], token)) { tsession->tokens[i] = tsession->tokens[tsession->token_count - 1]; GNUNET_array_grow (tsession->tokens, tsession->token_count, tsession->token_count - 1); i = -1; break; } } if (i != -1) { GNUNET_GE_BREAK (ectx, 0); GNUNET_GE_LOG (ectx, GNUNET_GE_ERROR | GNUNET_GE_DEVELOPER | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE, "Illegal attempt to disconnect transport; do not have token `%s'\n", token); GNUNET_mutex_unlock (lock); return GNUNET_SYSERR; } GNUNET_mutex_unlock (lock); i = tapis[tsession->ttype]->disconnect (tsession); GNUNET_GE_BREAK (NULL, i == GNUNET_OK); /* should never fail */ return i;}/** * Verify that a hello is ok. Call a method * if the verification was successful. * @return GNUNET_OK if the attempt to verify is on the way, * GNUNET_SYSERR if the transport mechanism is not supported */static inttransportVerifyHello (const GNUNET_MessageHello * hello){ unsigned short prot; if ((ntohs (hello->header.size) != GNUNET_sizeof_hello (hello)) ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -