static void start_connect(SESSION *session) { int fd; struct linger linger; /* * Some systems don't set the socket error when connect() fails early * (loopback) so we must deal with the error immediately, rather than * retrieving it later with getsockopt(). We can't use MSG_PEEK to * distinguish between server disconnect and connection refused. */ if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) msg_fatal("socket: %m"); (void) non_blocking(fd, NON_BLOCKING); linger.l_onoff = 1; linger.l_linger = 0; if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &linger, sizeof(linger)) < 0) msg_warn("setsockopt SO_LINGER %d: %m", linger.l_linger); session->stream = vstream_fdopen(fd, O_RDWR); event_enable_write(fd, connect_done, (char *) session); smtp_timeout_setup(session->stream, var_timeout); if (inet_windowsize > 0) set_inet_windowsize(fd, inet_windowsize); if (sane_connect(fd, sa, sa_length) < 0 && errno != EINPROGRESS) fail_connect(session); }
int inet_connect(const char *addr, int block_mode, int timeout) { char *buf; char *host; char *port; struct sockaddr_in sin; int sock; /* * Translate address information to internal form. No host defaults to * the local host. */ buf = inet_parse(addr, &host, &port); if (*host == 0) host = "localhost"; memset((char *) &sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = find_inet_addr(host); sin.sin_port = find_inet_port(port, "tcp"); myfree(buf); /* * Create a client socket. */ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) msg_fatal("socket: %m"); /* * Timed connect. */ if (timeout > 0) { non_blocking(sock, NON_BLOCKING); if (timed_connect(sock, (struct sockaddr *) & sin, sizeof(sin), 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, (struct sockaddr *) & sin, sizeof(sin)) < 0 && errno != EINPROGRESS) { close(sock); return (-1); } return (sock); } }
int unix_connect(const char *addr, int block_mode, int timeout) { #undef sun struct sockaddr_un sun; int len = strlen(addr); int sock; /* * Translate address information to internal form. */ if (len >= (int) sizeof(sun.sun_path)) msg_fatal("unix-domain name too long: %s", addr); memset((char *) &sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; #ifdef HAS_SUN_LEN sun.sun_len = len + 1; #endif memcpy(sun.sun_path, addr, len + 1); /* * Create a client socket. */ if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) return (-1); /* * Timed connect. */ if (timeout > 0) { non_blocking(sock, NON_BLOCKING); if (timed_connect(sock, (struct sockaddr *) & sun, sizeof(sun), 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, (struct sockaddr *) & sun, sizeof(sun)) < 0 && errno != EINPROGRESS) { close(sock); return (-1); } return (sock); } }
static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr *sa, int salen, SMTP_ITERATOR *iter, DSN_BUF *why, int sess_flags) { int conn_stat; int saved_errno; VSTREAM *stream; time_t start_time; const char *name = STR(iter->host); const char *addr = STR(iter->addr); unsigned port = iter->port; start_time = time((time_t *) 0); if (var_smtp_conn_tmout > 0) { non_blocking(sock, NON_BLOCKING); conn_stat = timed_connect(sock, sa, salen, var_smtp_conn_tmout); saved_errno = errno; non_blocking(sock, BLOCKING); errno = saved_errno; } else { conn_stat = sane_connect(sock, sa, salen); } if (conn_stat < 0) { if (port) dsb_simple(why, "4.4.1", "connect to %s[%s]:%d: %m", name, addr, ntohs(port)); else dsb_simple(why, "4.4.1", "connect to %s[%s]: %m", name, addr); close(sock); return (0); } stream = vstream_fdopen(sock, O_RDWR); /* * Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE. */ if (sa->sa_family == AF_INET #ifdef AF_INET6 || sa->sa_family == AF_INET6 #endif ) vstream_tweak_tcp(stream); /* * Bundle up what we have into a nice SMTP_SESSION object. */ return (smtp_session_alloc(stream, iter, start_time, sess_flags)); }
int timed_connect(int sock, struct sockaddr * sa, int len, int timeout) { int error; SOCKOPT_SIZE error_len; /* * Sanity check. Just like with timed_wait(), the timeout must be a * positive number. */ if (timeout <= 0) msg_panic("timed_connect: bad timeout: %d", timeout); /* * Start the connection, and handle all possible results. */ if (sane_connect(sock, sa, len) == 0) return (0); if (errno != EINPROGRESS) return (-1); /* * A connection is in progress. Wait for a limited amount of time for * something to happen. If nothing happens, report an error. */ if (write_wait(sock, timeout) < 0) return (-1); /* * Something happened. Some Solaris 2 versions have getsockopt() itself * return the error, instead of returning it via the parameter list. */ error = 0; error_len = sizeof(error); if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &error, &error_len) < 0) return (-1); if (error) { errno = error; return (-1); } /* * No problems. */ return (0); }