aboutsummaryrefslogtreecommitdiff
path: root/src/events/tlsdate_status.c
blob: caa71fdde1193c09b5f57034a2b55700e5e0d30c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
 * 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;
  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 (ret:%zd).",
             __func__, 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);
  debug ("[event:%s] fired", __func__);
  if (ret < 0)
    {
      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];
  /* One pair of pipes are reused along with the event. */
  if (new_tlsdate_monitor_pipe (fds))
    {
      return -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;
}