/*
* tlsdate_status.c - handles tlsdate-monitor responses
* Copyright (c) 2013 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <event2/event.h>
#include "src/conf.h"
#include "src/util.h"
#include "src/tlsdate.h"
/* Returns < 0 on error, > 0 on eagain, and 0 on success */
int
read_tlsdate_response (int fd, time_t *t)
{
/* TLS passes time as a 32-bit value. */
uint32_t server_time = 0;
ssize_t ret = IGNORE_EINTR (read (fd, &server_time, sizeof (server_time)));
if (ret == -1 && errno == EAGAIN)
{
/* Full response isn't ready yet. */
return 1;
}
if (ret != sizeof (server_time))
{
/* End of pipe (0) or truncated: death probable. */
error ("[event:(%s)] invalid time read from tlsdate (rd:%d,ret:%zd).",
__func__, server_time, ret);
return -1;
}
/* uint32_t moves to signed long so there is room for silliness. */
*t = server_time;
return 0;
}
void
action_tlsdate_timeout (evutil_socket_t fd, short what, void *arg)
{
struct state *state = arg;
info ("[event:%s] tlsdate timed out", __func__);
/* Force kill it and let action_sigchld rerun. */
if (state->tlsdate_pid)
kill (state->tlsdate_pid, SIGKILL);
}
void
action_tlsdate_status (evutil_socket_t fd, short what, void *arg)
{
struct state *state = arg;
time_t t = 0;
int ret = read_tlsdate_response (fd, &t);
verb_debug ("[event:%s] fired", __func__);
if (ret < 0)
{
verb_debug ("[event:%s] forcibly timing out tlsdate", __func__);
trigger_event (state, E_TLSDATE_TIMEOUT, 0);
return;
}
if (ret)
{
/* EAGAIN'd: wait for the rest. */
trigger_event (state, E_TLSDATE_STATUS, -1);
return;
}
if (is_sane_time (t))
{
/* Note that last_time is from an online source */
state->last_sync_type = SYNC_TYPE_NET;
state->last_time = t;
trigger_event (state, E_SAVE, -1);
}
else
{
error ("[event:%s] invalid time received from tlsdate: %ld",
__func__, t);
}
/* Restore the backoff and tries count on success, insane or not.
* On failure, the event handler does it.
*/
state->tries = 0;
state->backoff = state->opts.wait_between_tries;
return;
}
/* Returns 0 on success and populates |fds| */
int
new_tlsdate_monitor_pipe (int fds[2])
{
if (pipe (fds) < 0)
{
perror ("pipe failed");
return -1;
}
/* TODO(wad): CLOEXEC, Don't leak these into tlsdate proper. */
return 0;
}
/* Create a fd pair that the tlsdate runner will communicate over */
int
setup_tlsdate_status (struct state *state)
{
int fds[2] = { -1, -1 };
/* One pair of pipes are reused along with the event. */
if (new_tlsdate_monitor_pipe (fds))
{
return -1;
}
verb_debug ("[%s] monitor fd pair (%d, %d)", __func__, fds[0], fds[1]);
/* The fd that the monitor process will write to */
state->tlsdate_monitor_fd = fds[1];
/* Make the reader fd non-blocking and not leak into tlsdate. */
if (fcntl (fds[0], F_SETFL, O_NONBLOCK|O_CLOEXEC) < 0)
{
perror ("pipe[0] fcntl(O_NONBLOCK) failed");
return 1;
}
state->events[E_TLSDATE_STATUS] = event_new (state->base, fds[0],
EV_READ,
action_tlsdate_status, state);
if (!state->events[E_TLSDATE_STATUS])
{
error ("Failed to allocate tlsdate status event");
return 1;
}
event_priority_set (state->events[E_TLSDATE_STATUS], PRI_NET);
state->events[E_TLSDATE_TIMEOUT] = event_new (state->base, -1,
EV_TIMEOUT,
action_tlsdate_timeout, state);
if (!state->events[E_TLSDATE_TIMEOUT])
{
error ("Failed to allocate tlsdate timeout event");
return 1;
}
event_priority_set (state->events[E_TLSDATE_TIMEOUT], PRI_SAVE);
return 0;
}