Useful error messages when writing to a meta connection fails.
[tinc] / src / net_socket.c
index af3f2fe..986a766 100644 (file)
@@ -292,7 +292,8 @@ void retry_outgoing(outgoing_t *outgoing) {
 void finish_connecting(connection_t *c) {
        logger(DEBUG_CONNECTIONS, LOG_INFO, "Connected to %s (%s)", c->name, c->hostname);
 
-       configure_tcp(c);
+       if(proxytype != PROXY_EXEC)
+               configure_tcp(c);
 
        c->last_ping_time = time(NULL);
        c->status.connecting = false;
@@ -300,8 +301,57 @@ void finish_connecting(connection_t *c) {
        send_id(c);
 }
 
+static void do_outgoing_pipe(connection_t *c, char *command) {
+#ifndef HAVE_MINGW
+       int fd[2];
+
+       if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Could not create socketpair: %s", strerror(errno));
+               return;
+       }
+
+       if(fork()) {
+               c->socket = fd[0];
+               close(fd[1]);
+               logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Using proxy %s", command);
+               return;
+       }
+
+       close(0);
+       close(1);
+       close(fd[0]);
+       dup2(fd[1], 0);
+       dup2(fd[1], 1);
+       close(fd[1]);
+
+       // Other filedescriptors should be closed automatically by CLOEXEC
+
+       char *host = NULL;
+       char *port = NULL;
+
+       sockaddr2str(&c->address, &host, &port);
+       setenv("REMOTEADDRESS", host, true);
+       setenv("REMOTEPORT", port, true);
+       setenv("NODE", c->name, true);
+       setenv("NAME", myself->name, true);
+       if(netname)
+               setenv("NETNAME", netname, true);
+
+       int result = system(command);
+       if(result < 0)
+               logger(DEBUG_ALWAYS, LOG_ERR, "Could not execute %s: %s", command, strerror(errno));
+       else if(result)
+               logger(DEBUG_ALWAYS, LOG_ERR, "%s exited with non-zero status %d", command, result);
+       exit(result);
+#else
+       logger(DEBUG_ALWAYS, LOG_ERR, "Proxy type exec not supported on this platform!");
+       return;
+#endif
+}
+
 bool do_outgoing_connection(connection_t *c) {
        char *address, *port, *space;
+       struct addrinfo *proxyai = NULL;
        int result;
 
        if(!c->outgoing) {
@@ -357,32 +407,48 @@ begin:
        logger(DEBUG_CONNECTIONS, LOG_INFO, "Trying to connect to %s (%s)", c->name,
                           c->hostname);
 
-       c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
-
-#ifdef FD_CLOEXEC
-       fcntl(c->socket, F_SETFD, FD_CLOEXEC);
-#endif
+       if(!proxytype) {
+               c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
+               configure_tcp(c);
+       } else if(proxytype == PROXY_EXEC) {
+               do_outgoing_pipe(c, proxyhost);
+       } else {
+               proxyai = str2addrinfo(proxyhost, proxyport, SOCK_STREAM);
+               if(!proxyai)
+                       goto begin;
+               logger(DEBUG_CONNECTIONS, LOG_INFO, "Using proxy at %s port %s", proxyhost, proxyport);
+               c->socket = socket(proxyai->ai_family, SOCK_STREAM, IPPROTO_TCP);
+       }
 
        if(c->socket == -1) {
                logger(DEBUG_CONNECTIONS, LOG_ERR, "Creating socket for %s failed: %s", c->hostname, sockstrerror(sockerrno));
                goto begin;
        }
 
-#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
-       int option = 1;
-       if(c->address.sa.sa_family == AF_INET6)
-               setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
+#ifdef FD_CLOEXEC
+       fcntl(c->socket, F_SETFD, FD_CLOEXEC);
 #endif
 
-       bind_to_interface(c->socket);
-
-       /* Optimize TCP settings */
+       if(proxytype != PROXY_EXEC) {
+#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
+               int option = 1;
+               if(c->address.sa.sa_family == AF_INET6)
+                       setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
+#endif
 
-       configure_tcp(c);
+               bind_to_interface(c->socket);
+       }
 
        /* Connect */
 
-       result = connect(c->socket, &c->address.sa, SALEN(c->address.sa));
+       if(!proxytype) {
+               result = connect(c->socket, &c->address.sa, SALEN(c->address.sa));
+       } else if(proxytype == PROXY_EXEC) {
+               result = 0;
+       } else {
+               result = connect(c->socket, proxyai->ai_addr, proxyai->ai_addrlen);
+               freeaddrinfo(proxyai);
+       }
 
        if(result == -1) {
                if(sockinprogress(sockerrno)) {
@@ -407,7 +473,15 @@ static void handle_meta_write(int sock, short events, void *data) {
 
        ssize_t outlen = send(c->socket, c->outbuf.data + c->outbuf.offset, c->outbuf.len - c->outbuf.offset, 0);
        if(outlen <= 0) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Onoes, outlen = %d (%s)", (int)outlen, strerror(errno));
+               if(!errno || errno == EPIPE) {
+                       logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection closed by %s (%s)", c->name, c->hostname);
+               } else if(sockwouldblock(sockerrno)) {
+                       logger(DEBUG_CONNECTIONS, LOG_DEBUG, "Sending %d bytes to %s (%s) would block", c->outbuf.len - c->outbuf.offset, c->name, c->hostname);
+                       return;
+               } else {
+                       logger(DEBUG_CONNECTIONS, LOG_ERR, "Could not send %d bytes of data to %s (%s): %s", c->outbuf.len - c->outbuf.offset, c->name, c->hostname, strerror(errno));
+               }
+
                terminate_connection(c, c->status.active);
                return;
        }
@@ -510,6 +584,9 @@ void handle_new_meta_connection(int sock, short events, void *data) {
 }
 
 static void free_outgoing(outgoing_t *outgoing) {
+       if(event_initialized(&outgoing->ev))
+               event_del(&outgoing->ev);
+
        if(outgoing->ai)
                freeaddrinfo(outgoing->ai);
 
@@ -524,8 +601,19 @@ void try_outgoing_connections(void) {
        char *name;
        outgoing_t *outgoing;
        
-       outgoing_list = list_alloc((list_action_t)free_outgoing);
-                       
+       /* If there is no outgoing list yet, create one. Otherwise, mark all outgoings as deleted. */
+
+       if(!outgoing_list) {
+               outgoing_list = list_alloc((list_action_t)free_outgoing);
+       } else {
+               for(list_node_t *i = outgoing_list->head; i; i = i->next) {
+                       outgoing = i->data;
+                       outgoing->timeout = -1;
+               }
+       }
+
+       /* Make sure there is one outgoing_t in the list for each ConnectTo. */
+
        for(cfg = lookup_config(config_tree, "ConnectTo"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
                get_config_string(cfg, &name);
 
@@ -537,9 +625,42 @@ void try_outgoing_connections(void) {
                        continue;
                }
 
-               outgoing = xmalloc_and_zero(sizeof *outgoing);
-               outgoing->name = name;
-               list_insert_tail(outgoing_list, outgoing);
-               setup_outgoing_connection(outgoing);
+               bool found = false;
+
+               for(list_node_t *i = outgoing_list->head; i; i = i->next) {
+                       outgoing = i->data;
+                       if(!strcmp(outgoing->name, name)) {
+                               found = true;
+                               outgoing->timeout = 0;
+                               break;
+                       }
+               }
+
+               if(!found) {
+                       outgoing = xmalloc_and_zero(sizeof *outgoing);
+                       outgoing->name = name;
+                       list_insert_tail(outgoing_list, outgoing);
+                       setup_outgoing_connection(outgoing);
+               }
+       }
+
+       /* Terminate any connections whose outgoing_t is to be deleted. */
+
+       for(splay_node_t *n = connection_tree->head, *next; n; n = next) {
+               next = n->next;
+               connection_t *c = n->data;
+               if(c->outgoing && c->outgoing->timeout == -1) {
+                       c->outgoing = NULL;
+                       terminate_connection(c, c->status.active);
+               }
+       }
+
+       /* Delete outgoing_ts for which there is no ConnectTo. */
+
+       for(list_node_t *i = outgoing_list->head, *next; i; i = next) {
+               next = i->next;
+               outgoing = i->data;
+               if(outgoing->timeout == -1)
+                       list_delete_node(outgoing_list, i);
        }
 }