/src/dovecot/src/lib/connection.c
Line | Count | Source |
1 | | /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "str.h" |
5 | | #include "ioloop.h" |
6 | | #include "istream.h" |
7 | | #include "istream-unix.h" |
8 | | #include "ostream.h" |
9 | | #include "ostream-unix.h" |
10 | | #include "iostream.h" |
11 | | #include "net.h" |
12 | | #include "strescape.h" |
13 | | #include "llist.h" |
14 | | #include "time-util.h" |
15 | | #include "connection.h" |
16 | | |
17 | | #include <unistd.h> |
18 | | |
19 | | void connection_set_handshake_ready(struct connection *conn) |
20 | 0 | { |
21 | 0 | i_assert(conn->handshake_finished.tv_sec == 0); |
22 | 0 | conn->handshake_finished = ioloop_timeval; |
23 | 0 | if (conn->v.handshake_ready != NULL) |
24 | 0 | conn->v.handshake_ready(conn); |
25 | 0 | } |
26 | | |
27 | | bool connection_handshake_received(struct connection *conn) |
28 | 0 | { |
29 | 0 | return conn->handshake_finished.tv_sec != 0; |
30 | 0 | } |
31 | | |
32 | | static void connection_closed(struct connection *conn, |
33 | | enum connection_disconnect_reason reason) |
34 | 3.60k | { |
35 | 3.60k | conn->disconnect_reason = reason; |
36 | 3.60k | conn->v.destroy(conn); |
37 | 3.60k | } |
38 | | |
39 | | static void connection_idle_timeout(struct connection *conn) |
40 | 0 | { |
41 | 0 | connection_closed(conn, CONNECTION_DISCONNECT_IDLE_TIMEOUT); |
42 | 0 | } |
43 | | |
44 | | static void connection_connect_timeout(struct connection *conn) |
45 | 0 | { |
46 | 0 | connection_closed(conn, CONNECTION_DISCONNECT_CONNECT_TIMEOUT); |
47 | 0 | } |
48 | | |
49 | | static int connection_flush_callback(struct connection *conn) |
50 | 0 | { |
51 | 0 | int ret; |
52 | 0 | stream_flush_callback_t *callback = conn->flush_callback; |
53 | 0 | if (conn->flush_callback != NULL) { |
54 | 0 | ret = callback(conn->flush_context); |
55 | 0 | } else { |
56 | 0 | ret = o_stream_flush(conn->output); |
57 | 0 | } |
58 | 0 | if (ret < 0) { |
59 | 0 | e_error(conn->event, "write(%s) failed: %s", conn->name, |
60 | 0 | o_stream_get_error(conn->output)); |
61 | 0 | } else if (o_stream_get_buffer_used_size(conn->output) <= |
62 | 0 | conn->list->set.output_throttle_size / 3) { |
63 | | /* resume connection reading */ |
64 | 0 | e_debug(conn->event, "Output buffer has flushed enough - " |
65 | 0 | "resuming input"); |
66 | 0 | connection_input_resume(conn); |
67 | 0 | o_stream_unset_flush_callback(conn->output); |
68 | 0 | if (callback != NULL) |
69 | 0 | o_stream_set_flush_callback(conn->output, *callback, |
70 | 0 | conn->flush_context); |
71 | 0 | conn->flush_context = NULL; |
72 | 0 | conn->flush_callback = NULL; |
73 | 0 | } |
74 | 0 | return ret; |
75 | 0 | } |
76 | | |
77 | | static inline bool connection_output_throttle(struct connection *conn) |
78 | 0 | { |
79 | | /* not enabled */ |
80 | 0 | if (conn->list->set.output_throttle_size != 0 && |
81 | 0 | !conn->output->closed && |
82 | 0 | o_stream_get_buffer_used_size(conn->output) >= |
83 | 0 | conn->list->set.output_throttle_size) { |
84 | 0 | conn->flush_callback = |
85 | 0 | o_stream_get_flush_callback(conn->output, &conn->flush_context); |
86 | 0 | o_stream_set_flush_callback(conn->output, |
87 | 0 | connection_flush_callback, conn); |
88 | 0 | e_debug(conn->event, "Output buffer has reached throttle limit - " |
89 | 0 | "halting input"); |
90 | 0 | connection_input_halt(conn); |
91 | 0 | return TRUE; |
92 | 0 | } |
93 | 0 | return FALSE; |
94 | 0 | } |
95 | | |
96 | | static int connection_input_parse_lines(struct connection *conn) |
97 | 0 | { |
98 | 0 | const char *line; |
99 | 0 | struct istream *input; |
100 | 0 | struct ostream *output; |
101 | 0 | int ret = 0; |
102 | |
|
103 | 0 | input = conn->input; |
104 | 0 | output = conn->output; |
105 | 0 | i_stream_ref(input); |
106 | 0 | if (output != NULL) { |
107 | 0 | o_stream_ref(output); |
108 | 0 | o_stream_cork(output); |
109 | 0 | } |
110 | 0 | while (!input->closed && (line = i_stream_next_line(input)) != NULL) { |
111 | 0 | T_BEGIN { |
112 | 0 | if (!connection_handshake_received(conn) && |
113 | 0 | conn->v.handshake_line != NULL) { |
114 | 0 | ret = conn->v.handshake_line(conn, line); |
115 | 0 | if (ret > 0) |
116 | 0 | connection_set_handshake_ready(conn); |
117 | 0 | else if (ret == 0) |
118 | | /* continue reading */ |
119 | 0 | ret = 1; |
120 | 0 | else |
121 | 0 | conn->disconnect_reason = |
122 | 0 | CONNECTION_DISCONNECT_HANDSHAKE_FAILED; |
123 | 0 | } else { |
124 | 0 | ret = conn->v.input_line(conn, line); |
125 | 0 | } |
126 | 0 | } T_END; |
127 | | /* If throttled, stop reading */ |
128 | 0 | if (ret > 0 && connection_output_throttle(conn)) |
129 | 0 | ret = 0; |
130 | 0 | if (ret <= 0) |
131 | 0 | break; |
132 | 0 | if (conn->input != input) { |
133 | | /* Input handler changed the istream (and maybe |
134 | | ostream?) Restart reading using the new streams. */ |
135 | 0 | break; |
136 | 0 | } |
137 | 0 | } |
138 | 0 | if (output != NULL) { |
139 | 0 | o_stream_uncork(output); |
140 | 0 | o_stream_unref(&output); |
141 | 0 | } |
142 | 0 | if (ret < 0 && !input->closed) { |
143 | 0 | enum connection_disconnect_reason reason = |
144 | 0 | conn->disconnect_reason; |
145 | 0 | if (reason == CONNECTION_DISCONNECT_NOT) |
146 | 0 | reason = CONNECTION_DISCONNECT_DEINIT; |
147 | 0 | connection_closed(conn, reason); |
148 | 0 | } |
149 | 0 | if (input->closed) |
150 | 0 | ret = -1; |
151 | 0 | i_stream_unref(&input); |
152 | 0 | return ret; |
153 | 0 | } |
154 | | |
155 | | void connection_input_default(struct connection *conn) |
156 | 0 | { |
157 | 0 | int ret; |
158 | |
|
159 | 0 | if (!connection_handshake_received(conn) && |
160 | 0 | conn->v.handshake != NULL) { |
161 | 0 | if ((ret = conn->v.handshake(conn)) < 0) { |
162 | 0 | connection_closed( |
163 | 0 | conn, CONNECTION_DISCONNECT_HANDSHAKE_FAILED); |
164 | 0 | return; |
165 | 0 | } else if (ret == 0) { |
166 | 0 | return; |
167 | 0 | } else { |
168 | 0 | connection_set_handshake_ready(conn); |
169 | 0 | } |
170 | 0 | } |
171 | | |
172 | 0 | switch (connection_input_read(conn)) { |
173 | 0 | case -1: |
174 | 0 | return; |
175 | 0 | case 0: /* allow calling this function for buffered input */ |
176 | 0 | case 1: |
177 | 0 | break; |
178 | 0 | default: |
179 | 0 | i_unreached(); |
180 | 0 | } |
181 | | |
182 | 0 | while (connection_input_parse_lines(conn) > 0) ; |
183 | 0 | } |
184 | | |
185 | | int connection_verify_version(struct connection *conn, |
186 | | const char *service_name, |
187 | | unsigned int major_version, |
188 | | unsigned int minor_version) |
189 | 0 | { |
190 | 0 | i_assert(!conn->version_received); |
191 | | |
192 | 0 | if (strcmp(service_name, conn->list->set.service_name_in) != 0) { |
193 | 0 | e_error(conn->event, "Received wrong socket type. " |
194 | 0 | "We want '%s', but received '%s' (wrong socket path?)", |
195 | 0 | conn->list->set.service_name_in, service_name); |
196 | 0 | return -1; |
197 | 0 | } |
198 | | |
199 | 0 | if (major_version != conn->list->set.major_version) { |
200 | 0 | e_error(conn->event, "Socket supports major version %u, " |
201 | 0 | "but we support only %u (mixed old and new binaries?)", |
202 | 0 | major_version, conn->list->set.major_version); |
203 | 0 | return -1; |
204 | 0 | } |
205 | | |
206 | 0 | conn->minor_version = minor_version; |
207 | 0 | conn->version_received = TRUE; |
208 | 0 | return 0; |
209 | 0 | } |
210 | | |
211 | | int connection_handshake_args_default(struct connection *conn, |
212 | | const char *const *args) |
213 | 0 | { |
214 | 0 | unsigned int major_version, minor_version; |
215 | |
|
216 | 0 | if (conn->version_received) |
217 | 0 | return 1; |
218 | | |
219 | | /* VERSION <tab> service_name <tab> major version <tab> minor version */ |
220 | 0 | if (str_array_length(args) != 4 || |
221 | 0 | strcmp(args[0], "VERSION") != 0 || |
222 | 0 | str_to_uint(args[2], &major_version) < 0 || |
223 | 0 | str_to_uint(args[3], &minor_version) < 0) { |
224 | 0 | e_error(conn->event, "didn't reply with a valid VERSION line: %s", |
225 | 0 | t_strarray_join(args, "\t")); |
226 | 0 | return -1; |
227 | 0 | } |
228 | | |
229 | 0 | if (connection_verify_version(conn, args[1], |
230 | 0 | major_version, minor_version) < 0) |
231 | 0 | return -1; |
232 | 0 | return 1; |
233 | 0 | } |
234 | | |
235 | | int connection_input_line_default(struct connection *conn, const char *line) |
236 | 0 | { |
237 | 0 | const char *const *args; |
238 | |
|
239 | 0 | args = t_strsplit_tabescaped(line); |
240 | 0 | if (args[0] == NULL && !conn->list->set.allow_empty_args_input) { |
241 | 0 | e_error(conn->event, "Unexpectedly received empty line"); |
242 | 0 | return -1; |
243 | 0 | } |
244 | | |
245 | 0 | if (!connection_handshake_received(conn) && |
246 | 0 | (conn->v.handshake_args != connection_handshake_args_default || |
247 | 0 | conn->list->set.major_version != 0)) { |
248 | 0 | int ret; |
249 | 0 | if ((ret = conn->v.handshake_args(conn, args)) == 0) |
250 | 0 | ret = 1; /* continue reading */ |
251 | 0 | else if (ret > 0) |
252 | 0 | connection_set_handshake_ready(conn); |
253 | 0 | else { |
254 | 0 | conn->disconnect_reason = |
255 | 0 | CONNECTION_DISCONNECT_HANDSHAKE_FAILED; |
256 | 0 | } |
257 | 0 | return ret; |
258 | 0 | } else if (!connection_handshake_received(conn)) { |
259 | | /* we don't do handshakes */ |
260 | 0 | connection_set_handshake_ready(conn); |
261 | 0 | } |
262 | | |
263 | | /* version must be handled though, by something */ |
264 | 0 | i_assert(conn->version_received); |
265 | | |
266 | 0 | return conn->v.input_args(conn, args); |
267 | 0 | } |
268 | | |
269 | | void connection_input_halt(struct connection *conn) |
270 | 98.0k | { |
271 | 98.0k | io_remove(&conn->io); |
272 | 98.0k | timeout_remove(&conn->to); |
273 | 98.0k | } |
274 | | |
275 | | static void |
276 | | connection_input_resume_full(struct connection *conn, bool set_io_pending) |
277 | 41.4k | { |
278 | 41.4k | i_assert(!conn->disconnected); |
279 | | |
280 | 41.4k | if (conn->io != NULL) { |
281 | | /* do nothing */ |
282 | 41.4k | } else if (conn->input != NULL && !conn->input->closed) { |
283 | 41.4k | conn->io = io_add_istream_to(conn->ioloop, conn->input, |
284 | 41.4k | *conn->v.input, conn); |
285 | 41.4k | if (set_io_pending) |
286 | 35.4k | io_set_pending(conn->io); |
287 | 41.4k | } else if (conn->fd_in != -1) { |
288 | 0 | conn->io = io_add_to(conn->ioloop, conn->fd_in, IO_READ, |
289 | 0 | *conn->v.input, conn); |
290 | 0 | if (set_io_pending) |
291 | 0 | io_set_pending(conn->io); |
292 | 0 | } |
293 | 41.4k | if (conn->input_idle_timeout_secs != 0 && conn->to == NULL) { |
294 | 0 | conn->to = timeout_add_to(conn->ioloop, |
295 | 0 | conn->input_idle_timeout_secs*1000, |
296 | 0 | *conn->v.idle_timeout, conn); |
297 | 0 | } |
298 | 41.4k | } |
299 | | |
300 | | void connection_input_resume(struct connection *conn) |
301 | 35.4k | { |
302 | 35.4k | connection_input_resume_full(conn, TRUE); |
303 | 35.4k | } |
304 | | |
305 | | static void connection_update_property_label(struct connection *conn) |
306 | 6.03k | { |
307 | 6.03k | const char *label; |
308 | | |
309 | 6.03k | if (conn->remote_ip.family == 0) { |
310 | 6.03k | if (conn->remote_uid == (uid_t)-1) |
311 | 0 | label = NULL; |
312 | 6.03k | else if (conn->remote_pid != (pid_t)-1) { |
313 | 6.03k | label = t_strdup_printf("pid=%ld,uid=%ld", |
314 | 6.03k | (long)conn->remote_pid, |
315 | 6.03k | (long)conn->remote_uid); |
316 | 6.03k | } else { |
317 | 0 | label = t_strdup_printf("uid=%ld", |
318 | 0 | (long)conn->remote_uid); |
319 | 0 | } |
320 | 6.03k | } else if (conn->remote_ip.family == AF_INET6) { |
321 | 0 | label = t_strdup_printf("[%s]:%u", |
322 | 0 | net_ip2addr(&conn->remote_ip), |
323 | 0 | conn->remote_port); |
324 | 0 | } else { |
325 | 0 | label = t_strdup_printf("%s:%u", |
326 | 0 | net_ip2addr(&conn->remote_ip), |
327 | 0 | conn->remote_port); |
328 | 0 | } |
329 | | |
330 | 6.03k | i_assert(label != NULL || conn->property_label == NULL); |
331 | 6.03k | if (conn->property_label != NULL && |
332 | 0 | strcmp(conn->property_label, label) != 0) { |
333 | 0 | e_debug(conn->event, "Updated peer address to %s", label); |
334 | 0 | } |
335 | | |
336 | 6.03k | i_free(conn->property_label); |
337 | 6.03k | conn->property_label = i_strdup(label); |
338 | 6.03k | } |
339 | | |
340 | | static void connection_update_label(struct connection *conn) |
341 | 6.03k | { |
342 | 6.03k | bool unix_socket = conn->unix_socket || |
343 | 6.03k | (conn->remote_ip.family == 0 && conn->remote_uid != (uid_t)-1); |
344 | 6.03k | string_t *label; |
345 | | |
346 | 6.03k | label = t_str_new(64); |
347 | 6.03k | if (conn->base_name != NULL) |
348 | 0 | str_append(label, conn->base_name); |
349 | 6.03k | if (conn->property_label != NULL) { |
350 | 6.03k | if (str_len(label) == 0) |
351 | 6.03k | str_append(label, conn->property_label); |
352 | 0 | else { |
353 | 0 | str_append(label, " ("); |
354 | 0 | str_append(label, conn->property_label); |
355 | 0 | str_append(label, ")"); |
356 | 0 | } |
357 | 6.03k | } |
358 | 6.03k | if (str_len(label) == 0) { |
359 | 0 | if (conn->fd_in >= 0 && |
360 | 0 | (conn->fd_in == conn->fd_out || conn->fd_out < 0)) |
361 | 0 | str_printfa(label, "fd=%d", conn->fd_in); |
362 | 0 | else if (conn->fd_in < 0 && conn->fd_out >= 0) |
363 | 0 | str_printfa(label, "fd=%d", conn->fd_out); |
364 | 0 | else if (conn->fd_in >= 0 && conn->fd_out >= 0) { |
365 | 0 | str_printfa(label, "fd_in=%d,fd_out=%d", |
366 | 0 | conn->fd_in, conn->fd_out); |
367 | 0 | } |
368 | 0 | } |
369 | 6.03k | if (unix_socket && str_len(label) > 0) |
370 | 6.03k | str_insert(label, 0, "unix:"); |
371 | 6.03k | if (conn->list->set.log_connection_id) { |
372 | 6.03k | if (str_len(label) > 0) |
373 | 6.03k | str_append_c(label, ' '); |
374 | 6.03k | str_printfa(label, "[%u]", conn->id); |
375 | 6.03k | } |
376 | | |
377 | 6.03k | i_free(conn->label); |
378 | 6.03k | conn->label = i_strdup(str_c(label)); |
379 | 6.03k | } |
380 | | |
381 | | static const char * |
382 | | connection_create_stream_name(struct connection *conn, int fd) |
383 | 12.0k | { |
384 | 12.0k | string_t *name; |
385 | | |
386 | 12.0k | name = t_str_new(64); |
387 | 12.0k | str_append(name, "(conn"); |
388 | 12.0k | if (conn->unix_socket || |
389 | 12.0k | (conn->remote_ip.family == 0 && conn->remote_uid != (uid_t)-1)) |
390 | 12.0k | str_append(name, ":unix"); |
391 | 12.0k | if (conn->base_name != NULL) { |
392 | 0 | str_append_c(name, ':'); |
393 | 0 | str_append(name, conn->base_name); |
394 | 12.0k | } else if (conn->property_label != NULL) { |
395 | 12.0k | str_append_c(name, ':'); |
396 | 12.0k | str_append(name, conn->property_label); |
397 | 12.0k | } else if (fd >= 0) { |
398 | 0 | str_printfa(name, ":fd=%d", fd); |
399 | 0 | } |
400 | 12.0k | if (conn->list->set.log_connection_id) { |
401 | 12.0k | if (str_len(name) == 5) |
402 | 0 | str_append_c(name, ':'); |
403 | 12.0k | else |
404 | 12.0k | str_append_c(name, ','); |
405 | 12.0k | str_printfa(name, "id=%u", conn->id); |
406 | 12.0k | } |
407 | 12.0k | str_append_c(name, ')'); |
408 | | |
409 | 12.0k | return str_c(name); |
410 | 12.0k | } |
411 | | |
412 | | static void connection_update_stream_names(struct connection *conn) |
413 | 12.0k | { |
414 | 12.0k | if (conn->input != NULL) { |
415 | 6.03k | i_stream_set_name( |
416 | 6.03k | conn->input, |
417 | 6.03k | connection_create_stream_name(conn, conn->fd_in)); |
418 | 6.03k | } |
419 | 12.0k | if (conn->output != NULL) { |
420 | 6.03k | o_stream_set_name( |
421 | 6.03k | conn->output, |
422 | 6.03k | connection_create_stream_name(conn, conn->fd_out)); |
423 | 6.03k | } |
424 | 12.0k | } |
425 | | |
426 | | void connection_update_event(struct connection *conn) |
427 | 6.03k | { |
428 | 6.03k | string_t *prefix; |
429 | | |
430 | 6.03k | prefix = t_str_new(64); |
431 | 6.03k | str_append(prefix, "conn"); |
432 | 6.03k | if (strlen(conn->label) > 0) { |
433 | 6.03k | str_append_c(prefix, ' '); |
434 | 6.03k | str_append(prefix, conn->label); |
435 | 6.03k | } |
436 | 6.03k | str_append(prefix, ": "); |
437 | 6.03k | event_set_append_log_prefix(conn->event, str_c(prefix)); |
438 | | |
439 | 6.03k | if (conn->local_ip.family > 0) { |
440 | 0 | event_add_str(conn->event, conn->list->set.client ? |
441 | 0 | "source_ip" : "local_ip", |
442 | 0 | net_ip2addr(&conn->local_ip)); |
443 | 0 | } |
444 | 6.03k | if (conn->local_port > 0) { |
445 | 0 | event_add_int(conn->event, conn->list->set.client ? |
446 | 0 | "source_port" : "local_port", |
447 | 0 | conn->local_port); |
448 | 0 | } |
449 | | |
450 | 6.03k | if (conn->remote_ip.family > 0) { |
451 | 0 | event_add_str(conn->event, conn->list->set.client ? |
452 | 0 | "dest_ip" : "remote_ip", |
453 | 0 | net_ip2addr(&conn->remote_ip)); |
454 | 0 | } |
455 | 6.03k | if (conn->remote_port > 0) { |
456 | 0 | event_add_int(conn->event, conn->list->set.client ? |
457 | 0 | "dest_port" : "remote_port", |
458 | 0 | conn->remote_port); |
459 | 0 | } |
460 | | |
461 | 6.03k | if (conn->remote_pid != (pid_t)-1) |
462 | 6.03k | event_add_int(conn->event, "remote_pid", conn->remote_pid); |
463 | 6.03k | if (conn->remote_uid != (uid_t)-1) |
464 | 6.03k | event_add_int(conn->event, "remote_uid", conn->remote_uid); |
465 | 6.03k | if (conn->remote_gid != (gid_t)-1) |
466 | 6.03k | event_add_int(conn->event, "remote_gid", conn->remote_gid); |
467 | 6.03k | } |
468 | | |
469 | | void connection_update_properties(struct connection *conn) |
470 | 6.03k | { |
471 | 6.03k | int fd = (conn->fd_in < 0 ? conn->fd_out : conn->fd_in); |
472 | 6.03k | struct net_unix_cred cred; |
473 | | |
474 | 6.03k | if (conn->remote_ip.family != 0) { |
475 | | /* remote IP was already set */ |
476 | 6.03k | } else if (conn->unix_peer_checked) { |
477 | | /* already checked */ |
478 | 6.03k | } else if (fd < 0) { |
479 | | /* not connected yet - wait */ |
480 | 6.03k | } else { |
481 | 6.03k | if (net_getpeername(fd, &conn->remote_ip, |
482 | 6.03k | &conn->remote_port) == 0) { |
483 | | /* either TCP or UNIX socket connection */ |
484 | 6.03k | errno = 0; |
485 | 6.03k | } |
486 | | |
487 | 6.03k | if (conn->remote_ip.family != 0) { |
488 | | /* TCP connection */ |
489 | 0 | i_assert(conn->remote_port != 0); |
490 | 6.03k | } else if (errno == ENOTSOCK) { |
491 | | /* getpeername() already found out this can't be a UNIX |
492 | | socket connection */ |
493 | 6.03k | } else if (net_getunixcred(fd, &cred) == 0) { |
494 | 6.03k | conn->remote_pid = cred.pid; |
495 | 6.03k | conn->remote_uid = cred.uid; |
496 | 6.03k | conn->remote_gid = cred.gid; |
497 | 6.03k | conn->have_unix_credentials = TRUE; |
498 | 6.03k | } |
499 | 6.03k | conn->unix_peer_checked = TRUE; |
500 | 6.03k | } |
501 | | |
502 | 6.03k | connection_update_property_label(conn); |
503 | 6.03k | connection_update_label(conn); |
504 | 6.03k | connection_update_stream_names(conn); |
505 | 6.03k | connection_update_event(conn); |
506 | | |
507 | 6.03k | conn->name = (conn->base_name != NULL ? |
508 | 6.03k | conn->base_name : conn->property_label); |
509 | 6.03k | } |
510 | | |
511 | | static void connection_init_streams(struct connection *conn) |
512 | 6.03k | { |
513 | 6.03k | const struct connection_settings *set = &conn->list->set; |
514 | | |
515 | | /* If we're reconnecting, the iostreams still exist */ |
516 | 6.03k | if (conn->input != NULL) { |
517 | 0 | i_assert(conn->input->closed); |
518 | 0 | i_stream_destroy(&conn->input); |
519 | 0 | } |
520 | 6.03k | if (conn->output != NULL) { |
521 | 0 | i_assert(conn->output->closed); |
522 | 0 | o_stream_destroy(&conn->output); |
523 | 0 | } |
524 | | |
525 | 6.03k | i_assert(conn->io == NULL); |
526 | 6.03k | i_assert(conn->to == NULL); |
527 | | |
528 | 6.03k | i_zero(&conn->handshake_finished); |
529 | 6.03k | conn->version_received = set->major_version == 0; |
530 | | |
531 | 6.03k | if (set->input_max_size != 0) { |
532 | 6.03k | if (conn->unix_socket) |
533 | 0 | conn->input = i_stream_create_unix(conn->fd_in, |
534 | 0 | set->input_max_size); |
535 | 6.03k | else |
536 | 6.03k | conn->input = i_stream_create_fd(conn->fd_in, |
537 | 6.03k | set->input_max_size); |
538 | 6.03k | i_stream_switch_ioloop_to(conn->input, conn->ioloop); |
539 | 6.03k | } |
540 | 6.03k | if (set->output_max_size != 0) { |
541 | 6.03k | if (conn->unix_socket) |
542 | 0 | conn->output = o_stream_create_unix(conn->fd_out, |
543 | 0 | set->output_max_size); |
544 | 6.03k | else |
545 | 6.03k | conn->output = o_stream_create_fd(conn->fd_out, |
546 | 6.03k | set->output_max_size); |
547 | 6.03k | o_stream_set_no_error_handling(conn->output, TRUE); |
548 | 6.03k | o_stream_set_finish_via_child(conn->output, FALSE); |
549 | 6.03k | o_stream_switch_ioloop_to(conn->output, conn->ioloop); |
550 | 6.03k | } |
551 | 6.03k | connection_update_stream_names(conn); |
552 | | |
553 | 6.03k | conn->disconnected = FALSE; |
554 | 6.03k | i_assert(conn->to == NULL); |
555 | 6.03k | connection_input_resume_full(conn, FALSE); |
556 | 6.03k | i_assert(conn->to != NULL || conn->input_idle_timeout_secs == 0); |
557 | 6.03k | if (set->major_version != 0 && !set->dont_send_version) { |
558 | 0 | e_debug(conn->event, "Sending version handshake"); |
559 | 0 | o_stream_nsend_str(conn->output, t_strdup_printf( |
560 | 0 | "VERSION\t%s\t%u\t%u\n", set->service_name_out, |
561 | 0 | set->major_version, set->minor_version)); |
562 | 0 | } |
563 | 6.03k | } |
564 | | |
565 | | void connection_streams_changed(struct connection *conn) |
566 | 0 | { |
567 | 0 | const struct connection_settings *set = &conn->list->set; |
568 | |
|
569 | 0 | if (set->input_max_size != 0 && conn->io != NULL) { |
570 | 0 | connection_input_halt(conn); |
571 | 0 | connection_input_resume(conn); |
572 | 0 | } |
573 | 0 | } |
574 | | |
575 | | static void connection_client_connected(struct connection *conn, bool success) |
576 | 0 | { |
577 | 0 | i_assert(conn->list->set.client); |
578 | | |
579 | 0 | connection_update_properties(conn); |
580 | |
|
581 | 0 | conn->connect_finished = ioloop_timeval; |
582 | 0 | conn->client_connect_succeeded = success; |
583 | |
|
584 | 0 | struct event_passthrough *e = event_create_passthrough(conn->event)-> |
585 | 0 | set_name("server_connection_connected"); |
586 | 0 | if (success) { |
587 | 0 | e_debug(e->event(), "Client connected (fd=%d)", |
588 | 0 | conn->fd_in); |
589 | 0 | } else { |
590 | 0 | e_debug(e->event(), "Client connection failed (fd=%d)", |
591 | 0 | conn->fd_in); |
592 | 0 | } |
593 | |
|
594 | 0 | if (success) |
595 | 0 | connection_init_streams(conn); |
596 | 0 | if (conn->v.client_connected != NULL) |
597 | 0 | conn->v.client_connected(conn, success); |
598 | 0 | if (!success) { |
599 | 0 | connection_closed(conn, CONNECTION_DISCONNECT_CONN_CLOSED); |
600 | 0 | } |
601 | 0 | } |
602 | | |
603 | | static void |
604 | | connection_init_full(struct connection_list *list, struct connection *conn, |
605 | | const char *name, int fd_in, int fd_out) |
606 | 6.03k | { |
607 | 6.03k | if (conn->id == 0) { |
608 | 6.03k | if (list->id_counter == 0) |
609 | 6.03k | list->id_counter++; |
610 | 6.03k | conn->id = list->id_counter++; |
611 | 6.03k | } |
612 | | |
613 | 6.03k | i_zero(&conn->connect_started); |
614 | 6.03k | i_zero(&conn->connect_finished); |
615 | 6.03k | conn->client_connect_succeeded = FALSE; |
616 | | |
617 | 6.03k | conn->ioloop = current_ioloop; |
618 | 6.03k | conn->fd_in = fd_in; |
619 | 6.03k | conn->fd_out = fd_out; |
620 | 6.03k | conn->disconnected = TRUE; |
621 | 6.03k | conn->remote_uid = (uid_t)-1; |
622 | 6.03k | conn->remote_gid = (gid_t)-1; |
623 | 6.03k | conn->remote_pid = (pid_t)-1; |
624 | | |
625 | 6.03k | i_free(conn->base_name); |
626 | 6.03k | conn->base_name = i_strdup(name); |
627 | | |
628 | 6.03k | if (list->set.input_idle_timeout_secs != 0 && |
629 | 0 | conn->input_idle_timeout_secs == 0) { |
630 | 0 | conn->input_idle_timeout_secs = |
631 | 0 | list->set.input_idle_timeout_secs; |
632 | 0 | } |
633 | | |
634 | 6.03k | if (conn->event == NULL) |
635 | 6.03k | conn->event = event_create(conn->event_parent); |
636 | 6.03k | if (list->set.debug) |
637 | 0 | event_set_forced_debug(conn->event, TRUE); |
638 | | |
639 | | /* Use error iostreams until the client is connected. |
640 | | This way caller can rely on them always being non-NULL. */ |
641 | 6.03k | const char *conn_error = "connect() not finished yet"; |
642 | 6.03k | if (list->set.client && conn->input == NULL) { |
643 | 0 | conn->input = i_stream_create_error_str(EINPROGRESS, "%s", |
644 | 0 | conn_error); |
645 | 0 | } |
646 | 6.03k | if (list->set.client && conn->output == NULL) { |
647 | 0 | conn->output = o_stream_create_error_str(EINPROGRESS, "%s", |
648 | 0 | conn_error); |
649 | 0 | } |
650 | | |
651 | 6.03k | if (conn->list != NULL) { |
652 | 0 | i_assert(conn->list == list); |
653 | 6.03k | } else { |
654 | 6.03k | conn->list = list; |
655 | 6.03k | DLLIST_PREPEND(&list->connections, conn); |
656 | 6.03k | list->connections_count++; |
657 | 6.03k | } |
658 | | |
659 | 6.03k | connection_update_properties(conn); |
660 | 6.03k | connection_set_default_handlers(conn); |
661 | 6.03k | } |
662 | | |
663 | | void connection_init(struct connection_list *list, struct connection *conn, |
664 | | const char *name) |
665 | 0 | { |
666 | 0 | connection_init_full(list, conn, name, -1, -1); |
667 | 0 | } |
668 | | |
669 | | void connection_init_server(struct connection_list *list, |
670 | | struct connection *conn, const char *name, |
671 | | int fd_in, int fd_out) |
672 | 6.03k | { |
673 | 6.03k | i_assert(!list->set.client); |
674 | | |
675 | 6.03k | connection_init_full(list, conn, name, fd_in, fd_out); |
676 | | |
677 | 6.03k | struct event_passthrough *e = event_create_passthrough(conn->event)-> |
678 | 6.03k | set_name("client_connection_connected"); |
679 | | /* fd_out differs from fd_in only for stdin/stdout. Keep the logging |
680 | | output nice and clean by logging only the fd_in. If it's 0, it'll |
681 | | also be obvious that fd_out=1. */ |
682 | 6.03k | e_debug(e->event(), "Server accepted connection (fd=%d)", fd_in); |
683 | | |
684 | 6.03k | connection_init_streams(conn); |
685 | | /* Client has finished connecting to server, so record it |
686 | | here. */ |
687 | 6.03k | conn->connect_finished = ioloop_timeval; |
688 | | |
689 | 6.03k | if (conn->v.init != NULL) |
690 | 0 | conn->v.init(conn); |
691 | 6.03k | } |
692 | | |
693 | | void connection_init_server_ip(struct connection_list *list, |
694 | | struct connection *conn, const char *name, |
695 | | int fd_in, int fd_out, |
696 | | const struct ip_addr *remote_ip, |
697 | | in_port_t remote_port) |
698 | 6.03k | { |
699 | 6.03k | if (remote_ip != NULL && remote_ip->family != 0) |
700 | 0 | conn->remote_ip = *remote_ip; |
701 | 6.03k | if (remote_port != 0) |
702 | 0 | conn->remote_port = remote_port; |
703 | | |
704 | 6.03k | connection_init_server(list, conn, name, fd_in, fd_out); |
705 | 6.03k | } |
706 | | |
707 | | void connection_init_client_fd(struct connection_list *list, |
708 | | struct connection *conn, const char *name, |
709 | | int fd_in, int fd_out) |
710 | 0 | { |
711 | 0 | i_assert(list->set.client); |
712 | | |
713 | 0 | connection_init_full(list, conn, name, fd_in, fd_out); |
714 | |
|
715 | 0 | struct event_passthrough *e = event_create_passthrough(conn->event)-> |
716 | 0 | set_name("server_connection_connected"); |
717 | | /* fd_out differs from fd_in only for stdin/stdout. Keep the logging |
718 | | output nice and clean by logging only the fd_in. If it's 0, it'll |
719 | | also be obvious that fd_out=1. */ |
720 | 0 | e_debug(e->event(), "Client connected (fd=%d)", fd_in); |
721 | |
|
722 | 0 | if (conn->v.init != NULL) |
723 | 0 | conn->v.init(conn); |
724 | 0 | connection_client_connected(conn, TRUE); |
725 | 0 | } |
726 | | |
727 | | void connection_init_client_ip_from(struct connection_list *list, |
728 | | struct connection *conn, |
729 | | const char *hostname, |
730 | | const struct ip_addr *ip, in_port_t port, |
731 | | const struct ip_addr *my_ip) |
732 | 0 | { |
733 | 0 | const char *name = NULL; |
734 | |
|
735 | 0 | if (hostname != NULL) |
736 | 0 | name = t_strdup_printf("%s:%u", hostname, port); |
737 | |
|
738 | 0 | i_assert(list->set.client); |
739 | | |
740 | 0 | conn->remote_ip = *ip; |
741 | 0 | conn->remote_port = port; |
742 | |
|
743 | 0 | if (my_ip != NULL) |
744 | 0 | conn->local_ip = *my_ip; |
745 | 0 | else |
746 | 0 | i_zero(&conn->local_ip); |
747 | |
|
748 | 0 | connection_init(list, conn, name); |
749 | 0 | if (hostname != NULL) |
750 | 0 | event_add_str(conn->event, "dest_host", hostname); |
751 | 0 | connection_update_event(conn); |
752 | |
|
753 | 0 | if (conn->v.init != NULL) |
754 | 0 | conn->v.init(conn); |
755 | 0 | } |
756 | | |
757 | | void connection_init_client_ip(struct connection_list *list, |
758 | | struct connection *conn, const char *hostname, |
759 | | const struct ip_addr *ip, in_port_t port) |
760 | 0 | { |
761 | 0 | connection_init_client_ip_from(list, conn, hostname, ip, port, NULL); |
762 | 0 | } |
763 | | |
764 | | void connection_init_client_unix(struct connection_list *list, |
765 | | struct connection *conn, const char *path) |
766 | 0 | { |
767 | 0 | i_assert(list->set.client); |
768 | | |
769 | 0 | conn->unix_socket = TRUE; |
770 | |
|
771 | 0 | connection_init(list, conn, path); |
772 | 0 | event_add_str(conn->event, "socket_path", path); |
773 | |
|
774 | 0 | if (conn->v.init != NULL) |
775 | 0 | conn->v.init(conn); |
776 | 0 | } |
777 | | |
778 | | void connection_init_from_streams(struct connection_list *list, |
779 | | struct connection *conn, const char *name, |
780 | | struct istream *input, struct ostream *output) |
781 | 0 | { |
782 | 0 | connection_init_full(list, conn, name, |
783 | 0 | i_stream_get_fd(input), o_stream_get_fd(output)); |
784 | |
|
785 | 0 | i_assert(conn->fd_in >= 0); |
786 | 0 | i_assert(conn->fd_out >= 0); |
787 | 0 | i_assert(conn->io == NULL); |
788 | 0 | i_assert(conn->to == NULL); |
789 | | |
790 | 0 | i_stream_destroy(&conn->input); |
791 | 0 | conn->input = input; |
792 | 0 | i_stream_ref(conn->input); |
793 | |
|
794 | 0 | o_stream_destroy(&conn->output); |
795 | 0 | conn->output = output; |
796 | 0 | o_stream_ref(conn->output); |
797 | 0 | o_stream_set_no_error_handling(conn->output, TRUE); |
798 | |
|
799 | 0 | connection_update_stream_names(conn); |
800 | |
|
801 | 0 | conn->disconnected = FALSE; |
802 | 0 | connection_input_resume_full(conn, FALSE); |
803 | |
|
804 | 0 | if (conn->v.client_connected != NULL) |
805 | 0 | conn->v.client_connected(conn, TRUE); |
806 | 0 | } |
807 | | |
808 | | static void connection_set_connect_error_streams(struct connection *conn) |
809 | 0 | { |
810 | 0 | int stream_errno = errno; |
811 | 0 | const char *error = t_strdup_printf("connect(%s) failed: %m", |
812 | 0 | conn->name); |
813 | 0 | i_stream_destroy(&conn->input); |
814 | 0 | o_stream_destroy(&conn->output); |
815 | 0 | conn->input = i_stream_create_error_str(stream_errno, "%s", error); |
816 | 0 | conn->output = o_stream_create_error_str(stream_errno, "%s", error); |
817 | 0 | errno = stream_errno; |
818 | 0 | } |
819 | | |
820 | | static void connection_socket_connected(struct connection *conn) |
821 | 0 | { |
822 | 0 | io_remove(&conn->io); |
823 | 0 | timeout_remove(&conn->to); |
824 | |
|
825 | 0 | errno = net_geterror(conn->fd_in); |
826 | 0 | if (errno != 0) |
827 | 0 | connection_set_connect_error_streams(conn); |
828 | 0 | connection_client_connected(conn, errno == 0); |
829 | 0 | } |
830 | | |
831 | | int connection_client_connect_with_retries(struct connection *conn, |
832 | | unsigned int msecs) |
833 | 0 | { |
834 | 0 | const struct connection_settings *set = &conn->list->set; |
835 | 0 | int fd; |
836 | |
|
837 | 0 | i_assert(conn->list->set.client); |
838 | 0 | i_assert(conn->fd_in == -1); |
839 | | |
840 | 0 | e_debug(conn->event, "Connecting"); |
841 | |
|
842 | 0 | if (conn->remote_port != 0) { |
843 | 0 | fd = net_connect_ip(&conn->remote_ip, conn->remote_port, |
844 | 0 | (conn->local_ip.family != 0 ? |
845 | 0 | &conn->local_ip : NULL)); |
846 | 0 | } else if (msecs == 0) { |
847 | 0 | fd = net_connect_unix(conn->base_name); |
848 | 0 | } else { |
849 | 0 | fd = net_connect_unix_with_retries(conn->base_name, msecs); |
850 | 0 | } |
851 | 0 | if (fd == -1) { |
852 | 0 | connection_set_connect_error_streams(conn); |
853 | 0 | return -1; |
854 | 0 | } |
855 | 0 | conn->fd_in = conn->fd_out = fd; |
856 | 0 | conn->connect_started = ioloop_timeval; |
857 | 0 | conn->disconnected = FALSE; |
858 | |
|
859 | 0 | if (conn->remote_port != 0 || |
860 | 0 | conn->list->set.delayed_unix_client_connected_callback) { |
861 | 0 | connection_update_properties(conn); |
862 | 0 | conn->io = io_add_to(conn->ioloop, conn->fd_out, IO_WRITE, |
863 | 0 | connection_socket_connected, conn); |
864 | 0 | e_debug(conn->event, |
865 | 0 | "Waiting for connect (fd=%d) to finish for max %u msecs", |
866 | 0 | fd, set->client_connect_timeout_msecs); |
867 | 0 | if (set->client_connect_timeout_msecs != 0) { |
868 | 0 | conn->to = timeout_add_to(conn->ioloop, |
869 | 0 | set->client_connect_timeout_msecs, |
870 | 0 | *conn->v.connect_timeout, conn); |
871 | 0 | } |
872 | 0 | } else { |
873 | 0 | connection_client_connected(conn, TRUE); |
874 | 0 | } |
875 | 0 | return 0; |
876 | 0 | } |
877 | | |
878 | | int connection_client_connect(struct connection *conn) |
879 | 0 | { |
880 | 0 | return connection_client_connect_with_retries(conn, |
881 | 0 | conn->list->set.unix_client_connect_msecs); |
882 | 0 | } |
883 | | |
884 | | static void connection_client_connect_failed(struct connection *conn) |
885 | 0 | { |
886 | 0 | timeout_remove(&conn->to); |
887 | 0 | errno = conn->connect_failed_errno; |
888 | 0 | conn->v.client_connected(conn, FALSE); |
889 | 0 | connection_closed(conn, CONNECTION_DISCONNECT_CONN_CLOSED); |
890 | 0 | } |
891 | | |
892 | | int connection_client_connect_async(struct connection *conn) |
893 | 0 | { |
894 | 0 | i_assert(conn->v.client_connected != NULL); |
895 | | |
896 | 0 | if (connection_client_connect(conn) < 0) { |
897 | 0 | i_assert(conn->to == NULL); |
898 | 0 | conn->connect_failed_errno = errno; |
899 | 0 | conn->to = timeout_add_short(0, connection_client_connect_failed, conn); |
900 | 0 | return -1; |
901 | 0 | } |
902 | 0 | return 0; |
903 | 0 | } |
904 | | |
905 | | void connection_update_counters(struct connection *conn) |
906 | 12.0k | { |
907 | 12.0k | if (conn->input != NULL) |
908 | 12.0k | event_add_int(conn->event, "net_in_bytes", conn->input->v_offset); |
909 | 12.0k | if (conn->output != NULL) |
910 | 12.0k | event_add_int(conn->event, "net_out_bytes", conn->output->offset); |
911 | 12.0k | } |
912 | | |
913 | | void connection_disconnect(struct connection *conn) |
914 | 12.0k | { |
915 | 12.0k | if (conn->disconnected) |
916 | 6.03k | return; |
917 | 6.03k | connection_update_counters(conn); |
918 | | /* client connects to a Server, and Server gets connection from Client |
919 | | */ |
920 | 6.03k | const char *ename = conn->list->set.client ? |
921 | 0 | "server_connection_disconnected" : |
922 | 6.03k | "client_connection_disconnected"; |
923 | | |
924 | 6.03k | struct event_passthrough *e = event_create_passthrough(conn->event)-> |
925 | 6.03k | set_name(ename)-> |
926 | 6.03k | add_str("reason", connection_disconnect_reason(conn)); |
927 | 6.03k | e_debug(e->event(), "Disconnected: %s (fd=%d)", |
928 | 6.03k | connection_disconnect_reason(conn), conn->fd_in); |
929 | | |
930 | 6.03k | conn->last_input = 0; |
931 | 6.03k | i_zero(&conn->last_input_tv); |
932 | 6.03k | timeout_remove(&conn->to); |
933 | 6.03k | io_remove(&conn->io); |
934 | 6.03k | i_stream_close(conn->input); |
935 | 6.03k | o_stream_close(conn->output); |
936 | 6.03k | if (conn->fd_in == conn->fd_out) |
937 | 6.03k | (void)shutdown(conn->fd_out, SHUT_RDWR); |
938 | 6.03k | fd_close_maybe_stdio(&conn->fd_in, &conn->fd_out); |
939 | 6.03k | conn->disconnected = TRUE; |
940 | 6.03k | } |
941 | | |
942 | | void connection_deinit(struct connection *conn) |
943 | 6.03k | { |
944 | 6.03k | i_assert(conn->list->connections_count > 0); |
945 | | |
946 | 6.03k | conn->list->connections_count--; |
947 | 6.03k | DLLIST_REMOVE(&conn->list->connections, conn); |
948 | | |
949 | 6.03k | connection_disconnect(conn); |
950 | 6.03k | i_stream_destroy(&conn->input); |
951 | 6.03k | o_stream_destroy(&conn->output); |
952 | 6.03k | i_free(conn->base_name); |
953 | 6.03k | i_free(conn->label); |
954 | 6.03k | i_free(conn->property_label); |
955 | 6.03k | event_unref(&conn->event); |
956 | 6.03k | conn->list = NULL; |
957 | 6.03k | } |
958 | | |
959 | | int connection_input_read_stream(struct connection *conn, |
960 | | struct istream *input) |
961 | 0 | { |
962 | 0 | conn->last_input = ioloop_time; |
963 | 0 | conn->last_input_tv = ioloop_timeval; |
964 | 0 | if (conn->to != NULL) |
965 | 0 | timeout_reset(conn->to); |
966 | |
|
967 | 0 | switch (i_stream_read(input)) { |
968 | 0 | case -2: |
969 | | /* buffer full */ |
970 | 0 | switch (conn->list->set.input_full_behavior) { |
971 | 0 | case CONNECTION_BEHAVIOR_DESTROY: |
972 | 0 | connection_closed(conn, |
973 | 0 | CONNECTION_DISCONNECT_BUFFER_FULL); |
974 | 0 | return -1; |
975 | 0 | case CONNECTION_BEHAVIOR_ALLOW: |
976 | 0 | return -2; |
977 | 0 | } |
978 | 0 | i_unreached(); |
979 | 0 | case -1: |
980 | | /* disconnected */ |
981 | 0 | if (input != conn->input) { |
982 | 0 | i_stream_set_error(conn->input, input->stream_errno, |
983 | 0 | "%s", i_stream_get_error(input)); |
984 | 0 | } |
985 | 0 | connection_closed(conn, CONNECTION_DISCONNECT_CONN_CLOSED); |
986 | 0 | return -1; |
987 | 0 | case 0: |
988 | | /* nothing new read */ |
989 | 0 | return 0; |
990 | 0 | default: |
991 | | /* something was read */ |
992 | 0 | return 1; |
993 | 0 | } |
994 | 0 | } |
995 | | |
996 | | int connection_input_read(struct connection *conn) |
997 | 0 | { |
998 | 0 | return connection_input_read_stream(conn, conn->input); |
999 | 0 | } |
1000 | | |
1001 | | const char *connection_disconnect_reason(struct connection *conn) |
1002 | 6.03k | { |
1003 | 6.03k | switch (conn->disconnect_reason) { |
1004 | 3.60k | case CONNECTION_DISCONNECT_DEINIT: |
1005 | 3.60k | return "Deinitializing"; |
1006 | 0 | case CONNECTION_DISCONNECT_CONNECT_TIMEOUT: { |
1007 | 0 | unsigned int msecs = |
1008 | 0 | conn->list->set.client_connect_timeout_msecs; |
1009 | 0 | return t_strdup_printf("connect(%s) timed out in %u.%03u secs", |
1010 | 0 | conn->name, msecs/1000, msecs%1000); |
1011 | 0 | } |
1012 | 0 | case CONNECTION_DISCONNECT_IDLE_TIMEOUT: |
1013 | 0 | return "Idle timeout"; |
1014 | 0 | case CONNECTION_DISCONNECT_CONN_CLOSED: |
1015 | 0 | if (!conn->client_connect_succeeded) { |
1016 | | /* connect() error is in the error istream */ |
1017 | 0 | i_assert(conn->input != NULL); |
1018 | 0 | return i_stream_get_error(conn->input); |
1019 | 0 | } |
1020 | | /* fall through */ |
1021 | 2.43k | case CONNECTION_DISCONNECT_NOT: |
1022 | 2.43k | case CONNECTION_DISCONNECT_BUFFER_FULL: |
1023 | 2.43k | return io_stream_get_disconnect_reason(conn->input, conn->output); |
1024 | 0 | case CONNECTION_DISCONNECT_HANDSHAKE_FAILED: |
1025 | 0 | return "Handshake failed"; |
1026 | 6.03k | } |
1027 | 6.03k | i_unreached(); |
1028 | 6.03k | } |
1029 | | |
1030 | | const char *connection_input_timeout_reason(struct connection *conn) |
1031 | 0 | { |
1032 | 0 | if (conn->last_input_tv.tv_sec != 0) { |
1033 | 0 | long long diff = timeval_diff_msecs(&ioloop_timeval, |
1034 | 0 | &conn->last_input_tv); |
1035 | 0 | return t_strdup_printf("No input for %lld.%03lld secs", |
1036 | 0 | diff/1000, diff%1000); |
1037 | 0 | } else if (conn->connect_finished.tv_sec != 0) { |
1038 | 0 | long long diff = timeval_diff_msecs(&ioloop_timeval, |
1039 | 0 | &conn->connect_finished); |
1040 | 0 | return t_strdup_printf( |
1041 | 0 | "No input since connected %lld.%03lld secs ago", |
1042 | 0 | diff/1000, diff%1000); |
1043 | 0 | } else { |
1044 | 0 | long long diff = timeval_diff_msecs(&ioloop_timeval, |
1045 | 0 | &conn->connect_started); |
1046 | 0 | return t_strdup_printf( |
1047 | 0 | "connect(%s) timed out after %lld.%03lld secs", |
1048 | 0 | conn->name, diff/1000, diff%1000); |
1049 | 0 | } |
1050 | 0 | } |
1051 | | |
1052 | | void connection_set_handlers(struct connection *conn, |
1053 | | const struct connection_vfuncs *vfuncs) |
1054 | 6.03k | { |
1055 | 6.03k | connection_input_halt(conn); |
1056 | 6.03k | i_assert(vfuncs->destroy != NULL); |
1057 | 6.03k | conn->v = *vfuncs; |
1058 | 6.03k | if (conn->v.input == NULL) |
1059 | 0 | conn->v.input = connection_input_default; |
1060 | 6.03k | if (conn->v.input_line == NULL) |
1061 | 6.03k | conn->v.input_line = connection_input_line_default; |
1062 | 6.03k | if (conn->v.handshake_args == NULL) |
1063 | 6.03k | conn->v.handshake_args = connection_handshake_args_default; |
1064 | 6.03k | if (conn->v.idle_timeout == NULL) |
1065 | 6.03k | conn->v.idle_timeout = connection_idle_timeout; |
1066 | 6.03k | if (conn->v.connect_timeout == NULL) |
1067 | 6.03k | conn->v.connect_timeout = connection_connect_timeout; |
1068 | 6.03k | if (!conn->disconnected) |
1069 | 0 | connection_input_resume_full(conn, FALSE); |
1070 | 6.03k | } |
1071 | | |
1072 | | void connection_set_default_handlers(struct connection *conn) |
1073 | 6.03k | { |
1074 | 6.03k | connection_set_handlers(conn, &conn->list->v); |
1075 | 6.03k | } |
1076 | | |
1077 | | void connection_switch_ioloop_to(struct connection *conn, |
1078 | | struct ioloop *ioloop) |
1079 | 0 | { |
1080 | 0 | conn->ioloop = ioloop; |
1081 | 0 | if (conn->io != NULL) |
1082 | 0 | conn->io = io_loop_move_io_to(ioloop, &conn->io); |
1083 | 0 | if (conn->to != NULL) |
1084 | 0 | conn->to = io_loop_move_timeout_to(ioloop, &conn->to); |
1085 | 0 | if (conn->input != NULL) |
1086 | 0 | i_stream_switch_ioloop_to(conn->input, ioloop); |
1087 | 0 | if (conn->output != NULL) |
1088 | 0 | o_stream_switch_ioloop_to(conn->output, ioloop); |
1089 | 0 | } |
1090 | | |
1091 | | void connection_switch_ioloop(struct connection *conn) |
1092 | 0 | { |
1093 | 0 | connection_switch_ioloop_to(conn, current_ioloop); |
1094 | 0 | } |
1095 | | |
1096 | | struct connection_list * |
1097 | | connection_list_init(const struct connection_settings *set, |
1098 | | const struct connection_vfuncs *vfuncs) |
1099 | 6.03k | { |
1100 | 6.03k | struct connection_list *list; |
1101 | | |
1102 | 6.03k | i_assert(vfuncs->input != NULL || |
1103 | 6.03k | set->input_full_behavior != CONNECTION_BEHAVIOR_ALLOW); |
1104 | 6.03k | i_assert(set->major_version == 0 || |
1105 | 6.03k | (set->service_name_in != NULL && |
1106 | 6.03k | set->service_name_out != NULL && |
1107 | 6.03k | set->output_max_size != 0)); |
1108 | | |
1109 | 6.03k | list = i_new(struct connection_list, 1); |
1110 | 6.03k | list->set = *set; |
1111 | 6.03k | list->v = *vfuncs; |
1112 | | |
1113 | 6.03k | return list; |
1114 | 6.03k | } |
1115 | | |
1116 | | void connection_list_deinit(struct connection_list **_list) |
1117 | 6.03k | { |
1118 | 6.03k | struct connection_list *list = *_list; |
1119 | 6.03k | struct connection *conn; |
1120 | | |
1121 | 6.03k | if (list == NULL) |
1122 | 0 | return; |
1123 | 6.03k | *_list = NULL; |
1124 | | |
1125 | 9.63k | while (list->connections != NULL) { |
1126 | 3.60k | conn = list->connections; |
1127 | 3.60k | connection_closed(conn, CONNECTION_DISCONNECT_DEINIT); |
1128 | 3.60k | i_assert(conn != list->connections); |
1129 | 3.60k | } |
1130 | 6.03k | i_free(list); |
1131 | 6.03k | } |
1132 | | |
1133 | | bool connection_is_valid_dns_name(const char *name) |
1134 | 0 | { |
1135 | 0 | const char *p = name; |
1136 | 0 | if (*name == '\0') |
1137 | 0 | return FALSE; |
1138 | 0 | if (strstr(name, "..") != NULL) |
1139 | 0 | return FALSE; |
1140 | 0 | for (; *p != '\0'; p++) { |
1141 | 0 | if ((*p < '0' || *p > '9') && |
1142 | 0 | (*p < 'A' || *p > 'Z') && |
1143 | 0 | (*p < 'a' || *p > 'z') && |
1144 | 0 | *p != '.' && *p != '-' && |
1145 | 0 | *p != '_' && *p != ':') |
1146 | 0 | return FALSE; |
1147 | 0 | } |
1148 | 0 | return p - name < 256; /* maximum length is 255 by RFC 952 */ |
1149 | 0 | } |