summaryrefslogtreecommitdiffstats
path: root/src/lib/connection.h
blob: 612c5405586c0e50f334fc0e3d6746caa30eab28 (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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
#ifndef CONNECTION_H
#define CONNECTION_H

#include "net.h"

struct ioloop;
struct connection;

enum connection_behavior {
	CONNECTION_BEHAVIOR_DESTROY = 0,
	CONNECTION_BEHAVIOR_ALLOW
};

enum connection_disconnect_reason {
	/* not disconnected yet */
	CONNECTION_DISCONNECT_NOT = 0,
	/* normal requested disconnection */
	CONNECTION_DISCONNECT_DEINIT,
	/* input buffer full */
	CONNECTION_DISCONNECT_BUFFER_FULL,
	/* connection got disconnected */
	CONNECTION_DISCONNECT_CONN_CLOSED,
	/* connect() timed out */
	CONNECTION_DISCONNECT_CONNECT_TIMEOUT,
	/* remote didn't send input */
	CONNECTION_DISCONNECT_IDLE_TIMEOUT,
	/* handshake failed */
	CONNECTION_DISCONNECT_HANDSHAKE_FAILED,
};

struct connection_vfuncs {
	void (*init)(struct connection *conn);
	void (*destroy)(struct connection *conn);
	/* For UNIX socket clients this gets called immediately (unless
	   delayed_unix_client_connected_callback=TRUE) with success=TRUE,
	   for IP connections it gets called later:

	   If connect() fails, sets success=FALSE and errno. Streams aren't
	   initialized in that situation either. destroy() is called after
	   the callback. */
	void (*client_connected)(struct connection *conn, bool success);

	/* implement one of the input*() methods.
	   They return 1 = ok, continue. 0 = ok, but stop processing more
	   lines, -1 = error, disconnect the client. */
	void (*input)(struct connection *conn);
	int (*input_line)(struct connection *conn, const char *line);
	int (*input_args)(struct connection *conn, const char *const *args);

	/* handshake functions. Defaults to version checking.
	   must return 1 when handshake is completed, otherwise return 0.
	   return -1 to indicate error and disconnect client.

	   if you implement this, remember to call connection_verify_version
	   yourself, otherwise you end up with assert crash.

	   these will not be called if you implement `input` virtual function.
	*/
	int (*handshake)(struct connection *conn);
	int (*handshake_line)(struct connection *conn, const char *line);
	int (*handshake_args)(struct connection *conn, const char *const *args);

	/* Called when the connection handshake is ready. */
	void (*handshake_ready)(struct connection *conn);

	/* Called when input_idle_timeout_secs is reached, defaults to disconnect */
	void (*idle_timeout)(struct connection *conn);
	/* Called when client_connect_timeout_msecs is reached, defaults to disconnect */
	void (*connect_timeout)(struct connection *conn);
};

struct connection_settings {
	const char *service_name_in;
	const char *service_name_out;
	unsigned int major_version, minor_version;

	unsigned int client_connect_timeout_msecs;
	unsigned int input_idle_timeout_secs;

	/* These need to be non-zero for corresponding stream to
	   be created. */
	size_t input_max_size;
	size_t output_max_size;
	enum connection_behavior input_full_behavior;

	/* Set to TRUE if this is a client */
	bool client;

	/* Set to TRUE if version should not be sent */
	bool dont_send_version;
	/* By default when only input_args() is used, or when
	   connection_input_line_default() is used, empty lines aren't allowed
	   since it would result in additional args[0] == NULL check. Setting
	   this to TRUE passes it through instead of logging an error. */
	bool allow_empty_args_input;
	/* Don't call client_connected() immediately on
	   connection_client_connect() with UNIX sockets. This is mainly
	   to make the functionality identical with inet sockets, which may
	   simplify the calling code. */
	bool delayed_unix_client_connected_callback;
	/* Put the connection id in the log prefix */
	bool log_connection_id;
	/* If connect() to UNIX socket fails with EAGAIN, retry for this many
	   milliseconds before giving up (0 = try once) */
	unsigned int unix_client_connect_msecs;
	/* Turn on debug logging */
	bool debug;
};

struct connection {
	struct connection *prev, *next;
	struct connection_list *list;

	/* The name for the connection provided by the application. This is
	   usually a host name or a unix socket path. This may be NULL if the
	   application provides none. */
	char *base_name;
	/* The name of the connection determined by the connection API. It is
	   equal to base_name when that is available and otherwise it is
	   composed from the connection properties; e.g., "ip:port". */
	const char *name;

	char *label;
	char *property_label;
	unsigned int id;

	int fd_in, fd_out;
	struct ioloop *ioloop;
	struct io *io;
	struct istream *input;
	struct ostream *output;

	unsigned int input_idle_timeout_secs;
	struct timeout *to;
	time_t last_input;
	struct timeval last_input_tv;
	struct timeval connect_started;
	struct timeval connect_finished;

	/* set to parent event before calling init */
	struct event *event_parent;
	struct event *event;

	/* connection properties */
	struct ip_addr local_ip, remote_ip;
	in_port_t remote_port;
	pid_t remote_pid;
	uid_t remote_uid;

	/* received minor version */
	unsigned int minor_version;

	/* handlers */
	struct connection_vfuncs v;

	enum connection_disconnect_reason disconnect_reason;

	bool version_received:1;
	bool handshake_received:1;
	bool unix_socket:1;
	bool unix_peer_checked:1;
	bool disconnected:1;
};

struct connection_list {
	struct connection *connections;
	unsigned int connections_count;

	unsigned int id_counter;

	struct connection_settings set;
	struct connection_vfuncs v;
};

void connection_init(struct connection_list *list, struct connection *conn,
		     const char *name) ATTR_NULL(3);
void connection_init_server(struct connection_list *list,
			    struct connection *conn, const char *name,
			    int fd_in, int fd_out) ATTR_NULL(3);
void connection_init_server_ip(struct connection_list *list,
			       struct connection *conn, const char *name,
			       int fd_in, int fd_out,
			       const struct ip_addr *remote_ip,
			       in_port_t remote_port) ATTR_NULL(3, 6);
void connection_init_client_ip(struct connection_list *list,
			       struct connection *conn, const char *hostname,
			       const struct ip_addr *ip, in_port_t port)
			       ATTR_NULL(3);
void connection_init_client_ip_from(struct connection_list *list,
				    struct connection *conn,
				    const char *hostname,
				    const struct ip_addr *ip, in_port_t port,
				    const struct ip_addr *my_ip) ATTR_NULL(3,6);
void connection_init_client_unix(struct connection_list *list,
				 struct connection *conn, const char *path);
void connection_init_client_fd(struct connection_list *list,
			       struct connection *conn, const char *name,
			       int fd_int, int fd_out) ATTR_NULL(3);
void connection_init_from_streams(struct connection_list *list,
				  struct connection *conn, const char *name,
				  struct istream *input,
				  struct ostream *output) ATTR_NULL(3);

int connection_client_connect(struct connection *conn);

/* Disconnects a connection */
void connection_disconnect(struct connection *conn);

/* Deinitializes a connection, calls disconnect */
void connection_deinit(struct connection *conn);

void connection_input_halt(struct connection *conn);
/* Resume connection handling. If a new IO was added, it's marked as having
   pending input. */
void connection_input_resume(struct connection *conn);

/* Update event fields and log prefix based on connection properties. */
void connection_update_event(struct connection *conn);

/* Update connection properties and labels */
void connection_update_properties(struct connection *conn);

/* This needs to be called if the input/output streams are changed */
void connection_streams_changed(struct connection *conn);

/* Returns -1 = disconnected, 0 = nothing new, 1 = something new.
   If input_full_behavior is ALLOW, may return also -2 = buffer full. */
int connection_input_read(struct connection *conn);
/* Verify that VERSION input matches what we expect. */
int connection_verify_version(struct connection *conn,
			      const char *service_name,
			      unsigned int major_version,
			      unsigned int minor_version);

int connection_handshake_args_default(struct connection *conn,
				      const char *const *args);

/* Returns human-readable reason for why connection was disconnected. */
const char *connection_disconnect_reason(struct connection *conn);
/* Returns human-readable reason for why connection timed out,
   e.g. "No input for 10.023 secs". */
const char *connection_input_timeout_reason(struct connection *conn);

void connection_switch_ioloop_to(struct connection *conn,
				 struct ioloop *ioloop);
void connection_switch_ioloop(struct connection *conn);

struct connection_list *
connection_list_init(const struct connection_settings *set,
		     const struct connection_vfuncs *vfuncs);
void connection_list_deinit(struct connection_list **list);

void connection_input_default(struct connection *conn);
int connection_input_line_default(struct connection *conn, const char *line);

/* Change handlers, calls connection_input_halt and connection_input_resume */
void connection_set_handlers(struct connection *conn, const struct connection_vfuncs *vfuncs);
void connection_set_default_handlers(struct connection *conn);

#endif