1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597 |
- /*
- 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;
- GNUNET_strlcpy (un->sun_path, unixpath, sizeof(un->sun_path));
- #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 (connection->addr);
- GNUNET_free (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 %lu/%lu bytes from `%s' (%p)!\n",
- (unsigned long) ret,
- (unsigned long) 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 %lu fails (%s/%u), connection failed (%p).\n",
- (unsigned long) 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 %lu/%lu bytes to `%s' (%p)\n",
- (unsigned long) ret,
- (unsigned long) 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 */
|