Coverage Report

Created: 2026-06-10 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libcoap/src/coap_io.c
Line
Count
Source
1
/* coap_io.c -- Default network I/O functions for libcoap
2
 *
3
 * Copyright (C) 2012,2014,2016-2026 Olaf Bergmann <bergmann@tzi.org> and others
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 *
7
 * This file is part of the CoAP library libcoap. Please see
8
 * README for terms of use.
9
 */
10
11
/**
12
 * @file coap_io.c
13
 * @brief Network I/O functions
14
 */
15
16
#include "coap3/coap_libcoap_build.h"
17
18
#ifdef HAVE_STDIO_H
19
#  include <stdio.h>
20
#endif
21
#ifdef HAVE_UNISTD_H
22
# include <unistd.h>
23
#endif
24
25
#ifndef __ZEPHYR__
26
#ifdef HAVE_SYS_SELECT_H
27
# include <sys/select.h>
28
#endif
29
#ifdef HAVE_SYS_SOCKET_H
30
# include <sys/socket.h>
31
#endif
32
#ifdef HAVE_SYS_IOCTL_H
33
#include <sys/ioctl.h>
34
#endif
35
#ifdef HAVE_NETINET_IN_H
36
# include <netinet/in.h>
37
#endif
38
#ifdef HAVE_SYS_UIO_H
39
# include <sys/uio.h>
40
#endif
41
#ifdef _WIN32
42
#include <stdio.h>
43
#endif /* _WIN32 */
44
#ifdef COAP_EPOLL_SUPPORT
45
#include <sys/epoll.h>
46
#include <sys/timerfd.h>
47
#ifdef HAVE_LIMITS_H
48
#include <limits.h>
49
#endif
50
#endif /* COAP_EPOLL_SUPPORT */
51
#else /* __ZEPHYR__ */
52
#include <sys/ioctl.h>
53
#include <sys/select.h>
54
#endif /* __ZEPHYR__ */
55
56
#if COAP_SERVER_SUPPORT
57
coap_endpoint_t *
58
0
coap_malloc_endpoint(void) {
59
0
  return (coap_endpoint_t *)coap_malloc_type(COAP_ENDPOINT, sizeof(coap_endpoint_t));
60
0
}
61
62
void
63
0
coap_mfree_endpoint(coap_endpoint_t *ep) {
64
0
  coap_free_type(COAP_ENDPOINT, ep);
65
0
}
66
#endif /* COAP_SERVER_SUPPORT */
67
68
#ifndef WITH_CONTIKI
69
void
70
0
coap_update_io_timer(coap_context_t *context, coap_tick_t delay) {
71
0
#if COAP_EPOLL_SUPPORT
72
0
  if (context->eptimerfd != -1) {
73
0
    coap_tick_t now;
74
75
0
    coap_ticks(&now);
76
0
    if (context->next_timeout == 0 || context->next_timeout > now + delay) {
77
0
      struct itimerspec new_value;
78
0
      int ret;
79
80
0
      context->next_timeout = now + delay;
81
0
      memset(&new_value, 0, sizeof(new_value));
82
0
      if (delay == 0) {
83
0
        new_value.it_value.tv_nsec = 1; /* small but not zero */
84
0
      } else {
85
0
        new_value.it_value.tv_sec = delay / COAP_TICKS_PER_SECOND;
86
0
        new_value.it_value.tv_nsec = (delay % COAP_TICKS_PER_SECOND) *
87
0
                                     1000000;
88
0
      }
89
0
      ret = timerfd_settime(context->eptimerfd, 0, &new_value, NULL);
90
0
      if (ret == -1) {
91
0
        coap_log_err("%s: timerfd_settime failed: %s (%d)\n",
92
0
                     "coap_update_io_timer",
93
0
                     coap_socket_strerror(), errno);
94
0
      }
95
#ifdef COAP_DEBUG_WAKEUP_TIMES
96
      else {
97
        coap_log_debug("****** Next wakeup time %3ld.%09ld\n",
98
                       new_value.it_value.tv_sec, new_value.it_value.tv_nsec);
99
      }
100
#endif /* COAP_DEBUG_WAKEUP_TIMES */
101
0
    }
102
0
  }
103
#else /* ! COAP_EPOLL_SUPPORT */
104
  coap_tick_t now;
105
106
  coap_ticks(&now);
107
  if (context->next_timeout == 0 || context->next_timeout > now + delay) {
108
    context->next_timeout = now + delay;
109
  }
110
#endif /* ! COAP_EPOLL_SUPPORT */
111
0
}
112
#endif /* ! WITH_CONTIKI */
113
114
#ifdef _WIN32
115
void
116
coap_win_error_to_errno(void) {
117
  int w_error = WSAGetLastError();
118
  switch (w_error) {
119
  case WSA_NOT_ENOUGH_MEMORY:
120
    errno = ENOMEM;
121
    break;
122
  case WSA_INVALID_PARAMETER:
123
    errno = EINVAL;
124
    break;
125
  case WSAEINTR:
126
    errno = EINTR;
127
    break;
128
  case WSAEBADF:
129
    errno = EBADF;
130
    break;
131
  case WSAEACCES:
132
    errno = EACCES;
133
    break;
134
  case WSAEFAULT:
135
    errno = EFAULT;
136
    break;
137
  case WSAEINVAL:
138
    errno = EINVAL;
139
    break;
140
  case WSAEMFILE:
141
    errno = EMFILE;
142
    break;
143
  case WSAEWOULDBLOCK:
144
    errno = EWOULDBLOCK;
145
    break;
146
  case WSAENETDOWN:
147
    errno = ENETDOWN;
148
    break;
149
  case WSAENETUNREACH:
150
    errno = ENETUNREACH;
151
    break;
152
  case WSAENETRESET:
153
    errno = ENETRESET;
154
    break;
155
  case WSAECONNABORTED:
156
    errno = ECONNABORTED;
157
    break;
158
  case WSAECONNRESET:
159
    errno = ECONNRESET;
160
    break;
161
  case WSAENOBUFS:
162
    errno = ENOBUFS;
163
    break;
164
  case WSAETIMEDOUT:
165
    errno = ETIMEDOUT;
166
    break;
167
  case WSAECONNREFUSED:
168
    errno = ECONNREFUSED;
169
    break;
170
  case WSAEADDRNOTAVAIL:
171
    errno = EADDRNOTAVAIL;
172
    break;
173
  default:
174
    coap_log_err("WSAGetLastError: %d mapping to errno failed - please fix\n",
175
                 w_error);
176
    errno = EPERM;
177
    break;
178
  }
179
}
180
#endif /* _WIN32 */
181
182
#if !defined(WITH_LWIP) && !defined(__ZEPHYR__)
183
#if (!defined(WITH_CONTIKI)) != ( defined(HAVE_NETINET_IN_H) || defined(HAVE_WS2TCPIP_H) )
184
/* define struct in6_pktinfo and struct in_pktinfo if not available
185
   FIXME: check with configure
186
*/
187
#if !defined(__MINGW32__) && !defined(RIOT_VERSION)
188
struct in6_pktinfo {
189
  struct in6_addr ipi6_addr;        /* src/dst IPv6 address */
190
  unsigned int ipi6_ifindex;        /* send/recv interface index */
191
};
192
193
struct in_pktinfo {
194
  int ipi_ifindex;
195
  struct in_addr ipi_spec_dst;
196
  struct in_addr ipi_addr;
197
};
198
#endif /* ! __MINGW32__ && ! RIOT_VERSION */
199
#endif
200
#endif /* ! WITH_LWIP && ! __ZEPHYR__ */
201
202
void
203
0
coap_packet_get_memmapped(coap_packet_t *packet, unsigned char **address, size_t *length) {
204
0
  *address = packet->payload;
205
0
  *length = packet->length;
206
0
}
207
208
COAP_API unsigned int
209
0
coap_io_prepare_epoll(coap_context_t *ctx, coap_tick_t now) {
210
0
  unsigned int ret;
211
212
0
  coap_lock_lock(return 0);
213
0
  ret = coap_io_prepare_epoll_lkd(ctx, now);
214
0
  coap_lock_unlock();
215
0
  return ret;
216
0
}
217
218
unsigned int
219
0
coap_io_prepare_epoll_lkd(coap_context_t *ctx, coap_tick_t now) {
220
#ifndef COAP_EPOLL_SUPPORT
221
  (void)ctx;
222
  (void)now;
223
  coap_log_emerg("coap_io_prepare_epoll() requires libcoap compiled for using epoll\n");
224
  return 0;
225
#else /* COAP_EPOLL_SUPPORT */
226
0
  coap_socket_t *sockets[1];
227
0
  unsigned int max_sockets = sizeof(sockets)/sizeof(sockets[0]);
228
0
  unsigned int num_sockets;
229
0
  unsigned int timeout;
230
231
0
  coap_lock_check_locked();
232
  /* Use the common logic */
233
0
  timeout = coap_io_prepare_io_lkd(ctx, sockets, max_sockets, &num_sockets, now);
234
  /* Save when the next expected I/O is to take place */
235
0
  ctx->next_timeout = timeout ? now + timeout : 0;
236
0
  if (ctx->eptimerfd != -1) {
237
0
    struct itimerspec new_value;
238
0
    int ret;
239
240
0
    memset(&new_value, 0, sizeof(new_value));
241
0
    coap_ticks(&now);
242
0
    if (ctx->next_timeout != 0 && ctx->next_timeout > now) {
243
0
      coap_tick_t rem_timeout = ctx->next_timeout - now;
244
      /* Need to trigger an event on ctx->eptimerfd in the future */
245
0
      new_value.it_value.tv_sec = rem_timeout / COAP_TICKS_PER_SECOND;
246
0
      new_value.it_value.tv_nsec = (rem_timeout % COAP_TICKS_PER_SECOND) *
247
0
                                   1000000;
248
0
    }
249
#ifdef COAP_DEBUG_WAKEUP_TIMES
250
    coap_log_debug("****** Next wakeup time %3ld.%09ld\n",
251
                   new_value.it_value.tv_sec, new_value.it_value.tv_nsec);
252
#endif /* COAP_DEBUG_WAKEUP_TIMES */
253
    /* reset, or specify a future time for eptimerfd to trigger */
254
0
    ret = timerfd_settime(ctx->eptimerfd, 0, &new_value, NULL);
255
0
    if (ret == -1) {
256
0
      coap_log_err("%s: timerfd_settime failed: %s (%d)\n",
257
0
                   "coap_io_prepare_epoll",
258
0
                   coap_socket_strerror(), errno);
259
0
    }
260
0
  }
261
0
  return timeout;
262
0
#endif /* COAP_EPOLL_SUPPORT */
263
0
}
264
265
/*
266
 * return  0 No i/o pending
267
 *       +ve millisecs to next i/o activity
268
 */
269
COAP_API unsigned int
270
coap_io_prepare_io(coap_context_t *ctx,
271
                   coap_socket_t *sockets[],
272
                   unsigned int max_sockets,
273
                   unsigned int *num_sockets,
274
0
                   coap_tick_t now) {
275
0
  unsigned int ret;
276
277
0
  coap_lock_lock(return 0);
278
0
  ret = coap_io_prepare_io_lkd(ctx, sockets, max_sockets, num_sockets, now);
279
0
  coap_lock_unlock();
280
0
  return ret;
281
0
}
282
283
#if COAP_CLIENT_SUPPORT
284
static coap_tick_t
285
coap_check_need_reconnect(coap_context_t *ctx, coap_session_t *s, coap_tick_t now,
286
0
                          coap_tick_t timeout) {
287
0
  coap_tick_t s_timeout;
288
289
0
  if (s->client_initiated && s->session_failed && ctx->reconnect_time) {
290
0
    if (s->last_rx_tx + ctx->reconnect_time * COAP_TICKS_PER_SECOND <= now) {
291
0
      if (!coap_session_reconnect(s)) {
292
        /* server is not back up yet - delay retry a while */
293
0
        s->last_rx_tx = now;
294
0
        s_timeout = ctx->reconnect_time * COAP_TICKS_PER_SECOND;
295
0
        if (timeout == 0 || s_timeout < timeout)
296
0
          timeout = s_timeout;
297
0
      }
298
0
    } else {
299
      /* Always positive due to if() above */
300
0
      s_timeout = (s->last_rx_tx + ctx->reconnect_time * COAP_TICKS_PER_SECOND) - now;
301
0
      if (s_timeout < timeout)
302
0
        timeout = s_timeout;
303
0
    }
304
0
  }
305
0
  return timeout;
306
0
}
307
308
void
309
0
coap_reset_doing_first(coap_session_t *session) {
310
0
  if (session->doing_first) {
311
0
    session->doing_first = 0;
312
0
    while (!session->doing_first && session->doing_first_pdu) {
313
0
      coap_pdu_t *k_pdu = session->doing_first_pdu;
314
315
0
      LL_DELETE(session->doing_first_pdu, session->doing_first_pdu);
316
0
      coap_log_debug("** %s: mid=0x%04x: released\n",
317
0
                     coap_session_str(session), k_pdu->mid);
318
0
      if (coap_send_lkd(session, k_pdu) == COAP_INVALID_MID) {
319
0
        coap_handle_event_lkd(session->context, COAP_EVENT_FIRST_PDU_FAIL, session);
320
0
      }
321
0
    }
322
0
  }
323
0
}
324
#endif /* COAP_CLIENT_SUPPORT */
325
326
/*
327
 * return  0 No i/o pending
328
 *       +ve millisecs to next i/o activity
329
 */
330
unsigned int
331
coap_io_prepare_io_lkd(coap_context_t *ctx,
332
                       coap_socket_t *sockets[],
333
                       unsigned int max_sockets,
334
                       unsigned int *num_sockets,
335
0
                       coap_tick_t now) {
336
0
  coap_queue_t *nextpdu;
337
0
  coap_session_t *s, *stmp;
338
0
  coap_tick_t timeout = COAP_MAX_DELAY_TICKS;
339
0
  coap_tick_t s_timeout;
340
0
#if COAP_SERVER_SUPPORT
341
0
  int check_dtls_timeouts = 0;
342
0
#endif /* COAP_SERVER_SUPPORT */
343
0
#if defined(COAP_EPOLL_SUPPORT) || defined(WITH_LWIP) || defined(RIOT_VERSION)
344
0
  (void)sockets;
345
0
  (void)max_sockets;
346
0
#endif /* COAP_EPOLL_SUPPORT || WITH_LWIP || RIOT_VERSION*/
347
348
0
  coap_lock_check_locked();
349
0
  *num_sockets = 0;
350
351
0
#if COAP_SERVER_SUPPORT
352
  /* Check to see if we need to send off any Observe requests */
353
0
  coap_check_notify_lkd(ctx);
354
355
0
#if COAP_ASYNC_SUPPORT
356
  /* Check to see if we need to send off any Async requests */
357
0
  if (coap_check_async(ctx, now, &s_timeout)) {
358
0
    if (s_timeout < timeout)
359
0
      timeout = s_timeout;
360
0
  }
361
0
#endif /* COAP_ASYNC_SUPPORT */
362
0
#endif /* COAP_SERVER_SUPPORT */
363
364
  /* Check to see if we need to send off any retransmit request */
365
0
  nextpdu = coap_peek_next(ctx);
366
0
  while (nextpdu && now >= ctx->sendqueue_basetime &&
367
0
         nextpdu->t <= now - ctx->sendqueue_basetime) {
368
0
    coap_retransmit(ctx, coap_pop_next(ctx));
369
0
    nextpdu = coap_peek_next(ctx);
370
0
  }
371
0
  if (nextpdu && now >= ctx->sendqueue_basetime &&
372
0
      (nextpdu->t - (now - ctx->sendqueue_basetime) < timeout))
373
0
    timeout = nextpdu->t - (now - ctx->sendqueue_basetime);
374
375
  /* Check for DTLS timeouts */
376
0
  if (ctx->dtls_context) {
377
0
    if (coap_dtls_is_context_timeout()) {
378
0
      coap_tick_t tls_timeout = coap_dtls_get_context_timeout(ctx->dtls_context);
379
0
      if (tls_timeout > 0) {
380
0
        if (tls_timeout < now + COAP_TICKS_PER_SECOND / 10)
381
0
          tls_timeout = now + COAP_TICKS_PER_SECOND / 10;
382
0
        coap_log_debug("** DTLS global timeout set to %dms\n",
383
0
                       (int)((tls_timeout - now) * 1000 / COAP_TICKS_PER_SECOND));
384
0
        if (tls_timeout - now < timeout)
385
0
          timeout = tls_timeout - now;
386
0
      }
387
0
#if COAP_SERVER_SUPPORT
388
0
    } else {
389
0
      check_dtls_timeouts = 1;
390
0
#endif /* COAP_SERVER_SUPPORT */
391
0
    }
392
0
  }
393
0
#if COAP_PROXY_SUPPORT
394
0
  if (coap_proxy_check_timeouts(ctx, now, &s_timeout)) {
395
0
    if (s_timeout < timeout)
396
0
      timeout = s_timeout;
397
0
  }
398
0
#endif /* COAP_PROXY_SUPPORT */
399
0
#if COAP_SERVER_SUPPORT
400
0
  coap_endpoint_t *ep;
401
0
  coap_tick_t session_timeout;
402
403
0
  if (ctx->session_timeout > 0)
404
0
    session_timeout = ctx->session_timeout * COAP_TICKS_PER_SECOND;
405
0
  else
406
0
    session_timeout = COAP_DEFAULT_SESSION_TIMEOUT * COAP_TICKS_PER_SECOND;
407
408
0
  LL_FOREACH(ctx->endpoint, ep) {
409
#if !defined(COAP_EPOLL_SUPPORT) && !defined(WITH_LWIP) && !defined(RIOT_VERSION)
410
    if (ep->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_WRITE | COAP_SOCKET_WANT_ACCEPT)) {
411
      if (*num_sockets < max_sockets)
412
        sockets[(*num_sockets)++] = &ep->sock;
413
    }
414
#endif /* ! COAP_EPOLL_SUPPORT && ! WITH_LWIP && ! RIOT_VERSION */
415
0
    SESSIONS_ITER_SAFE(ep->sessions, s, stmp) {
416
      /* Check whether any idle server sessions should be released */
417
0
      if (s->type == COAP_SESSION_TYPE_SERVER && s->ref == 0 &&
418
0
          s->delayqueue == NULL &&
419
0
#if COAP_CLIENT_SUPPORT
420
0
          !(s->client_initiated && ctx->reconnect_time) &&
421
0
#endif /* COAP_CLIENT_SUPPORT */
422
0
          (s->last_rx_tx + session_timeout <= now ||
423
0
           s->state == COAP_SESSION_STATE_NONE)) {
424
0
        coap_handle_event_lkd(ctx, COAP_EVENT_SERVER_SESSION_DEL, s);
425
0
        coap_session_free(s);
426
0
        continue;
427
0
      } else if (s->is_rate_limiting) {
428
0
        continue;
429
0
      } else {
430
0
        if (s->type == COAP_SESSION_TYPE_SERVER && s->ref == 0 &&
431
0
            s->delayqueue == NULL && s->last_rx_tx + session_timeout <= now) {
432
          /* Has to be positive based on if() above */
433
0
          s_timeout = (s->last_rx_tx + session_timeout) - now;
434
0
          if (s_timeout < timeout)
435
0
            timeout = s_timeout;
436
0
        }
437
        /* Make sure the session object is not deleted in any callbacks */
438
0
        coap_session_reference_lkd(s);
439
        /* Check any DTLS timeouts and expire if appropriate */
440
0
        if (check_dtls_timeouts && s->state == COAP_SESSION_STATE_HANDSHAKE &&
441
0
            s->proto == COAP_PROTO_DTLS && s->tls) {
442
0
          coap_tick_t tls_timeout = coap_dtls_get_timeout(s, now);
443
0
          while (tls_timeout > 0 && tls_timeout <= now) {
444
0
            coap_log_debug("** %s: DTLS retransmit timeout\n",
445
0
                           coap_session_str(s));
446
0
            if (coap_dtls_handle_timeout(s))
447
0
              goto release_1;
448
449
0
            if (s->tls)
450
0
              tls_timeout = coap_dtls_get_timeout(s, now);
451
0
            else {
452
0
              tls_timeout = 0;
453
0
              timeout = 1;
454
0
            }
455
0
          }
456
0
          if (tls_timeout > 0 && tls_timeout - now < timeout)
457
0
            timeout = tls_timeout - now;
458
0
        }
459
        /* Check if any server large receives are missing blocks */
460
0
        if (s->lg_srcv) {
461
0
          if (coap_block_check_lg_srcv_timeouts(s, now, &s_timeout)) {
462
0
            if (s_timeout < timeout)
463
0
              timeout = s_timeout;
464
0
          }
465
0
        }
466
        /* Check if any server large sending have timed out */
467
0
        if (s->lg_xmit) {
468
0
          if (coap_block_check_lg_xmit_timeouts(s, now, &s_timeout)) {
469
0
            if (s_timeout < timeout)
470
0
              timeout = s_timeout;
471
0
          }
472
0
        }
473
#if !defined(COAP_EPOLL_SUPPORT) && !defined(WITH_LWIP) && !defined(RIOT_VERSION)
474
        if (s->sock.flags & (COAP_SOCKET_WANT_READ|COAP_SOCKET_WANT_WRITE)) {
475
          if (*num_sockets < max_sockets && !(s->sock.flags & COAP_SOCKET_SLAVE))
476
            sockets[(*num_sockets)++] = &s->sock;
477
        }
478
#endif /* ! COAP_EPOLL_SUPPORT && ! WITH_LWIP && ! RIOT_VERSION */
479
0
#if COAP_Q_BLOCK_SUPPORT
480
        /*
481
         * Check if any server large transmits have hit MAX_PAYLOAD and need
482
         * restarting
483
         */
484
0
        if (s->lg_xmit && !s->is_rate_limiting) {
485
0
          if (coap_block_check_q_block2_xmit(s, now, &s_timeout)) {
486
0
            if (s_timeout < timeout)
487
0
              timeout = s_timeout;
488
0
          }
489
0
        }
490
0
#endif /* COAP_Q_BLOCK_SUPPORT */
491
0
release_1:
492
0
        coap_session_release_lkd(s);
493
0
      }
494
0
      if (s->type == COAP_SESSION_TYPE_SERVER &&
495
0
          s->state == COAP_SESSION_STATE_ESTABLISHED &&
496
0
          (s->ref_subscriptions || s->ref_proxy_subs
497
0
#if COAP_CLIENT_SUPPORT
498
0
           || (s->client_initiated && ctx->reconnect_time)
499
0
#endif /* COAP_CLIENT_SUPPORT */
500
0
          ) &&
501
0
          !s->con_active && ctx->ping_timeout > 0) {
502
        /* Only do this if this session is observing or reconnecting */
503
0
        if (s->last_rx_tx + ctx->ping_timeout * COAP_TICKS_PER_SECOND <= now) {
504
          /* Time to send a ping */
505
0
          coap_mid_t mid;
506
507
0
          if ((mid = coap_session_send_ping_lkd(s)) == COAP_INVALID_MID) {
508
            /* Some issue - not safe to continue processing */
509
0
            s->ping_failed++;
510
0
            s->last_rx_tx = now;
511
0
#if COAP_CLIENT_SUPPORT
512
0
            if (s->client_initiated && ctx->reconnect_time) {
513
0
              coap_session_failed(s);
514
0
            }
515
0
#endif /* COAP_CLIENT_SUPPORT */
516
0
            if (s->ping_failed <= COAP_MAX_PING_FAILURES)
517
0
              continue;
518
0
          }
519
0
          s->last_ping_mid = mid;
520
0
          if ((s->last_ping > 0 && s->last_pong < s->last_ping) || s->ping_failed > COAP_MAX_PING_FAILURES) {
521
0
#if COAP_CLIENT_SUPPORT
522
0
            if (s->client_initiated && ctx->reconnect_time) {
523
0
              coap_session_failed(s);
524
0
            } else {
525
0
#endif /* COAP_CLIENT_SUPPORT */
526
0
              coap_session_server_keepalive_failed(s);
527
0
#if COAP_CLIENT_SUPPORT
528
0
            }
529
0
#endif /* COAP_CLIENT_SUPPORT */
530
            /* check the next session */
531
0
            continue;
532
0
          }
533
0
          s->last_rx_tx = now;
534
0
          s->last_ping = now;
535
0
        } else {
536
          /* Always positive due to if() above */
537
0
          s_timeout = (s->last_rx_tx + ctx->ping_timeout * COAP_TICKS_PER_SECOND) - now;
538
0
          if (s_timeout < timeout)
539
0
            timeout = s_timeout;
540
0
        }
541
0
      }
542
0
#if COAP_CLIENT_SUPPORT
543
0
      timeout = coap_check_need_reconnect(ctx, s, now, timeout);
544
0
#endif /* COAP_CLIENT_SUPPORT */
545
0
    }
546
0
  }
547
0
#endif /* COAP_SERVER_SUPPORT */
548
0
#if COAP_CLIENT_SUPPORT
549
0
  SESSIONS_ITER_SAFE(ctx->sessions, s, stmp) {
550
0
    if (s->is_rate_limiting) {
551
0
      continue;
552
0
    }
553
0
    if (s->type == COAP_SESSION_TYPE_CLIENT &&
554
0
        s->state == COAP_SESSION_STATE_ESTABLISHED && !s->con_active &&
555
0
        ctx->ping_timeout > 0) {
556
0
      if (s->last_rx_tx + ctx->ping_timeout * COAP_TICKS_PER_SECOND <= now) {
557
        /* Time to send a ping */
558
0
        coap_mid_t mid;
559
560
0
        if ((mid = coap_session_send_ping_lkd(s)) == COAP_INVALID_MID) {
561
          /* Some issue - not safe to continue processing */
562
0
          s->ping_failed++;
563
0
          s->last_rx_tx = now;
564
0
          coap_session_failed(s);
565
0
          if (s->ping_failed <= COAP_MAX_PING_FAILURES)
566
0
            continue;
567
0
        }
568
0
        s->last_ping_mid = mid;
569
0
        if (s->last_ping > 0 && s->last_pong < s->last_ping) {
570
0
          coap_handle_event_lkd(s->context, COAP_EVENT_KEEPALIVE_FAILURE, s);
571
0
        }
572
0
        if (s->ping_failed > COAP_MAX_PING_FAILURES) {
573
0
#if COAP_CLIENT_SUPPORT
574
0
          if (s->client_initiated) {
575
0
            if (ctx->reconnect_time) {
576
0
              coap_session_failed(s);
577
0
            } else {
578
0
              coap_session_disconnected_lkd(s, COAP_NACK_NOT_DELIVERABLE);
579
0
            }
580
0
          } else {
581
0
#endif /* COAP_CLIENT_SUPPORT */
582
0
#if COAP_SERVER_SUPPORT
583
0
            coap_session_server_keepalive_failed(s);
584
0
#endif /* COAP_SERVER_SUPPORT */
585
0
#if COAP_CLIENT_SUPPORT
586
0
          }
587
0
#endif /* COAP_CLIENT_SUPPORT */
588
0
          continue;
589
0
        } else {
590
0
          s->last_rx_tx = now;
591
0
          s->last_ping = now;
592
0
        }
593
0
      } else {
594
        /* Always positive due to if() above */
595
0
        s_timeout = (s->last_rx_tx + ctx->ping_timeout * COAP_TICKS_PER_SECOND) - now;
596
0
        if (s_timeout < timeout)
597
0
          timeout = s_timeout;
598
0
      }
599
0
    }
600
0
    timeout = coap_check_need_reconnect(ctx, s, now, timeout);
601
602
0
#if !COAP_DISABLE_TCP
603
0
    if (s->type == COAP_SESSION_TYPE_CLIENT && COAP_PROTO_RELIABLE(s->proto) &&
604
0
        s->state == COAP_SESSION_STATE_CSM && ctx->csm_timeout_ms > 0) {
605
0
      if (s->csm_tx == 0) {
606
0
        s->csm_tx = now;
607
0
        s_timeout = (ctx->csm_timeout_ms * COAP_TICKS_PER_SECOND) / 1000;
608
0
      } else if (s->csm_tx + (ctx->csm_timeout_ms * COAP_TICKS_PER_SECOND) / 1000 <= now) {
609
        /* timed out - cannot handle 0, so has to be just +ve */
610
0
        s_timeout = 1;
611
0
      } else {
612
0
        s_timeout = (s->csm_tx + (ctx->csm_timeout_ms * COAP_TICKS_PER_SECOND) / 1000) - now;
613
0
      }
614
0
      if (s_timeout < timeout)
615
0
        timeout = s_timeout;
616
0
    }
617
0
#endif /* !COAP_DISABLE_TCP */
618
619
    /* Make sure the session object is not deleted in any callbacks */
620
0
    coap_session_reference_lkd(s);
621
    /* Check any DTLS timeouts and expire if appropriate */
622
0
    if (s->state == COAP_SESSION_STATE_HANDSHAKE &&
623
0
        s->proto == COAP_PROTO_DTLS && s->tls) {
624
0
      coap_tick_t tls_timeout = coap_dtls_get_timeout(s, now);
625
0
      while (tls_timeout > 0 && tls_timeout <= now) {
626
0
        coap_log_debug("** %s: DTLS retransmit timeout\n", coap_session_str(s));
627
0
        if (coap_dtls_handle_timeout(s))
628
0
          goto release_2;
629
630
0
        if (s->tls)
631
0
          tls_timeout = coap_dtls_get_timeout(s, now);
632
0
        else {
633
0
          tls_timeout = 0;
634
0
          timeout = 1;
635
0
        }
636
0
      }
637
0
      if (tls_timeout > 0 && tls_timeout - now < timeout)
638
0
        timeout = tls_timeout - now;
639
0
    }
640
641
    /* Check if any client large receives are missing blocks */
642
0
    if (s->lg_crcv) {
643
0
      if (coap_block_check_lg_crcv_timeouts(s, now, &s_timeout)) {
644
0
        if (s_timeout < timeout)
645
0
          timeout = s_timeout;
646
0
      }
647
0
    }
648
    /* Check if any client large sending have timed out */
649
0
    if (s->lg_xmit) {
650
0
      if (coap_block_check_lg_xmit_timeouts(s, now, &s_timeout)) {
651
0
        if (s_timeout < timeout)
652
0
          timeout = s_timeout;
653
0
      }
654
0
    }
655
0
    if (s->doing_first && s->doing_first_timeout) {
656
0
      if ((s->doing_first_timeout + 5 * COAP_TICKS_PER_SECOND) > now) {
657
0
        s_timeout = s->doing_first_timeout + 5 * COAP_TICKS_PER_SECOND - now;
658
0
        if (s_timeout < timeout)
659
0
          timeout = s_timeout;
660
0
      } else {
661
        /* Session set up has failed */
662
0
        coap_queue_t *sent;
663
0
        coap_queue_t *p = NULL;
664
0
        coap_queue_t *q;
665
0
        coap_queue_t *tmp;
666
667
0
        if (s->state == COAP_SESSION_STATE_CSM) {
668
0
          coap_log_debug("** %s: timeout waiting for CSM response\n",
669
0
                         coap_session_str(s));
670
0
          s->csm_not_seen = 1;
671
0
        } else {
672
0
          coap_log_debug("** %s: timeout waiting for first response\n",
673
0
                         coap_session_str(s));
674
0
        }
675
0
#if COAP_Q_BLOCK_SUPPORT
676
        /* Check if testing for Q-Block */
677
0
        if (s->block_mode & COAP_BLOCK_PROBE_Q_BLOCK) {
678
0
          coap_log_debug("Q-Block support assumed not available\n");
679
0
          set_block_mode_drop_q(s->block_mode);
680
0
        }
681
0
#endif /* COAP_Q_BLOCK_SUPPORT */
682
        /* Check if testing for Extended Token */
683
0
        if (s->max_token_checked == COAP_EXT_T_CHECKING) {
684
0
          s->max_token_size = COAP_TOKEN_DEFAULT_MAX;
685
0
          s->max_token_checked = COAP_EXT_T_CHECKED;
686
0
          coap_log_debug("Extended Token support assumed not available\n");
687
0
        }
688
        /*
689
         * Remove the test packet to stop re-transmission.
690
         * The test packet may be in the delayqueue ((D)TLS has not been set up),
691
         * or in the sendqueue if sent and is pending re-transmission.
692
         */
693
0
        LL_FOREACH_SAFE(s->delayqueue, q, tmp) {
694
0
          if (q->pdu->mid == s->remote_test_mid) {
695
0
            if (q->pdu->type==COAP_MESSAGE_CON) {
696
0
              coap_handle_nack(s, q->pdu,
697
0
                               s->proto == COAP_PROTO_DTLS ?
698
0
                               COAP_NACK_TLS_FAILED : COAP_NACK_NOT_DELIVERABLE,
699
0
                               q->id);
700
0
            }
701
0
            coap_log_debug("** %s: mid=0x%04x: removed\n",
702
0
                           coap_session_str(s), q->pdu->mid);
703
0
            if (p) {
704
0
              p->next = q->next;
705
0
            } else {
706
0
              s->delayqueue = q->next;
707
0
            }
708
0
            coap_delete_node_lkd(q);
709
0
            break;
710
0
          }
711
0
          p = q;
712
0
        }
713
0
        coap_remove_from_queue(&ctx->sendqueue, s, s->remote_test_mid, &sent);
714
0
        if (sent && sent->pdu && sent->pdu->type == COAP_MESSAGE_CON && COAP_PROTO_NOT_RELIABLE(s->proto)) {
715
0
          if (s->con_active)
716
0
            s->con_active--;
717
0
        }
718
0
        coap_reset_doing_first(s);
719
0
        if (s->state == COAP_SESSION_STATE_ESTABLISHED) {
720
          /* Flush out any entries on session->delayqueue */
721
0
          coap_session_connected(s);
722
0
        }
723
0
      }
724
0
    }
725
726
0
#if COAP_Q_BLOCK_SUPPORT
727
    /*
728
     * Check if any client large transmits have hit MAX_PAYLOAD and need
729
     * restarting
730
     */
731
0
    if (s->lg_xmit) {
732
0
      if (coap_block_check_q_block1_xmit(s, now, &s_timeout)) {
733
0
        if (s_timeout < timeout)
734
0
          timeout = s_timeout;
735
0
      }
736
0
    }
737
0
#endif /* COAP_Q_BLOCK_SUPPORT */
738
739
#if !defined(COAP_EPOLL_SUPPORT) && !defined(WITH_LWIP) && !defined(RIOT_VERSION)
740
    assert(s->ref > 1);
741
    if (s->sock.flags & (COAP_SOCKET_WANT_READ |
742
                         COAP_SOCKET_WANT_WRITE |
743
                         COAP_SOCKET_WANT_CONNECT)) {
744
      if (*num_sockets < max_sockets && !(s->sock.flags & COAP_SOCKET_SLAVE))
745
        sockets[(*num_sockets)++] = &s->sock;
746
    }
747
#endif /* ! COAP_EPOLL_SUPPORT && ! WITH_LWIP && ! RIOT_VERSION */
748
0
release_2:
749
0
    coap_session_release_lkd(s);
750
0
  }
751
0
#endif /* COAP_CLIENT_SUPPORT */
752
753
0
  return (unsigned int)((timeout * 1000 + COAP_TICKS_PER_SECOND - 1) / COAP_TICKS_PER_SECOND);
754
0
}
755
756
/*
757
 * return  0 Insufficient space to hold fds, or fds not supported
758
 *         1 All fds found
759
 */
760
COAP_API unsigned int
761
coap_io_get_fds(coap_context_t *ctx,
762
                coap_fd_t read_fds[],
763
                unsigned int *have_read_fds,
764
                unsigned int max_read_fds,
765
                coap_fd_t write_fds[],
766
                unsigned int *have_write_fds,
767
                unsigned int max_write_fds,
768
0
                unsigned int *rem_timeout_ms) {
769
0
  unsigned int ret;
770
771
0
  coap_lock_lock(return 0);
772
0
  ret = coap_io_get_fds_lkd(ctx, read_fds, have_read_fds, max_read_fds, write_fds,
773
0
                            have_write_fds, max_write_fds, rem_timeout_ms);
774
0
  coap_lock_unlock();
775
0
  return ret;
776
0
}
777
778
#if !defined(WITH_LWIP) && !defined(WITH_CONTIKI)
779
static int
780
coap_add_fd(coap_fd_t fd, coap_fd_t this_fds[], unsigned int *have_this_fds,
781
0
            unsigned int max_this_fds) {
782
0
  if (*have_this_fds < max_this_fds) {
783
0
    this_fds[(*have_this_fds)++] = fd;
784
0
    return 1;
785
0
  }
786
0
  coap_log_warn("coap_io_get_fds: Insufficient space for new fd (%u >= %u)\n", *have_this_fds,
787
0
                max_this_fds);
788
0
  return 0;
789
0
}
790
791
/*
792
 * return  0 Insufficient space to hold fds, or fds not supported
793
 *         1 All fds found
794
 */
795
unsigned int
796
coap_io_get_fds_lkd(coap_context_t *ctx,
797
                    coap_fd_t read_fds[],
798
                    unsigned int *have_read_fds,
799
                    unsigned int max_read_fds,
800
                    coap_fd_t write_fds[],
801
                    unsigned int *have_write_fds,
802
                    unsigned int max_write_fds,
803
0
                    unsigned int *rem_timeout_ms) {
804
0
  *have_read_fds = 0;
805
0
  *have_write_fds = 0;
806
807
0
#ifdef COAP_EPOLL_SUPPORT
808
0
  (void)write_fds;
809
0
  (void)max_write_fds;;
810
811
0
  if (!coap_add_fd(ctx->epfd, read_fds, have_read_fds, max_read_fds))
812
0
    return 0;
813
  /* epoll is making use of timerfd, so no need to return any timeout */
814
0
  *rem_timeout_ms = 0;
815
0
  return 1;
816
#else /* ! COAP_EPOLL_SUPPORT */
817
  coap_session_t *s, *rtmp;
818
  coap_tick_t now;
819
  unsigned int timeout_ms;
820
#if COAP_SERVER_SUPPORT
821
  coap_endpoint_t *ep;
822
823
  LL_FOREACH(ctx->endpoint, ep) {
824
    if (ep->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_ACCEPT)) {
825
      if (!coap_add_fd(ep->sock.fd, read_fds, have_read_fds, max_read_fds))
826
        return 0;
827
    }
828
    if (ep->sock.flags & (COAP_SOCKET_WANT_WRITE | COAP_SOCKET_WANT_CONNECT)) {
829
      if (!coap_add_fd(ep->sock.fd, write_fds, have_write_fds, max_write_fds))
830
        return 0;
831
    }
832
    SESSIONS_ITER_SAFE(ep->sessions, s, rtmp) {
833
      if (s->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_ACCEPT)) {
834
        if (!coap_add_fd(s->sock.fd, read_fds, have_read_fds, max_read_fds))
835
          return 0;
836
      }
837
      if (s->sock.flags & (COAP_SOCKET_WANT_WRITE | COAP_SOCKET_WANT_CONNECT)) {
838
        if (!coap_add_fd(s->sock.fd, write_fds, have_write_fds, max_write_fds))
839
          return 0;
840
      }
841
    }
842
  }
843
#endif /* COAP_SERVER_SUPPORT */
844
845
#if COAP_CLIENT_SUPPORT
846
  SESSIONS_ITER_SAFE(ctx->sessions, s, rtmp) {
847
    if (s->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_ACCEPT)) {
848
      if (!coap_add_fd(s->sock.fd, read_fds, have_read_fds, max_read_fds))
849
        return 0;
850
    }
851
    if (s->sock.flags & (COAP_SOCKET_WANT_WRITE | COAP_SOCKET_WANT_CONNECT)) {
852
      if (!coap_add_fd(s->sock.fd, write_fds, have_write_fds, max_write_fds))
853
        return 0;
854
    }
855
  }
856
#endif /* COAP_CLIENT_SUPPORT */
857
858
  coap_ticks(&now);
859
  timeout_ms = (unsigned int)(ctx->next_timeout ? ctx->next_timeout > now ?
860
                              ctx->next_timeout - now : 0 : 0) *
861
               1000 / COAP_TICKS_PER_SECOND;
862
  *rem_timeout_ms = timeout_ms;
863
  return 1;
864
#endif /* ! COAP_EPOLL_SUPPORT */
865
0
}
866
867
#else /* WITH_LWIP || WITH_CONTIKI */
868
869
/*
870
 * return  0 Insufficient space to hold fds, or fds not supported
871
 *         1 All fds found
872
 */
873
unsigned int
874
coap_io_get_fds_lkd(coap_context_t *ctx,
875
                    coap_fd_t read_fds[],
876
                    unsigned int *have_read_fds,
877
                    unsigned int max_read_fds,
878
                    coap_fd_t write_fds[],
879
                    unsigned int *have_write_fds,
880
                    unsigned int max_write_fds,
881
                    unsigned int *rem_timeout_ms) {
882
  (void)ctx;
883
  (void)read_fds;
884
  (void)max_read_fds;
885
  (void)write_fds;
886
  (void)max_write_fds;
887
888
  *have_read_fds = 0;
889
  *have_write_fds = 0;
890
  *rem_timeout_ms = 0;
891
892
  coap_log_warn("coap_io_get_fds: Not supported\n");
893
  return 0;
894
}
895
#endif /* WITH_LWIP || WITH_CONTIKI */
896
897
COAP_API int
898
0
coap_io_pending(coap_context_t *context) {
899
0
  int ret;
900
901
0
  coap_lock_lock(return 0);
902
0
  ret = coap_io_pending_lkd(context);
903
0
  coap_lock_unlock();
904
0
  return ret;
905
0
}
906
907
/*
908
 * return 1  I/O pending
909
 *        0  No I/O pending
910
 */
911
int
912
0
coap_io_pending_lkd(coap_context_t *context) {
913
0
  coap_session_t *s, *rtmp;
914
0
#if COAP_SERVER_SUPPORT
915
0
  coap_endpoint_t *ep;
916
0
#endif /* COAP_SERVER_SUPPORT */
917
918
0
  if (!context)
919
0
    return 0;
920
0
  coap_lock_check_locked();
921
0
  if (coap_io_process_lkd(context, COAP_IO_NO_WAIT) < 0)
922
0
    return 0;
923
924
0
  if (context->sendqueue)
925
0
    return 1;
926
0
#if COAP_SERVER_SUPPORT
927
0
  LL_FOREACH(context->endpoint, ep) {
928
0
    SESSIONS_ITER(ep->sessions, s, rtmp) {
929
0
      if (s->delayqueue)
930
0
        return 1;
931
0
      if (s->lg_xmit)
932
0
        return 1;
933
0
      if (s->lg_srcv)
934
0
        return 1;
935
0
    }
936
0
  }
937
0
#endif /* COAP_SERVER_SUPPORT */
938
0
#if COAP_CLIENT_SUPPORT
939
0
  SESSIONS_ITER(context->sessions, s, rtmp) {
940
0
    if (s->delayqueue)
941
0
      return 1;
942
0
    if (s->lg_xmit)
943
0
      return 1;
944
0
    if (s->lg_crcv)
945
0
      return 1;
946
0
  }
947
0
#endif /* COAP_CLIENT_SUPPORT */
948
0
  return 0;
949
0
}
950
951
const char *
952
0
coap_socket_format_errno(int error) {
953
0
  return strerror(error);
954
0
}
955
#ifdef _WIN32
956
const char *
957
coap_socket_strerror(void) {
958
  coap_win_error_to_errno();
959
  return coap_socket_format_errno(errno);
960
}
961
#else /* _WIN32 */
962
const char *
963
0
coap_socket_strerror(void) {
964
0
  return coap_socket_format_errno(errno);
965
0
}
966
#endif /* _WIN32 */
967
968
COAP_API coap_fd_t
969
0
coap_socket_get_fd(coap_socket_t *sock) {
970
0
#if !defined(WITH_LWIP) && !defined(WITH_CONTIKI)
971
0
  return sock->fd;
972
#else
973
  (void)(sock);
974
  return COAP_INVALID_SOCKET;
975
#endif
976
0
}
977
978
COAP_API coap_socket_flags_t
979
0
coap_socket_get_flags(coap_socket_t *sock) {
980
0
  return sock->flags;
981
0
}
982
983
COAP_API void
984
0
coap_socket_set_flags(coap_socket_t *sock, coap_socket_flags_t flags) {
985
0
  sock->flags = flags;
986
0
}