1/* 2 * client-coap.c -- RIOT client example 3 * 4 * Copyright (C) 2023 Jon Shallow <supjps-libcoap@jpshallow.com> 5 * 6 * SPDX-License-Identifier: BSD-2-Clause 7 * 8 * This file is part of the CoAP library libcoap. Please see README for terms 9 * of use. 10 */ 11 12#include "coap_config.h" 13#include <coap3/coap.h> 14#include <sys/types.h> 15#include <sys/socket.h> 16#include <netdb.h> 17#include "client-coap.h" 18#include "macros/utils.h" 19#include "net/utils.h" 20#include <arpa/inet.h> 21#include <thread.h> 22#include <debug.h> 23 24#ifdef CONFIG_LIBCOAP_CLIENT_URI 25#define COAP_CLIENT_URI CONFIG_LIBCOAP_CLIENT_URI 26#else /* ! CONFIG_LIBCOAP_CLIENT_URI */ 27#define COAP_CLIENT_URI "coap://[fe80::405:5aff:fe15:9b7f]/.well-known/core" 28#endif /* ! CONFIG_LIBCOAP_CLIENT_URI */ 29 30#ifdef CONFIG_LIBCOAP_USE_PSK 31#define COAP_USE_PSK CONFIG_LIBCOAP_USE_PSK 32#else /* ! CONFIG_LIBCOAP_USE_PSK */ 33#define COAP_USE_PSK NULL 34#endif /* ! CONFIG_LIBCOAP_USE_PSK */ 35 36#ifdef CONFIG_LIBCOAP_USE_PSK_ID 37#define COAP_USE_PSK_ID CONFIG_LIBCOAP_USE_PSK_ID 38#else /* ! CONFIG_LIBCOAP_USE_PSK_ID */ 39#define COAP_USE_PSK_ID NULL 40#endif /* ! CONFIG_LIBCOAP_USE_PSK_ID */ 41 42static coap_context_t *main_coap_context = NULL; 43static coap_optlist_t *optlist = NULL; 44 45static int quit = 0; 46 47static coap_response_t 48message_handler(coap_session_t *session, 49 const coap_pdu_t *sent, 50 const coap_pdu_t *received, 51 const coap_mid_t id) { 52 const uint8_t *data; 53 size_t len; 54 size_t offset; 55 size_t total; 56 57 (void)session; 58 (void)sent; 59 (void)id; 60 if (coap_get_data_large(received, &len, &data, &offset, &total)) { 61 printf("%*.*s", (int)len, (int)len, (const char *)data); 62 if (len + offset == total) { 63 printf("\n"); 64 quit = 1; 65 } 66 } 67 return COAP_RESPONSE_OK; 68} 69 70static void 71nack_handler(coap_session_t *session COAP_UNUSED, 72 const coap_pdu_t *sent COAP_UNUSED, 73 const coap_nack_reason_t reason, 74 const coap_mid_t id COAP_UNUSED) { 75 76 switch (reason) { 77 case COAP_NACK_TOO_MANY_RETRIES: 78 case COAP_NACK_NOT_DELIVERABLE: 79 case COAP_NACK_RST: 80 case COAP_NACK_TLS_FAILED: 81 case COAP_NACK_TLS_LAYER_FAILED: 82 case COAP_NACK_WS_LAYER_FAILED: 83 case COAP_NACK_WS_FAILED: 84 coap_log_err("cannot send CoAP pdu\n"); 85 quit = 1; 86 break; 87 case COAP_NACK_ICMP_ISSUE: 88 case COAP_NACK_BAD_RESPONSE: 89 default: 90 break; 91 } 92 return; 93} 94 95static int 96resolve_address(const char *host, const char *service, coap_address_t *dst, 97 int scheme_hint_bits) { 98 uint16_t port = service ? atoi(service) : 0; 99 int ret = 0; 100 coap_str_const_t str_host; 101 coap_addr_info_t *addr_info; 102 103 str_host.s = (const uint8_t *)host; 104 str_host.length = strlen(host); 105 addr_info = coap_resolve_address_info(&str_host, port, port, port, port, 106 AF_UNSPEC, scheme_hint_bits, 107 COAP_RESOLVE_TYPE_REMOTE); 108 if (addr_info) { 109 ret = 1; 110 *dst = addr_info->addr; 111 } 112 113 coap_free_address_info(addr_info); 114 return ret; 115} 116 117void 118client_coap_init(int argc, char **argv) { 119 coap_session_t *session = NULL; 120 coap_pdu_t *pdu; 121 coap_address_t dst; 122 coap_mid_t mid; 123 int len; 124 coap_uri_t uri; 125 char portbuf[8]; 126#define BUFSIZE 100 127 unsigned char buf[BUFSIZE]; 128 int res; 129 const char *coap_uri = COAP_CLIENT_URI; 130 131 if (argc > 1) { 132 coap_uri = argv[1]; 133 } 134 135 /* Initialize libcoap library */ 136 coap_startup(); 137 138 coap_set_log_level(COAP_MAX_LOGGING_LEVEL); 139 140 /* Parse the URI */ 141 len = coap_split_uri((const unsigned char *)coap_uri, strlen(coap_uri), &uri); 142 if (len != 0) { 143 coap_log_warn("Failed to parse uri %s\n", coap_uri); 144 goto fail; 145 } 146 147 snprintf(portbuf, sizeof(portbuf), "%d", uri.port); 148 snprintf((char *)buf, sizeof(buf), "%*.*s", (int)uri.host.length, 149 (int)uri.host.length, (const char *)uri.host.s); 150 /* resolve destination address where packet should be sent */ 151 len = resolve_address((const char *)buf, portbuf, &dst, 1 << uri.scheme); 152 if (len <= 0) { 153 coap_log_warn("Failed to resolve address %*.*s\n", (int)uri.host.length, 154 (int)uri.host.length, (const char *)uri.host.s); 155 goto fail; 156 } 157 158 main_coap_context = coap_new_context(NULL); 159 if (!main_coap_context) { 160 coap_log_warn("Failed to initialize context\n"); 161 goto fail; 162 } 163 164 coap_context_set_block_mode(main_coap_context, COAP_BLOCK_USE_LIBCOAP); 165 166 if (uri.scheme == COAP_URI_SCHEME_COAP) { 167 session = coap_new_client_session(main_coap_context, NULL, &dst, 168 COAP_PROTO_UDP); 169 } else if (uri.scheme == COAP_URI_SCHEME_COAP_TCP) { 170 session = coap_new_client_session(main_coap_context, NULL, &dst, 171 COAP_PROTO_TCP); 172#if defined (COAP_USE_PSK) && defined(COAP_USE_PSK_ID) 173 } else { 174 static coap_dtls_cpsk_t dtls_psk; 175 static char client_sni[256]; 176 177 memset(client_sni, 0, sizeof(client_sni)); 178 memset(&dtls_psk, 0, sizeof(dtls_psk)); 179 dtls_psk.version = COAP_DTLS_CPSK_SETUP_VERSION; 180 if (uri.host.length) { 181 memcpy(client_sni, uri.host.s, 182 MIN(uri.host.length, sizeof(client_sni) - 1)); 183 } 184 else { 185 memcpy(client_sni, "localhost", 9); 186 } 187 dtls_psk.client_sni = client_sni; 188 dtls_psk.psk_info.identity.s = (const uint8_t *)COAP_USE_PSK_ID; 189 dtls_psk.psk_info.identity.length = strlen(COAP_USE_PSK_ID); 190 dtls_psk.psk_info.key.s = (const uint8_t *)COAP_USE_PSK; 191 dtls_psk.psk_info.key.length = strlen(COAP_USE_PSK); 192 193 session = coap_new_client_session_psk2(main_coap_context, NULL, &dst, 194 COAP_PROTO_DTLS, &dtls_psk); 195#else /* ! COAP_USE_PSK && ! COAP_USE_PSK_ID */ 196 coap_log_err("CONFIG_LIBCOAP_USE_PSK and CONFIG_LIBCOAP_USE_PSK_ID not defined\n"); 197 goto fail; 198#endif /* ! COAP_USE_PSK && ! COAP_USE_PSK_ID */ 199 } 200 201 if (!session) { 202 coap_log_warn("Failed to create session\n"); 203 goto fail; 204 } 205 206 coap_register_response_handler(main_coap_context, message_handler); 207 coap_register_nack_handler(main_coap_context, nack_handler); 208 209 /* construct CoAP message */ 210 pdu = coap_pdu_init(COAP_MESSAGE_CON, 211 COAP_REQUEST_CODE_GET, 212 coap_new_message_id(session), 213 coap_session_max_pdu_size(session)); 214 if (!pdu) { 215 coap_log_warn("Failed to create PDU\n"); 216 goto fail; 217 } 218 219 len = coap_uri_into_options(&uri, &dst, &optlist, 1, buf, sizeof(buf)); 220 if (len) { 221 coap_log_warn("Failed to create options\n"); 222 goto fail; 223 } 224 225 /* Add option list (which will be sorted) to the PDU */ 226 if (optlist) { 227 res = coap_add_optlist_pdu(pdu, &optlist); 228 if (res != 1) { 229 coap_log_warn("Failed to add options to PDU\n"); 230 goto fail; 231 } 232 } 233 234 /* and send the PDU */ 235 mid = coap_send(session, pdu); 236 if (mid == COAP_INVALID_MID) { 237 coap_log_warn("Failed to send PDU\n"); 238 goto fail; 239 } 240 while (!quit) { 241 coap_io_process(main_coap_context, 1000); 242 } 243fail: 244 /* Clean up library usage so client can be run again */ 245 quit = 0; 246 coap_delete_optlist(optlist); 247 optlist = NULL; 248 coap_session_release(session); 249 session = NULL; 250 coap_free_context(main_coap_context); 251 main_coap_context = NULL; 252 coap_cleanup(); 253} 254