1/*
2 * server-coap.c -- LwIP example
3 *
4 * Copyright (C) 2013-2016 Christian Amsüss <chrysn@fsfe.org>
5 * Copyright (C) 2018-2023 Jon Shallow <supjps-libcoap@jpshallow.com>
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 *
9 * This file is part of the CoAP library libcoap. Please see README for terms
10 * of use.
11 */
12
13#include "coap_config.h"
14#include <coap3/coap.h>
15#include "server-coap.h"
16
17coap_context_t *main_coap_context;
18
19static coap_time_t clock_offset;
20/* changeable clock base (see handle_put_time()) */
21static coap_time_t my_clock_base = 0;
22static coap_resource_t *time_resource = NULL; /* just for testing */
23
24#ifndef min
25# define min(a,b) ((a) < (b) ? (a) : (b))
26#endif
27
28void
29hnd_get_time(coap_resource_t *resource, coap_session_t  *session,
30             const coap_pdu_t *request, const coap_string_t *query,
31             coap_pdu_t *response) {
32  unsigned char buf[40];
33  size_t len;
34  coap_tick_t now;
35  coap_tick_t t;
36
37  (void)resource;
38  (void)session;
39  (void)request;
40  /* FIXME: return time, e.g. in human-readable by default and ticks
41   * when query ?ticks is given. */
42
43  /* if my_clock_base was deleted, we pretend to have no such resource */
44  response->code =
45      my_clock_base ? COAP_RESPONSE_CODE(205) : COAP_RESPONSE_CODE(404);
46
47  if (my_clock_base)
48    coap_add_option(response, COAP_OPTION_CONTENT_FORMAT,
49                    coap_encode_var_safe(buf, sizeof(buf),
50                                         COAP_MEDIATYPE_TEXT_PLAIN),
51                    buf);
52
53  coap_add_option(response, COAP_OPTION_MAXAGE,
54                  coap_encode_var_safe(buf, sizeof(buf), 0x01), buf);
55
56  if (my_clock_base) {
57
58    /* calculate current time */
59    coap_ticks(&t);
60    now = my_clock_base + (t / COAP_TICKS_PER_SECOND);
61
62
63    if (query != NULL
64        && coap_string_equal(query, coap_make_str_const("ticks"))) {
65      /* output ticks */
66      len = snprintf((char *)buf, sizeof(buf), "%u", (unsigned int)now);
67      coap_add_data(response, len, buf);
68    }
69  }
70}
71
72void
73init_coap_resources(coap_context_t *ctx) {
74  coap_resource_t *r;
75#if 0
76  r = coap_resource_init(NULL, 0, 0);
77  coap_register_handler(r, COAP_REQUEST_GET, hnd_get_index);
78
79  coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
80  coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"General Info\""), 0);
81  coap_add_resource(ctx, r);
82#endif
83  /* store clock base to use in /time */
84  my_clock_base = clock_offset;
85
86  r = coap_resource_init(coap_make_str_const("time"), 0);
87  if (!r)
88    goto error;
89
90  coap_resource_set_get_observable(r, 1);
91  time_resource = r;
92  coap_register_handler(r, COAP_REQUEST_GET, hnd_get_time);
93#if 0
94  coap_register_handler(r, COAP_REQUEST_PUT, hnd_put_time);
95  coap_register_handler(r, COAP_REQUEST_DELETE, hnd_delete_time);
96#endif
97  coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
98  /* coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"Internal Clock\""), 0); */
99  coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("\"ticks\""), 0);
100  coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("\"clock\""), 0);
101
102  coap_add_resource(ctx, r);
103#if 0
104  if (coap_async_is_supported()) {
105    r = coap_resource_init(coap_make_str_const("async"), 0);
106    coap_register_handler(r, COAP_REQUEST_GET, hnd_get_async);
107
108    coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
109    coap_add_resource(ctx, r);
110  }
111#endif
112
113  return;
114error:
115  coap_log_crit("cannot create resource\n");
116}
117
118void
119server_coap_init(coap_lwip_input_wait_handler_t input_wait,
120                 void *input_arg, int argc, char **argv) {
121  int opt;
122  coap_log_t log_level = COAP_LOG_WARN;
123  coap_log_t dtls_log_level = COAP_LOG_ERR;
124  const char *use_psk = "secretPSK";
125  uint32_t scheme_hint_bits = 0;
126  coap_addr_info_t *info = NULL;
127  coap_addr_info_t *info_list = NULL;
128  int have_ep = 0;
129  coap_str_const_t node;
130
131  /* Initialize libcoap library */
132  coap_startup();
133
134  while ((opt = getopt(argc, argv, ":k:v:V:")) != -1) {
135    switch (opt) {
136    case 'k':
137      use_psk = optarg;
138      break;
139    case 'v':
140      log_level = atoi(optarg);
141      break;
142    case 'V':
143      dtls_log_level = atoi(optarg);
144      break;
145    default:
146      printf("%s [-k PSK] [-v level] [ -V level]\n", argv[0]);
147      exit(1);
148    }
149  }
150
151  coap_startup();
152  coap_set_log_level(log_level);
153  coap_dtls_set_log_level(dtls_log_level);
154
155  main_coap_context = coap_new_context(NULL);
156  LWIP_ASSERT("Failed to initialize context", main_coap_context != NULL);
157
158  if (coap_dtls_is_supported()) {
159    coap_dtls_spsk_t setup_data;
160
161    memset(&setup_data, 0, sizeof(setup_data));
162    setup_data.version = COAP_DTLS_SPSK_SETUP_VERSION;
163    setup_data.psk_info.key.s = (const uint8_t *)use_psk;
164    setup_data.psk_info.key.length = strlen(use_psk);
165    coap_context_set_psk2(main_coap_context, &setup_data);
166  }
167
168  node.s = (const uint8_t *)"::";
169  node.length = 2;
170  scheme_hint_bits =
171      coap_get_available_scheme_hint_bits(use_psk[0],
172                                          0, COAP_PROTO_NONE);
173  info_list = coap_resolve_address_info(&node, 0, 0,
174                                        0, 0,
175                                        0,
176                                        scheme_hint_bits,
177                                        COAP_RESOLVE_TYPE_LOCAL);
178  for (info = info_list; info != NULL; info = info->next) {
179    coap_endpoint_t *ep;
180
181    ep = coap_new_endpoint(main_coap_context, &info->addr, info->proto);
182    if (!ep) {
183      coap_log_warn("cannot create endpoint for proto %u\n",
184                    info->proto);
185    } else {
186      have_ep = 1;
187    }
188  }
189  coap_free_address_info(info_list);
190  LWIP_ASSERT("Failed to initialize context", have_ep != 0);
191
192  /* Limit the number of idle sessions to save RAM (MEMP_NUM_COAPSESSION) */
193  LWIP_ASSERT("Need a minimum of 2 for MEMP_NUM_COAPSESSION", MEMP_NUM_COAPSESSION > 1);
194  coap_context_set_max_idle_sessions(main_coap_context, MEMP_NUM_COAPSESSION -1);
195  clock_offset = 1; /* Need a non-zero value */
196  init_coap_resources(main_coap_context);
197  coap_lwip_set_input_wait_handler(main_coap_context, input_wait, input_arg);
198}
199
200void
201server_coap_finished(void) {
202  coap_free_context(main_coap_context);
203  main_coap_context = NULL;
204  coap_cleanup();
205}
206
207void
208server_coap_poll(void) {
209  static coap_time_t last_time = 0;
210  coap_tick_t ticks_now;
211  coap_time_t time_now;
212
213  coap_io_process(main_coap_context, 1000);
214  coap_ticks(&ticks_now);
215  time_now = coap_ticks_to_rt(ticks_now);
216
217  if (last_time != time_now) {
218    /* This takes place once a second */
219    last_time = time_now;
220    coap_resource_notify_observers(time_resource, NULL);
221  }
222  coap_check_notify(main_coap_context);
223}
224