/*
 * Pound - the reverse-proxy load-balancer
 * Copyright (C) 2002-2020 Apsis GmbH
 *
 * This file is part of Pound.
 *
 * Pound 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 3 of the License, or
 * (at your option) any later version.
 *
 * Pound 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/> .
 *
 * Contact information:
 * Apsis GmbH
 * P.O.Box
 * 8707 Uetikon am See
 * Switzerland
 * EMail: roseg@apsis.ch
 */

#include    "pound.h"

#ifdef HAVE_STDARG_H
void
logmsg(int level, const char *fmt, ...)
{
    char    buf[MAXBUF];
    va_list ap;

    if(level > global.log_level)
        return;
    buf[MAXBUF - 1] = '\0';
    va_start(ap, fmt);
    vsnprintf(buf, MAXBUF - 1, fmt, ap);
    va_end(ap);
    if(global.log_level > 0)
        fprintf(stderr, "%s\n", buf);
    else
        syslog(global.log_facility | LOG_INFO, "%s", buf);
    return;
}
#else
void
logmsg(int level, const char *fmt, va_alist)
va_dcl
{
    char    buf[MAXBUF];
    va_list ap;
    struct tm   *t_now, t_res;

    if(level > global.log_level)
        return;
    buf[MAXBUF - 1] = '\0';
    va_start(ap);
    vsnprintf(buf, MAXBUF - 1, fmt, ap);
    va_end(ap);
    if(global.log_level > 0)
        fprintf(stderr, "%s\n", buf);
    else
        syslog(global.log_facility | LOG_INFO, "%s", buf);
    return;
}
#endif

void *
thr_resurrect(void *arg)
{
    BACKEND **dead_be, *t_be;
    int     n_be, i, j, flags;
    struct pollfd    *dead_poll;
    char    be_addr[NI_MAXHOST];

    logmsg(1, "Starting resurrector thread %s:%d", __FILE__, __LINE__);
    for(;;) {
        sleep(RESURRECT_CYCLE - RESURRECT_TO);
        logmsg(2, "Resurrect: wake-up %s:%d", __FILE__, __LINE__);
        for(n_be = 0, i = 0; i < backends_len; i++)
            if(backends[i].is_dead)
                n_be++;
        if(n_be == 0)
            continue;
        if((dead_be = calloc(n_be, sizeof(BACKEND *))) == NULL) {
            logmsg(0, "resurrect: out of memory");
            continue;
        }
        if((dead_poll = calloc(n_be, sizeof(struct pollfd))) == NULL) {
            free(dead_be);
            logmsg(0, "resurrect: out of memory");
            continue;
        }
        for(i = 0, j = 0; i < backends_len && j < n_be; i++)
            if(backends[i].is_dead) {
                dead_poll[j].fd = socket(backends[i].addr->ai_family, SOCK_STREAM, 0);
                flags = fcntl(dead_poll[j].fd, F_GETFL, &flags);
                fcntl(dead_poll[j].fd, F_SETFL, flags | O_NONBLOCK);
                if(!connect(dead_poll[j].fd, backends[i].addr->ai_addr, backends[i].addr->ai_addrlen)) {
                    logmsg(2, "Resurrect: backend %d immediate connect %s:%d", i, __FILE__, __LINE__);
                    backends[i].is_dead = 0;
                    close(dead_poll[j].fd);
                    continue;
                }
                if(errno != EINPROGRESS) {
                    backends[i].is_dead = 1;
                    close(dead_poll[j].fd);
                    continue;
                }
                dead_poll[j].events = POLLOUT;
                dead_be[j++] = &backends[i];
            }
        n_be = j;
        sleep(RESURRECT_TO);
        logmsg(2, "Resurrect: check backends %s:%d", i, __FILE__, __LINE__);
        poll(dead_poll, n_be, 1);
        for(i = 0; i < n_be; i++) {
            if(dead_poll[i].revents & POLLOUT) {
                dead_be[i]->is_dead = 0;
                getnameinfo(dead_be[i]->addr->ai_addr, dead_be[i]->addr->ai_addrlen, be_addr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
                logmsg(0, "Backend %s resurrected", be_addr);
            }
            close(dead_poll[i].fd);
        }
        free(dead_be);
        free(dead_poll);
    }
}

void
time_stamp(char *buf)
{
    time_t      t_now;
    struct tm   *s_now;

    t_now = time(NULL);
    s_now = localtime(&t_now);
    if(s_now == NULL) {
        *buf = '\0';
        return;
    }
    strftime(buf, MAXBUF, "%c", s_now);
    return;
}

int
do_sni(void *data, mbedtls_ssl_context *ctx, const unsigned char *host_name, size_t host_len)
{
    char    *name;
    int     i, j;
    SNI     **sni;

    logmsg(1, "%lX start sni %s:%d", pthread_self(), __FILE__, __LINE__);
    sni = (SNI **)data;
    if((name = malloc(host_len + 1)) == NULL) {
        logmsg(0, "SNI: out of memory!");
        return 0;
    }
    memset(name, '\0', host_len + 1);
    strncpy(name, host_name, host_len);
    logmsg(4, "%lX sni for %s %s:%d", pthread_self(), name, __FILE__, __LINE__);
    for(i = 0; sni[i] != NULL; i++)
    for(j = 0; j < sni[i]->host_len; j++)
        if(!regexec(&sni[i]->host[j], name, 0, NULL, REG_ICASE)) {
            logmsg(2, "%lX: found match at %d %s:%d", pthread_self(), j, __FILE__, __LINE__);
            return mbedtls_ssl_set_hs_own_cert(ctx, &sni[i]->certificate, &sni[i]->key);
        }
    /* if no match found: use the default certificate (from conf, aka the first sni) and let the client figure it out */
    logmsg(2, "%lX: no match found %s:%d", pthread_self(), __FILE__, __LINE__);
    return 0;
}
