summaryrefslogtreecommitdiff
path: root/src/rexmpp_tcp.h
blob: 5a296cc6553b73773afa974988dd9bd7f2dc86b8 (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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/**
   @file rexmpp_tcp.h
   @brief TCP connection establishment.
   @author defanor <defanor@uberspace.net>
   @date 2020
   @copyright MIT license.

   This module tries to establish a TCP connection to a given host
   and port.

   A connection establishment procedure begins with
   ::rexmpp_tcp_conn_init, followed by repeated calls to
   ::rexmpp_tcp_conn_proceed while the return code is
   ::REXMPP_CONN_IN_PROGRESS, at the times suggested by
   ::rexmpp_tcp_conn_timeout and on events suggested by
   ::rexmpp_tcp_conn_fds, and ends with ::rexmpp_tcp_conn_finish.
*/

#ifndef REXMPP_TCP_H
#define REXMPP_TCP_H

#include <sys/time.h>

#include "rexmpp.h"
#include "rexmpp_dns.h"

#define REXMPP_TCP_MAX_CONNECTION_ATTEMPTS 20
#define REXMPP_TCP_IPV6_DELAY_MS 50
#define REXMPP_TCP_CONN_DELAY_MS 250

/**
   @brief Resolution status.
 */
enum rexmpp_tcp_conn_resolution_status {
  /** The resolution is not active. */
  REXMPP_CONN_RESOLUTION_INACTIVE,
  /** Waiting for resolution. */
  REXMPP_CONN_RESOLUTION_WAITING,
  /** Resolved successfully. */
  REXMPP_CONN_RESOLUTION_SUCCESS,
  /** Failed to resolve. */
  REXMPP_CONN_RESOLUTION_FAILURE
};

typedef enum rexmpp_tcp_conn_resolution_status
rexmpp_tcp_conn_resolution_status_t;

/**
   @brief Connection errors.
*/
enum rexmpp_tcp_conn_error {
  /** Connected, no error. */
  REXMPP_CONN_DONE,
  /** Resolver error occurred. The exact error code can be read from
      the connection structure. */
  REXMPP_CONN_RESOLVER_ERROR,
  /** Connection in progress, no error yet. */
  REXMPP_CONN_IN_PROGRESS,
  /** All the connection attempts failed. */
  REXMPP_CONN_FAILURE,
  /** An unexpected error during connection. */
  REXMPP_CONN_ERROR
};

typedef enum rexmpp_tcp_conn_error rexmpp_tcp_conn_error_t;

typedef struct rexmpp_tcp_connection rexmpp_tcp_conn_t;

/** @brief A connection establishment structure. */
struct rexmpp_tcp_connection {
  /** @brief A host we are connecting to. */
  const char *host;
  /** @brief A port we are connecting to. */
  uint16_t port;

  /** @brief State of A record resolution. */
  enum rexmpp_tcp_conn_resolution_status resolution_v4;
  /** @brief Status of A record resolution, as returned by the
      resolver. */
  int resolver_status_v4;
  /** @brief Resolved A records. */
  rexmpp_dns_result_t *resolved_v4;
  /** @brief The AF_INET address number we are currently at. */
  int addr_cur_v4;

  /** @brief State of AAAA record resolution. */
  enum rexmpp_tcp_conn_resolution_status resolution_v6;
  /** @brief Status of AAAA record resolution, as returned by the
      resolver. */
  int resolver_status_v6;
  /** @brief Resolved AAAA records. */
  rexmpp_dns_result_t *resolved_v6;
  /** @brief The AF_INET6 address number we are currently at. */
  int addr_cur_v6;

  /** @brief Socket array, one for each connection attempt. */
  int sockets[REXMPP_TCP_MAX_CONNECTION_ATTEMPTS];
  /** @brief The number of connection attempts so far. */
  int connection_attempts;

  /** @brief Next scheduled connection time. */
  struct timeval next_connection_time;
  /** @brief File descriptor of a connected socket. */
  int fd;
  /** @brief Whether the A or AAAA records used to establish the final
      connection were verified with DNSSEC. */
  int dns_secure;
};

/**
    @brief Initiates a connection.
    @param[in] s ::rexmpp
    @param[out] conn An allocated connection structure.
    @param[in] host A host to connect to. This could be a domain name,
    or a textual representation of an IPv4 or an IPv6 address.
    @param[in] port A port to connect to.
    @returns A ::rexmpp_tcp_conn_error state.
*/
rexmpp_tcp_conn_error_t
rexmpp_tcp_conn_init (rexmpp_t *s,
                      rexmpp_tcp_conn_t *conn,
                      const char *host,
                      uint16_t port);

/**
    @brief Continues a connection process.
    @param[in] s ::rexmpp
    @param[in,out] conn An active connection structure.
    @param[in] read_fds File descriptors available for reading from.
    @param[in] write_fds File descriptors available for writing to.
    @returns A ::rexmpp_tcp_conn_error state.
*/
rexmpp_tcp_conn_error_t
rexmpp_tcp_conn_proceed (rexmpp_t *s,
                         rexmpp_tcp_conn_t *conn,
                         fd_set *read_fds,
                         fd_set *write_fds);

/**
   @brief Finalises a connection process.

   Closes pending connections except for the established one, frees
   additionally allocated resources.

   Normally must be called on any state other than
   ::REXMPP_CONN_IN_PROGRESS. The connection structure can be freed
   after this.

   @param[in,out] conn An active connection structure.
   @returns A connected socket's file descriptor, or -1.
 */
int rexmpp_tcp_conn_finish (rexmpp_tcp_conn_t *conn);

/**
   @brief Reports file descriptors a connection process is interested in.

   File descriptors are only added to an @c fd_set, so the ones it
   already contains will not be lost.

   @param[in] s ::rexmpp
   @param[in] conn An active connection structure.
   @param[out] read_fds File descriptors a connection process is
   interested in reading from.
   @param[out] write_fds File descriptors a connection process is
   interested in writing to.
   @returns Maximum file descriptor number, plus 1.
 */
int rexmpp_tcp_conn_fds (rexmpp_t *s,
                         rexmpp_tcp_conn_t *conn,
                         fd_set *read_fds,
                         fd_set *write_fds);

/**
   @brief Reports timeouts.
   @param[in] s ::rexmpp
   @param[in] conn An active connection structure.
   @param[in] max_tv An existing maximum timeout.
   @param[out] tv A timeval structure to store a new timeout in.
   @returns A pointer to either max_tv or tv, depending on which one
   is smaller.
*/
struct timeval *rexmpp_tcp_conn_timeout (rexmpp_t *s,
                                         rexmpp_tcp_conn_t *conn,
                                         struct timeval *max_tv,
                                         struct timeval *tv);

#endif