• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

proftpd / proftpd / 30135626854

25 Jul 2026 12:15AM UTC coverage: 93.034% (+0.6%) from 92.428%
30135626854

push

github

51363 of 55209 relevant lines covered (93.03%)

219.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.98
/src/netio.c
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 2001-2026 The ProFTPD Project team
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
17
 *
18
 * As a special exemption, the ProFTPD Project team and other respective
19
 * copyright holders give permission to link this program with OpenSSL, and
20
 * distribute the resulting executable, without including the source code for
21
 * OpenSSL in the source distribution.
22
 */
23

24
/* NetIO routines */
25

26
#include "conf.h"
27

28
/* See RFC 854 for the definition of these Telnet values */
29

30
/* Telnet "Interpret As Command" indicator */
31
#ifndef TELNET_IAC
32
# define TELNET_IAC        255
33
#endif
34

35
#ifndef TELNET_DONT
36
# define TELNET_DONT        254
37
#endif
38

39
#ifndef TELNET_DO
40
# define TELNET_DO        253
41
#endif
42

43
#ifndef TELNET_WONT
44
# define TELNET_WONT        252
45
#endif
46

47
#ifndef TELNET_WILL
48
# define TELNET_WILL        251
49
#endif
50

51
/* Telnet "Interrupt Process" code */
52
#ifndef TELNET_IP
53
# define TELNET_IP        244
54
#endif
55

56
/* Telnet "Data Mark" code */
57
#ifndef TELNET_DM
58
# define TELNET_DM        242
59
#endif
60

61
static const char *trace_channel = "netio";
62

63
static pr_netio_t *default_ctrl_netio = NULL, *ctrl_netio = NULL;
64
static pr_netio_t *default_data_netio = NULL, *data_netio = NULL;
65
static pr_netio_t *default_othr_netio = NULL, *othr_netio = NULL;
66

67
/* Used to track whether the previous text read from the client's control
68
 * connection was a properly-terminated command.  If so, then read in the
69
 * next/current text as per normal.  If NOT (e.g. the client sent a too-long
70
 * command), then read in the next/current text, but ignore it.  Only clear
71
 * this flag if the next/current command can be read as per normal.
72
 *
73
 * The pr_netio_telnet_gets() uses this variable, in conjunction with its
74
 * saw_newline flag, for handling too-long commands from clients.
75
 */
76
static int properly_terminated_prev_command = TRUE;
77

78
static pr_netio_stream_t *netio_stream_alloc(pool *parent_pool) {
79
  pool *netio_pool = NULL;
129✔
80
  pr_netio_stream_t *nstrm = NULL;
129✔
81

129✔
82
  if (!parent_pool) {
83
    errno = EINVAL;
129✔
84
    return NULL;
×
85
  }
×
86

87
  netio_pool = make_sub_pool(parent_pool);
88
  pr_pool_tag(netio_pool, "netio stream pool");
129✔
89
  nstrm = pcalloc(netio_pool, sizeof(pr_netio_stream_t));
129✔
90

129✔
91
  nstrm->strm_pool = netio_pool;
92
  nstrm->strm_fd = -1;
129✔
93
  nstrm->strm_mode = 0;
129✔
94
  nstrm->strm_flags = 0;
129✔
95
  nstrm->strm_buf = NULL;
129✔
96
  nstrm->strm_data = NULL;
129✔
97
  nstrm->strm_errno = 0;
129✔
98

129✔
99
  /* This table will not contain that many entries, so a low number
100
   * of chains should suffice.
101
   */
102
  nstrm->notes = pr_table_nalloc(nstrm->strm_pool, 0, 4);
103

129✔
104
  return nstrm;
105
}
129✔
106

107
pr_buffer_t *pr_netio_buffer_alloc(pr_netio_stream_t *nstrm) {
108
  size_t bufsz;
28✔
109
  pr_buffer_t *pbuf = NULL;
28✔
110

28✔
111
  if (nstrm == NULL) {
112
    errno = EINVAL;
28✔
113
    return NULL;
1✔
114
  }
1✔
115

116
  pbuf = pcalloc(nstrm->strm_pool, sizeof(pr_buffer_t));
117

27✔
118
  /* Allocate a buffer. */
119
  bufsz = pr_config_get_server_xfer_bufsz(nstrm->strm_mode);
120
  pbuf->buf = pcalloc(nstrm->strm_pool, bufsz);
27✔
121
  pbuf->buflen = bufsz;
27✔
122

27✔
123
  /* Position the offset at the start of the buffer, and set the
124
   * remaining bytes value accordingly.
125
   */
126
  pbuf->current = pbuf->buf;
127
  pbuf->remaining = bufsz;
27✔
128

27✔
129
  /* Add this buffer to the given stream. */
130
  nstrm->strm_buf = pbuf;
131

27✔
132
  return pbuf;
133
}
27✔
134

135
/* Default core NetIO handlers
136
 */
137

138
static void core_netio_abort_cb(pr_netio_stream_t *nstrm) {
139
  nstrm->strm_flags |= PR_NETIO_SESS_ABORT;
3✔
140
}
3✔
141

3✔
142
static int core_netio_close_cb(pr_netio_stream_t *nstrm) {
143
  int res = 0;
83✔
144

83✔
145
  if (nstrm->strm_fd != -1) {
146
    res = close(nstrm->strm_fd);
83✔
147
    nstrm->strm_fd = -1;
16✔
148

16✔
149
  } else {
150
    errno = EBADF;
151
    res = -1;
67✔
152
  }
67✔
153

154
  return res;
155
}
83✔
156

157
static pr_netio_stream_t *core_netio_open_cb(pr_netio_stream_t *nstrm, int fd,
158
    int mode) {
128✔
159

160
  nstrm->strm_fd = fd;
161

128✔
162
  /* The stream's strm_mode field does not need to be set, as it is set
163
   * by the NetIO layer's open() wrapper function.
164
   */
165

166
  return nstrm;
167
}
128✔
168

169
static int core_netio_poll_cb(pr_netio_stream_t *nstrm) {
170
  int res;
8✔
171
  fd_set rfds, *rfdsp, wfds, *wfdsp;
8✔
172
  struct timeval tval;
8✔
173

8✔
174
  FD_ZERO(&rfds);
175
  rfdsp = NULL;
136✔
176
  FD_ZERO(&wfds);
136✔
177
  wfdsp = NULL;
136✔
178

8✔
179
  if (nstrm->strm_mode == PR_NETIO_IO_RD) {
180
    if (nstrm->strm_fd >= 0) {
8✔
181
      FD_SET(nstrm->strm_fd, &rfds);
×
182
      rfdsp = &rfds;
×
183
    }
×
184

185
  } else {
186
    if (nstrm->strm_fd >= 0) {
187
      FD_SET(nstrm->strm_fd, &wfds);
8✔
188
      wfdsp = &wfds;
8✔
189
    }
8✔
190
  }
191

192
  tval.tv_sec = ((nstrm->strm_flags & PR_NETIO_SESS_INTR) ?
193
    nstrm->strm_interval : 60);
16✔
194
  tval.tv_usec = 0;
8✔
195

8✔
196
  res = select(nstrm->strm_fd + 1, rfdsp, wfdsp, NULL, &tval);
197
  while (res < 0) {
8✔
198
    int xerrno = errno;
8✔
199

×
200
    if (!(nstrm->strm_flags & PR_NETIO_SESS_INTR)) {
201
      /* Watch for EAGAIN, and handle it by delaying temporarily. */
×
202
      if (xerrno == EAGAIN) {
203
        errno = EINTR;
×
204
        pr_signals_handle();
×
205
        continue;
×
206
      }
×
207
    }
208

209
    errno = nstrm->strm_errno = xerrno;
210
    break;
×
211
  }
×
212

213
  return res;
214
}
8✔
215

216
static int core_netio_postopen_cb(pr_netio_stream_t *nstrm) {
217
  return 0;
3✔
218
}
3✔
219

220
static int core_netio_read_cb(pr_netio_stream_t *nstrm, char *buf,
221
    size_t buflen) {
×
222
  return read(nstrm->strm_fd, buf, buflen);
223
}
×
224

225
static pr_netio_stream_t *core_netio_reopen_cb(pr_netio_stream_t *nstrm, int fd,
226
    int mode) {
3✔
227

228
  if (nstrm->strm_fd != -1) {
229
    close(nstrm->strm_fd);
3✔
230
  }
×
231

232
  nstrm->strm_fd = fd;
233
  nstrm->strm_mode = mode;
3✔
234

3✔
235
  return nstrm;
236
}
3✔
237

238
static int core_netio_shutdown_cb(pr_netio_stream_t *nstrm, int how) {
239
  return shutdown(nstrm->strm_fd, how);
4✔
240
}
4✔
241

242
static int core_netio_write_cb(pr_netio_stream_t *nstrm, char *buf,
243
    size_t buflen) {
5✔
244
  return write(nstrm->strm_fd, buf, buflen);
245
}
5✔
246

247
static const char *netio_stream_mode(int strm_mode) {
248
  const char *modestr = "(unknown)";
407✔
249

407✔
250
  switch (strm_mode) {
251
    case PR_NETIO_IO_RD:
407✔
252
      modestr = "reading";
211✔
253
      break;
211✔
254

211✔
255
    case PR_NETIO_IO_WR:
256
      modestr = "writing";
191✔
257
      break;
191✔
258

191✔
259
    default:
260
      break;
261
  }
262

263
  return modestr;
264
}
407✔
265

266
/* NetIO API wrapper functions. */
267

268
void pr_netio_abort(pr_netio_stream_t *nstrm) {
269
  const char *nstrm_mode;
4✔
270

4✔
271
  if (nstrm == NULL) {
272
    errno = EINVAL;
4✔
273
    return;
1✔
274
  }
1✔
275

276
  nstrm_mode = netio_stream_mode(nstrm->strm_mode);
277

3✔
278
  switch (nstrm->strm_type) {
279
    case PR_NETIO_STRM_CTRL:
3✔
280
      if (ctrl_netio != NULL) {
1✔
281
        pr_trace_msg(trace_channel, 19,
1✔
282
          "using %s abort() for control %s stream",
×
283
          ctrl_netio->owner_name, nstrm_mode);
284
        (ctrl_netio->abort)(nstrm);
285

×
286
      } else {
287
        pr_trace_msg(trace_channel, 19,
288
          "using %s abort() for control %s stream",
1✔
289
          default_ctrl_netio->owner_name, nstrm_mode);
290
        (default_ctrl_netio->abort)(nstrm);
1✔
291
      }
1✔
292

293
      break;
294

295
    case PR_NETIO_STRM_DATA:
296
      if (data_netio != NULL) {
1✔
297
        pr_trace_msg(trace_channel, 19, "using %s abort() for data %s stream",
1✔
298
          data_netio->owner_name, nstrm_mode);
×
299
        (data_netio->abort)(nstrm);
300

×
301
      } else {
302
        pr_trace_msg(trace_channel, 19, "using %s abort() for data %s stream",
303
          default_data_netio->owner_name, nstrm_mode);
1✔
304
        (default_data_netio->abort)(nstrm);
1✔
305
      }
1✔
306
      break;
307

308
    case PR_NETIO_STRM_OTHR:
309
      if (othr_netio != NULL) {
1✔
310
        pr_trace_msg(trace_channel, 19, "using %s abort() for other %s stream",
1✔
311
          othr_netio->owner_name, nstrm_mode);
×
312
        (othr_netio->abort)(nstrm);
313

×
314
      } else {
315
        pr_trace_msg(trace_channel, 19, "using %s abort() for other %s stream",
316
          default_othr_netio->owner_name, nstrm_mode);
1✔
317
        (default_othr_netio->abort)(nstrm);
1✔
318
      }
1✔
319
      break;
320

321
    default:
322
      errno = EINVAL;
×
323
      return;
×
324
  }
×
325
}
326

327
int pr_netio_close(pr_netio_stream_t *nstrm) {
328
  int res = -1, xerrno = 0;
123✔
329
  const char *nstrm_mode;
123✔
330

123✔
331
  if (nstrm == NULL) {
332
    errno = EINVAL;
123✔
333
    return -1;
1✔
334
  }
1✔
335

336
  nstrm_mode = netio_stream_mode(nstrm->strm_mode);
337

122✔
338
  switch (nstrm->strm_type) {
339
    case PR_NETIO_STRM_CTRL:
122✔
340
      if (ctrl_netio != NULL) {
85✔
341
        pr_trace_msg(trace_channel, 19,
85✔
342
          "using %s close() for control %s stream", ctrl_netio->owner_name,
21✔
343
          nstrm_mode);
344
        res = (ctrl_netio->close)(nstrm);
345

21✔
346
      } else {
347
        pr_trace_msg(trace_channel, 19,
348
          "using %s close() for control %s stream",
64✔
349
          default_ctrl_netio->owner_name, nstrm_mode);
350
        res = (default_ctrl_netio->close)(nstrm);
64✔
351
      }
64✔
352
      xerrno = errno;
353
      break;
85✔
354

85✔
355
    case PR_NETIO_STRM_DATA:
356
      if (data_netio != NULL) {
26✔
357
        pr_trace_msg(trace_channel, 19, "using %s close() for data %s stream",
26✔
358
          data_netio->owner_name, nstrm_mode);
21✔
359
        res = (data_netio->close)(nstrm);
360

21✔
361
      } else {
362
        pr_trace_msg(trace_channel, 19, "using %s close() for data %s stream",
363
          default_data_netio->owner_name, nstrm_mode);
5✔
364
        res = (default_data_netio->close)(nstrm);
5✔
365
      }
5✔
366
      xerrno = errno;
367
      break;
26✔
368

26✔
369
    case PR_NETIO_STRM_OTHR:
370
      if (othr_netio != NULL) {
10✔
371
        pr_trace_msg(trace_channel, 19, "using %s close() for other %s stream",
10✔
372
          othr_netio->owner_name, nstrm_mode);
5✔
373
        res = (othr_netio->close)(nstrm);
374

5✔
375
      } else {
376
        pr_trace_msg(trace_channel, 19, "using %s close() for other %s stream",
377
          default_othr_netio->owner_name, nstrm_mode);
5✔
378
        res = (default_othr_netio->close)(nstrm);
5✔
379
      }
5✔
380
      xerrno = errno;
381
      break;
10✔
382

10✔
383
    default:
384
      errno = EPERM;
1✔
385
      return -1;
1✔
386
  }
1✔
387

388
  /* Make sure to scrub any buffered memory, too. */
389
  if (nstrm->strm_buf != NULL) {
390
    pr_buffer_t *pbuf;
121✔
391

26✔
392
    pbuf = nstrm->strm_buf;
393
    pr_memscrub(pbuf->buf, pbuf->buflen);
26✔
394
  }
26✔
395

396
  destroy_pool(nstrm->strm_pool);
397
  errno = xerrno;
121✔
398
  return res;
121✔
399
}
121✔
400

401
static int netio_lingering_close(pr_netio_stream_t *nstrm, long linger,
402
    int flags) {
10✔
403
  int res;
404
  const char *nstrm_mode;
10✔
405

10✔
406
  if (nstrm == NULL) {
407
    errno = EINVAL;
10✔
408
    return -1;
1✔
409
  }
1✔
410

411
  switch (nstrm->strm_type) {
412
    case PR_NETIO_STRM_CTRL:
9✔
413
    case PR_NETIO_STRM_DATA:
414
    case PR_NETIO_STRM_OTHR:
415
      break;
416

8✔
417
    default:
418
      errno = EPERM;
1✔
419
      return -1;
1✔
420
  }
1✔
421

422
  if (nstrm->strm_fd < 0) {
423
    /* Already closed. */
8✔
424
    return 0;
425
  }
426

427
  nstrm_mode = netio_stream_mode(nstrm->strm_mode);
428

3✔
429
  if (!(flags & NETIO_LINGERING_CLOSE_FL_NO_SHUTDOWN)) {
430
    pr_netio_shutdown(nstrm, 1);
3✔
431
  }
×
432

433
  if (nstrm->strm_fd >= 0) {
434
    struct timeval tv;
3✔
435
    fd_set rfds;
3✔
436
    time_t when = time(NULL) + linger;
3✔
437

3✔
438
    tv.tv_sec = linger;
439
    tv.tv_usec = 0L;
3✔
440

3✔
441
    /* Handle timers during reading, once selected for read this
442
     * should mean all buffers have been flushed and the receiving end
443
     * has closed.
444
     */
445
    while (TRUE) {
446
      run_schedule();
3✔
447

3✔
448
      FD_ZERO(&rfds);
449
      FD_SET(nstrm->strm_fd, &rfds);
54✔
450

3✔
451
      pr_trace_msg(trace_channel, 8,
452
        "lingering %lu %s before closing fd %d",
3✔
453
        (unsigned long) tv.tv_sec, tv.tv_sec != 1 ? "secs" : "sec",
454
        nstrm->strm_fd);
3✔
455

456
      res = select(nstrm->strm_fd+1, &rfds, NULL, NULL, &tv);
457
      if (res == -1) {
3✔
458
        if (errno == EINTR) {
3✔
459
          time_t now = time(NULL);
×
460
          pr_signals_handle();
×
461

×
462
          /* Still here? If the requested lingering interval hasn't passed,
463
           * continue lingering.  Reset the timeval struct's fields to
464
           * linger for the interval remaining in the given period of time.
465
           */
466
          if (now < when) {
467
            tv.tv_sec = when - now;
×
468
            tv.tv_usec = 0L;
×
469
            continue;
×
470
          }
×
471

472
        } else {
473
          nstrm->strm_errno = errno;
474
          return -1;
×
475
        }
×
476

477
      } else {
478
        if (FD_ISSET(nstrm->strm_fd, &rfds)) {
479
          pr_trace_msg(trace_channel, 8,
3✔
480
            "received data for reading on fd %d, ignoring", nstrm->strm_fd);
3✔
481
        }
482
      }
483

484
      break;
485
    }
3✔
486
  }
487

488
  pr_trace_msg(trace_channel, 8, "done lingering, closing fd %d",
489
    nstrm->strm_fd);
3✔
490

491
  switch (nstrm->strm_type) {
492
    case PR_NETIO_STRM_CTRL:
3✔
493
      if (ctrl_netio != NULL) {
1✔
494
        pr_trace_msg(trace_channel, 19,
1✔
495
          "using %s close() for control %s stream", ctrl_netio->owner_name,
1✔
496
          nstrm_mode);
497
        res = (ctrl_netio->close)(nstrm);
498

1✔
499
      } else {
500
        pr_trace_msg(trace_channel, 19,
501
          "using %s close() for control %s stream",
×
502
          default_ctrl_netio->owner_name, nstrm_mode);
503
        res = (default_ctrl_netio->close)(nstrm);
×
504
      }
×
505
      break;
506

507
    case PR_NETIO_STRM_DATA:
508
      if (data_netio != NULL) {
1✔
509
        pr_trace_msg(trace_channel, 19, "using %s close() for data %s stream",
1✔
510
          data_netio->owner_name, nstrm_mode);
1✔
511
        res = (data_netio->close)(nstrm);
512

1✔
513
      } else {
514
        pr_trace_msg(trace_channel, 19, "using %s close() for data %s stream",
515
          default_data_netio->owner_name, nstrm_mode);
×
516
        res = (default_data_netio->close)(nstrm);
×
517
      }
×
518
      break;
519

520
    case PR_NETIO_STRM_OTHR:
521
    default:
1✔
522
      if (othr_netio != NULL) {
523
        pr_trace_msg(trace_channel, 19, "using %s close() for other %s stream",
1✔
524
          othr_netio->owner_name, nstrm_mode);
1✔
525
        res = (othr_netio->close)(nstrm);
526

1✔
527
      } else {
528
        pr_trace_msg(trace_channel, 19, "using %s close() for other %s stream",
529
          default_othr_netio->owner_name, nstrm_mode);
×
530
        res = (default_othr_netio->close)(nstrm);
×
531
      }
×
532
      break;
533
  }
534

535
  return res;
536
}
537

538
int pr_netio_lingering_abort(pr_netio_stream_t *nstrm, long linger) {
539
  int res;
6✔
540

6✔
541
  if (nstrm == NULL) {
542
    errno = EINVAL;
6✔
543
    return -1;
1✔
544
  }
1✔
545

546
  switch (nstrm->strm_type) {
547
    case PR_NETIO_STRM_CTRL:
5✔
548
    case PR_NETIO_STRM_DATA:
549
    case PR_NETIO_STRM_OTHR:
550
      break;
551

4✔
552
    default:
553
      errno = EINVAL;
1✔
554
      return -1;
1✔
555
  }
1✔
556

557
  /* Send an appropriate response code down the stream asynchronously. */
558
  pr_response_send_async(R_426, _("Transfer aborted. Data connection closed."));
559

4✔
560
  pr_netio_shutdown(nstrm, 1);
561

4✔
562
  if (nstrm->strm_fd >= 0) {
563
    fd_set rs;
4✔
564
    struct timeval tv;
3✔
565

3✔
566
    /* Wait for just a little while for the shutdown to take effect. */
567
    tv.tv_sec = 0L;
568
    tv.tv_usec = 300000L;
3✔
569

3✔
570
    while (TRUE) {
571
      run_schedule();
3✔
572

3✔
573
      FD_ZERO(&rs);
574
      FD_SET(nstrm->strm_fd, &rs);
54✔
575

3✔
576
      res = select(nstrm->strm_fd+1, &rs, NULL, NULL, &tv);
577
      if (res == -1) {
3✔
578
        if (errno == EINTR) {
3✔
579
          pr_signals_handle();
×
580

×
581
          /* Linger some more. */
582
          tv.tv_sec = 0L;
583
          tv.tv_usec = 300000L;
×
584
          continue;
×
585
        }
×
586

587
        nstrm->strm_errno = errno;
588
        return -1;
×
589
      }
×
590

591
      break;
592
    }
3✔
593
  }
594

595
  nstrm->strm_flags |= PR_NETIO_SESS_ABORT;
596

4✔
597
  /* Now continue with a normal lingering close. */
598
  return netio_lingering_close(nstrm, linger,
599
    NETIO_LINGERING_CLOSE_FL_NO_SHUTDOWN);
4✔
600
}
601

602
int pr_netio_lingering_close(pr_netio_stream_t *nstrm, long linger) {
603
  return netio_lingering_close(nstrm, linger, 0);
6✔
604
}
6✔
605

606
pr_netio_stream_t *pr_netio_open(pool *parent_pool, int strm_type, int fd,
607
    int mode) {
130✔
608
  pr_netio_stream_t *nstrm = NULL, *res = NULL;
609
  const char *nstrm_mode;
130✔
610

130✔
611
  if (parent_pool == NULL) {
612
    errno = EINVAL;
130✔
613
    return NULL;
1✔
614
  }
1✔
615

616
  /* Create a new stream object, then pass that the NetIO open handler. */
617
  nstrm = netio_stream_alloc(parent_pool);
618
  nstrm_mode = netio_stream_mode(mode);
129✔
619

129✔
620
  switch (strm_type) {
621
    case PR_NETIO_STRM_CTRL:
129✔
622
      nstrm->strm_type = PR_NETIO_STRM_CTRL;
90✔
623
      nstrm->strm_mode = mode;
90✔
624

90✔
625
      if (ctrl_netio != NULL) {
626
        if (pr_table_add(nstrm->notes, pstrdup(nstrm->strm_pool, "core.netio"),
90✔
627
            ctrl_netio, sizeof(pr_netio_t *)) < 0) {
22✔
628
          pr_trace_msg(trace_channel, 9,
629
            "error stashing 'core.netio' note for ctrl stream: %s",
×
630
            strerror(errno));
631
        }
×
632
        pr_trace_msg(trace_channel, 19, "using %s open() for control %s stream",
633
          ctrl_netio->owner_name, nstrm_mode);
22✔
634
        res = (ctrl_netio->open)(nstrm, fd, mode);
22✔
635
        if (res != NULL) {
22✔
636
          res->strm_netio = ctrl_netio;
22✔
637
        }
22✔
638

639
      } else {
640
        if (pr_table_add(nstrm->notes, pstrdup(nstrm->strm_pool, "core.netio"),
641
            default_ctrl_netio, sizeof(pr_netio_t *)) < 0) {
68✔
642
          pr_trace_msg(trace_channel, 9,
643
            "error stashing 'core.netio' note for ctrl stream: %s",
×
644
            strerror(errno));
645
        }
×
646
        pr_trace_msg(trace_channel, 19, "using %s open() for control %s stream",
647
          default_ctrl_netio->owner_name, nstrm_mode);
68✔
648
        res = (default_ctrl_netio->open)(nstrm, fd, mode);
68✔
649
        if (res != NULL) {
68✔
650
          res->strm_netio = default_ctrl_netio;
68✔
651
        }
68✔
652
      }
653
      break;
654

655
    case PR_NETIO_STRM_DATA:
656
      nstrm->strm_type = PR_NETIO_STRM_DATA;
27✔
657
      nstrm->strm_mode = mode;
27✔
658

27✔
659
      if (data_netio != NULL) {
660
        if (pr_table_add(nstrm->notes, pstrdup(nstrm->strm_pool, "core.netio"),
27✔
661
            data_netio, sizeof(pr_netio_t *)) < 0) {
21✔
662
          pr_trace_msg(trace_channel, 9,
663
            "error stashing 'core.netio' note for data stream: %s",
×
664
            strerror(errno));
665
        }
×
666
        pr_trace_msg(trace_channel, 19, "using %s open() for data %s stream",
667
          data_netio->owner_name, nstrm_mode);
21✔
668
        res = (data_netio->open)(nstrm, fd, mode);
21✔
669
        if (res != NULL) {
21✔
670
          res->strm_netio = data_netio;
21✔
671
        }
21✔
672

673
      } else {
674
        if (pr_table_add(nstrm->notes, pstrdup(nstrm->strm_pool, "core.netio"),
675
            default_data_netio, sizeof(pr_netio_t *)) < 0) {
6✔
676
          pr_trace_msg(trace_channel, 9,
677
            "error stashing 'core.netio' note for data stream: %s",
×
678
            strerror(errno));
679
        }
×
680
        pr_trace_msg(trace_channel, 19, "using %s open() for data %s stream",
681
          default_data_netio->owner_name, nstrm_mode);
6✔
682
        res = (default_data_netio->open)(nstrm, fd, mode);
6✔
683
        if (res != NULL) {
6✔
684
          res->strm_netio = default_data_netio;
6✔
685
        }
6✔
686
      }
687
      break;
688

689
    case PR_NETIO_STRM_OTHR:
690
      nstrm->strm_type = PR_NETIO_STRM_OTHR;
11✔
691
      nstrm->strm_mode = mode;
11✔
692

11✔
693
      if (othr_netio != NULL) {
694
        if (pr_table_add(nstrm->notes, pstrdup(nstrm->strm_pool, "core.netio"),
11✔
695
            othr_netio, sizeof(pr_netio_t *)) < 0) {
5✔
696
          pr_trace_msg(trace_channel, 9,
697
            "error stashing 'core.netio' note for othr stream: %s",
×
698
            strerror(errno));
699
        }
×
700
        pr_trace_msg(trace_channel, 19, "using %s open() for other %s stream",
701
          othr_netio->owner_name, nstrm_mode);
5✔
702
        res = (othr_netio->open)(nstrm, fd, mode);
5✔
703
        if (res != NULL) {
5✔
704
          res->strm_netio = othr_netio;
5✔
705
        }
5✔
706

707
      } else {
708
        if (pr_table_add(nstrm->notes, pstrdup(nstrm->strm_pool, "core.netio"),
709
            default_othr_netio, sizeof(pr_netio_t *)) < 0) {
6✔
710
          pr_trace_msg(trace_channel, 9,
711
            "error stashing 'core.netio' note for othr stream: %s",
×
712
            strerror(errno));
713
        }
×
714
        pr_trace_msg(trace_channel, 19, "using %s open() for other %s stream",
715
          default_othr_netio->owner_name, nstrm_mode);
6✔
716
        res = (default_othr_netio->open)(nstrm, fd, mode);
6✔
717
        if (res != NULL) {
6✔
718
          res->strm_netio = default_othr_netio;
6✔
719
        }
6✔
720
      }
721
      break;
722

723
    default:
724
      destroy_pool(nstrm->strm_pool);
1✔
725
      errno = EINVAL;
1✔
726
      res = NULL;
1✔
727
  }
1✔
728

729
  return res;
730
}
731

732
pr_netio_stream_t *pr_netio_reopen(pr_netio_stream_t *nstrm, int fd, int mode) {
733
  pr_netio_stream_t *res;
5✔
734
  const char *nstrm_mode;
5✔
735

5✔
736
  if (nstrm == NULL) {
737
    errno = EINVAL;
5✔
738
    return NULL;
1✔
739
  }
1✔
740

741
  nstrm_mode = netio_stream_mode(mode);
742

4✔
743
  switch (nstrm->strm_type) {
744
    case PR_NETIO_STRM_CTRL:
4✔
745
      if (ctrl_netio != NULL) {
1✔
746
        pr_trace_msg(trace_channel, 19,
1✔
747
          "using %s reopen() for control %s stream", ctrl_netio->owner_name,
×
748
          nstrm_mode);
749
        res = (ctrl_netio->reopen)(nstrm, fd, mode);
750

×
751
      } else {
752
        pr_trace_msg(trace_channel, 19,
753
          "using %s reopen() for control %s stream",
1✔
754
          default_ctrl_netio->owner_name, nstrm_mode);
755
        res = (default_ctrl_netio->reopen)(nstrm, fd, mode);
1✔
756
      }
1✔
757
      return res;
758

759
    case PR_NETIO_STRM_DATA:
760
      if (data_netio != NULL) {
1✔
761
        pr_trace_msg(trace_channel, 19, "using %s reopen() for data %s stream",
1✔
762
          data_netio->owner_name, nstrm_mode);
×
763
        res = (data_netio->reopen)(nstrm, fd, mode);
764

×
765
      } else {
766
        pr_trace_msg(trace_channel, 19, "using %s reopen() for data %s stream",
767
          default_data_netio->owner_name, nstrm_mode);
1✔
768
        res = (default_data_netio->reopen)(nstrm, fd, mode);
1✔
769
      }
1✔
770
      return res;
771

772
    case PR_NETIO_STRM_OTHR:
773
      if (othr_netio != NULL) {
1✔
774
        pr_trace_msg(trace_channel, 19, "using %s reopen() for other %s stream",
1✔
775
          othr_netio->owner_name, nstrm_mode);
×
776
        res = (othr_netio->reopen)(nstrm, fd, mode);
777

×
778
      } else {
779
        pr_trace_msg(trace_channel, 19, "using %s reopen() for other %s stream",
780
          default_othr_netio->owner_name, nstrm_mode);
1✔
781
        res = (default_othr_netio->reopen)(nstrm, fd, mode);
1✔
782
      }
1✔
783
      return res;
784
  }
785

786
  errno = EPERM;
787
  return NULL;
1✔
788
}
1✔
789

790
void pr_netio_reset_poll_interval(pr_netio_stream_t *nstrm) {
791
  if (nstrm == NULL) {
16✔
792
    errno = EINVAL;
16✔
793
    return;
1✔
794
  }
1✔
795

796
  /* Simply clear the "interruptible" flag. */
797
  nstrm->strm_flags &= ~PR_NETIO_SESS_INTR;
798
}
15✔
799

800
void pr_netio_set_poll_interval(pr_netio_stream_t *nstrm, unsigned int secs) {
801
  if (nstrm == NULL) {
16✔
802
    return;
16✔
803
  }
804

805
  nstrm->strm_flags |= PR_NETIO_SESS_INTR;
806
  nstrm->strm_interval = secs;
15✔
807
}
15✔
808

809
int pr_netio_poll(pr_netio_stream_t *nstrm) {
810
  int res = 0, xerrno = 0;
78✔
811
  const char *nstrm_mode;
78✔
812

78✔
813
  /* Sanity checks. */
814
  if (nstrm == NULL) {
815
    errno = EINVAL;
78✔
816
    return -1;
1✔
817
  }
1✔
818

819
  if (nstrm->strm_fd < 0) {
820
    errno = EBADF;
77✔
821
    return -1;
1✔
822
  }
1✔
823

824
  /* Has this stream been aborted? */
825
  if (nstrm->strm_flags & PR_NETIO_SESS_ABORT) {
826
    nstrm->strm_flags &= ~PR_NETIO_SESS_ABORT;
76✔
827
    return 1;
1✔
828
  }
1✔
829

830
  nstrm_mode = netio_stream_mode(nstrm->strm_mode);
831

146✔
832
  while (TRUE) {
833
    run_schedule();
75✔
834
    pr_signals_handle();
75✔
835

75✔
836
    switch (nstrm->strm_type) {
837
      case PR_NETIO_STRM_CTRL:
75✔
838
        if (ctrl_netio != NULL) {
39✔
839
          pr_trace_msg(trace_channel, 19,
39✔
840
            "using %s poll() for control %s stream", ctrl_netio->owner_name,
33✔
841
            nstrm_mode);
842
          res = (ctrl_netio->poll)(nstrm);
843

33✔
844
        } else {
845
          pr_trace_msg(trace_channel, 19,
846
            "using %s poll() for control %s stream",
6✔
847
            default_ctrl_netio->owner_name, nstrm_mode);
848
          res = (default_ctrl_netio->poll)(nstrm);
6✔
849
        }
6✔
850
        break;
851

852
      case PR_NETIO_STRM_DATA:
853
        if (data_netio != NULL) {
28✔
854
          pr_trace_msg(trace_channel, 19,
28✔
855
            "using %s poll() for data %s stream", data_netio->owner_name,
27✔
856
            nstrm_mode);
857
          res = (data_netio->poll)(nstrm);
858

27✔
859
        } else {
860
          pr_trace_msg(trace_channel, 19,
861
            "using %s poll() for data %s stream",
1✔
862
            default_data_netio->owner_name, nstrm_mode);
863
          res = (default_data_netio->poll)(nstrm);
1✔
864
        }
1✔
865
        break;
866

867
      case PR_NETIO_STRM_OTHR:
868
        if (othr_netio != NULL) {
7✔
869
          pr_trace_msg(trace_channel, 19,
7✔
870
            "using %s poll() for other %s stream", othr_netio->owner_name,
6✔
871
            nstrm_mode);
872
          res = (othr_netio->poll)(nstrm);
873

6✔
874
        } else {
875
          pr_trace_msg(trace_channel, 19,
876
            "using %s poll() for other %s stream",
1✔
877
            default_othr_netio->owner_name, nstrm_mode);
878
          res = (default_othr_netio->poll)(nstrm);
1✔
879
        }
1✔
880
        break;
881
    }
882

883
    if (res == -1) {
884
      xerrno = errno;
75✔
885
      if (xerrno == EINTR) {
×
886
        if (nstrm->strm_flags & PR_NETIO_SESS_ABORT) {
×
887
          nstrm->strm_flags &= ~PR_NETIO_SESS_ABORT;
×
888
          return 1;
×
889
        }
×
890

891
        if (nstrm->strm_flags & PR_NETIO_SESS_INTR) {
892
          errno = nstrm->strm_errno = xerrno;
×
893

×
894
          /* Per SESS_INTR description in netio.h, return -2 here. */
895
          return -2;
896
        }
×
897

898
        /* Otherwise, restart the call */
899
        pr_signals_handle();
900
        continue;
×
901
      }
×
902

903
      /* Some other error occurred */
904
      nstrm->strm_errno = xerrno;
905

×
906
      /* If this is the control stream, and the error indicates a
907
       * broken pipe (i.e. the client went away), AND there is a data
908
       * transfer is progress, abort the transfer.
909
       */
910
      if (xerrno == EPIPE &&
911
          nstrm->strm_type == PR_NETIO_STRM_CTRL &&
×
912
          (session.sf_flags & SF_XFER)) {
×
913
        pr_trace_msg(trace_channel, 5,
×
914
          "received EPIPE on control connection, setting 'aborted' "
×
915
          "session flag");
916
        session.sf_flags |= SF_ABORT;
917
      }
×
918

919
      errno = nstrm->strm_errno;
920
      return -1;
×
921
    }
×
922

923
    if (res == 0) {
924
      /* In case the kernel doesn't support interrupted syscalls. */
75✔
925
      if (nstrm->strm_flags & PR_NETIO_SESS_ABORT) {
926
        nstrm->strm_flags &= ~PR_NETIO_SESS_ABORT;
1✔
927
        return 1;
×
928
      }
×
929

930
      /* If the stream has been marked as "interruptible", AND the
931
       * poll interval is zero seconds (meaning a true poll, not blocking),
932
       * then return here.
933
       */
934
      if ((nstrm->strm_flags & PR_NETIO_SESS_INTR) &&
935
          nstrm->strm_interval == 0) {
1✔
936
        errno = EOF;
1✔
937
        return -1;
1✔
938
      }
1✔
939

940
      continue;
941
    }
×
942

943
    break;
944
  }
945

946
  return 0;
947
}
948

949
int pr_netio_postopen(pr_netio_stream_t *nstrm) {
950
  int res;
4✔
951
  const char *nstrm_mode;
4✔
952

4✔
953
  if (nstrm == NULL) {
954
    errno = EINVAL;
4✔
955
    return -1;
1✔
956
  }
1✔
957

958
  nstrm_mode = netio_stream_mode(nstrm->strm_mode);
959

3✔
960
  switch (nstrm->strm_type) {
961
    case PR_NETIO_STRM_CTRL:
3✔
962
      if (ctrl_netio != NULL) {
1✔
963
        pr_trace_msg(trace_channel, 19,
1✔
964
          "using %s postopen() for control %s stream", ctrl_netio->owner_name,
×
965
          nstrm_mode);
966
        res = (ctrl_netio->postopen)(nstrm);
967

×
968
      } else {
969
        pr_trace_msg(trace_channel, 19,
970
          "using %s postopen() for control %s stream",
1✔
971
          default_ctrl_netio->owner_name, nstrm_mode);
972
        res = (default_ctrl_netio->postopen)(nstrm);
1✔
973
      }
1✔
974
      return res;
975

976
    case PR_NETIO_STRM_DATA:
977
      if (data_netio != NULL) {
1✔
978
        pr_trace_msg(trace_channel, 19,
1✔
979
          "using %s postopen() for data %s stream", data_netio->owner_name,
×
980
          nstrm_mode);
981
        res = (data_netio->postopen)(nstrm);
982

×
983
      } else {
984
        pr_trace_msg(trace_channel, 19,
985
          "using %s postopen() for data %s stream",
1✔
986
          default_data_netio->owner_name, nstrm_mode);
987
        res = (default_data_netio->postopen)(nstrm);
1✔
988
      }
1✔
989
      return res;
990

991
    case PR_NETIO_STRM_OTHR:
992
      if (othr_netio != NULL) {
1✔
993
        pr_trace_msg(trace_channel, 19,
1✔
994
          "using %s postopen() for other %s stream", othr_netio->owner_name,
×
995
          nstrm_mode);
996
        res = (othr_netio->postopen)(nstrm);
997

×
998
      } else {
999
        pr_trace_msg(trace_channel, 19,
1000
          "using %s postopen() for other %s stream",
1✔
1001
          default_othr_netio->owner_name, nstrm_mode);
1002
        res = (default_othr_netio->postopen)(nstrm);
1✔
1003
      }
1✔
1004
      return res;
1005
  }
1006

1007
  errno = EPERM;
1008
  return -1;
×
1009
}
×
1010

1011
int pr_netio_vprintf(pr_netio_stream_t *nstrm, const char *fmt, va_list msg) {
1012
  char buf[PR_RESPONSE_BUFFER_SIZE] = {'\0'};
20✔
1013

20✔
1014
  pr_vsnprintf(buf, sizeof(buf), fmt, msg);
1015
  buf[sizeof(buf)-1] = '\0';
20✔
1016

20✔
1017
  return pr_netio_write(nstrm, buf, strlen(buf));
1018
}
20✔
1019

1020
int pr_netio_printf(pr_netio_stream_t *nstrm, const char *fmt, ...) {
1021
  int res;
25✔
1022
  va_list msg;
25✔
1023

25✔
1024
  if (!nstrm) {
1025
    errno = EINVAL;
25✔
1026
    return -1;
5✔
1027
  }
5✔
1028

1029
  va_start(msg, fmt);
1030
  res = pr_netio_vprintf(nstrm, fmt, msg);
20✔
1031
  va_end(msg);
20✔
1032

20✔
1033
  return res;
1034
}
20✔
1035

1036
int pr_netio_printf_async(pr_netio_stream_t *nstrm, char *fmt, ...) {
1037
  va_list msg;
2✔
1038
  char buf[PR_RESPONSE_BUFFER_SIZE] = {'\0'};
2✔
1039

2✔
1040
  if (!nstrm) {
1041
    errno = EINVAL;
2✔
1042
    return -1;
1✔
1043
  }
1✔
1044

1045
  va_start(msg, fmt);
1046
  pr_vsnprintf(buf, sizeof(buf), fmt, msg);
1✔
1047
  va_end(msg);
1✔
1048
  buf[sizeof(buf)-1] = '\0';
1✔
1049

1✔
1050
  return pr_netio_write_async(nstrm, buf, strlen(buf));
1051
}
1✔
1052

1053
int pr_netio_write(pr_netio_stream_t *nstrm, char *buf, size_t buflen) {
1054
  int bwritten = 0, total = 0;
63✔
1055
  const char *nstrm_mode;
63✔
1056
  pr_buffer_t *pbuf;
63✔
1057
  pool *tmp_pool;
63✔
1058

63✔
1059
  /* Sanity check */
1060
  if (nstrm == NULL ||
1061
      buf == NULL ||
63✔
1062
      buflen == 0) {
63✔
1063
    errno = EINVAL;
1064
    return -1;
23✔
1065
  }
23✔
1066

1067
  if (nstrm->strm_fd < 0) {
1068
    errno = (nstrm->strm_errno ? nstrm->strm_errno : EBADF);
40✔
1069
    return -1;
4✔
1070
  }
4✔
1071

1072
  nstrm_mode = netio_stream_mode(nstrm->strm_mode);
1073

36✔
1074
  /* Before we send out the data to the client, generate an event
1075
   * for any listeners which may want to examine this data.  To do this, we
1076
   * need to allocate a pr_buffer_t for sending the buffer data to the
1077
   * listeners.
1078
   *
1079
   * We could just use nstrm->strm_pool, but for a long-lived control
1080
   * connection, this would amount to a slow memory increase.  So instead,
1081
   * we create a subpool from the stream's pool, and allocate the
1082
   * pr_buffer_t out of that.  Then simply destroy the subpool when done.
1083
   */
1084

1085
  tmp_pool = make_sub_pool(nstrm->strm_pool);
1086
  pbuf = pcalloc(tmp_pool, sizeof(pr_buffer_t));
36✔
1087
  pbuf->buf = buf;
36✔
1088
  pbuf->buflen = buflen;
36✔
1089
  pbuf->current = pbuf->buf;
36✔
1090
  pbuf->remaining = 0;
36✔
1091

36✔
1092
  switch (nstrm->strm_type) {
1093
    case PR_NETIO_STRM_CTRL:
36✔
1094
      pr_event_generate("core.ctrl-write", pbuf);
20✔
1095
      break;
20✔
1096

20✔
1097
    case PR_NETIO_STRM_DATA:
1098
      pr_event_generate("core.data-write", pbuf);
13✔
1099
      break;
13✔
1100

13✔
1101
    case PR_NETIO_STRM_OTHR:
1102
      pr_event_generate("core.othr-write", pbuf);
3✔
1103
      break;
3✔
1104
  }
3✔
1105

1106
  /* The event listeners may have changed the data to write out. */
1107
  buf = pbuf->buf;
1108
  buflen = pbuf->buflen - pbuf->remaining;
36✔
1109
  destroy_pool(tmp_pool);
36✔
1110

36✔
1111
  while (buflen) {
1112

61✔
1113
    switch (pr_netio_poll(nstrm)) {
1114
      case 1:
36✔
1115
        /* pr_netio_poll() returns 1 only if the stream has been aborted. */
×
1116
        errno = ECONNABORTED;
1117
        return -2;
×
1118

×
1119
      case -1:
1120
        return -1;
1121

1122
      default:
1123
        /* We have to potentially restart here as well, in case we get EINTR. */
×
1124
        do {
1125
          pr_signals_handle();
36✔
1126
          run_schedule();
36✔
1127

36✔
1128
          switch (nstrm->strm_type) {
1129
            case PR_NETIO_STRM_CTRL:
36✔
1130
              if (ctrl_netio != NULL) {
20✔
1131
                pr_trace_msg(trace_channel, 19,
20✔
1132
                  "using %s write() for control %s stream",
15✔
1133
                  ctrl_netio->owner_name, nstrm_mode);
1134
                bwritten = (ctrl_netio->write)(nstrm, buf, buflen);
1135

15✔
1136
              } else {
1137
                pr_trace_msg(trace_channel, 19,
1138
                  "using %s write() for control %s stream",
5✔
1139
                  default_ctrl_netio->owner_name, nstrm_mode);
1140
                bwritten = (default_ctrl_netio->write)(nstrm, buf, buflen);
5✔
1141
              }
5✔
1142
              break;
1143

1144
            case PR_NETIO_STRM_DATA:
1145
              if (XFER_ABORTED) {
13✔
1146
                break;
13✔
1147
              }
1148

1149
              if (data_netio != NULL) {
1150
                pr_trace_msg(trace_channel, 19,
13✔
1151
                  "using %s write() for data %s stream", data_netio->owner_name,
13✔
1152
                  nstrm_mode);
1153
                bwritten = (data_netio->write)(nstrm, buf, buflen);
1154

13✔
1155
              } else {
1156
                pr_trace_msg(trace_channel, 19,
1157
                  "using %s write() for data %s stream",
×
1158
                  default_data_netio->owner_name, nstrm_mode);
1159
                bwritten = (default_data_netio->write)(nstrm, buf, buflen);
×
1160
              }
×
1161
              break;
1162

1163
            case PR_NETIO_STRM_OTHR:
1164
              if (othr_netio != NULL) {
3✔
1165
                pr_trace_msg(trace_channel, 19,
3✔
1166
                  "using %s write() for other %s stream",
3✔
1167
                  othr_netio->owner_name, nstrm_mode);
1168
                bwritten = (othr_netio->write)(nstrm, buf, buflen);
1169

3✔
1170
              } else {
1171
                pr_trace_msg(trace_channel, 19,
1172
                  "using %s write() for other %s stream",
×
1173
                  default_othr_netio->owner_name, nstrm_mode);
1174
                bwritten = (default_othr_netio->write)(nstrm, buf, buflen);
×
1175
              }
×
1176
              break;
1177
          }
1178

1179
        } while (bwritten == -1 && errno == EINTR);
1180
        break;
36✔
1181
    }
36✔
1182

1183
    if (bwritten == -1) {
1184
      nstrm->strm_errno = errno;
36✔
1185
      return -1;
11✔
1186
    }
11✔
1187

1188
    buf += bwritten;
1189
    total += bwritten;
25✔
1190
    buflen -= bwritten;
25✔
1191
  }
25✔
1192

1193
  session.total_raw_out += total;
1194
  return total;
25✔
1195
}
25✔
1196

1197
int pr_netio_write_async(pr_netio_stream_t *nstrm, char *buf, size_t buflen) {
1198
  int bwritten = 0, flags = 0, total = 0;
6✔
1199
  const char *nstrm_mode;
6✔
1200
  pr_buffer_t *pbuf;
6✔
1201
  pool *tmp_pool;
6✔
1202

6✔
1203
  /* Sanity check */
1204
  if (nstrm == NULL) {
1205
    errno = EINVAL;
6✔
1206
    return -1;
1✔
1207
  }
1✔
1208

1209
  if (nstrm->strm_fd < 0) {
1210
    errno = (nstrm->strm_errno ? nstrm->strm_errno : EBADF);
5✔
1211
    return -1;
1✔
1212
  }
1✔
1213

1214
  /* Prepare the descriptor for nonblocking IO. */
1215
  flags = fcntl(nstrm->strm_fd, F_GETFL);
1216
  if (flags < 0) {
4✔
1217
    return -1;
4✔
1218
  }
1219

1220
  if (fcntl(nstrm->strm_fd, F_SETFL, flags|O_NONBLOCK) < 0) {
1221
    return -1;
4✔
1222
  }
1223

1224
  nstrm_mode = netio_stream_mode(nstrm->strm_mode);
1225

4✔
1226
  /* Before we send out the data to the client, generate an event
1227
   * for any listeners which may want to examine this data.
1228
   */
1229

1230
  tmp_pool = make_sub_pool(nstrm->strm_pool);
1231
  pbuf = pcalloc(tmp_pool, sizeof(pr_buffer_t));
4✔
1232
  pbuf->buf = buf;
4✔
1233
  pbuf->buflen = buflen;
4✔
1234
  pbuf->current = pbuf->buf;
4✔
1235
  pbuf->remaining = 0;
4✔
1236

4✔
1237
  switch (nstrm->strm_type) {
1238
    case PR_NETIO_STRM_CTRL:
4✔
1239
      pr_event_generate("core.ctrl-write", pbuf);
2✔
1240
      break;
2✔
1241

2✔
1242
    case PR_NETIO_STRM_DATA:
1243
      pr_event_generate("core.data-write", pbuf);
1✔
1244
      break;
1✔
1245

1✔
1246
    case PR_NETIO_STRM_OTHR:
1247
      pr_event_generate("core.othr-write", pbuf);
1✔
1248
      break;
1✔
1249
  }
1✔
1250

1251
  /* The event listeners may have changed the data to write out. */
1252
  buf = pbuf->buf;
1253
  buflen = pbuf->buflen - pbuf->remaining;
4✔
1254
  destroy_pool(tmp_pool);
4✔
1255

4✔
1256
  while (buflen) {
1257
    do {
8✔
1258

4✔
1259
      /* Do NOT check for XFER_ABORTED here.  After a client aborts a
1260
       * transfer, proftpd still needs to send the 426 response code back
1261
       * to the client via the control connection; checking for XFER_ABORTED
1262
       * here would block that response code sending, which in turn causes
1263
       * problems for clients waiting for that response code.
1264
       */
1265

1266
      pr_signals_handle();
1267

4✔
1268
      switch (nstrm->strm_type) {
1269
        case PR_NETIO_STRM_CTRL:
4✔
1270
          if (ctrl_netio != NULL) {
2✔
1271
            pr_trace_msg(trace_channel, 19,
2✔
1272
              "using %s write() for control %s stream", ctrl_netio->owner_name,
2✔
1273
              nstrm_mode);
1274
            bwritten = (ctrl_netio->write)(nstrm, buf, buflen);
1275

2✔
1276
          } else {
1277
            pr_trace_msg(trace_channel, 19,
1278
              "using %s write() for control %s stream",
×
1279
              default_ctrl_netio->owner_name, nstrm_mode);
1280
            bwritten = (default_ctrl_netio->write)(nstrm, buf, buflen);
×
1281
          }
×
1282
          break;
1283

1284
        case PR_NETIO_STRM_DATA:
1285
          if (data_netio != NULL) {
1✔
1286
            pr_trace_msg(trace_channel, 19,
1✔
1287
              "using %s write() for data %s stream", data_netio->owner_name,
1✔
1288
              nstrm_mode);
1289
            bwritten = (data_netio->write)(nstrm, buf, buflen);
1290

1✔
1291
          } else {
1292
            pr_trace_msg(trace_channel, 19,
1293
              "using %s write() for data %s stream",
×
1294
              default_data_netio->owner_name, nstrm_mode);
1295
            bwritten = (default_data_netio->write)(nstrm, buf, buflen);
×
1296
          }
×
1297
          break;
1298

1299
        case PR_NETIO_STRM_OTHR:
1300
          if (othr_netio != NULL) {
1✔
1301
            pr_trace_msg(trace_channel, 19,
1✔
1302
              "using %s write() for other %s stream", othr_netio->owner_name,
1✔
1303
              nstrm_mode);
1304
            bwritten = (othr_netio->write)(nstrm, buf, buflen);
1305

1✔
1306
          } else {
1307
            pr_trace_msg(trace_channel, 19,
1308
              "using %s write() for other %s stream",
×
1309
              default_othr_netio->owner_name, nstrm_mode);
1310
            bwritten = (default_othr_netio->write)(nstrm, buf, buflen);
×
1311
          }
×
1312
          break;
1313
      }
1314

1315
    } while (bwritten == -1 && errno == EINTR);
1316

4✔
1317
    if (bwritten < 0) {
1318
      nstrm->strm_errno = errno;
4✔
1319
      (void) fcntl(nstrm->strm_fd, F_SETFL, flags);
×
1320

×
1321
      if (nstrm->strm_errno == EWOULDBLOCK) {
1322
        /* Give up ... */
×
1323
        return total;
1324
      }
1325

1326
      return -1;
1327
    }
×
1328

1329
    buf += bwritten;
1330
    total += bwritten;
4✔
1331
    buflen -= bwritten;
4✔
1332
  }
4✔
1333

1334
  (void) fcntl(nstrm->strm_fd, F_SETFL, flags);
1335
  return total;
4✔
1336
}
4✔
1337

1338
int pr_netio_read(pr_netio_stream_t *nstrm, char *buf, size_t buflen,
1339
    int bufmin) {
56✔
1340
  int bread = 0, total = 0;
1341
  const char *nstrm_mode;
56✔
1342
  pr_buffer_t *pbuf;
56✔
1343
  pool *tmp_pool;
56✔
1344

56✔
1345
  /* Sanity check. */
1346
  if (nstrm == NULL ||
1347
      buf == NULL ||
56✔
1348
      buflen == 0) {
56✔
1349
    errno = EINVAL;
1350
    return -1;
14✔
1351
  }
14✔
1352

1353
  if (nstrm->strm_fd < 0) {
1354
    errno = (nstrm->strm_errno ? nstrm->strm_errno : EBADF);
42✔
1355
    return -1;
22✔
1356
  }
22✔
1357

1358
  if (bufmin < 1) {
1359
    bufmin = 1;
20✔
1360
  }
1361

1362
  if ((size_t) bufmin > buflen) {
1363
    bufmin = buflen;
20✔
1364
  }
×
1365

1366
  nstrm_mode = netio_stream_mode(nstrm->strm_mode);
1367

20✔
1368
  while (bufmin > 0) {
1369
    polling:
34✔
1370

20✔
1371
    switch (pr_netio_poll(nstrm)) {
1372
      case 1:
21✔
1373
        return -2;
1374

1375
      case -1:
1376
        return -1;
×
1377

×
1378
      default:
1379
        do {
×
1380
          pr_signals_handle();
21✔
1381

21✔
1382
          run_schedule();
1383

21✔
1384
          switch (nstrm->strm_type) {
1385
            case PR_NETIO_STRM_CTRL:
21✔
1386
              if (ctrl_netio != NULL) {
4✔
1387
                pr_trace_msg(trace_channel, 19,
4✔
1388
                  "using %s read() for control %s stream",
4✔
1389
                  ctrl_netio->owner_name, nstrm_mode);
1390
                bread = (ctrl_netio->read)(nstrm, buf, buflen);
1391

4✔
1392
              } else {
1393
                pr_trace_msg(trace_channel, 19,
1394
                  "using %s read() for control %s stream",
×
1395
                  default_ctrl_netio->owner_name, nstrm_mode);
1396
                bread = (default_ctrl_netio->read)(nstrm, buf, buflen);
×
1397
              }
×
1398
              break;
1399

1400
            case PR_NETIO_STRM_DATA:
1401
              if (XFER_ABORTED) {
14✔
1402
                break;
14✔
1403
              }
1404

1405
              if (data_netio != NULL) {
1406
                pr_trace_msg(trace_channel, 19,
14✔
1407
                  "using %s read() for data %s stream", data_netio->owner_name,
14✔
1408
                  nstrm_mode);
1409
                bread = (data_netio->read)(nstrm, buf, buflen);
1410

14✔
1411
              } else {
1412
                pr_trace_msg(trace_channel, 19,
1413
                  "using %s read() for data %s stream",
×
1414
                  default_data_netio->owner_name, nstrm_mode);
1415
                bread = (default_data_netio->read)(nstrm, buf, buflen);
×
1416
              }
×
1417
              break;
1418

1419
            case PR_NETIO_STRM_OTHR:
1420
              if (othr_netio != NULL) {
3✔
1421
                pr_trace_msg(trace_channel, 19,
3✔
1422
                  "using %s read() for other %s stream",
3✔
1423
                  othr_netio->owner_name, nstrm_mode);
1424
                bread = (othr_netio->read)(nstrm, buf, buflen);
1425

3✔
1426
              } else {
1427
                pr_trace_msg(trace_channel, 19,
1428
                  "using %s read() for other %s stream",
×
1429
                  default_othr_netio->owner_name, nstrm_mode);
1430
                bread = (default_othr_netio->read)(nstrm, buf, buflen);
×
1431
              }
×
1432
              break;
1433
          }
1434

1435
#ifdef EAGAIN
1436
          if (bread == -1 &&
1437
              errno == EAGAIN) {
21✔
1438
            int xerrno = EAGAIN;
4✔
1439

1✔
1440
            /* Treat this as an interrupted call, call pr_signals_handle()
1441
             * (which will delay for a few msecs because of EINTR), and try
1442
             * again.
1443
             *
1444
             * This should avoid a tightly spinning loop if read(2) returns
1445
             * EAGAIN, as on a data transfer (Bug#3639).
1446
             */
1447

1448
            errno = EINTR;
1449
            pr_signals_handle();
1✔
1450

1✔
1451
            errno = xerrno;
1452
            goto polling;
1✔
1453
          }
1✔
1454
#endif
1455

1456
        } while (bread == -1 && errno == EINTR);
1457
        break;
20✔
1458
    }
20✔
1459

1460
    if (bread == -1) {
1461
      nstrm->strm_errno = errno;
20✔
1462
      return -1;
3✔
1463
    }
3✔
1464

1465
    /* EOF? */
1466
    if (bread == 0) {
1467
      if (nstrm->strm_type == PR_NETIO_STRM_CTRL) {
17✔
1468
        pr_trace_msg(trace_channel, 7,
3✔
1469
          "read %d bytes from control stream fd %d, handling as EOF", bread,
1✔
1470
          nstrm->strm_fd);
1471
      }
1472

1473
      nstrm->strm_errno = 0;
1474
      errno = EOF;
3✔
1475
      break;
3✔
1476
    }
3✔
1477

1478
    /* Before we provide the data from the client, generate an event
1479
     * for any listeners which may want to examine this data.  To do this, we
1480
     * need to allocate a pr_buffer_t for sending the buffer data to the
1481
     * listeners.
1482
     *
1483
     * We could just use nstrm->strm_pool, but for a long-lived control
1484
     * connection, this would amount to a slow memory increase.  So instead,
1485
     * we create a subpool from the stream's pool, and allocate the
1486
     * pr_buffer_t out of that.  Then simply destroy the subpool when done.
1487
     */
1488

1489
    tmp_pool = make_sub_pool(nstrm->strm_pool);
1490
    pbuf = pcalloc(tmp_pool, sizeof(pr_buffer_t));
14✔
1491
    pbuf->buf = buf;
14✔
1492
    pbuf->buflen = bread;
14✔
1493
    pbuf->current = pbuf->buf;
14✔
1494
    pbuf->remaining = 0;
14✔
1495

14✔
1496
    switch (nstrm->strm_type) {
1497
      case PR_NETIO_STRM_CTRL:
14✔
1498
        pr_event_generate("core.ctrl-read", pbuf);
2✔
1499
        break;
2✔
1500

2✔
1501
      case PR_NETIO_STRM_DATA:
1502
        pr_event_generate("core.data-read", pbuf);
11✔
1503
        break;
11✔
1504

11✔
1505
      case PR_NETIO_STRM_OTHR:
1506
        pr_event_generate("core.othr-read", pbuf);
1✔
1507
        break;
1✔
1508
    }
1✔
1509

1510
    /* The event listeners may have changed the data read in out. */
1511
    buf = pbuf->buf;
1512
    bread = pbuf->buflen - pbuf->remaining;
14✔
1513
    destroy_pool(tmp_pool);
14✔
1514

14✔
1515
    buf += bread;
1516
    total += bread;
14✔
1517
    bufmin -= bread;
14✔
1518
    buflen -= bread;
14✔
1519
  }
14✔
1520

1521
  session.total_raw_in += total;
1522
  return total;
17✔
1523
}
17✔
1524

1525
int pr_netio_shutdown(pr_netio_stream_t *nstrm, int how) {
1526
  int res = -1;
9✔
1527
  const char *nstrm_mode;
9✔
1528

9✔
1529
  if (nstrm == NULL) {
1530
    errno = EINVAL;
9✔
1531
    return -1;
1✔
1532
  }
1✔
1533

1534
  nstrm_mode = netio_stream_mode(nstrm->strm_mode);
1535

8✔
1536
  switch (nstrm->strm_type) {
1537
    case PR_NETIO_STRM_CTRL:
8✔
1538
      if (ctrl_netio != NULL) {
3✔
1539
        pr_trace_msg(trace_channel, 19,
3✔
1540
          "using %s shutdown() for control %s stream", ctrl_netio->owner_name,
2✔
1541
          nstrm_mode);
1542
        res = (ctrl_netio->shutdown)(nstrm, how);
1543

2✔
1544
      } else {
1545
        pr_trace_msg(trace_channel, 19,
1546
          "using %s shutdown() for control %s stream",
1✔
1547
          default_ctrl_netio->owner_name, nstrm_mode);
1548
        res = (default_ctrl_netio->shutdown)(nstrm, how);
1✔
1549
      }
1✔
1550
      break;
1551

1552
    case PR_NETIO_STRM_DATA:
1553
      if (data_netio != NULL) {
2✔
1554
        pr_trace_msg(trace_channel, 19,
2✔
1555
          "using %s shutdown() for data %s stream", data_netio->owner_name,
2✔
1556
          nstrm_mode);
1557
        res = (data_netio->shutdown)(nstrm, how);
1558

2✔
1559
      } else {
1560
        pr_trace_msg(trace_channel, 19,
1561
          "using %s shutdown() for data %s stream",
×
1562
          default_data_netio->owner_name, nstrm_mode);
1563
        res = (default_data_netio->shutdown)(nstrm, how);
×
1564
      }
×
1565
      break;
1566

1567
    case PR_NETIO_STRM_OTHR:
1568
      if (othr_netio != NULL) {
2✔
1569
        pr_trace_msg(trace_channel, 19,
2✔
1570
          "using %s shutdown() for other %s stream", othr_netio->owner_name,
2✔
1571
          nstrm_mode);
1572
        res = (othr_netio->shutdown)(nstrm, how);
1573

2✔
1574
      } else {
1575
        pr_trace_msg(trace_channel, 19,
1576
          "using %s shutdown() for other %s stream",
×
1577
          default_othr_netio->owner_name, nstrm_mode);
1578
        res = (default_othr_netio->shutdown)(nstrm, how);
×
1579
      }
×
1580
      break;
1581

1582
    default:
1583
      errno = EINVAL;
1✔
1584
      return -1;
1✔
1585
  }
1✔
1586

1587
  return res;
1588
}
1589

1590
char *pr_netio_gets(char *buf, size_t buflen, pr_netio_stream_t *nstrm) {
1591
  char *bp = buf;
4✔
1592
  int toread;
4✔
1593
  pr_buffer_t *pbuf = NULL;
4✔
1594

4✔
1595
  if (nstrm == NULL ||
1596
      buf == NULL ||
4✔
1597
      buflen == 0) {
4✔
1598
    errno = EINVAL;
1599
    return NULL;
3✔
1600
  }
3✔
1601

1602
  buflen--;
1603

1✔
1604
  if (nstrm->strm_buf) {
1605
    pbuf = nstrm->strm_buf;
1✔
1606

1✔
1607
  } else {
1608
    pbuf = pr_netio_buffer_alloc(nstrm);
1609
  }
1✔
1610

1611
  while (buflen) {
1612

1✔
1613
    /* Is the buffer empty? */
1614
    if (!pbuf->current ||
1615
        pbuf->remaining == pbuf->buflen) {
1✔
1616

1✔
1617
      toread = pr_netio_read(nstrm, pbuf->buf,
1618
        (buflen < pbuf->buflen ?  buflen : pbuf->buflen), 1);
2✔
1619

1✔
1620
      if (toread <= 0) {
1621
        if (bp != buf) {
1✔
1622
          *bp = '\0';
×
1623
          return buf;
×
1624
        }
×
1625

1626
        return NULL;
1627
      }
1628

1629
      pbuf->remaining = pbuf->buflen - toread;
1630
      pbuf->current = pbuf->buf;
1✔
1631

1✔
1632
      pbuf->remaining = pbuf->buflen - toread;
1633
      pbuf->current = pbuf->buf;
1✔
1634

1✔
1635
      /* Before we begin iterating through the data read in from the
1636
       * network, generate an event for any listeners which may want to
1637
       * examine this data as well.
1638
       */
1639
      pr_event_generate("core.othr-read", pbuf);
1640
    }
1✔
1641

1642
    toread = pbuf->buflen - pbuf->remaining;
1643

1✔
1644
    while (buflen && *pbuf->current != '\n' && toread--) {
1645
      if (*pbuf->current & 0x80) {
15✔
1646
        pbuf->current++;
14✔
1647

×
1648
      } else {
1649
        *bp++ = *pbuf->current++;
1650
        buflen--;
14✔
1651
      }
14✔
1652
      pbuf->remaining++;
1653
    }
14✔
1654

1655
    if (buflen && toread && *pbuf->current == '\n') {
1656
      buflen--;
1✔
1657
      toread--;
1✔
1658
      *bp++ = *pbuf->current++;
1✔
1659
      pbuf->remaining++;
1✔
1660
      break;
1✔
1661
    }
1✔
1662

1663
    if (!toread) {
1664
      pbuf->current = NULL;
×
1665
    }
×
1666
  }
1667

1668
  *bp = '\0';
1669
  return buf;
1✔
1670
}
1✔
1671

1672
static int telnet_mode = 0;
1673

1674
int pr_netio_telnet_gets2(char *buf, size_t bufsz,
1675
    pr_netio_stream_t *in_nstrm, pr_netio_stream_t *out_nstrm) {
31✔
1676
  char *bp = buf;
1677
  unsigned char cp;
31✔
1678
  int toread, handle_iac = TRUE, saw_newline = FALSE;
31✔
1679
  pr_buffer_t *pbuf = NULL;
31✔
1680
  size_t buflen = bufsz;
31✔
1681

31✔
1682
  if (buflen == 0 ||
1683
      in_nstrm == NULL ||
31✔
1684
      out_nstrm == NULL) {
31✔
1685
    errno = EINVAL;
1686
    return -1;
4✔
1687
  }
4✔
1688

1689
#if defined(PR_USE_NLS)
1690
  handle_iac = pr_encode_supports_telnet_iac();
1691
#endif /* PR_USE_NLS */
27✔
1692

1693
  buflen--;
1694

27✔
1695
  if (in_nstrm->strm_buf != NULL) {
1696
    pbuf = in_nstrm->strm_buf;
27✔
1697

27✔
1698
  } else {
1699
    pbuf = pr_netio_buffer_alloc(in_nstrm);
1700
  }
×
1701

1702
  while (buflen > 0) {
1703
    pr_signals_handle();
50✔
1704

48✔
1705
    /* Is the buffer empty? */
1706
    if (pbuf->current == NULL ||
1707
        pbuf->remaining == pbuf->buflen) {
48✔
1708

27✔
1709
      toread = pr_netio_read(in_nstrm, pbuf->buf,
1710
        (buflen < pbuf->buflen ? buflen : pbuf->buflen), 1);
42✔
1711

21✔
1712
      if (toread <= 0) {
1713
        if (bp != buf) {
21✔
1714
          *bp = '\0';
21✔
1715
          return (bufsz - buflen - 1);
21✔
1716
        }
21✔
1717

1718
        return -1;
1719
      }
1720

1721
      pbuf->remaining = pbuf->buflen - toread;
1722
      pbuf->current = pbuf->buf;
×
1723

×
1724
      /* Before we begin iterating through the data read in from the
1725
       * network, handing any Telnet characters and such, generate an event
1726
       * for any listeners which may want to examine this data as well.
1727
       */
1728
      pr_event_generate("core.ctrl-read", pbuf);
1729
    }
×
1730

1731
    toread = pbuf->buflen - pbuf->remaining;
1732

27✔
1733
    /* If we do not encounter an LF, or, if we DO encounter an LF (and the
1734
     * character before the LF is not a CR), we copy the encountered character
1735
     * as is into our output buffer, handling the Telnet IAC codes as necessary.
1736
     */
1737
    while (buflen > 0 &&
1738
           toread > 0 &&
27✔
1739
           (*pbuf->current != '\n' ||
174,931✔
1740
            (pbuf->current > pbuf->buf && *(pbuf->current - 1) != '\r')) &&
87,454✔
1741
           toread--) {
87,843✔
1742
      pr_signals_handle();
87,450✔
1743

87,450✔
1744
      /* Note that `cp` here is declared to be `unsigned char`; this is where
1745
       * the signedness of the bytes in the input buffer is changed to unsigned.
1746
       */
1747
      cp = *pbuf->current++;
1748
      pbuf->remaining++;
87,450✔
1749

87,450✔
1750
      if (handle_iac == TRUE) {
1751
        switch (telnet_mode) {
87,450✔
1752
          case TELNET_IAC:
87,450✔
1753
            switch (cp) {
326✔
1754
              case TELNET_WILL:
326✔
1755
              case TELNET_WONT:
16✔
1756
              case TELNET_DO:
1757
              case TELNET_DONT:
1758
              case TELNET_IP:
1759
              case TELNET_DM:
1760
                /* Why do we do this crazy thing where we set the "telnet mode"
1761
                 * to be the action, and let the while loop, on the next pass,
1762
                 * handle that action?  It's because we don't know, right now,
1763
                 * whether there actually a "next byte" in the input buffer.
1764
                 * There _should_ be -- but we can't be sure.  And that next
1765
                 * byte is needed for properly responding with WONT/DONT
1766
                 * responses.
1767
                 */
1768
                telnet_mode = cp;
1769
                continue;
16✔
1770

16✔
1771
              case TELNET_IAC:
1772
                /* In this case, we know that the previous byte was TELNET_IAC,
3✔
1773
                 * and that the current byte is another TELNET_IAC.  The
1774
                 * first TELNET_IAC thus "escapes" the second, telling us
1775
                 * that the current byte (TELNET_IAC) should be written out
1776
                 * as is (Bug#3697).
1777
                 */
1778
                telnet_mode = 0;
1779
                break;
3✔
1780

3✔
1781
              default:
1782
                /* In this case, we know that the previous byte was TELNET_IAC,
307✔
1783
                 * but the current byte is not a value we care about.  So
1784
                 * write the TELNET_IAC into the output buffer, break out of
1785
                 * of the switch, and let that handle the writing of the
1786
                 * current byte into the output buffer.
1787
                 */
1788
                *bp++ = (char) TELNET_IAC;
1789
                buflen--;
307✔
1790

307✔
1791
                telnet_mode = 0;
1792
                break;
307✔
1793
            }
307✔
1794
            break;
1795

1796
          case TELNET_WILL:
1797
          case TELNET_WONT:
4✔
1798
            pr_netio_printf(out_nstrm, "%c%c%c", TELNET_IAC, TELNET_DONT, cp);
1799
            telnet_mode = 0;
4✔
1800
            continue;
4✔
1801

4✔
1802
          case TELNET_DO:
1803
          case TELNET_DONT:
4✔
1804
            pr_netio_printf(out_nstrm, "%c%c%c", TELNET_IAC, TELNET_WONT, cp);
1805
            telnet_mode = 0;
4✔
1806
            continue;
4✔
1807

4✔
1808
          case TELNET_IP:
1809
          case TELNET_DM:
87,116✔
1810
          default:
1811
            if (cp == TELNET_IAC) {
1812
              telnet_mode = cp;
87,116✔
1813
              continue;
326✔
1814
            }
326✔
1815
            break;
1816
        }
1817
      }
1818

1819
      /* In the situation where the previous byte was an IAC, we wrote IAC
1820
       * into the output buffer, and decremented buflen (size of the output
1821
       * buffer remaining).  Thus we need to check here if buflen is zero,
1822
       * before trying to decrement buflen again (and possibly underflowing
1823
       * the buflen size_t data type).
1824
       */
1825
      if (buflen == 0) {
1826
        break;
87,100✔
1827
      }
1828

1829
      *bp++ = cp;
1830
      buflen--;
87,100✔
1831
    }
87,100✔
1832

1833
    if (buflen > 0 &&
1834
        toread > 0 &&
27✔
1835
        *pbuf->current == '\n') {
27✔
1836

4✔
1837
      /* If the current character is LF, and the previous character we
1838
       * copied was a CR, then strip the CR by overwriting it with the LF,
1839
       * turning the copied data from Telnet CRLF line termination to
1840
       * Unix LF line termination.
1841
       */
1842
      if (bp > buf &&
1843
          *(bp-1) == '\r') {
4✔
1844
        /* We already decrement the buffer length for the CR; no need to
4✔
1845
         * do it again since we are overwriting that CR.
1846
         */
1847
        *(bp-1) = *pbuf->current++;
1848

4✔
1849
      } else {
1850
        *bp++ = *pbuf->current++;
1851
        buflen--;
×
1852
      }
×
1853

1854
      pbuf->remaining++;
1855
      toread--;
4✔
1856
      saw_newline = TRUE;
4✔
1857
      break;
4✔
1858
    }
4✔
1859

1860
    if (toread == 0) {
1861
      /* No more input?  Set pbuf->current to null, so that at the top of
23✔
1862
       * the loop, we read more.
1863
       */
1864
      pbuf->current = NULL;
1865
    }
21✔
1866
  }
1867

1868
  if (saw_newline == FALSE) {
1869
    /* If we haven't seen a newline, then assume the client is deliberately
6✔
1870
     * sending a too-long command, trying to exploit buffer sizes and make
1871
     * the server make some possibly bad assumptions.
1872
     */
1873

1874
    properly_terminated_prev_command = FALSE;
1875
    errno = E2BIG;
2✔
1876
    return -1;
2✔
1877
  }
2✔
1878

1879
  if (properly_terminated_prev_command == FALSE) {
1880
    properly_terminated_prev_command = TRUE;
4✔
1881
    pr_log_pri(PR_LOG_NOTICE, "client sent too-long command, ignoring");
×
1882
    errno = E2BIG;
×
1883
    return -1;
×
1884
  }
×
1885

1886
  properly_terminated_prev_command = TRUE;
1887
  *bp = '\0';
4✔
1888
  return (bufsz - buflen - 1);
4✔
1889
}
4✔
1890

1891
char *pr_netio_telnet_gets(char *buf, size_t bufsz,
1892
    pr_netio_stream_t *in_nstrm, pr_netio_stream_t *out_nstrm) {
27✔
1893
  int res;
1894

27✔
1895
  res = pr_netio_telnet_gets2(buf, bufsz, in_nstrm, out_nstrm);
1896
  if (res < 0) {
27✔
1897
    return NULL;
27✔
1898
  }
6✔
1899

1900
  return buf;
1901
}
1902

1903
int pr_register_netio(pr_netio_t *netio, int strm_types) {
1904

152✔
1905
  if (netio == NULL) {
1906
    pr_netio_t *default_netio = NULL;
152✔
1907

106✔
1908
    /* Only instantiate the default NetIO objects once, reusing the same
1909
     * pointer.
1910
     */
1911
    if (default_ctrl_netio == NULL) {
1912
      default_ctrl_netio = default_netio = pr_alloc_netio2(permanent_pool,
106✔
1913
        NULL, NULL);
106✔
1914
    }
1915

1916
    if (default_data_netio == NULL) {
1917
      default_data_netio = default_netio ? default_netio :
106✔
1918
        (default_netio = pr_alloc_netio2(permanent_pool, NULL, NULL));
106✔
1919
    }
×
1920

1921
    if (default_othr_netio == NULL) {
1922
      default_othr_netio = default_netio ? default_netio :
106✔
1923
        (default_netio = pr_alloc_netio2(permanent_pool, NULL, NULL));
106✔
1924
    }
×
1925

1926
    return 0;
1927
  }
106✔
1928

1929
  if (netio->abort == NULL ||
1930
      netio->close == NULL ||
46✔
1931
      netio->open == NULL ||
45✔
1932
      netio->poll == NULL ||
44✔
1933
      netio->postopen == NULL ||
43✔
1934
      netio->read == NULL ||
42✔
1935
      netio->reopen == NULL ||
41✔
1936
      netio->shutdown == NULL ||
40✔
1937
      netio->write == NULL) {
39✔
1938
    errno = EINVAL;
38✔
1939
    return -1;
9✔
1940
  }
9✔
1941

1942
  if (strm_types & PR_NETIO_STRM_CTRL) {
1943
    ctrl_netio = netio;
37✔
1944
  }
19✔
1945

1946
  if (strm_types & PR_NETIO_STRM_DATA) {
1947
    data_netio = netio;
37✔
1948
  }
13✔
1949

1950
  if (strm_types & PR_NETIO_STRM_OTHR) {
1951
    othr_netio = netio;
37✔
1952
  }
5✔
1953

1954
  return 0;
1955
}
1956

1957
int pr_unregister_netio(int strm_types) {
1958
  if (strm_types == 0) {
131✔
1959
    errno = EINVAL;
131✔
1960
    return -1;
1✔
1961
  }
1✔
1962

1963
  /* NOTE: consider using cleanups here in the future? */
1964

1965
  if (strm_types & PR_NETIO_STRM_CTRL) {
1966
    ctrl_netio = NULL;
130✔
1967
  }
119✔
1968

1969
  if (strm_types & PR_NETIO_STRM_DATA) {
1970
    data_netio = NULL;
130✔
1971
  }
78✔
1972

1973
  if (strm_types & PR_NETIO_STRM_OTHR) {
1974
    othr_netio = NULL;
130✔
1975
  }
57✔
1976

1977
  return 0;
1978
}
1979

1980
pr_netio_t *pr_get_netio(int strm_type) {
1981
  pr_netio_t *netio = NULL;
8✔
1982

8✔
1983
  if (strm_type == 0) {
1984
    errno = EINVAL;
8✔
1985
    return NULL;
1✔
1986
  }
1✔
1987

1988
  switch (strm_type) {
1989
    case PR_NETIO_STRM_CTRL:
7✔
1990
      netio = ctrl_netio;
2✔
1991
      break;
2✔
1992

2✔
1993
    case PR_NETIO_STRM_DATA:
1994
      netio = data_netio;
2✔
1995
      break;
2✔
1996

2✔
1997
    case PR_NETIO_STRM_OTHR:
1998
      netio = othr_netio;
2✔
1999
      break;
2✔
2000

2✔
2001
    default:
2002
      errno = ENOENT;
1✔
2003
  }
1✔
2004

2005
  return netio;
2006
}
2007

2008
extern pid_t mpid;
2009

2010
pr_netio_t *pr_alloc_netio2(pool *parent_pool, module *owner,
2011
    const char *owner_name) {
136✔
2012
  pr_netio_t *netio = NULL;
2013
  pool *netio_pool = NULL;
136✔
2014

136✔
2015
  if (parent_pool == NULL) {
2016
    errno = EINVAL;
136✔
2017
    return NULL;
2✔
2018
  }
2✔
2019

2020
  netio_pool = make_sub_pool(parent_pool);
2021

134✔
2022
  /* If this is the daemon process, we are allocating a sub-pool from the
2023
   * permanent_pool.  You might wonder why the daemon process needs netio
2024
   * objects.  It doesn't, really -- but it's for use by all of the session
2025
   * processes that will be forked.  They will be able to reuse the memory
2026
   * already allocated for the main ctrl/data/other netios, as is.
2027
   *
2028
   * This being the case, we should label the sub-pool accordingly.
2029
   */
2030
  if (mpid == getpid()) {
2031
    pr_pool_tag(netio_pool, "Shared Netio Pool");
134✔
2032

×
2033
  } else {
2034
    pr_pool_tag(netio_pool, "netio pool");
2035
  }
134✔
2036

2037
  netio = pcalloc(netio_pool, sizeof(pr_netio_t));
2038
  netio->pool = netio_pool;
134✔
2039
  netio->owner = owner;
134✔
2040

134✔
2041
  if (owner != NULL) {
2042
    if (owner_name != NULL) {
134✔
2043
      netio->owner_name = pstrdup(netio_pool, owner_name);
×
2044

×
2045
    } else {
2046
      netio->owner_name = pstrdup(netio_pool, owner->name);
2047
    }
×
2048

2049
  } else {
2050
    if (owner_name != NULL) {
2051
      netio->owner_name = owner_name;
134✔
2052

27✔
2053
    } else {
2054
      netio->owner_name = "default";
2055
    }
107✔
2056
  }
2057

2058
  /* Set the default NetIO handlers to the core handlers. */
2059
  netio->abort = core_netio_abort_cb;
2060
  netio->close = core_netio_close_cb;
134✔
2061
  netio->open = core_netio_open_cb;
134✔
2062
  netio->poll = core_netio_poll_cb;
134✔
2063
  netio->postopen = core_netio_postopen_cb;
134✔
2064
  netio->read = core_netio_read_cb;
134✔
2065
  netio->reopen = core_netio_reopen_cb;
134✔
2066
  netio->shutdown = core_netio_shutdown_cb;
134✔
2067
  netio->write = core_netio_write_cb;
134✔
2068

134✔
2069
  return netio;
2070
}
134✔
2071

2072
pr_netio_t *pr_alloc_netio(pool *parent_pool) {
2073
  return pr_alloc_netio2(parent_pool, NULL, NULL);
2✔
2074
}
2✔
2075

2076
void init_netio(void) {
2077
  signal(SIGPIPE, SIG_IGN);
106✔
2078
  signal(SIGURG, SIG_IGN);
106✔
2079

106✔
2080
  pr_register_netio(NULL, 0);
2081
}
106✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc