123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659 |
- /*
- This file is part of GNUnet.
- Copyright (C) 2009-2013 GNUnet e.V.
- GNUnet is free software: you can redistribute it and/or modify it
- under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License,
- 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
- Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: AGPL3.0-or-later
- */
- /**
- * @file util/connection.c
- * @brief TCP connection management
- * @author Christian Grothoff
- *
- * This code is rather complex. Only modify it if you
- * 1) Have a NEW testcase showing that the new code
- * is needed and correct
- * 2) All EXISTING testcases pass with the new code
- * These rules should apply in general, but for this
- * module they are VERY, VERY important.
- */
- #include "platform.h"
- #include "gnunet_util_lib.h"
- #include "gnunet_resolver_service.h"
- /**
- * Timeout we use on TCP connect before trying another
- * result from the DNS resolver. Actual value used
- * is this value divided by the number of address families.
- * Default is 5s.
- */
- #define CONNECT_RETRY_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
- #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-connection", syscall)
- /**
- * Transmission handle. There can only be one for each connection.
- */
- struct GNUNET_CONNECTION_TransmitHandle
- {
- /**
- * Function to call if the send buffer has notify_size
- * bytes available.
- */
- GNUNET_CONNECTION_TransmitReadyNotify notify_ready;
- /**
- * Closure for notify_ready.
- */
- void *notify_ready_cls;
- /**
- * Our connection handle.
- */
- struct GNUNET_CONNECTION_Handle *connection;
- /**
- * Timeout for receiving (in absolute time).
- */
- struct GNUNET_TIME_Absolute transmit_timeout;
- /**
- * Task called on timeout.
- */
- struct GNUNET_SCHEDULER_Task * timeout_task;
- /**
- * At what number of bytes available in the
- * write buffer should the notify method be called?
- */
- size_t notify_size;
- };
- /**
- * During connect, we try multiple possible IP addresses
- * to find out which one might work.
- */
- struct AddressProbe
- {
- /**
- * This is a linked list.
- */
- struct AddressProbe *next;
- /**
- * This is a doubly-linked list.
- */
- struct AddressProbe *prev;
- /**
- * The address; do not free (allocated at the end of this struct).
- */
- const struct sockaddr *addr;
- /**
- * Underlying OS's socket.
- */
- struct GNUNET_NETWORK_Handle *sock;
- /**
- * Connection for which we are probing.
- */
- struct GNUNET_CONNECTION_Handle *connection;
- /**
- * Lenth of addr.
- */
- socklen_t addrlen;
- /**
- * Task waiting for the connection to finish connecting.
- */
- struct GNUNET_SCHEDULER_Task * task;
- };
- /**
- * @brief handle for a network connection
- */
- struct GNUNET_CONNECTION_Handle
- {
- /**
- * Configuration to use.
- */
- const struct GNUNET_CONFIGURATION_Handle *cfg;
- /**
- * Linked list of sockets we are currently trying out
- * (during connect).
- */
- struct AddressProbe *ap_head;
- /**
- * Linked list of sockets we are currently trying out
- * (during connect).
- */
- struct AddressProbe *ap_tail;
- /**
- * Network address of the other end-point, may be NULL.
- */
- struct sockaddr *addr;
- /**
- * Pointer to the hostname if connection was
- * created using DNS lookup, otherwise NULL.
- */
- char *hostname;
- /**
- * Underlying OS's socket, set to NULL after fatal errors.
- */
- struct GNUNET_NETWORK_Handle *sock;
- /**
- * Function to call on data received, NULL if no receive is pending.
- */
- GNUNET_CONNECTION_Receiver receiver;
- /**
- * Closure for @e receiver.
- */
- void *receiver_cls;
- /**
- * Pointer to our write buffer.
- */
- char *write_buffer;
- /**
- * Current size of our @e write_buffer.
- */
- size_t write_buffer_size;
- /**
- * Current write-offset in @e write_buffer (where
- * would we write next).
- */
- size_t write_buffer_off;
- /**
- * Current read-offset in @e write_buffer (how many
- * bytes have already been sent).
- */
- size_t write_buffer_pos;
- /**
- * Length of @e addr.
- */
- socklen_t addrlen;
- /**
- * Read task that we may need to wait for.
- */
- struct GNUNET_SCHEDULER_Task *read_task;
- /**
- * Write task that we may need to wait for.
- */
- struct GNUNET_SCHEDULER_Task *write_task;
- /**
- * Handle to a pending DNS lookup request.
- */
- struct GNUNET_RESOLVER_RequestHandle *dns_active;
- /**
- * The handle we return for #GNUNET_CONNECTION_notify_transmit_ready().
- */
- struct GNUNET_CONNECTION_TransmitHandle nth;
- /**
- * Timeout for receiving (in absolute time).
- */
- struct GNUNET_TIME_Absolute receive_timeout;
- /**
- * Maximum number of bytes to read (for receiving).
- */
- size_t max;
- /**
- * Port to connect to.
- */
- uint16_t port;
- /**
- * When shutdown, do not ever actually close the socket, but
- * free resources. Only should ever be set if using program
- * termination as a signal (because only then will the leaked
- * socket be freed!)
- */
- int8_t persist;
- /**
- * Usually 0. Set to 1 if this handle is in use, and should
- * #GNUNET_CONNECTION_destroy() be called right now, the action needs
- * to be deferred by setting it to -1.
- */
- int8_t destroy_later;
- /**
- * Handle to subsequent connection after proxy handshake completes,
- */
- struct GNUNET_CONNECTION_Handle *proxy_handshake;
- };
- /**
- * Set the persist option on this connection handle. Indicates
- * that the underlying socket or fd should never really be closed.
- * Used for indicating process death.
- *
- * @param connection the connection to set persistent
- */
- void
- GNUNET_CONNECTION_persist_ (struct GNUNET_CONNECTION_Handle *connection)
- {
- connection->persist = GNUNET_YES;
- }
- /**
- * Disable the "CORK" feature for communication with the given connection,
- * forcing the OS to immediately flush the buffer on transmission
- * instead of potentially buffering multiple messages. Essentially
- * reduces the OS send buffers to zero.
- * Used to make sure that the last messages sent through the connection
- * reach the other side before the process is terminated.
- *
- * @param connection the connection to make flushing and blocking
- * @return #GNUNET_OK on success
- */
- int
- GNUNET_CONNECTION_disable_corking (struct GNUNET_CONNECTION_Handle *connection)
- {
- return GNUNET_NETWORK_socket_disable_corking (connection->sock);
- }
- /**
- * Create a connection handle by boxing an existing OS socket. The OS
- * socket should henceforth be no longer used directly.
- * #GNUNET_connection_destroy() will close it.
- *
- * @param osSocket existing socket to box
- * @return the boxed connection handle
- */
- struct GNUNET_CONNECTION_Handle *
- GNUNET_CONNECTION_create_from_existing (struct GNUNET_NETWORK_Handle *osSocket)
- {
- struct GNUNET_CONNECTION_Handle *connection;
- connection = GNUNET_new (struct GNUNET_CONNECTION_Handle);
- connection->write_buffer_size = GNUNET_MIN_MESSAGE_SIZE;
- connection->write_buffer = GNUNET_malloc (connection->write_buffer_size);
- connection->sock = osSocket;
- return connection;
- }
- /**
- * Create a connection handle by accepting on a listen socket. This
- * function may block if the listen socket has no connection ready.
- *
- * @param access_cb function to use to check if access is allowed
- * @param access_cb_cls closure for @a access_cb
- * @param lsock listen socket
- * @return the connection handle, NULL on error
- */
- struct GNUNET_CONNECTION_Handle *
- GNUNET_CONNECTION_create_from_accept (GNUNET_CONNECTION_AccessCheck access_cb,
- void *access_cb_cls,
- struct GNUNET_NETWORK_Handle *lsock)
- {
- struct GNUNET_CONNECTION_Handle *connection;
- char addr[128];
- socklen_t addrlen;
- struct GNUNET_NETWORK_Handle *sock;
- int aret;
- struct sockaddr_in *v4;
- struct sockaddr_in6 *v6;
- struct sockaddr *sa;
- void *uaddr;
- #ifdef SO_PEERCRED
- struct ucred uc;
- socklen_t olen;
- #endif
- struct GNUNET_CONNECTION_Credentials *gcp;
- #if HAVE_GETPEEREID || defined(SO_PEERCRED) || HAVE_GETPEERUCRED
- struct GNUNET_CONNECTION_Credentials gc;
- gc.uid = 0;
- gc.gid = 0;
- #endif
- addrlen = sizeof (addr);
- sock =
- GNUNET_NETWORK_socket_accept (lsock,
- (struct sockaddr *) &addr,
- &addrlen);
- if (NULL == sock)
- {
- if (EAGAIN != errno)
- LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "accept");
- return NULL;
- }
- if ((addrlen > sizeof (addr)) || (addrlen < sizeof (sa_family_t)))
- {
- GNUNET_break (0);
- GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
- return NULL;
- }
- sa = (struct sockaddr *) addr;
- v6 = (struct sockaddr_in6 *) addr;
- if ( (AF_INET6 == sa->sa_family) &&
- (IN6_IS_ADDR_V4MAPPED (&v6->sin6_addr)) )
- {
- /* convert to V4 address */
- v4 = GNUNET_new (struct sockaddr_in);
- memset (v4, 0, sizeof (struct sockaddr_in));
- v4->sin_family = AF_INET;
- #if HAVE_SOCKADDR_IN_SIN_LEN
- v4->sin_len = (u_char) sizeof (struct sockaddr_in);
- #endif
- GNUNET_memcpy (&v4->sin_addr,
- &((char *) &v6->sin6_addr)[sizeof (struct in6_addr) -
- sizeof (struct in_addr)],
- sizeof (struct in_addr));
- v4->sin_port = v6->sin6_port;
- uaddr = v4;
- addrlen = sizeof (struct sockaddr_in);
- }
- else
- {
- uaddr = GNUNET_malloc (addrlen);
- GNUNET_memcpy (uaddr, addr, addrlen);
- }
- gcp = NULL;
- if (AF_UNIX == sa->sa_family)
- {
- #if HAVE_GETPEEREID
- /* most BSDs */
- if (0 == getpeereid (GNUNET_NETWORK_get_fd (sock),
- &gc.uid,
- &gc.gid))
- gcp = &gc;
- #else
- #ifdef SO_PEERCRED
- /* largely traditional GNU/Linux */
- olen = sizeof (uc);
- if ( (0 ==
- getsockopt (GNUNET_NETWORK_get_fd (sock),
- SOL_SOCKET,
- SO_PEERCRED,
- &uc,
- &olen)) &&
- (olen == sizeof (uc)) )
- {
- gc.uid = uc.uid;
- gc.gid = uc.gid;
- gcp = &gc;
- }
- #else
- #if HAVE_GETPEERUCRED
- /* this is for Solaris 10 */
- ucred_t *uc;
- uc = NULL;
- if (0 == getpeerucred (GNUNET_NETWORK_get_fd (sock), &uc))
- {
- gc.uid = ucred_geteuid (uc);
- gc.gid = ucred_getegid (uc);
- gcp = &gc;
- }
- ucred_free (uc);
- #endif
- #endif
- #endif
- }
- if ( (NULL != access_cb) &&
- (GNUNET_YES != (aret = access_cb (access_cb_cls,
- gcp,
- uaddr,
- addrlen))) )
- {
- if (GNUNET_NO == aret)
- LOG (GNUNET_ERROR_TYPE_INFO,
- _("Access denied to `%s'\n"),
- GNUNET_a2s (uaddr,
- addrlen));
- GNUNET_break (GNUNET_OK ==
- GNUNET_NETWORK_socket_shutdown (sock,
- SHUT_RDWR));
- GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
- GNUNET_free (uaddr);
- return NULL;
- }
- connection = GNUNET_new (struct GNUNET_CONNECTION_Handle);
- connection->write_buffer_size = GNUNET_MIN_MESSAGE_SIZE;
- connection->write_buffer = GNUNET_malloc (connection->write_buffer_size);
- connection->addr = uaddr;
- connection->addrlen = addrlen;
- connection->sock = sock;
- LOG (GNUNET_ERROR_TYPE_INFO,
- _("Accepting connection from `%s': %p\n"),
- GNUNET_a2s (uaddr,
- addrlen),
- connection);
- return connection;
- }
- /**
- * Obtain the network address of the other party.
- *
- * @param connection the client to get the address for
- * @param addr where to store the address
- * @param addrlen where to store the length of the @a addr
- * @return #GNUNET_OK on success
- */
- int
- GNUNET_CONNECTION_get_address (struct GNUNET_CONNECTION_Handle *connection,
- void **addr,
- size_t *addrlen)
- {
- if ((NULL == connection->addr) || (0 == connection->addrlen))
- return GNUNET_NO;
- *addr = GNUNET_malloc (connection->addrlen);
- GNUNET_memcpy (*addr, connection->addr, connection->addrlen);
- *addrlen = connection->addrlen;
- return GNUNET_OK;
- }
- /**
- * Tell the receiver callback that we had an IO error.
- *
- * @param connection connection to signal error
- * @param errcode error code to send
- */
- static void
- signal_receive_error (struct GNUNET_CONNECTION_Handle *connection,
- int errcode)
- {
- GNUNET_CONNECTION_Receiver receiver;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Receive encounters error (%s), connection closed (%p)\n",
- STRERROR (errcode),
- connection);
- GNUNET_assert (NULL != (receiver = connection->receiver));
- connection->receiver = NULL;
- receiver (connection->receiver_cls,
- NULL,
- 0,
- connection->addr,
- connection->addrlen,
- errcode);
- }
- /**
- * Tell the receiver callback that a timeout was reached.
- *
- * @param connection connection to signal for
- */
- static void
- signal_receive_timeout (struct GNUNET_CONNECTION_Handle *connection)
- {
- GNUNET_CONNECTION_Receiver receiver;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Connection signals timeout to receiver (%p)!\n",
- connection);
- GNUNET_assert (NULL != (receiver = connection->receiver));
- connection->receiver = NULL;
- receiver (connection->receiver_cls, NULL, 0, NULL, 0, 0);
- }
- /**
- * We failed to transmit data to the service, signal the error.
- *
- * @param connection handle that had trouble
- * @param ecode error code (errno)
- */
- static void
- signal_transmit_error (struct GNUNET_CONNECTION_Handle *connection,
- int ecode)
- {
- GNUNET_CONNECTION_TransmitReadyNotify notify;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Transmission encounterd error (%s), connection closed (%p)\n",
- STRERROR (ecode),
- connection);
- if (NULL != connection->sock)
- {
- (void) GNUNET_NETWORK_socket_shutdown (connection->sock,
- SHUT_RDWR);
- GNUNET_break (GNUNET_OK ==
- GNUNET_NETWORK_socket_close (connection->sock));
- connection->sock = NULL;
- GNUNET_assert (NULL == connection->write_task);
- }
- if (NULL != connection->read_task)
- {
- /* send errors trigger read errors... */
- GNUNET_SCHEDULER_cancel (connection->read_task);
- connection->read_task = NULL;
- signal_receive_timeout (connection);
- return;
- }
- if (NULL == connection->nth.notify_ready)
- return; /* nobody to tell about it */
- notify = connection->nth.notify_ready;
- connection->nth.notify_ready = NULL;
- notify (connection->nth.notify_ready_cls,
- 0,
- NULL);
- }
- /**
- * We've failed for good to establish a connection (timeout or
- * no more addresses to try).
- *
- * @param connection the connection we tried to establish
- */
- static void
- connect_fail_continuation (struct GNUNET_CONNECTION_Handle *connection)
- {
- LOG (GNUNET_ERROR_TYPE_INFO,
- "Failed to establish TCP connection to `%s:%u', no further addresses to try.\n",
- connection->hostname,
- connection->port);
- GNUNET_break (NULL == connection->ap_head);
- GNUNET_break (NULL == connection->ap_tail);
- GNUNET_break (GNUNET_NO == connection->dns_active);
- GNUNET_break (NULL == connection->sock);
- GNUNET_assert (NULL == connection->write_task);
- GNUNET_assert (NULL == connection->proxy_handshake);
- /* signal errors for jobs that used to wait on the connection */
- connection->destroy_later = 1;
- if (NULL != connection->receiver)
- signal_receive_error (connection,
- ECONNREFUSED);
- if (NULL != connection->nth.notify_ready)
- {
- GNUNET_assert (NULL != connection->nth.timeout_task);
- GNUNET_SCHEDULER_cancel (connection->nth.timeout_task);
- connection->nth.timeout_task = NULL;
- signal_transmit_error (connection,
- ECONNREFUSED);
- }
- if (-1 == connection->destroy_later)
- {
- /* do it now */
- connection->destroy_later = 0;
- GNUNET_CONNECTION_destroy (connection);
- return;
- }
- connection->destroy_later = 0;
- }
- /**
- * We are ready to transmit (or got a timeout).
- *
- * @param cls our connection handle
- */
- static void
- transmit_ready (void *cls);
- /**
- * This function is called once we either timeout or have data ready
- * to read.
- *
- * @param cls connection to read from
- */
- static void
- receive_ready (void *cls);
- /**
- * We've succeeded in establishing a connection.
- *
- * @param connection the connection we tried to establish
- */
- static void
- connect_success_continuation (struct GNUNET_CONNECTION_Handle *connection)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Connection to `%s' succeeded! (%p)\n",
- GNUNET_a2s (connection->addr,
- connection->addrlen),
- connection);
- /* trigger jobs that waited for the connection */
- if (NULL != connection->receiver)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Connection succeeded, starting with receiving data (%p)\n",
- connection);
- GNUNET_assert (NULL == connection->read_task);
- connection->read_task =
- GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining
- (connection->receive_timeout),
- connection->sock,
- &receive_ready, connection);
- }
- if (NULL != connection->nth.notify_ready)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Connection succeeded, starting with sending data (%p)\n",
- connection);
- GNUNET_assert (connection->nth.timeout_task != NULL);
- GNUNET_SCHEDULER_cancel (connection->nth.timeout_task);
- connection->nth.timeout_task = NULL;
- GNUNET_assert (connection->write_task == NULL);
- connection->write_task =
- GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining
- (connection->nth.transmit_timeout), connection->sock,
- &transmit_ready, connection);
- }
- }
- /**
- * Scheduler let us know that we're either ready to write on the
- * socket OR connect timed out. Do the right thing.
- *
- * @param cls the `struct AddressProbe *` with the address that we are probing
- */
- static void
- connect_probe_continuation (void *cls)
- {
- struct AddressProbe *ap = cls;
- struct GNUNET_CONNECTION_Handle *connection = ap->connection;
- const struct GNUNET_SCHEDULER_TaskContext *tc;
- struct AddressProbe *pos;
- int error;
- socklen_t len;
- GNUNET_assert (NULL != ap->sock);
- GNUNET_CONTAINER_DLL_remove (connection->ap_head,
- connection->ap_tail,
- ap);
- len = sizeof (error);
- errno = 0;
- error = 0;
- tc = GNUNET_SCHEDULER_get_task_context ();
- if ( (0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) ||
- (GNUNET_OK !=
- GNUNET_NETWORK_socket_getsockopt (ap->sock,
- SOL_SOCKET,
- SO_ERROR,
- &error,
- &len)) ||
- (0 != error) )
- {
- GNUNET_break (GNUNET_OK ==
- GNUNET_NETWORK_socket_close (ap->sock));
- GNUNET_free (ap);
- if ( (NULL == connection->ap_head) &&
- (GNUNET_NO == connection->dns_active) &&
- (NULL == connection->proxy_handshake) )
- connect_fail_continuation (connection);
- return;
- }
- GNUNET_assert (NULL == connection->sock);
- connection->sock = ap->sock;
- GNUNET_assert (NULL == connection->addr);
- connection->addr = GNUNET_malloc (ap->addrlen);
- GNUNET_memcpy (connection->addr, ap->addr, ap->addrlen);
- connection->addrlen = ap->addrlen;
- GNUNET_free (ap);
- /* cancel all other attempts */
- while (NULL != (pos = connection->ap_head))
- {
- GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pos->sock));
- GNUNET_SCHEDULER_cancel (pos->task);
- GNUNET_CONTAINER_DLL_remove (connection->ap_head,
- connection->ap_tail,
- pos);
- GNUNET_free (pos);
- }
- connect_success_continuation (connection);
- }
- /**
- * Try to establish a connection given the specified address.
- * This function is called by the resolver once we have a DNS reply.
- *
- * @param cls our `struct GNUNET_CONNECTION_Handle *`
- * @param addr address to try, NULL for "last call"
- * @param addrlen length of @a addr
- */
- static void
- try_connect_using_address (void *cls,
- const struct sockaddr *addr,
- socklen_t addrlen)
- {
- struct GNUNET_CONNECTION_Handle *connection = cls;
- struct AddressProbe *ap;
- struct GNUNET_TIME_Relative delay;
- if (NULL == addr)
- {
- connection->dns_active = NULL;
- if ((NULL == connection->ap_head) &&
- (NULL == connection->sock) &&
- (NULL == connection->proxy_handshake))
- connect_fail_continuation (connection);
- return;
- }
- if (NULL != connection->sock)
- return; /* already connected */
- GNUNET_assert (NULL == connection->addr);
- /* try to connect */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Trying to connect using address `%s:%u/%s:%u'\n",
- connection->hostname,
- connection->port,
- GNUNET_a2s (addr, addrlen),
- connection->port);
- ap = GNUNET_malloc (sizeof (struct AddressProbe) + addrlen);
- ap->addr = (const struct sockaddr *) &ap[1];
- GNUNET_memcpy (&ap[1], addr, addrlen);
- ap->addrlen = addrlen;
- ap->connection = connection;
- switch (ap->addr->sa_family)
- {
- case AF_INET:
- ((struct sockaddr_in *) ap->addr)->sin_port = htons (connection->port);
- break;
- case AF_INET6:
- ((struct sockaddr_in6 *) ap->addr)->sin6_port = htons (connection->port);
- break;
- default:
- GNUNET_break (0);
- GNUNET_free (ap);
- return; /* not supported by us */
- }
- ap->sock = GNUNET_NETWORK_socket_create (ap->addr->sa_family,
- SOCK_STREAM, 0);
- if (NULL == ap->sock)
- {
- GNUNET_free (ap);
- return; /* not supported by OS */
- }
- LOG (GNUNET_ERROR_TYPE_INFO,
- "Trying to connect to `%s' (%p)\n",
- GNUNET_a2s (ap->addr, ap->addrlen),
- connection);
- if ((GNUNET_OK !=
- GNUNET_NETWORK_socket_connect (ap->sock,
- ap->addr,
- ap->addrlen)) &&
- (EINPROGRESS != errno))
- {
- /* maybe refused / unsupported address, try next */
- LOG_STRERROR (GNUNET_ERROR_TYPE_INFO, "connect");
- GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ap->sock));
- GNUNET_free (ap);
- return;
- }
- GNUNET_CONTAINER_DLL_insert (connection->ap_head, connection->ap_tail, ap);
- delay = CONNECT_RETRY_TIMEOUT;
- if (NULL != connection->nth.notify_ready)
- delay = GNUNET_TIME_relative_min (delay,
- GNUNET_TIME_absolute_get_remaining (connection->nth.transmit_timeout));
- if (NULL != connection->receiver)
- delay = GNUNET_TIME_relative_min (delay,
- GNUNET_TIME_absolute_get_remaining (connection->receive_timeout));
- ap->task = GNUNET_SCHEDULER_add_write_net (delay,
- ap->sock,
- &connect_probe_continuation,
- ap);
- }
- /**
- * Create a connection handle by (asynchronously) connecting to a host.
- * This function returns immediately, even if the connection has not
- * yet been established. This function only creates TCP connections.
- *
- * @param cfg configuration to use
- * @param hostname name of the host to connect to
- * @param port port to connect to
- * @return the connection handle
- */
- struct GNUNET_CONNECTION_Handle *
- GNUNET_CONNECTION_create_from_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
- const char *hostname,
- uint16_t port)
- {
- struct GNUNET_CONNECTION_Handle *connection;
- GNUNET_assert (0 < strlen (hostname)); /* sanity check */
- connection = GNUNET_new (struct GNUNET_CONNECTION_Handle);
- connection->cfg = cfg;
- connection->write_buffer_size = GNUNET_MIN_MESSAGE_SIZE;
- connection->write_buffer = GNUNET_malloc (connection->write_buffer_size);
- connection->port = port;
- connection->hostname = GNUNET_strdup (hostname);
- connection->dns_active =
- GNUNET_RESOLVER_ip_get (connection->hostname,
- AF_UNSPEC,
- CONNECT_RETRY_TIMEOUT,
- &try_connect_using_address,
- connection);
- return connection;
- }
- /**
- * Create a connection handle by connecting to a UNIX domain service.
- * This function returns immediately, even if the connection has not
- * yet been established. This function only creates UNIX connections.
- *
- * @param cfg configuration to use
- * @param unixpath path to connect to
- * @return the connection handle, NULL on systems without UNIX support
- */
- struct GNUNET_CONNECTION_Handle *
- GNUNET_CONNECTION_create_from_connect_to_unixpath (const struct GNUNET_CONFIGURATION_Handle *cfg,
- const char *unixpath)
- {
- #ifdef AF_UNIX
- struct GNUNET_CONNECTION_Handle *connection;
- struct sockaddr_un *un;
- GNUNET_assert (0 < strlen (unixpath)); /* sanity check */
- un = GNUNET_new (struct sockaddr_un);
- un->sun_family = AF_UNIX;
- strncpy (un->sun_path, unixpath, sizeof (un->sun_path) - 1);
- #ifdef LINUX
- {
- int abstract;
- abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg,
- "TESTING",
- "USE_ABSTRACT_SOCKETS");
- if (GNUNET_YES == abstract)
- un->sun_path[0] = '\0';
- }
- #endif
- #if HAVE_SOCKADDR_UN_SUN_LEN
- un->sun_len = (u_char) sizeof (struct sockaddr_un);
- #endif
- connection = GNUNET_new (struct GNUNET_CONNECTION_Handle);
- connection->cfg = cfg;
- connection->write_buffer_size = GNUNET_MIN_MESSAGE_SIZE;
- connection->write_buffer = GNUNET_malloc (connection->write_buffer_size);
- connection->port = 0;
- connection->hostname = NULL;
- connection->addr = (struct sockaddr *) un;
- connection->addrlen = sizeof (struct sockaddr_un);
- connection->sock = GNUNET_NETWORK_socket_create (AF_UNIX,
- SOCK_STREAM,
- 0);
- if (NULL == connection->sock)
- {
- GNUNET_free (connection->addr);
- GNUNET_free (connection->write_buffer);
- GNUNET_free (connection);
- return NULL;
- }
- if ( (GNUNET_OK !=
- GNUNET_NETWORK_socket_connect (connection->sock,
- connection->addr,
- connection->addrlen)) &&
- (EINPROGRESS != errno) )
- {
- /* Just return; we expect everything to work eventually so don't fail HARD */
- GNUNET_break (GNUNET_OK ==
- GNUNET_NETWORK_socket_close (connection->sock));
- connection->sock = NULL;
- return connection;
- }
- connect_success_continuation (connection);
- return connection;
- #else
- return NULL;
- #endif
- }
- /**
- * Create a connection handle by (asynchronously) connecting to a host.
- * This function returns immediately, even if the connection has not
- * yet been established. This function only creates TCP connections.
- *
- * @param s socket to connect
- * @param serv_addr server address
- * @param addrlen length of @a serv_addr
- * @return the connection handle
- */
- struct GNUNET_CONNECTION_Handle *
- GNUNET_CONNECTION_connect_socket (struct GNUNET_NETWORK_Handle *s,
- const struct sockaddr *serv_addr,
- socklen_t addrlen)
- {
- struct GNUNET_CONNECTION_Handle *connection;
- if ( (GNUNET_OK !=
- GNUNET_NETWORK_socket_connect (s, serv_addr, addrlen)) &&
- (EINPROGRESS != errno) )
- {
- /* maybe refused / unsupported address, try next */
- LOG_STRERROR (GNUNET_ERROR_TYPE_DEBUG,
- "connect");
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Attempt to connect to `%s' failed\n",
- GNUNET_a2s (serv_addr,
- addrlen));
- GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (s));
- return NULL;
- }
- connection = GNUNET_CONNECTION_create_from_existing (s);
- connection->addr = GNUNET_malloc (addrlen);
- GNUNET_memcpy (connection->addr, serv_addr, addrlen);
- connection->addrlen = addrlen;
- LOG (GNUNET_ERROR_TYPE_INFO,
- "Trying to connect to `%s' (%p)\n",
- GNUNET_a2s (serv_addr, addrlen),
- connection);
- return connection;
- }
- /**
- * Create a connection handle by creating a socket and
- * (asynchronously) connecting to a host. This function returns
- * immediately, even if the connection has not yet been established.
- * This function only creates TCP connections.
- *
- * @param af_family address family to use
- * @param serv_addr server address
- * @param addrlen length of @a serv_addr
- * @return the connection handle
- */
- struct GNUNET_CONNECTION_Handle *
- GNUNET_CONNECTION_create_from_sockaddr (int af_family,
- const struct sockaddr *serv_addr,
- socklen_t addrlen)
- {
- struct GNUNET_NETWORK_Handle *s;
- s = GNUNET_NETWORK_socket_create (af_family, SOCK_STREAM, 0);
- if (NULL == s)
- {
- LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
- "socket");
- return NULL;
- }
- return GNUNET_CONNECTION_connect_socket (s,
- serv_addr,
- addrlen);
- }
- /**
- * Check if connection is valid (no fatal errors have happened so far).
- * Note that a connection that is still trying to connect is considered
- * valid.
- *
- * @param connection connection to check
- * @return #GNUNET_YES if valid, #GNUNET_NO otherwise
- */
- int
- GNUNET_CONNECTION_check (struct GNUNET_CONNECTION_Handle *connection)
- {
- if ((NULL != connection->ap_head) ||
- (NULL != connection->dns_active) ||
- (NULL != connection->proxy_handshake))
- return GNUNET_YES; /* still trying to connect */
- if ( (0 != connection->destroy_later) ||
- (NULL == connection->sock) )
- return GNUNET_NO;
- return GNUNET_YES;
- }
- /**
- * Close the connection and free associated resources. There must
- * not be any pending requests for reading or writing to the
- * connection at this time.
- *
- * @param connection connection to destroy
- */
- void
- GNUNET_CONNECTION_destroy (struct GNUNET_CONNECTION_Handle *connection)
- {
- struct AddressProbe *pos;
- if (0 != connection->destroy_later)
- {
- connection->destroy_later = -1;
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Shutting down connection (%p)\n",
- connection);
- GNUNET_assert (NULL == connection->nth.notify_ready);
- GNUNET_assert (NULL == connection->receiver);
- if (NULL != connection->write_task)
- {
- GNUNET_SCHEDULER_cancel (connection->write_task);
- connection->write_task = NULL;
- connection->write_buffer_off = 0;
- }
- if (NULL != connection->read_task)
- {
- GNUNET_SCHEDULER_cancel (connection->read_task);
- connection->read_task = NULL;
- }
- if (NULL != connection->nth.timeout_task)
- {
- GNUNET_SCHEDULER_cancel (connection->nth.timeout_task);
- connection->nth.timeout_task = NULL;
- }
- connection->nth.notify_ready = NULL;
- if (NULL != connection->dns_active)
- {
- GNUNET_RESOLVER_request_cancel (connection->dns_active);
- connection->dns_active = NULL;
- }
- if (NULL != connection->proxy_handshake)
- {
- /* GNUNET_CONNECTION_destroy (connection->proxy_handshake); */
- connection->proxy_handshake->destroy_later = -1;
- connection->proxy_handshake = NULL; /* Not leaked ??? */
- }
- while (NULL != (pos = connection->ap_head))
- {
- GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pos->sock));
- GNUNET_SCHEDULER_cancel (pos->task);
- GNUNET_CONTAINER_DLL_remove (connection->ap_head,
- connection->ap_tail,
- pos);
- GNUNET_free (pos);
- }
- if ( (NULL != connection->sock) &&
- (GNUNET_YES != connection->persist) )
- {
- if ((GNUNET_OK !=
- GNUNET_NETWORK_socket_shutdown (connection->sock,
- SHUT_RDWR)) &&
- (ENOTCONN != errno) &&
- (ECONNRESET != errno) )
- LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
- "shutdown");
- }
- if (NULL != connection->sock)
- {
- if (GNUNET_YES != connection->persist)
- {
- GNUNET_break (GNUNET_OK ==
- GNUNET_NETWORK_socket_close (connection->sock));
- }
- else
- {
- GNUNET_NETWORK_socket_free_memory_only_ (connection->sock); /* at least no memory leak (we deliberately
- * leak the socket in this special case) ... */
- }
- }
- GNUNET_free_non_null (connection->addr);
- GNUNET_free_non_null (connection->hostname);
- GNUNET_free (connection->write_buffer);
- GNUNET_free (connection);
- }
- /**
- * This function is called once we either timeout
- * or have data ready to read.
- *
- * @param cls connection to read from
- */
- static void
- receive_ready (void *cls)
- {
- struct GNUNET_CONNECTION_Handle *connection = cls;
- const struct GNUNET_SCHEDULER_TaskContext *tc;
- char buffer[connection->max];
- ssize_t ret;
- GNUNET_CONNECTION_Receiver receiver;
- connection->read_task = NULL;
- tc = GNUNET_SCHEDULER_get_task_context ();
- if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Receive from `%s' encounters error: timeout (%s, %p)\n",
- GNUNET_a2s (connection->addr,
- connection->addrlen),
- GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (connection->receive_timeout),
- GNUNET_YES),
- connection);
- signal_receive_timeout (connection);
- return;
- }
- if (NULL == connection->sock)
- {
- /* connect failed for good */
- signal_receive_error (connection, ECONNREFUSED);
- return;
- }
- GNUNET_assert (GNUNET_NETWORK_fdset_isset (tc->read_ready,
- connection->sock));
- RETRY:
- ret = GNUNET_NETWORK_socket_recv (connection->sock,
- buffer,
- connection->max);
- if (-1 == ret)
- {
- if (EINTR == errno)
- goto RETRY;
- signal_receive_error (connection, errno);
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "receive_ready read %u/%u bytes from `%s' (%p)!\n",
- (unsigned int) ret,
- connection->max,
- GNUNET_a2s (connection->addr,
- connection->addrlen),
- connection);
- GNUNET_assert (NULL != (receiver = connection->receiver));
- connection->receiver = NULL;
- receiver (connection->receiver_cls,
- buffer,
- ret,
- connection->addr,
- connection->addrlen,
- 0);
- }
- /**
- * Receive data from the given connection. Note that this function
- * will call @a receiver asynchronously using the scheduler. It will
- * "immediately" return. Note that there MUST only be one active
- * receive call per connection at any given point in time (so do not
- * call receive again until the receiver callback has been invoked).
- *
- * @param connection connection handle
- * @param max maximum number of bytes to read
- * @param timeout maximum amount of time to wait
- * @param receiver function to call with received data
- * @param receiver_cls closure for @a receiver
- * @return #GNUNET_SYSERR if @a connection died (receiver was
- * called with error)
- */
- int
- GNUNET_CONNECTION_receive (struct GNUNET_CONNECTION_Handle *connection,
- size_t max,
- struct GNUNET_TIME_Relative timeout,
- GNUNET_CONNECTION_Receiver receiver,
- void *receiver_cls)
- {
- GNUNET_assert ((NULL == connection->read_task) &&
- (NULL == connection->receiver));
- GNUNET_assert (NULL != receiver);
- connection->receiver = receiver;
- connection->receiver_cls = receiver_cls;
- connection->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout);
- connection->max = max;
- if (NULL != connection->sock)
- {
- connection->read_task =
- GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining
- (connection->receive_timeout),
- connection->sock,
- &receive_ready,
- connection);
- return GNUNET_OK;
- }
- if ((NULL == connection->dns_active) &&
- (NULL == connection->ap_head) &&
- (NULL == connection->proxy_handshake))
- {
- connection->receiver = NULL;
- receiver (receiver_cls,
- NULL, 0,
- NULL, 0,
- ETIMEDOUT);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
- }
- /**
- * Cancel receive job on the given connection. Note that the
- * receiver callback must not have been called yet in order
- * for the cancellation to be valid.
- *
- * @param connection connection handle
- * @return closure of the original receiver callback closure
- */
- void *
- GNUNET_CONNECTION_receive_cancel (struct GNUNET_CONNECTION_Handle *connection)
- {
- if (NULL != connection->read_task)
- {
- GNUNET_assert (connection ==
- GNUNET_SCHEDULER_cancel (connection->read_task));
- connection->read_task = NULL;
- }
- connection->receiver = NULL;
- return connection->receiver_cls;
- }
- /**
- * Try to call the transmit notify method (check if we do
- * have enough space available first)!
- *
- * @param connection connection for which we should do this processing
- * @return #GNUNET_YES if we were able to call notify
- */
- static int
- process_notify (struct GNUNET_CONNECTION_Handle *connection)
- {
- size_t used;
- size_t avail;
- size_t size;
- GNUNET_CONNECTION_TransmitReadyNotify notify;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "process_notify is running\n");
- GNUNET_assert (NULL == connection->write_task);
- if (NULL == (notify = connection->nth.notify_ready))
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "No one to notify\n");
- return GNUNET_NO;
- }
- used = connection->write_buffer_off - connection->write_buffer_pos;
- avail = connection->write_buffer_size - used;
- size = connection->nth.notify_size;
- if (size > avail)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Not enough buffer\n");
- return GNUNET_NO;
- }
- connection->nth.notify_ready = NULL;
- if (connection->write_buffer_size - connection->write_buffer_off < size)
- {
- /* need to compact */
- memmove (connection->write_buffer,
- &connection->write_buffer[connection->write_buffer_pos],
- used);
- connection->write_buffer_off -= connection->write_buffer_pos;
- connection->write_buffer_pos = 0;
- }
- avail = connection->write_buffer_size - connection->write_buffer_off;
- GNUNET_assert (avail >= size);
- size =
- notify (connection->nth.notify_ready_cls, avail,
- &connection->write_buffer[connection->write_buffer_off]);
- GNUNET_assert (size <= avail);
- if (0 != size)
- connection->write_buffer_off += size;
- return GNUNET_YES;
- }
- /**
- * Task invoked by the scheduler when a call to transmit
- * is timing out (we never got enough buffer space to call
- * the callback function before the specified timeout
- * expired).
- *
- * This task notifies the client about the timeout.
- *
- * @param cls the `struct GNUNET_CONNECTION_Handle`
- */
- static void
- transmit_timeout (void *cls)
- {
- struct GNUNET_CONNECTION_Handle *connection = cls;
- GNUNET_CONNECTION_TransmitReadyNotify notify;
- connection->nth.timeout_task = NULL;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Transmit to `%s:%u/%s' fails, time out reached (%p).\n",
- connection->hostname,
- connection->port,
- GNUNET_a2s (connection->addr,
- connection->addrlen),
- connection);
- notify = connection->nth.notify_ready;
- GNUNET_assert (NULL != notify);
- connection->nth.notify_ready = NULL;
- notify (connection->nth.notify_ready_cls,
- 0,
- NULL);
- }
- /**
- * Task invoked by the scheduler when we failed to connect
- * at the time of being asked to transmit.
- *
- * This task notifies the client about the error.
- *
- * @param cls the `struct GNUNET_CONNECTION_Handle`
- */
- static void
- connect_error (void *cls)
- {
- struct GNUNET_CONNECTION_Handle *connection = cls;
- GNUNET_CONNECTION_TransmitReadyNotify notify;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Transmission request of size %u fails (%s/%u), connection failed (%p).\n",
- connection->nth.notify_size,
- connection->hostname,
- connection->port,
- connection);
- connection->write_task = NULL;
- notify = connection->nth.notify_ready;
- connection->nth.notify_ready = NULL;
- notify (connection->nth.notify_ready_cls,
- 0,
- NULL);
- }
- /**
- * We are ready to transmit (or got a timeout).
- *
- * @param cls our connection handle
- */
- static void
- transmit_ready (void *cls)
- {
- struct GNUNET_CONNECTION_Handle *connection = cls;
- GNUNET_CONNECTION_TransmitReadyNotify notify;
- const struct GNUNET_SCHEDULER_TaskContext *tc;
- ssize_t ret;
- size_t have;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "transmit_ready running (%p).\n",
- connection);
- GNUNET_assert (NULL != connection->write_task);
- connection->write_task = NULL;
- GNUNET_assert (NULL == connection->nth.timeout_task);
- tc = GNUNET_SCHEDULER_get_task_context ();
- if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Transmit to `%s' fails, time out reached (%p).\n",
- GNUNET_a2s (connection->addr,
- connection->addrlen),
- connection);
- notify = connection->nth.notify_ready;
- GNUNET_assert (NULL != notify);
- connection->nth.notify_ready = NULL;
- notify (connection->nth.notify_ready_cls, 0, NULL);
- return;
- }
- GNUNET_assert (NULL != connection->sock);
- if (NULL == tc->write_ready)
- {
- /* special circumstances (in particular, PREREQ_DONE after
- * connect): not yet ready to write, but no "fatal" error either.
- * Hence retry. */
- goto SCHEDULE_WRITE;
- }
- if (! GNUNET_NETWORK_fdset_isset (tc->write_ready,
- connection->sock))
- {
- GNUNET_assert (NULL == connection->write_task);
- /* special circumstances (in particular, shutdown): not yet ready
- * to write, but no "fatal" error either. Hence retry. */
- goto SCHEDULE_WRITE;
- }
- GNUNET_assert (connection->write_buffer_off >= connection->write_buffer_pos);
- if ((NULL != connection->nth.notify_ready) &&
- (connection->write_buffer_size < connection->nth.notify_size))
- {
- connection->write_buffer =
- GNUNET_realloc (connection->write_buffer, connection->nth.notify_size);
- connection->write_buffer_size = connection->nth.notify_size;
- }
- process_notify (connection);
- have = connection->write_buffer_off - connection->write_buffer_pos;
- if (0 == have)
- {
- /* no data ready for writing, terminate write loop */
- return;
- }
- GNUNET_assert (have <= connection->write_buffer_size);
- GNUNET_assert (have + connection->write_buffer_pos <= connection->write_buffer_size);
- GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_size);
- RETRY:
- ret =
- GNUNET_NETWORK_socket_send (connection->sock,
- &connection->write_buffer[connection->write_buffer_pos],
- have);
- if (-1 == ret)
- {
- if (EINTR == errno)
- goto RETRY;
- if (NULL != connection->write_task)
- {
- GNUNET_SCHEDULER_cancel (connection->write_task);
- connection->write_task = NULL;
- }
- signal_transmit_error (connection, errno);
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Connection transmitted %u/%u bytes to `%s' (%p)\n",
- (unsigned int) ret,
- have,
- GNUNET_a2s (connection->addr,
- connection->addrlen),
- connection);
- connection->write_buffer_pos += ret;
- if (connection->write_buffer_pos == connection->write_buffer_off)
- {
- /* transmitted all pending data */
- connection->write_buffer_pos = 0;
- connection->write_buffer_off = 0;
- }
- if ( (0 == connection->write_buffer_off) &&
- (NULL == connection->nth.notify_ready) )
- return; /* all data sent! */
- /* not done writing, schedule more */
- SCHEDULE_WRITE:
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Re-scheduling transmit_ready (more to do) (%p).\n",
- connection);
- have = connection->write_buffer_off - connection->write_buffer_pos;
- GNUNET_assert ( (NULL != connection->nth.notify_ready) ||
- (have > 0) );
- if (NULL == connection->write_task)
- connection->write_task =
- GNUNET_SCHEDULER_add_write_net ((connection->nth.notify_ready ==
- NULL) ? GNUNET_TIME_UNIT_FOREVER_REL :
- GNUNET_TIME_absolute_get_remaining
- (connection->nth.transmit_timeout),
- connection->sock,
- &transmit_ready, connection);
- }
- /**
- * Ask the connection to call us once the specified number of bytes
- * are free in the transmission buffer. Will never call the @a notify
- * callback in this task, but always first go into the scheduler.
- *
- * @param connection connection
- * @param size number of bytes to send
- * @param timeout after how long should we give up (and call
- * @a notify with buf NULL and size 0)?
- * @param notify function to call
- * @param notify_cls closure for @a notify
- * @return non-NULL if the notify callback was queued,
- * NULL if we are already going to notify someone else (busy)
- */
- struct GNUNET_CONNECTION_TransmitHandle *
- GNUNET_CONNECTION_notify_transmit_ready (struct GNUNET_CONNECTION_Handle *connection,
- size_t size,
- struct GNUNET_TIME_Relative timeout,
- GNUNET_CONNECTION_TransmitReadyNotify notify,
- void *notify_cls)
- {
- if (NULL != connection->nth.notify_ready)
- {
- GNUNET_assert (0);
- return NULL;
- }
- GNUNET_assert (NULL != notify);
- GNUNET_assert (size < GNUNET_MAX_MESSAGE_SIZE);
- GNUNET_assert (connection->write_buffer_off <= connection->write_buffer_size);
- GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_size);
- GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_off);
- connection->nth.notify_ready = notify;
- connection->nth.notify_ready_cls = notify_cls;
- connection->nth.connection = connection;
- connection->nth.notify_size = size;
- connection->nth.transmit_timeout = GNUNET_TIME_relative_to_absolute (timeout);
- GNUNET_assert (NULL == connection->nth.timeout_task);
- if ((NULL == connection->sock) &&
- (NULL == connection->ap_head) &&
- (NULL == connection->dns_active) &&
- (NULL == connection->proxy_handshake))
- {
- if (NULL != connection->write_task)
- GNUNET_SCHEDULER_cancel (connection->write_task);
- connection->write_task = GNUNET_SCHEDULER_add_now (&connect_error,
- connection);
- return &connection->nth;
- }
- if (NULL != connection->write_task)
- return &connection->nth; /* previous transmission still in progress */
- if (NULL != connection->sock)
- {
- /* connected, try to transmit now */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Scheduling transmission (%p).\n",
- connection);
- connection->write_task =
- GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining
- (connection->nth.transmit_timeout),
- connection->sock,
- &transmit_ready, connection);
- return &connection->nth;
- }
- /* not yet connected, wait for connection */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Need to wait to schedule transmission for connection, adding timeout task (%p).\n",
- connection);
- connection->nth.timeout_task =
- GNUNET_SCHEDULER_add_delayed (timeout,
- &transmit_timeout,
- connection);
- return &connection->nth;
- }
- /**
- * Cancel the specified transmission-ready notification.
- *
- * @param th notification to cancel
- */
- void
- GNUNET_CONNECTION_notify_transmit_ready_cancel (struct GNUNET_CONNECTION_TransmitHandle *th)
- {
- GNUNET_assert (NULL != th->notify_ready);
- th->notify_ready = NULL;
- if (NULL != th->timeout_task)
- {
- GNUNET_SCHEDULER_cancel (th->timeout_task);
- th->timeout_task = NULL;
- }
- if (NULL != th->connection->write_task)
- {
- GNUNET_SCHEDULER_cancel (th->connection->write_task);
- th->connection->write_task = NULL;
- }
- }
- /**
- * Create a connection to be proxied using a given connection.
- *
- * @param cph connection to proxy server
- * @return connection to be proxied
- */
- struct GNUNET_CONNECTION_Handle *
- GNUNET_CONNECTION_create_proxied_from_handshake (struct GNUNET_CONNECTION_Handle *cph)
- {
- struct GNUNET_CONNECTION_Handle *proxied = GNUNET_CONNECTION_create_from_existing (NULL);
- proxied->proxy_handshake = cph;
- return proxied;
- }
- /**
- * Activate proxied connection and destroy initial proxy handshake connection.
- * There must not be any pending requests for reading or writing to the
- * proxy hadshake connection at this time.
- *
- * @param proxied connection connection to proxy server
- */
- void
- GNUNET_CONNECTION_acivate_proxied (struct GNUNET_CONNECTION_Handle *proxied)
- {
- struct GNUNET_CONNECTION_Handle *cph = proxied->proxy_handshake;
- GNUNET_assert (NULL != cph);
- GNUNET_assert (NULL == proxied->sock);
- GNUNET_assert (NULL != cph->sock);
- proxied->sock = cph->sock;
- cph->sock = NULL;
- GNUNET_CONNECTION_destroy (cph);
- connect_success_continuation (proxied);
- }
- /* end of connection.c */
|