LRN: Fix automake deps to allow -j* builds again
[oweals/gnunet.git] / src / util / connection.c
index 07f44fc512034415afdb3cbf65898b33c2a01411..f26130a0689d4ffdbb161f299952e6da4fc6eb5c 100644 (file)
@@ -41,7 +41,6 @@
 
 #define DEBUG_CONNECTION GNUNET_NO
 
-
 /**
  * Possible functions to call after connect failed or succeeded.
  */
@@ -302,6 +301,23 @@ void GNUNET_CONNECTION_persist_(struct GNUNET_CONNECTION_Handle *sock)
   sock->persist = GNUNET_YES;
 }
 
+
+/**
+ * Disable the "CORK" feature for communication with the given socket,
+ * 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 sock the connection to make flushing and blocking
+ * @return GNUNET_OK on success
+ */
+int GNUNET_CONNECTION_disable_corking (struct GNUNET_CONNECTION_Handle *sock)
+{
+  return GNUNET_NETWORK_socket_disable_corking (sock->sock);
+}
+
 /**
  * Create a socket handle by boxing an existing OS socket.  The OS
  * socket should henceforth be no longer used directly.
@@ -346,6 +362,12 @@ GNUNET_CONNECTION_create_from_accept (GNUNET_CONNECTION_AccessCheck access,
   struct sockaddr_in6 *v6;
   struct sockaddr *sa;
   void *uaddr;
+  struct GNUNET_CONNECTION_Credentials *gcp;  
+  struct GNUNET_CONNECTION_Credentials gc;  
+#ifdef SO_PEERCRED
+  struct ucred uc;
+  socklen_t olen;
+#endif
 
   addrlen = sizeof (addr);
   sock =
@@ -371,6 +393,9 @@ GNUNET_CONNECTION_create_from_accept (GNUNET_CONNECTION_AccessCheck access,
       v4 = GNUNET_malloc (sizeof (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
       memcpy (&v4->sin_addr,
               &((char *) &v6->sin6_addr)[sizeof (struct in6_addr) -
                                          sizeof (struct in_addr)],
@@ -384,9 +409,50 @@ GNUNET_CONNECTION_create_from_accept (GNUNET_CONNECTION_AccessCheck access,
       uaddr = GNUNET_malloc (addrlen);
       memcpy (uaddr, addr, addrlen);
     }
+  gcp = NULL;
+  gc.uid = 0;
+  gc.gid = 0;
+  if (sa->sa_family == AF_UNIX)
+    {
+#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 ((access != NULL) &&
-      (GNUNET_YES != (aret = access (access_cls, uaddr, addrlen))))
+      (GNUNET_YES != (aret = access (access_cls, gcp, uaddr, addrlen))))
     {
       if (aret == GNUNET_NO)
         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -499,7 +565,8 @@ destroy_continuation (void *cls,
       if (sock->persist != GNUNET_YES)
       {
         if ( (GNUNET_YES != GNUNET_NETWORK_socket_shutdown (sock->sock, SHUT_RDWR)) &&
-            (errno != ENOTCONN) )
+            (errno != ENOTCONN) &&
+             (errno != ECONNRESET) )
           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "shutdown");
       }
     }
@@ -595,8 +662,7 @@ connect_fail_continuation (struct GNUNET_CONNECTION_Handle *h)
                   h);
 #endif
       h->ccs -= COCO_RECEIVE_AGAIN;
-      h->read_task = GNUNET_SCHEDULER_add_after (GNUNET_SCHEDULER_NO_TASK,
-                                                 &receive_again, h);
+      h->read_task = GNUNET_SCHEDULER_add_now (&receive_again, h);
     }
   if (0 != (h->ccs & COCO_TRANSMIT_READY))
     {
@@ -609,9 +675,9 @@ connect_fail_continuation (struct GNUNET_CONNECTION_Handle *h)
       GNUNET_SCHEDULER_cancel (h->nth.timeout_task);
       h->nth.timeout_task = GNUNET_SCHEDULER_NO_TASK;
       h->ccs -= COCO_TRANSMIT_READY;
+      GNUNET_assert (h->nth.notify_ready != NULL);
       GNUNET_assert (h->write_task == GNUNET_SCHEDULER_NO_TASK);
-      h->write_task = GNUNET_SCHEDULER_add_after (GNUNET_SCHEDULER_NO_TASK,
-                                                  &transmit_ready, h);
+      h->write_task = GNUNET_SCHEDULER_add_now (&transmit_ready, h);
     }
   if (0 != (h->ccs & COCO_DESTROY_CONTINUATION))
     {
@@ -651,8 +717,7 @@ connect_success_continuation (struct GNUNET_CONNECTION_Handle *h)
                   h);
 #endif
       h->ccs -= COCO_RECEIVE_AGAIN;
-      h->read_task = GNUNET_SCHEDULER_add_after (GNUNET_SCHEDULER_NO_TASK,
-                                                 &receive_again, h);
+      h->read_task = GNUNET_SCHEDULER_add_now (&receive_again, h);
     }
   if (0 != (h->ccs & COCO_TRANSMIT_READY))
     {
@@ -666,6 +731,7 @@ connect_success_continuation (struct GNUNET_CONNECTION_Handle *h)
       h->nth.timeout_task = GNUNET_SCHEDULER_NO_TASK;
       h->ccs -= COCO_TRANSMIT_READY;
       GNUNET_assert (h->write_task == GNUNET_SCHEDULER_NO_TASK);
+      GNUNET_assert (h->nth.notify_ready != NULL);
       h->write_task =
         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining
                                         (h->nth.transmit_timeout), h->sock,
@@ -854,8 +920,7 @@ GNUNET_CONNECTION_create_from_connect (const struct
   ret->write_buffer = GNUNET_malloc(ret->write_buffer_size);
   ret->port = port;
   ret->hostname = GNUNET_strdup (hostname);
-  ret->dns_active = GNUNET_RESOLVER_ip_get (cfg,
-                                            ret->hostname,
+  ret->dns_active = GNUNET_RESOLVER_ip_get (ret->hostname,
                                             AF_UNSPEC,
                                             GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT,
                                             &try_connect_using_address, ret);
@@ -885,17 +950,19 @@ GNUNET_CONNECTION_create_from_connect_to_unixpath (const struct
   GNUNET_assert (0 < strlen (unixpath));        /* sanity check */
   un = GNUNET_malloc (sizeof (struct sockaddr_un));
   un->sun_family = AF_UNIX;
-  slen = strlen (unixpath) + 1;
+  slen = strlen (unixpath);
   if (slen >= sizeof (un->sun_path))
     slen = sizeof (un->sun_path) - 1;
   memcpy (un->sun_path,
          unixpath,
          slen);
   un->sun_path[slen] = '\0';
-  slen += sizeof (sa_family_t);
+  slen = sizeof (struct sockaddr_un);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+  un->sun_len = (u_char) slen;
+#endif
 #if LINUX
   un->sun_path[0] = '\0';
-  slen = sizeof (struct sockaddr_un);
 #endif
   ret = GNUNET_malloc (sizeof (struct GNUNET_CONNECTION_Handle));
   ret->cfg = cfg;
@@ -917,11 +984,10 @@ GNUNET_CONNECTION_create_from_connect_to_unixpath (const struct
                                                  ret->addr,
                                                  ret->addrlen)) 
     {
+      /* Just return; we expect everything to work eventually so don't fail HARD */
       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ret->sock));
-      GNUNET_free (ret->addr);
-      GNUNET_free (ret->write_buffer);
-      GNUNET_free (ret);
-      return NULL;
+      ret->sock = NULL;
+      return ret;
     }
   connect_success_continuation (ret);
   return ret;
@@ -962,6 +1028,9 @@ GNUNET_CONNECTION_create_from_sockaddr (int af_family,
     {
       /* maybe refused / unsupported address, try next */
       GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "connect");
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
+                 _("Attempt to connect to `%s' failed\n"),
+                 GNUNET_a2s (serv_addr, addrlen));
       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (s));
       return NULL;
     }
@@ -1020,6 +1089,7 @@ GNUNET_CONNECTION_destroy (struct GNUNET_CONNECTION_Handle *sock,
          sock->write_task = GNUNET_SCHEDULER_NO_TASK;
          sock->write_buffer_off = 0;
        }
+      sock->nth.notify_ready = NULL;
     }
   if ((sock->write_buffer_off == 0) && (sock->dns_active != NULL))
     {
@@ -1326,9 +1396,13 @@ process_notify (struct GNUNET_CONNECTION_Handle *sock)
  * expired).
  *
  * This task notifies the client about the timeout.
+ *
+ * @param cls the 'struct GNUNET_CONNECTION_Handle'
+ * @param tc scheduler context
  */
 static void
-transmit_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+transmit_timeout (void *cls, 
+                 const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct GNUNET_CONNECTION_Handle *sock = cls;
   GNUNET_CONNECTION_TransmitReadyNotify notify;
@@ -1357,17 +1431,24 @@ transmit_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  * at the time of being asked to transmit.
  *
  * This task notifies the client about the error.
+ *
+ * @param cls the 'struct GNUNET_CONNECTION_Handle'
+ * @param tc scheduler context
  */
 static void
-connect_error (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+connect_error (void *cls, 
+              const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct GNUNET_CONNECTION_Handle *sock = cls;
   GNUNET_CONNECTION_TransmitReadyNotify notify;
 
 #if DEBUG_CONNECTION
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Transmission request of size %u fails, connection failed (%p).\n",
-              sock->nth.notify_size, sock);
+              "Transmission request of size %u fails (%s/%u), connection failed (%p).\n",
+              sock->nth.notify_size, 
+             sock->hostname,
+             sock->port,
+             sock);
 #endif
   sock->write_task = GNUNET_SCHEDULER_NO_TASK;
   notify = sock->nth.notify_ready;
@@ -1376,6 +1457,11 @@ connect_error (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 }
 
 
+/**
+ * FIXME
+ *
+ * @param sock FIXME
+ */
 static void
 transmit_error (struct GNUNET_CONNECTION_Handle *sock)
 {
@@ -1436,8 +1522,11 @@ transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
                   GNUNET_a2s (sock->addr, sock->addrlen), sock);
 #endif
       notify = sock->nth.notify_ready;
-      sock->nth.notify_ready = NULL;
-      notify (sock->nth.notify_ready_cls, 0, NULL);
+      if (NULL != notify)
+       {
+         sock->nth.notify_ready = NULL;
+         notify (sock->nth.notify_ready_cls, 0, NULL);
+       }
       return;
     }
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
@@ -1448,6 +1537,7 @@ transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
                   GNUNET_a2s (sock->addr, sock->addrlen), sock);
 #endif
       notify = sock->nth.notify_ready;
+      GNUNET_assert (NULL != notify);
       sock->nth.notify_ready = NULL;
       notify (sock->nth.notify_ready_cls, 0, NULL);
       return;
@@ -1533,11 +1623,15 @@ SCHEDULE_WRITE:
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Re-scheduling transmit_ready (more to do) (%p).\n", sock);
 #endif
+  have = sock->write_buffer_off - sock->write_buffer_pos;
+  GNUNET_assert ( (sock->nth.notify_ready != NULL) || (have > 0) );
   if (sock->write_task == GNUNET_SCHEDULER_NO_TASK)
     sock->write_task =
-      GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining
-                                      (sock->nth.transmit_timeout),
-                                      sock->sock, &transmit_ready, sock);
+      GNUNET_SCHEDULER_add_write_net ((sock->nth.notify_ready == NULL) 
+                                     ? GNUNET_TIME_UNIT_FOREVER_REL 
+                                     : GNUNET_TIME_absolute_get_remaining (sock->nth.transmit_timeout),
+                                     sock->sock, 
+                                     &transmit_ready, sock);
 }
 
 
@@ -1563,7 +1657,10 @@ GNUNET_CONNECTION_notify_transmit_ready (struct GNUNET_CONNECTION_Handle
                                          notify, void *notify_cls)
 {
   if (sock->nth.notify_ready != NULL)
-    return NULL;
+    {
+      GNUNET_assert (0);
+      return NULL;
+    }
   GNUNET_assert (notify != NULL);
   GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
   GNUNET_assert (sock->write_buffer_off <= sock->write_buffer_size);
@@ -1615,8 +1712,9 @@ GNUNET_CONNECTION_notify_transmit_ready (struct GNUNET_CONNECTION_Handle
 
 
 /**
- * Cancel the specified transmission-ready
- * notification.
+ * Cancel the specified transmission-ready notification.
+ *
+ * @param th notification to cancel
  */
 void
 GNUNET_CONNECTION_notify_transmit_ready_cancel (struct