/* * Copyright (C) 2016-18 Kaspar Schleiser * 2018 Inria * 2018 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @ingroup net_nanocoap * @{ * * @file * @brief Nanocoap sock helpers * * @author Kaspar Schleiser * * @} */ #include #include #include #include "net/nanocoap_sock.h" #include "net/sock/udp.h" #define ENABLE_DEBUG (0) #include "debug.h" ssize_t nanocoap_request(coap_pkt_t *pkt, sock_udp_ep_t *local, sock_udp_ep_t *remote, size_t len) { ssize_t res; size_t pdu_len = (pkt->payload - (uint8_t *)pkt->hdr) + pkt->payload_len; uint8_t *buf = (uint8_t*)pkt->hdr; sock_udp_t sock; if (!remote->port) { remote->port = COAP_PORT; } res = sock_udp_create(&sock, local, remote, 0); if (res < 0) { return res; } /* TODO: timeout random between between ACK_TIMEOUT and (ACK_TIMEOUT * * ACK_RANDOM_FACTOR) */ uint32_t timeout = COAP_ACK_TIMEOUT * US_PER_SEC; unsigned tries_left = COAP_MAX_RETRANSMIT + 1; /* add 1 for initial transmit */ while (tries_left) { res = sock_udp_send(&sock, buf, pdu_len, NULL); if (res <= 0) { DEBUG("nanocoap: error sending coap request, %d\n", (int)res); break; } res = sock_udp_recv(&sock, buf, len, timeout, NULL); if (res <= 0) { if (res == -ETIMEDOUT) { DEBUG("nanocoap: timeout\n"); timeout *= 2; tries_left--; if (!tries_left) { DEBUG("nanocoap: maximum retries reached\n"); } continue; } DEBUG("nanocoap: error receiving coap response, %d\n", (int)res); break; } else { if (coap_parse(pkt, (uint8_t *)buf, res) < 0) { DEBUG("nanocoap: error parsing packet\n"); res = -EBADMSG; } break; } } sock_udp_close(&sock); return res; } ssize_t nanocoap_get(sock_udp_ep_t *remote, const char *path, uint8_t *buf, size_t len) { ssize_t res; coap_pkt_t pkt; uint8_t *pktpos = buf; pkt.hdr = (coap_hdr_t*)buf; pktpos += coap_build_hdr(pkt.hdr, COAP_TYPE_CON, NULL, 0, COAP_METHOD_GET, 1); pktpos += coap_opt_put_uri_path(pktpos, 0, path); pkt.payload = pktpos; pkt.payload_len = 0; res = nanocoap_request(&pkt, NULL, remote, len); if (res < 0) { return res; } else { res = coap_get_code(&pkt); if (res != 205) { res = -res; } else { if (pkt.payload_len) { memmove(buf, pkt.payload, pkt.payload_len); } res = pkt.payload_len; } } return res; } int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize) { sock_udp_t sock; sock_udp_ep_t remote; if (!local->port) { local->port = COAP_PORT; } ssize_t res = sock_udp_create(&sock, local, NULL, 0); if (res != 0) { return -1; } while (1) { res = sock_udp_recv(&sock, buf, bufsize, -1, &remote); if (res < 0) { DEBUG("error receiving UDP packet %d\n", (int)res); } else if (res > 0) { coap_pkt_t pkt; if (coap_parse(&pkt, (uint8_t *)buf, res) < 0) { DEBUG("error parsing packet\n"); continue; } if ((res = coap_handle_req(&pkt, buf, bufsize)) > 0) { sock_udp_send(&sock, buf, res, &remote); } else { DEBUG("error handling request %d\n", (int)res); } } } return 0; }