/*
* Soft: Keepalived is a failover program for the LVS project
* <www.linuxvirtualserver.org>. It monitor & manipulate
* a loadbalanced server pool using multi-layer checks.
*
* Part: SMTP CHECK. Check an SMTP-server.
*
* Authors: Jeremy Rumpf, <jrumpf@heavyload.net>
* Alexandre Cassen, <acassen@linux-vs.org>
*
* This program 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 General Public License for more details.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Copyright (C) 2001-2017 Alexandre Cassen, <acassen@gmail.com>
*/
#include "config.h"
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include "check_smtp.h"
#include "logger.h"
#include "ipwrapper.h"
#include "utils.h"
#include "parser.h"
#if !HAVE_DECL_SOCK_CLOEXEC
#include "old_socket.h"
#endif
#include "layer4.h"
#include "smtp.h"
#ifdef THREAD_DUMP
#include "scheduler.h"
#endif
static conn_opts_t* default_co; /* Default conn_opts for SMTP_CHECK */
static conn_opts_t *sav_co; /* Saved conn_opts while host{} block processed */
static int smtp_connect_thread(thread_t *);
static int smtp_final(thread_t *thread, int error, const char *format, ...)
__attribute__ ((format (printf, 3, 4)));
/*
* Used as a callback from free_list() to free all
* the list elements in smtp_checker->host before we
* free smtp_checker itself.
*/
static void
smtp_free_host(void *data)
{
FREE(data);
}
/* Used as a callback from the checker api, queue_checker(),
* to free up a checker entry and all its associated data.
*/
static void
free_smtp_check(void *data)
{
smtp_checker_t *smtp_checker = CHECKER_DATA(data);
free_list(&smtp_checker->host);
FREE(smtp_checker->helo_name);
FREE(smtp_checker);
FREE(data);
}
/*
* Callback for whenever we've been requested to dump our
* configuration.
*/
static void
dump_smtp_check(FILE *fp, void *data)
{
checker_t *checker = data;
smtp_checker_t *smtp_checker = checker->data;
conf_write(fp, " Keepalive method = SMTP_CHECK");
conf_write(fp, " helo = %s", smtp_checker->helo_name);
dump_checker_opts(fp, checker);
if (smtp_checker->host) {
conf_write(fp, " Host list");
dump_list(fp, smtp_checker->host);
}
}
static bool
smtp_check_compare(void *a, void *b)
{
smtp_checker_t *old = CHECKER_DATA(a);
smtp_checker_t *new = CHECKER_DATA(b);
size_t n;
conn_opts_t *h1, *h2;
if (strcmp(old->helo_name, new->helo_name) != 0)
return false;
if (!compare_conn_opts(CHECKER_CO(a), CHECKER_CO(b)))
return false;
if (LIST_SIZE(old->host) != LIST_SIZE(new->host))
return false;
for (n = 0; n < LIST_SIZE(new->host); n++) {
h1 = (conn_opts_t *)list_element(old->host, n);
h2 = (conn_opts_t *)list_element(new->host, n);
if (!compare_conn_opts(h1, h2)) {
return false;
}
}
return true;
}
/*
* Callback for whenever an SMTP_CHECK keyword is encountered
* in the config file.
*/
static void
smtp_check_handler(__attribute__((unused)) vector_t *strvec)
{
smtp_checker_t *smtp_checker = (smtp_checker_t *)MALLOC(sizeof(smtp_checker_t));
/* We keep a copy of the default settings for completing incomplete settings */
default_co = (conn_opts_t*)MALLOC(sizeof(conn_opts_t));
/* Have the checker queue code put our checker into the checkers_queue list. */
queue_checker(free_smtp_check, dump_smtp_check, smtp_connect_thread,
smtp_check_compare, smtp_checker, default_co);
/* Set an empty conn_opts for any connection configured */
((checker_t *)checkers_queue->tail->data)->co = (conn_opts_t*)MALLOC(sizeof(conn_opts_t));
/*
* Last, allocate the list that will hold all the per host
* configuration structures. We already have the "default host"
* in our checker->co.
* If there are additional "host" sections in the config, they will
* be used instead of the default, but all the uninitialized options
* of those hosts will be set to the default's values.
*/
smtp_checker->host = alloc_list(smtp_free_host, dump_connection_opts);
}
static void
smtp_check_end_handler(void)
{
smtp_checker_t *smtp_checker = CHECKER_GET();
conn_opts_t *co = CHECKER_GET_CO();
if (!smtp_checker->helo_name) {
smtp_checker->helo_name = (char *)MALLOC(strlen(SMTP_DEFAULT_HELO) + 1);
strcpy(smtp_checker->helo_name, SMTP_DEFAULT_HELO);
}
/* If any connection component has been configured, we want to add it to the host list */
if (co->dst.ss_family != AF_UNSPEC ||
(co->dst.ss_family == AF_UNSPEC && ((struct sockaddr_in *)&co->dst)->sin_port) ||
co->bindto.ss_family != AF_UNSPEC ||
(co->bindto.ss_family == AF_UNSPEC && ((struct sockaddr_in *)&co->bindto)->sin_port) ||
co->bind_if[0] ||
#ifdef _WITH_SO_MARK_
co->fwmark ||
#endif
co->connection_to) {
/* Set any necessary defaults */
if (co->dst.ss_family == AF_UNSPEC) {
if (((struct sockaddr_in *)&co->dst)->sin_port) {
uint16_t saved_port = ((struct sockaddr_in *)&co->dst)->sin_port;
co->dst = default_co->dst;
checker_set_dst_port(&co->dst, saved_port);
}
else
co->dst = default_co->dst;
}
if (!co->connection_to)
co->connection_to = 5 * TIMER_HZ;
if (!check_conn_opts(co)) {
dequeue_new_checker();
FREE(co);
} else
list_add(smtp_checker->host, co);
}
else
FREE(co);
CHECKER_GET_CO() = NULL;
/* If there was no host{} section, add a single host to the list */
if (LIST_ISEMPTY(smtp_checker->host)) {
list_add(smtp_checker->host, default_co);
} else
FREE(default_co);
default_co = NULL;
}
/* Callback for "host" keyword */
static void
smtp_host_handler(__attribute__((unused)) vector_t *strvec)
{
checker_t *checker = CHECKER_GET_CURRENT();
/* save the main conn_opts_t and set a new default for the host */
sav_co = checker->co;
checker->co = (conn_opts_t*)MALLOC(sizeof(conn_opts_t));
memcpy(checker->co, default_co, sizeof(*default_co));
log_message(LOG_INFO, "The SMTP_CHECK host block is deprecated. Please define additional checkers.");
}
static void
smtp_host_end_handler(void)
{
checker_t *checker = CHECKER_GET_CURRENT();
smtp_checker_t *smtp_checker = (smtp_checker_t *)checker->data;
if (!check_conn_opts(checker->co))
FREE(checker->co);
else
list_add(smtp_checker->host, checker->co);
checker->co = sav_co;
}
/* "helo_name" keyword */
static void
smtp_helo_name_handler(vector_t *strvec)
{
smtp_checker_t *smtp_checker = CHECKER_GET();
if (smtp_checker->helo_name)
FREE(smtp_checker->helo_name);
smtp_checker->helo_name = CHECKER_VALUE_STRING(strvec);
}
/* Config callback installer */
void
install_smtp_check_keyword(void)
{
/*
* Notify the config log parser that we need to be notified via
* callbacks when the following keywords are encountered in the
* keepalive.conf file.
*/
install_keyword("SMTP_CHECK", &smtp_check_handler);
install_sublevel();
install_keyword("helo_name", &smtp_helo_name_handler);
install_checker_common_keywords(true);
/*
* The host list feature is deprecated. It makes config fussy by
* adding another nesting level and is excessive since it is possible
* to attach multiple checkers to a RS.
* So these keywords below are kept for compatibility with users'
* existing configs.
*/
install_keyword("host", &smtp_host_handler);
install_sublevel();
install_checker_common_keywords(true);
install_sublevel_end_handler(smtp_host_end_handler);
install_sublevel_end();
install_sublevel_end_handler(&smtp_check_end_handler);
install_sublevel_end();
}
/*
* Final handler. Determines if we need a retry or not.
* Also has to make a decision if we need to bring the resulting
* service down in case of error.
*/
static int
smtp_final(thread_t *thread, int error, const char *format, ...)
{
checker_t *checker = THREAD_ARG(thread);
smtp_checker_t *smtp_checker = CHECKER_ARG(checker);
char error_buff[512];
char smtp_buff[542];
va_list varg_list;
bool checker_was_up;
bool rs_was_alive;
/* Error or no error we should always have to close the socket */
thread_close_fd(thread);
/* If we're here, an attempt HAS been made already for the current host */
checker->retry_it++;
if (error) {
/* Always syslog the error when the real server is up */
if (checker->is_up) {
if (format != NULL) {
/* prepend format with the "SMTP_CHECK " string */
strncpy(error_buff, "SMTP_CHECK ", sizeof(error_buff) - 1);
strncat(error_buff, format, sizeof(error_buff) - 11 - 1);
va_start(varg_list, format);
vlog_message(LOG_INFO, error_buff, varg_list);
va_end(varg_list);
} else {
log_message(LOG_INFO, "SMTP_CHECK Unknown error");
}
}
/*
* If we still have retries left, try this host again by
* scheduling the main thread to check it again after the
* configured backoff delay. Otherwise down the RS.
*/
if (checker->retry_it < checker->retry) {
thread_add_timer(thread->master, smtp_connect_thread, checker,
checker->delay_before_retry);
return 0;
}
/*
* No more retries, pull the real server from the virtual server.
* Only smtp_alert if it wasn't previously down. It should
* be noted that smtp_alert makes a copy of the string arguments, so
* we don't have to keep them statically allocated.
*/
if (checker->is_up || !checker->has_run) {
checker_was_up = checker->is_up;
rs_was_alive = checker->rs->alive;
update_svr_checker_state(DOWN, checker);
if (checker->rs->smtp_alert && checker_was_up &&
(rs_was_alive != checker->rs->alive || !global_data->no_checker_emails)) {
if (format != NULL) {
snprintf(error_buff, sizeof(error_buff), "=> CHECK failed on service : %s <=", format);
va_start(varg_list, format);
vsnprintf(smtp_buff, sizeof(smtp_buff), error_buff, varg_list);
va_end(varg_list);
} else
strncpy(smtp_buff, "=> CHECK failed on service <=", sizeof(smtp_buff));
smtp_buff[sizeof(smtp_buff) - 1] = '\0';
smtp_alert(SMTP_MSG_RS, checker, NULL, smtp_buff);
}
}
/* Reset everything back to the first host in the list */
checker->retry_it = 0;
smtp_checker->host_ctr = 0;
/* Reschedule the main thread using the configured delay loop */;
thread_add_timer(thread->master, smtp_connect_thread, checker, checker->delay_loop);
return 0;
}
/*
* Ok this host was successful, increment to the next host in the list
* and reset the retry_it counter. We'll then reschedule the main thread again.
* If host_ptr exceeds the end of the list, http_main_thread will
* take note and bring up the real server as well as inject the delay_loop.
*/
checker->retry_it = 0;
smtp_checker->host_ctr++;
thread_add_timer(thread->master, smtp_connect_thread, checker, 1);
return 0;
}
/*
* Zeros out the rx/tx buffer
*/
static void
smtp_clear_buff(thread_t *thread)
{
checker_t *checker = THREAD_ARG(thread);
smtp_checker_t *smtp_checker = CHECKER_ARG(checker);
memset(smtp_checker->buff, 0, SMTP_BUFF_MAX);
smtp_checker->buff_ctr = 0;
}
/*
* One thing to note here is we do a very cheap check for a newline.
* We could receive two lines (with two newline characters) in a
* single packet, but we don't care. We are only looking at the
* SMTP response codes at the beginning anyway.
*/
static int
smtp_get_line_cb(thread_t *thread)
{
checker_t *checker = THREAD_ARG(thread);
smtp_checker_t *smtp_checker = CHECKER_ARG(checker);
conn_opts_t *smtp_host = smtp_checker->host_ptr;
unsigned x;
ssize_t r;
/* Handle read timeout */
if (thread->type == THREAD_READ_TIMEOUT) {
smtp_final(thread, 1, "Read timeout from server %s"
, FMT_SMTP_RS(smtp_host));
return 0;
}
/* wrap the buffer, if full, by clearing it */
if (smtp_checker->buff_ctr > SMTP_BUFF_MAX) {
log_message(LOG_INFO, "SMTP_CHECK Buffer overflow reading from server %s. "
"Increase SMTP_BUFF_MAX in smtp_check.h"
, FMT_SMTP_RS(smtp_host));
smtp_clear_buff(thread);
}
/* read the data */
r = read(thread->u.fd, smtp_checker->buff + smtp_checker->buff_ctr,
SMTP_BUFF_MAX - smtp_checker->buff_ctr);
if (r == -1 && (errno == EAGAIN || errno == EINTR)) {
thread_add_read(thread->master, smtp_get_line_cb, checker,
thread->u.fd, smtp_host->connection_to);
return 0;
} else if (r > 0)
smtp_checker->buff_ctr += (size_t)r;
/* check if we have a newline, if so, callback */
for (x = 0; x < SMTP_BUFF_MAX; x++) {
if (smtp_checker->buff[x] == '\n') {
smtp_checker->buff[SMTP_BUFF_MAX - 1] = '\0';
DBG("SMTP_CHECK %s < %s"
, FMT_SMTP_RS(smtp_host)
, smtp_checker->buff);
(smtp_checker->buff_cb)(thread);
return 0;
}
}
/*
* If the connection was closed or there was
* some sort of error, notify smtp_final()
*/
if (r <= 0) {
smtp_final(thread, 1, "Read failure from server %s"
, FMT_SMTP_RS(smtp_host));
return 0;
}
/*
* Last case, we haven't read enough data yet
* to pull a newline. Schedule ourselves for
* another round.
*/
thread_add_read(thread->master, smtp_get_line_cb, checker,
thread->u.fd, smtp_host->connection_to);
return 0;
}
/*
* Ok a caller has asked us to asyncronously schedule a single line
* to be received from the server. They have also passed us a call back
* function that we'll call once we have the newline. If something bad
* happens, the caller assumes we'll pass the error off to smtp_final(),
* which will either down the real server or schedule a retry. The
* function smtp_get_line_cb is what does the dirty work since the
* sceduler can only accept a single *thread argument.
*/
static void
smtp_get_line(thread_t *thread, int (*callback) (thread_t *))
{
checker_t *checker = THREAD_ARG(thread);
smtp_checker_t *smtp_checker = CHECKER_ARG(checker);
conn_opts_t *smtp_host = smtp_checker->host_ptr;
/* clear the buffer */
smtp_clear_buff(thread);
/* set the callback */
smtp_checker->buff_cb = callback;
/* schedule the I/O with our helper function */
thread_add_read(thread->master, smtp_get_line_cb, checker,
thread->u.fd, smtp_host->connection_to);
thread_del_write(thread);
return;
}
/*
* The scheduler function that puts the data out on the wire.
* All our data will fit into one packet, so we only check if
* the current write would block or not. If it wants to block,
* we'll return to the scheduler and try again later.
*/
static int
smtp_put_line_cb(thread_t *thread)
{
checker_t *checker = THREAD_ARG(thread);
smtp_checker_t *smtp_checker = CHECKER_ARG(checker);
conn_opts_t *smtp_host = smtp_checker->host_ptr;
ssize_t w;
/* Handle read timeout */
if (thread->type == THREAD_WRITE_TIMEOUT) {
smtp_final(thread, 1, "Write timeout to server %s"
, FMT_SMTP_RS(smtp_host));
return 0;
}
/* write the data */
w = write(thread->u.fd, smtp_checker->buff, smtp_checker->buff_ctr);
if (w == -1 && (errno == EAGAIN || errno == EINTR)) {
thread_add_write(thread->master, smtp_put_line_cb, checker,
thread->u.fd, smtp_host->connection_to);
return 0;
}
DBG("SMTP_CHECK %s > %s"
, FMT_SMTP_RS(smtp_host)
, smtp_checker->buff);
/*
* If the connection was closed or there was
* some sort of error, notify smtp_final()
*/
if (w <= 0) {
smtp_final(thread, 1, "Write failure to server %s"
, FMT_SMTP_RS(smtp_host));
return 0;
}
/* Execute the callback */
(smtp_checker->buff_cb)(thread);
return 0;
}
/*
* This is the same as smtp_get_line() except that we're sending a
* line of data instead of receiving one.
*/
static void
smtp_put_line(thread_t *thread, int (*callback) (thread_t *))
{
checker_t *checker = THREAD_ARG(thread);
smtp_checker_t *smtp_checker = CHECKER_ARG(checker);
conn_opts_t *smtp_host = smtp_checker->host_ptr;
smtp_checker->buff[SMTP_BUFF_MAX - 1] = '\0';
smtp_checker->buff_ctr = strlen(smtp_checker->buff);
/* set the callback */
smtp_checker->buff_cb = callback;
/* schedule the I/O with our helper function */
thread_add_write(thread->master, smtp_put_line_cb, checker,
thread->u.fd, smtp_host->connection_to);
thread_del_read(thread);
return;
}
/*
* Ok, our goal here is to snag the status code out of the
* buffer and return it as an integer. If it's not legible,
* return -1.
*/
static int
smtp_get_status(thread_t *thread)
{
checker_t *checker = THREAD_ARG(thread);
smtp_checker_t *smtp_checker = CHECKER_ARG(checker);
char *buff = smtp_checker->buff;
int status;
char *endptr;
status = strtoul(buff, &endptr, 10);
if (endptr - buff != 3 ||
(*endptr && *endptr != ' '))
return -1;
return status;
}
/*
* We have a connected socket and are ready to begin
* the conversation. This function schedules itself to
* be called via callbacks and tracking state in
* smtp_checker->state. Upon first calling, smtp_checker->state
* should be set to SMTP_START.
*/
static int
smtp_engine_thread(thread_t *thread)
{
checker_t *checker = THREAD_ARG(thread);
smtp_checker_t *smtp_checker = CHECKER_ARG(checker);
conn_opts_t *smtp_host = smtp_checker->host_ptr;
switch (smtp_checker->state) {
/* First step, schedule to receive the greeting banner */
case SMTP_START:
/*
* Ok, if smtp_get_line schedules us back, we will
* have data to analyze. Otherwise, smtp_get_line
* will defer directly to smtp_final.
*/
smtp_checker->state = SMTP_HAVE_BANNER;
smtp_get_line(thread, smtp_engine_thread);
return 0;
break;
/* Second step, analyze banner, send HELO */
case SMTP_HAVE_BANNER:
/* Check for "220 some.mailserver.com" in the greeting */
if (smtp_get_status(thread) != 220) {
smtp_final(thread, 1, "Bad greeting banner from server %s"
, FMT_SMTP_RS(smtp_host));
} else {
/*
* Schedule to send the HELO, smtp_put_line will
* defer directly to smtp_final on error.
*/
smtp_checker->state = SMTP_SENT_HELO;
snprintf(smtp_checker->buff, SMTP_BUFF_MAX, "HELO %s\r\n",
smtp_checker->helo_name);
smtp_put_line(thread, smtp_engine_thread);
}
break;
/* Third step, schedule to read the HELO response */
case SMTP_SENT_HELO:
smtp_checker->state = SMTP_RECV_HELO;
smtp_get_line(thread, smtp_engine_thread);
break;
/* Fourth step, analyze HELO return, send QUIT */
case SMTP_RECV_HELO:
/* Check for "250 Please to meet you..." */
if (smtp_get_status(thread) != 250) {
smtp_final(thread, 1, "Bad HELO response from server %s"
, FMT_SMTP_RS(smtp_host));
} else {
smtp_checker->state = SMTP_SENT_QUIT;
snprintf(smtp_checker->buff, SMTP_BUFF_MAX, "QUIT\r\n");
smtp_put_line(thread, smtp_engine_thread);
}
break;
/* Fifth step, schedule to receive QUIT confirmation */
case SMTP_SENT_QUIT:
smtp_checker->state = SMTP_RECV_QUIT;
smtp_get_line(thread, smtp_engine_thread);
break;
/* Sixth step, wrap up success to smtp_final */
case SMTP_RECV_QUIT:
smtp_final(thread, 0, NULL);
break;
default:
/* We shouldn't be here */
smtp_final(thread, 1, "Unknown smtp engine state encountered");
break;
}
return 0;
}
/*
* Second step in the process. Here we'll see if the connection
* to the host we're checking was successful or not.
*/
static int
smtp_check_thread(thread_t *thread)
{
checker_t *checker = THREAD_ARG(thread);
smtp_checker_t *smtp_checker = CHECKER_ARG(checker);
conn_opts_t *smtp_host = smtp_checker->host_ptr;
int status;
status = tcp_socket_state(thread, smtp_check_thread);
switch (status) {
case connect_error:
smtp_final(thread, 1, "Error connecting to server %s"
, FMT_SMTP_RS(smtp_host));
break;
case connect_timeout:
smtp_final(thread, 1, "Connection timeout to server %s"
, FMT_SMTP_RS(smtp_host));
break;
case connect_success:
DBG("SMTP_CHECK Remote SMTP server %s connected"
, FMT_SMTP_RS(smtp_host));
/* Enter the engine at SMTP_START */
smtp_checker->state = SMTP_START;
smtp_engine_thread(thread);
break;
default:
/* we shouldn't be here */
smtp_final(thread, 1, "Unknown connection error to server %s"
, FMT_SMTP_RS(smtp_host));
break;
}
return 0;
}
/*
* This is the main thread, where all the action starts.
* When the check daemon comes up, it goes down the checkers_queue
* and launches a thread for each checker that got registered.
* This is the callback/event function for that initial thread.
*
* It should be noted that we ARE responsible for sceduling
* ourselves to run again. It doesn't have to be right here,
* but eventually has to happen.
*/
static int
smtp_connect_thread(thread_t *thread)
{
checker_t *checker = THREAD_ARG(thread);
smtp_checker_t *smtp_checker = CHECKER_ARG(checker);
conn_opts_t *smtp_host;
enum connect_result status;
int sd;
bool checker_was_up;
bool rs_was_alive;
/* Let's review our data structures.
*
* Thread is the structure used by the sceduler
* for sceduling many types of events. thread->arg in this
* case points to a checker structure. The checker
* structure holds data about the vs and rs configurations
* as well as the delay loop, etc. Each real server
* defined in the keepalived.conf will more than likely have
* a checker structure assigned to it. Each checker structure
* has a data element that is meant to hold per checker
* configurations. So thread->arg(checker)->data points to
* a smtp_checker structure. In the smtp_checker structure
* we hold global configuration data for the smtp check.
* Smtp_checker has a list of per host (smtp_host) configuration
* data in smtp_checker->host.
*
* So this whole thing looks like this:
* thread->arg(checker)->data(smtp_checker)->host(smtp_host)
*
* To make life simple, we'll break the structures out so
* that "checker" always points to the current checker structure,
* "smtp_checker" points to the current smtp_checker structure,
* and "smtp_host" points to the current smtp_host structure.
*/
/*
* If we're disabled, we'll do nothing at all.
* But we still have to register ourselves again so
* we don't fall of the face of the earth.
*/
if (!checker->enabled) {
thread_add_timer(thread->master, smtp_connect_thread, checker,
checker->delay_loop);
return 0;
}
/*
* Set the internal host pointer to the host that we'll be
* working on. If it's NULL, we've successfully tested all hosts.
* We'll bring the service up (if it's not already), reset the host list,
* and insert the delay loop. When we get scheduled again the host list
* will be reset and we will continue on checking them one by one.
*/
if ((smtp_checker->host_ptr = list_element(smtp_checker->host, smtp_checker->host_ctr)) == NULL) {
if (!checker->is_up || !checker->has_run) {
log_message(LOG_INFO, "Remote SMTP server %s succeed on service."
, FMT_CHK(checker));
checker_was_up = checker->is_up;
rs_was_alive = checker->rs->alive;
update_svr_checker_state(UP, checker);
if (checker->rs->smtp_alert && !checker_was_up &&
(rs_was_alive != checker->rs->alive || !global_data->no_checker_emails))
smtp_alert(SMTP_MSG_RS, checker, NULL,
"=> CHECK succeed on service <=");
}
checker->retry_it = 0;
smtp_checker->host_ctr = 0;
smtp_checker->host_ptr = list_element(smtp_checker->host, 0);
thread_add_timer(thread->master, smtp_connect_thread, checker, checker->delay_loop);
return 0;
}
smtp_host = smtp_checker->host_ptr;
/* Create the socket, failing here should be an oddity */
if ((sd = socket(smtp_host->dst.ss_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_TCP)) == -1) {
log_message(LOG_INFO, "SMTP_CHECK connection failed to create socket. Rescheduling.");
thread_add_timer(thread->master, smtp_connect_thread, checker,
checker->delay_loop);
return 0;
}
#if !HAVE_DECL_SOCK_NONBLOCK
if (set_sock_flags(sd, F_SETFL, O_NONBLOCK))
log_message(LOG_INFO, "Unable to set NONBLOCK on smtp socket - %s (%d)", strerror(errno), errno);
#endif
#if !HAVE_DECL_SOCK_CLOEXEC
if (set_sock_flags(sd, F_SETFD, FD_CLOEXEC))
log_message(LOG_INFO, "Unable to set CLOEXEC on smtp socket - %s (%d)", strerror(errno), errno);
#endif
status = tcp_bind_connect(sd, smtp_host);
/* handle tcp connection status & register callback the next setp in the process */
if(tcp_connection_state(sd, status, thread, smtp_check_thread, smtp_host->connection_to)) {
close(sd);
log_message(LOG_INFO, "SMTP_CHECK socket bind failed. Rescheduling.");
thread_add_timer(thread->master, smtp_connect_thread, checker,
checker->delay_loop);
}
return 0;
}
#ifdef THREAD_DUMP
void
register_check_smtp_addresses(void)
{
register_thread_address("smtp_check_thread", smtp_check_thread);
register_thread_address("smtp_connect_thread", smtp_connect_thread);
register_thread_address("smtp_get_line_cb", smtp_get_line_cb);
register_thread_address("smtp_put_line_cb", smtp_put_line_cb);
}
#endif