summaryrefslogtreecommitdiffstats
path: root/src/util/inet_connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/inet_connect.c')
-rw-r--r--src/util/inet_connect.c189
1 files changed, 189 insertions, 0 deletions
diff --git a/src/util/inet_connect.c b/src/util/inet_connect.c
new file mode 100644
index 0000000..0f5542e
--- /dev/null
+++ b/src/util/inet_connect.c
@@ -0,0 +1,189 @@
+/*++
+/* NAME
+/* inet_connect 3
+/* SUMMARY
+/* connect to TCP listener
+/* SYNOPSIS
+/* #include <connect.h>
+/*
+/* int inet_windowsize;
+/*
+/* int inet_connect(addr, block_mode, timeout)
+/* const char *addr;
+/* int block_mode;
+/* int timeout;
+/* DESCRIPTION
+/* inet_connect connects to a TCP listener at
+/* the specified address, and returns the resulting file descriptor.
+/*
+/* Specify an inet_windowsize value > 0 to override the TCP
+/* window size that the client advertises to the server.
+/*
+/* Arguments:
+/* .IP addr
+/* The destination to connect to. The format is host:port. If no
+/* host is specified, a port on the local host is assumed.
+/* Host and port information may be given in numerical form
+/* or as symbolical names.
+/* .IP block_mode
+/* Either NON_BLOCKING for a non-blocking socket, or BLOCKING for
+/* blocking mode.
+/* .IP timeout
+/* Bounds the number of seconds that the operation may take. Specify
+/* a value <= 0 to disable the time limit.
+/* DIAGNOSTICS
+/* The result is -1 when the connection could not be made.
+/* The nature of the error is available via the global \fIerrno\fR
+/* variable.
+/* Fatal errors: other system call failures.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System interfaces. */
+
+#include <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+
+/* Utility library. */
+
+#include "mymalloc.h"
+#include "msg.h"
+#include "iostuff.h"
+#include "host_port.h"
+#include "sane_connect.h"
+#include "connect.h"
+#include "timed_connect.h"
+#include "myaddrinfo.h"
+#include "sock_addr.h"
+#include "inet_proto.h"
+
+static int inet_connect_one(struct addrinfo *, int, int);
+
+/* inet_connect - connect to TCP listener */
+
+int inet_connect(const char *addr, int block_mode, int timeout)
+{
+ char *buf;
+ char *host;
+ char *port;
+ const char *parse_err;
+ struct addrinfo *res;
+ struct addrinfo *res0;
+ int aierr;
+ int sock;
+ MAI_HOSTADDR_STR hostaddr;
+ const INET_PROTO_INFO *proto_info;
+ int found;
+
+ /*
+ * Translate address information to internal form. No host defaults to
+ * the local host.
+ */
+ buf = mystrdup(addr);
+ if ((parse_err = host_port(buf, &host, "localhost", &port, (char *) 0)) != 0)
+ msg_fatal("%s: %s", addr, parse_err);
+ if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res0)) != 0)
+ msg_warn("host or service %s not found: %s",
+ addr, MAI_STRERROR(aierr));
+ myfree(buf);
+ if (aierr) {
+ errno = EADDRNOTAVAIL; /* for up-stream "%m" */
+ return (-1);
+ }
+ proto_info = inet_proto_info();
+ for (sock = -1, found = 0, res = res0; res != 0; res = res->ai_next) {
+
+ /*
+ * Safety net.
+ */
+ if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
+ msg_info("skipping address family %d for host %s",
+ res->ai_family, host);
+ continue;
+ }
+ found++;
+
+ /*
+ * In case of multiple addresses, show what address we're trying now.
+ */
+ if (msg_verbose) {
+ SOCKADDR_TO_HOSTADDR(res->ai_addr, res->ai_addrlen,
+ &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
+ msg_info("trying... [%s]", hostaddr.buf);
+ }
+ if ((sock = inet_connect_one(res, block_mode, timeout)) < 0) {
+ if (msg_verbose)
+ msg_info("%m");
+ } else
+ break;
+ }
+ if (found == 0)
+ msg_fatal("host not found: %s", addr);
+ freeaddrinfo(res0);
+ return (sock);
+}
+
+/* inet_connect_one - try to connect to one address */
+
+static int inet_connect_one(struct addrinfo * res, int block_mode, int timeout)
+{
+ int sock;
+
+ /*
+ * Create a client socket.
+ */
+ sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (sock < 0)
+ return (-1);
+
+ /*
+ * Window scaling workaround.
+ */
+ if (inet_windowsize > 0)
+ set_inet_windowsize(sock, inet_windowsize);
+
+ /*
+ * Timed connect.
+ */
+ if (timeout > 0) {
+ non_blocking(sock, NON_BLOCKING);
+ if (timed_connect(sock, res->ai_addr, res->ai_addrlen, timeout) < 0) {
+ close(sock);
+ return (-1);
+ }
+ if (block_mode != NON_BLOCKING)
+ non_blocking(sock, block_mode);
+ return (sock);
+ }
+
+ /*
+ * Maybe block until connected.
+ */
+ else {
+ non_blocking(sock, block_mode);
+ if (sane_connect(sock, res->ai_addr, res->ai_addrlen) < 0
+ && errno != EINPROGRESS) {
+ close(sock);
+ return (-1);
+ }
+ return (sock);
+ }
+}