Asterisk - The Open Source Telephony Project  21.4.1
Macros | Typedefs | Functions
iostream.h File Reference

Generic abstraction for input/output streams. More...

#include "asterisk.h"

Go to the source code of this file.

Macros

#define DO_SSL   /* comment in/out if you want to support ssl */
 

Typedefs

typedef struct ssl_st SSL
 
typedef struct ssl_ctx_st SSL_CTX
 

Functions

int ast_iostream_close (struct ast_iostream *stream)
 Close an iostream. More...
 
ssize_t ast_iostream_discard (struct ast_iostream *stream, size_t count)
 Discard the specified number of bytes from an iostream. More...
 
struct ast_iostreamast_iostream_from_fd (int *fd)
 Create an iostream from a file descriptor. More...
 
int ast_iostream_get_fd (struct ast_iostream *stream)
 Get an iostream's file descriptor. More...
 
SSL * ast_iostream_get_ssl (struct ast_iostream *stream)
 Get a pointer to an iostream's OpenSSL SSL structure. More...
 
ssize_t ast_iostream_gets (struct ast_iostream *stream, char *buffer, size_t size)
 Read a LF-terminated string from an iostream. More...
 
void ast_iostream_nonblock (struct ast_iostream *stream)
 Make an iostream non-blocking. More...
 
ssize_t ast_iostream_printf (struct ast_iostream *stream, const char *format,...)
 Write a formatted string to an iostream. More...
 
ssize_t ast_iostream_read (struct ast_iostream *stream, void *buffer, size_t count)
 Read data from an iostream. More...
 
void ast_iostream_set_exclusive_input (struct ast_iostream *stream, int exclusive_input)
 Set the iostream if it can exclusively depend upon the set timeouts. More...
 
int ast_iostream_set_sni_hostname (struct ast_iostream *stream, const char *sni_hostname)
 Set the iostream's SNI hostname for TLS client connections. More...
 
void ast_iostream_set_timeout_disable (struct ast_iostream *stream)
 Disable the iostream timeout timer. More...
 
void ast_iostream_set_timeout_idle_inactivity (struct ast_iostream *stream, int timeout, int timeout_reset)
 Set the iostream inactivity & idle timeout timers. More...
 
void ast_iostream_set_timeout_inactivity (struct ast_iostream *stream, int timeout)
 Set the iostream inactivity timeout timer. More...
 
void ast_iostream_set_timeout_sequence (struct ast_iostream *stream, struct timeval start, int timeout)
 Set the iostream I/O sequence timeout timer. More...
 
int ast_iostream_start_tls (struct ast_iostream **stream, SSL_CTX *ctx, int client)
 Begin TLS on an iostream. More...
 
int ast_iostream_wait_for_input (struct ast_iostream *stream, int timeout)
 Wait for input on the iostream's file descriptor. More...
 
ssize_t ast_iostream_write (struct ast_iostream *stream, const void *buffer, size_t count)
 Write data to an iostream. More...
 

Detailed Description

Generic abstraction for input/output streams.

Definition in file iostream.h.

Function Documentation

int ast_iostream_close ( struct ast_iostream stream)

Close an iostream.

Parameters
streamA pointer to an iostream
Return values
0success
-1failure
Note
On failure, errno may be set providing additional information on why the failure occurred.

Definition at line 539 of file iostream.c.

Referenced by ast_tcptls_close_session_file(), ast_websocket_close(), generic_http_callback(), and session_destroy_fn().

540 {
541  if (!stream) {
542  errno = EBADF;
543  return -1;
544  }
545 
546  if (stream->fd != -1) {
547 #if defined(DO_SSL)
548  if (stream->ssl) {
549  int res;
550 
551  /*
552  * According to the TLS standard, it is acceptable for an
553  * application to only send its shutdown alert and then
554  * close the underlying connection without waiting for
555  * the peer's response (this way resources can be saved,
556  * as the process can already terminate or serve another
557  * connection).
558  */
559  res = SSL_shutdown(stream->ssl);
560  if (res < 0) {
561  int sslerr = SSL_get_error(stream->ssl, res);
562  char err[256];
563  ast_log(LOG_ERROR, "SSL_shutdown() failed: %s, %s\n",
564  ERR_error_string(sslerr, err), ssl_error_to_string(sslerr, res));
565  }
566 
567 #if !(defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2070000L)) && (OPENSSL_VERSION_NUMBER >= 0x10100000L)
568  if (!SSL_is_server(stream->ssl)) {
569 #else
570  if (!stream->ssl->server) {
571 #endif
572  /* For client threads, ensure that the error stack is cleared */
573 #if defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x10100000L)
574 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
575  ERR_remove_thread_state(NULL);
576 #else
577  ERR_remove_state(0);
578 #endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */
579 #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
580  }
581 
582  SSL_free(stream->ssl);
583  stream->ssl = NULL;
584  }
585 #endif /* defined(DO_SSL) */
586 
587  /*
588  * Issuing shutdown() is necessary here to avoid a race
589  * condition where the last data written may not appear
590  * in the TCP stream. See ASTERISK-23548
591  */
592  shutdown(stream->fd, SHUT_RDWR);
593  if (close(stream->fd)) {
594  ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno));
595  }
596  stream->fd = -1;
597  }
598  ao2_t_ref(stream, -1, "Closed ast_iostream");
599 
600  return 0;
601 }
ssize_t ast_iostream_discard ( struct ast_iostream stream,
size_t  count 
)

Discard the specified number of bytes from an iostream.

Parameters
streamA pointer to an iostream
countThe number of bytes to discard.
Returns
Upon successful completion, returns the number of bytes discarded. Otherwise, -1 is returned and errno may be set indicating the error.

Definition at line 368 of file iostream.c.

References ast_iostream_read().

369 {
370  char buf[1024];
371  size_t remaining = size;
372  ssize_t ret;
373 
374  while (remaining) {
375  ret = ast_iostream_read(stream, buf, remaining > sizeof(buf) ? sizeof(buf) : remaining);
376  if (ret <= 0) {
377  return ret;
378  }
379  remaining -= ret;
380  }
381 
382  return size;
383 }
ssize_t ast_iostream_read(struct ast_iostream *stream, void *buffer, size_t count)
Read data from an iostream.
Definition: iostream.c:284
struct ast_iostream* ast_iostream_from_fd ( int *  fd)

Create an iostream from a file descriptor.

Parameters
fdA pointer to an open file descriptor
Returns
A newly allocated iostream or NULL if allocation fails.

Definition at line 611 of file iostream.c.

References AO2_ALLOC_OPT_LOCK_NOLOCK.

Referenced by ast_tcptls_client_create(), and generic_http_callback().

612 {
613  struct ast_iostream *stream;
614 
615  stream = ao2_alloc_options(sizeof(*stream), iostream_dtor,
617  if (stream) {
618  stream->timeout = -1;
619  stream->timeout_reset = -1;
620  stream->fd = *fd;
621  *fd = -1;
622  }
623 
624  return stream;
625 }
int ast_iostream_get_fd ( struct ast_iostream stream)

Get an iostream's file descriptor.

Parameters
streamA pointer to an iostream
Returns
The file descriptor for the given iostream
Return values
-1if the iostream has no open file descriptor.

Definition at line 85 of file iostream.c.

Referenced by get_input(), handle_kickmanconn(), handle_showmanconn(), and session_do().

86 {
87  return stream->fd;
88 }
SSL* ast_iostream_get_ssl ( struct ast_iostream stream)

Get a pointer to an iostream's OpenSSL SSL structure.

Parameters
streamA pointer to an iostream
Returns
A pointer to the OpenSSL SSL structure for the given iostream
Return values
NULLif TLS has not been initiated.
Note
If OpenSSL support is not included in the build, this will always return NULL.

Definition at line 109 of file iostream.c.

Referenced by handle_tcptls_connection().

110 {
111  return stream->ssl;
112 }
ssize_t ast_iostream_gets ( struct ast_iostream stream,
char *  buffer,
size_t  size 
)

Read a LF-terminated string from an iostream.

Parameters
streamA pointer to an iostream
bufferPointer to a buffer to store the read bytes.
sizeThe total size of buffer in bytes.
Returns
The number of bytes stored in buffer, excluding the null byte used to terminate the string. If the size of buffer (indicated by the caller with the size argument) is not sufficient to store the entire line it will be truncated to fit the available space. The contents of buffer will always be terminated with a null byte. In the case of an error, -1 will be returned and errno may be set indicating the error.

Definition at line 311 of file iostream.c.

312 {
313  size_t remaining = size;
314  ssize_t accum_size = 0;
315  ssize_t len;
316  char *newline;
317 
318  for (;;) {
319  /* Search for newline */
320  newline = memchr(stream->rbufhead, '\n', stream->rbuflen);
321  if (newline) {
322  len = newline - stream->rbufhead + 1;
323  if (len > remaining - 1) {
324  len = remaining - 1;
325  }
326  break;
327  }
328 
329  /* Enough buffered line data to fill request buffer? */
330  if (stream->rbuflen >= remaining - 1) {
331  len = remaining - 1;
332  break;
333  }
334  if (stream->rbuflen) {
335  /* Put leftover buffered line data into request buffer */
336  memcpy(buffer + accum_size, stream->rbufhead, stream->rbuflen);
337  remaining -= stream->rbuflen;
338  accum_size += stream->rbuflen;
339  stream->rbuflen = 0;
340  }
341  stream->rbufhead = stream->rbuf;
342 
343  len = iostream_read(stream, stream->rbuf, sizeof(stream->rbuf));
344  if (len == 0) {
345  /* Nothing new was read. Return whatever we have accumulated. */
346  break;
347  }
348  if (len < 0) {
349  if (accum_size) {
350  /* We have an accumulated buffer so return that instead. */
351  len = 0;
352  break;
353  }
354  return len;
355  }
356  stream->rbuflen += len;
357  }
358 
359  /* Return read buffer string length */
360  memcpy(buffer + accum_size, stream->rbufhead, len);
361  buffer[accum_size + len] = 0;
362  stream->rbuflen -= len;
363  stream->rbufhead += len;
364 
365  return accum_size + len;
366 }
void ast_iostream_nonblock ( struct ast_iostream stream)

Make an iostream non-blocking.

Parameters
streamA pointer to an iostream

Definition at line 104 of file iostream.c.

References ast_fd_set_flags.

Referenced by session_do().

105 {
106  ast_fd_set_flags(stream->fd, O_NONBLOCK);
107 }
#define ast_fd_set_flags(fd, flags)
Set flags on the given file descriptor.
Definition: utils.h:1039
ssize_t ast_iostream_printf ( struct ast_iostream stream,
const char *  format,
  ... 
)

Write a formatted string to an iostream.

Parameters
streamA pointer to an iostream
formatA format string, as documented by printf(3)
...Arguments for the provided format string
Returns
The number of bytes written, or -1 if an error occurs. Note that if -1 is returned, the number of bytes written to the iostream is unspecified.

Definition at line 502 of file iostream.c.

References ast_iostream_write(), and ast_malloc.

Referenced by ast_http_send().

503 {
504  char sbuf[512], *buf = sbuf;
505  int len, len2, ret = -1;
506  va_list va;
507 
508  va_start(va, format);
509  len = vsnprintf(buf, sizeof(sbuf), format, va);
510  va_end(va);
511 
512  if (len > sizeof(sbuf) - 1) {
513  /* Add one to the string length to accommodate the NULL byte */
514  size_t buf_len = len + 1;
515 
516  buf = ast_malloc(buf_len);
517  if (!buf) {
518  return -1;
519  }
520  va_start(va, format);
521  len2 = vsnprintf(buf, buf_len, format, va);
522  va_end(va);
523  if (len2 != len) {
524  goto error;
525  }
526  }
527 
528  if (ast_iostream_write(stream, buf, len) == len)
529  ret = len;
530 
531 error:
532  if (buf != sbuf) {
533  ast_free(buf);
534  }
535 
536  return ret;
537 }
ssize_t ast_iostream_write(struct ast_iostream *stream, const void *buffer, size_t count)
Write data to an iostream.
Definition: iostream.c:385
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
ssize_t ast_iostream_read ( struct ast_iostream stream,
void *  buffer,
size_t  count 
)

Read data from an iostream.

Parameters
streamA pointer to an iostream
bufferPointer to a buffer to store the read bytes.
countThe number of bytes to read.
Returns
Upon successful completion, returns a non-negative integer indicating the number of bytes actually read. Otherwise, returns -1 and may set errno to indicate the error.

Definition at line 284 of file iostream.c.

Referenced by ast_iostream_discard(), and get_input().

285 {
286  if (!count) {
287  /* You asked for no data you got no data. */
288  return 0;
289  }
290 
291  if (!stream || stream->fd == -1) {
292  errno = EBADF;
293  return -1;
294  }
295 
296  /* Get any remains from the read buffer */
297  if (stream->rbuflen) {
298  size_t r = count;
299  if (r > stream->rbuflen) {
300  r = stream->rbuflen;
301  }
302  memcpy(buffer, stream->rbufhead, r);
303  stream->rbuflen -= r;
304  stream->rbufhead += r;
305  return r;
306  }
307 
308  return iostream_read(stream, buffer, count);
309 }
void ast_iostream_set_exclusive_input ( struct ast_iostream stream,
int  exclusive_input 
)

Set the iostream if it can exclusively depend upon the set timeouts.

Parameters
streamA pointer to an iostream
exclusive_inputTRUE if stream can exclusively wait for fd input. Otherwise, the stream will not wait for fd input. It will wait while trying to send data.
Note
The stream timeouts still need to be set.

Definition at line 149 of file iostream.c.

Referenced by session_do().

150 {
151  ast_assert(stream != NULL);
152 
153  stream->exclusive_input = exclusive_input;
154 }
int ast_iostream_set_sni_hostname ( struct ast_iostream stream,
const char *  sni_hostname 
)

Set the iostream's SNI hostname for TLS client connections.

Parameters
streamA pointer to an iostream
sni_hostnameThe hostname to use for SNI when in client mode
Return values
0if the hostname was set successfully.
-1if memory could not be allocated for the hostname.

Definition at line 156 of file iostream.c.

References ast_strdup.

Referenced by ast_tcptls_client_create().

157 {
158  ast_assert(stream != NULL);
159 
160  ast_free(stream->sni_hostname);
161  stream->sni_hostname = ast_strdup(sni_hostname);
162 
163  return stream->sni_hostname ? 0 : -1;
164 }
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
void ast_iostream_set_timeout_disable ( struct ast_iostream stream)

Disable the iostream timeout timer.

Parameters
streamA pointer to an iostream

Definition at line 114 of file iostream.c.

Referenced by ast_websocket_close(), ast_websocket_write(), send_string(), and session_do().

115 {
116  ast_assert(stream != NULL);
117 
118  stream->timeout = -1;
119  stream->timeout_reset = -1;
120 }
void ast_iostream_set_timeout_idle_inactivity ( struct ast_iostream stream,
int  timeout,
int  timeout_reset 
)

Set the iostream inactivity & idle timeout timers.

Parameters
streamA pointer to an iostream
timeoutNumber of milliseconds to wait for initial data transfer with the peer.
timeout_resetNumber of milliseconds to wait for subsequent data transfer with the peer.

As an example, if you want to timeout a peer if they do not send an initial message within 5 seconds or if they do not send a message at least every 30 seconds, you would set timeout to 5000 and timeout_reset to 30000.

Note
Setting either of these timeouts to -1 will disable them.

Definition at line 131 of file iostream.c.

132 {
133  ast_assert(stream != NULL);
134 
135  stream->start.tv_sec = 0;
136  stream->timeout = timeout;
137  stream->timeout_reset = timeout_reset;
138 }
void ast_iostream_set_timeout_inactivity ( struct ast_iostream stream,
int  timeout 
)

Set the iostream inactivity timeout timer.

Parameters
streamA pointer to an iostream
timeoutNumber of milliseconds to wait for data transfer with the peer.

This is basically how much time we are willing to spend in an I/O call before we declare the peer unresponsive.

Note
Setting timeout to -1 disables the timeout.
Setting this timeout replaces the I/O sequence timeout timer.

Definition at line 122 of file iostream.c.

Referenced by ast_websocket_close(), and send_string().

123 {
124  ast_assert(stream != NULL);
125 
126  stream->start.tv_sec = 0;
127  stream->timeout = timeout;
128  stream->timeout_reset = timeout;
129 }
void ast_iostream_set_timeout_sequence ( struct ast_iostream stream,
struct timeval  start,
int  timeout 
)

Set the iostream I/O sequence timeout timer.

Parameters
streamA pointer to an iostream
startTime the I/O sequence timer starts.
timeoutNumber of milliseconds from the start time before timeout.

This is how much time are we willing to allow the peer to complete an operation that can take several I/O calls. The main use is as an authentication timer with us.

Note
Setting timeout to -1 disables the timeout.
Setting this timeout replaces the inactivity timeout timer.

Definition at line 140 of file iostream.c.

Referenced by ast_websocket_write(), and session_do().

141 {
142  ast_assert(stream != NULL);
143 
144  stream->start = start;
145  stream->timeout = timeout;
146  stream->timeout_reset = timeout;
147 }
int ast_iostream_start_tls ( struct ast_iostream **  stream,
SSL_CTX *  ctx,
int  client 
)

Begin TLS on an iostream.

Parameters
streamA pointer to an iostream pointer
ctxA pointer to an SSL_CTX which will be passed to SSL_new()
clientNon-zero to indicate that we are the client, zero to indicate that we are the server.
Return values
0success
-1failure
Note
The iostream that is passed in stream may be replaced with a different one before this function returns.
On failure, errno may be set providing additional information on why the failure occurred.

Definition at line 627 of file iostream.c.

Referenced by handle_tcptls_connection().

628 {
629 #ifdef DO_SSL
630  struct ast_iostream *stream = *pstream;
631  int (*ssl_setup)(SSL *) = client ? SSL_connect : SSL_accept;
632  int res;
633 
634  stream->ssl = SSL_new(ssl_ctx);
635  if (!stream->ssl) {
636  ast_log(LOG_ERROR, "Unable to create new SSL connection\n");
637  errno = ENOMEM;
638  return -1;
639  }
640 
641  /*
642  * This function takes struct ast_iostream **, so it can chain
643  * SSL over any ast_iostream. For now we assume it's a file descriptor.
644  * But later this should instead use BIO wrapper to tie SSL to another
645  * ast_iostream.
646  */
647  SSL_set_fd(stream->ssl, stream->fd);
648 
649  if (client && !ast_strlen_zero(stream->sni_hostname)) {
650  if (!SSL_set_tlsext_host_name(stream->ssl, stream->sni_hostname)) {
651  ast_log(LOG_ERROR, "Unable to set SNI hostname '%s'\n",
652  stream->sni_hostname);
653  errno = EIO;
654  return -1;
655  }
656  }
657 
658  res = ssl_setup(stream->ssl);
659  if (res <= 0) {
660  int sslerr = SSL_get_error(stream->ssl, res);
661  char err[256];
662 
663  ast_log(LOG_ERROR, "Problem setting up ssl connection: %s, %s\n",
664  ERR_error_string(sslerr, err), ssl_error_to_string(sslerr, res));
665  errno = EIO;
666  return -1;
667  }
668 
669  return 0;
670 #else
671  ast_log(LOG_ERROR, "SSL not enabled in this build\n");
672  errno = ENOTSUP;
673  return -1;
674 #endif
675 }
int ast_iostream_wait_for_input ( struct ast_iostream stream,
int  timeout 
)

Wait for input on the iostream's file descriptor.

Since
16.8.0
17.2.0
Parameters
streamA pointer to an iostream
timeoutthe number of milliseconds to wait
Return values
-1if error occurred
0if the timeout expired
1if the stream is ready for reading

Definition at line 90 of file iostream.c.

91 {
92 #if defined(DO_SSL)
93  /* Because SSL is read in blocks, it's possible that the last time we read we
94  got more than we asked for and it is now buffered inside OpenSSL. If that
95  is the case, calling ast_wait_for_input() will block until the fd is ready
96  for reading again, which might never happen. */
97  if (stream->ssl && SSL_pending(stream->ssl)) {
98  return 1;
99  }
100 #endif
101  return ast_wait_for_input(stream->fd, timeout);
102 }
ssize_t ast_iostream_write ( struct ast_iostream stream,
const void *  buffer,
size_t  count 
)

Write data to an iostream.

Parameters
streamA pointer to an iostream
bufferPointer to a buffer from which to read bytes.
countThe number of bytes from buffer to write.
Returns
Upon successful completion, returns the number of bytes actually written to the iostream. This number shall never be greater than count. Otherwise, returns -1 and may set errno to indicate the error.

Definition at line 385 of file iostream.c.

References ast_debug, ast_remaining_ms(), and ast_tvnow().

Referenced by ast_http_send(), ast_iostream_printf(), ast_websocket_close(), ast_websocket_write(), and send_string().

386 {
387  struct timeval start;
388  int ms;
389  int res;
390  int written;
391  int remaining;
392 
393  if (!size) {
394  /* You asked to write no data you wrote no data. */
395  return 0;
396  }
397 
398  if (!stream || stream->fd == -1) {
399  errno = EBADF;
400  return -1;
401  }
402 
403  if (stream->start.tv_sec) {
404  start = stream->start;
405  } else {
406  start = ast_tvnow();
407  }
408 
409 #if defined(DO_SSL)
410  if (stream->ssl) {
411  written = 0;
412  remaining = size;
413  for (;;) {
414  int sslerr;
415  char err[256];
416  res = SSL_write(stream->ssl, buffer + written, remaining);
417  if (res == remaining) {
418  /* Everything was written. */
419  return size;
420  }
421  if (0 < res) {
422  /* Successfully wrote part of the buffer. Try to write the rest. */
423  written += res;
424  remaining -= res;
425  continue;
426  }
427  sslerr = SSL_get_error(stream->ssl, res);
428  switch (sslerr) {
429  case SSL_ERROR_ZERO_RETURN:
430  ast_debug(1, "TLS clean shutdown alert writing data\n");
431  if (written) {
432  /* Report partial write. */
433  return written;
434  }
435  errno = EBADF;
436  return -1;
437  case SSL_ERROR_WANT_READ:
438  ms = ast_remaining_ms(start, stream->timeout);
439  if (!ms) {
440  /* Report partial write. */
441  ast_debug(1, "TLS timeout writing data (want read)\n");
442  return written;
443  }
444  ast_wait_for_input(stream->fd, ms);
445  break;
446  case SSL_ERROR_WANT_WRITE:
447  ms = ast_remaining_ms(start, stream->timeout);
448  if (!ms) {
449  /* Report partial write. */
450  ast_debug(1, "TLS timeout writing data (want write)\n");
451  return written;
452  }
453  ast_wait_for_output(stream->fd, ms);
454  break;
455  default:
456  /* Undecoded SSL or transport error. */
457  ast_debug(1, "TLS transport or SSL error writing data: %s, %s\n", ERR_error_string(sslerr, err),
458  ssl_error_to_string(sslerr, res));
459  if (written) {
460  /* Report partial write. */
461  return written;
462  }
463  errno = EBADF;
464  return -1;
465  }
466  }
467  }
468 #endif /* defined(DO_SSL) */
469 
470  written = 0;
471  remaining = size;
472  for (;;) {
473  res = write(stream->fd, buffer + written, remaining);
474  if (res == remaining) {
475  /* Yay everything was written. */
476  return size;
477  }
478  if (0 < res) {
479  /* Successfully wrote part of the buffer. Try to write the rest. */
480  written += res;
481  remaining -= res;
482  continue;
483  }
484  if (errno != EINTR && errno != EAGAIN) {
485  /* Not a retryable error. */
486  ast_debug(1, "TCP socket error writing: %s\n", strerror(errno));
487  if (written) {
488  return written;
489  }
490  return -1;
491  }
492  ms = ast_remaining_ms(start, stream->timeout);
493  if (!ms) {
494  /* Report partial write. */
495  ast_debug(1, "TCP timeout writing data\n");
496  return written;
497  }
498  ast_wait_for_output(stream->fd, ms);
499  }
500 }
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
#define ast_debug(level,...)
Log a DEBUG message.
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
Definition: utils.c:2281