Coverage Report

Created: 2026-03-15 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}