/* net.c -- CoAP network interface
*
* Copyright (C) 2010--2014 Olaf Bergmann <bergmann@tzi.org>
*
* This file is part of the CoAP library libcoap. Please see
* README for terms of use.
*/
#include "config.h"
#include <ctype.h>
#include <stdio.h>
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#elif HAVE_SYS_UNISTD_H
#include <sys/unistd.h>
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef WITH_LWIP
#include <lwip/pbuf.h>
#include <lwip/udp.h>
#include <lwip/timers.h>
#endif
#include "debug.h"
#include "mem.h"
#include "str.h"
#include "async.h"
#include "resource.h"
#include "option.h"
#include "encode.h"
#include "block.h"
#include "net.h"
#if defined(WITH_POSIX)
time_t clock_offset;
static inline coap_queue_t *
coap_malloc_node() {
return (coap_queue_t *)coap_malloc(sizeof(coap_queue_t));
}
static inline void
coap_free_node(coap_queue_t *node) {
coap_free(node);
}
#endif /* WITH_POSIX */
#ifdef WITH_LWIP
#include <lwip/memp.h>
static void coap_retransmittimer_execute(void *arg);
static void coap_retransmittimer_restart(coap_context_t *ctx);
static inline coap_queue_t *
coap_malloc_node() {
return (coap_queue_t *)memp_malloc(MEMP_COAP_NODE);
}
static inline void
coap_free_node(coap_queue_t *node) {
memp_free(MEMP_COAP_NODE, node);
}
#endif /* WITH_LWIP */
#ifdef WITH_CONTIKI
# ifndef DEBUG
# define DEBUG DEBUG_PRINT
# endif /* DEBUG */
#include "memb.h"
#include "net/uip-debug.h"
clock_time_t clock_offset;
#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLIPH_LEN])
void coap_resources_init();
void coap_pdu_resources_init();
unsigned char initialized = 0;
coap_context_t the_coap_context;
MEMB(node_storage, coap_queue_t, COAP_PDU_MAXCNT);
PROCESS(coap_retransmit_process, "message retransmit process");
static inline coap_queue_t *
coap_malloc_node() {
return (coap_queue_t *)memb_alloc(&node_storage);
}
static inline void
coap_free_node(coap_queue_t *node) {
memb_free(&node_storage, node);
}
#endif /* WITH_CONTIKI */
#ifdef WITH_LWIP
/** Callback to udp_recv when using lwIP. Gets called by lwIP on arriving
* packages, places a reference in context->pending_package, and calls
* coap_read to process the package. Thus, coap_read needs not be called in
* lwIP main loops. (When modifying this for thread-like operation, ie. if you
* remove the coap_read call from this, make sure that coap_read gets a chance
* to run before this callback is entered the next time.)
*/
static void received_package(void *arg, struct udp_pcb *upcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
{
struct coap_context_t *context = (coap_context_t *)arg;
LWIP_ASSERT("pending_package was not cleared.", context->pending_package == NULL);
context->pending_package = p; /* we don't free it, coap_read has to do that */
context->pending_address.addr = addr->addr; /* FIXME: this has to become address-type independent, probably there'll be an lwip function for that */
context->pending_port = port;
coap_read(context);
}
#endif /* WITH_LWIP */
int print_wellknown(coap_context_t *, unsigned char *, size_t *, size_t, coap_opt_t *);
void coap_handle_failed_notify(coap_context_t *, const coap_address_t *,
const str *);
unsigned int
coap_adjust_basetime(coap_context_t *ctx, coap_tick_t now) {
unsigned int result = 0;
coap_tick_diff_t delta = now - ctx->sendqueue_basetime;
if (ctx->sendqueue) {
/* delta < 0 means that the new time stamp is before the old. */
if (delta <= 0) {
ctx->sendqueue->t -= delta;
} else {
/* This case is more complex: The time must be advanced forward,
* thus possibly leading to timed out elements at the queue's
* start. For every element that has timed out, its relative
* time is set to zero and the result counter is increased. */
coap_queue_t *q = ctx->sendqueue;
coap_tick_t t = 0;
while (q && (t + q->t < (coap_tick_t)delta)) {
t += q->t;
q->t = 0;
result++;
q = q->next;
}
/* finally adjust the first element that has not expired */
if (q) {
q->t = (coap_tick_t)delta - t;
}
}
}
/* adjust basetime */
ctx->sendqueue_basetime += delta;
return result;
}
int
coap_insert_node(coap_queue_t **queue, coap_queue_t *node) {
coap_queue_t *p, *q;
if ( !queue || !node )
return 0;
/* set queue head if empty */
if ( !*queue ) {
*queue = node;
return 1;
}
/* replace queue head if PDU's time is less than head's time */
q = *queue;
if (node->t < q->t) {
node->next = q;
*queue = node;
q->t -= node->t; /* make q->t relative to node->t */
return 1;
}
/* search for right place to insert */
do {
node->t -= q->t; /* make node-> relative to q->t */
p = q;
q = q->next;
} while (q && q->t <= node->t);
/* insert new item */
if (q) {
q->t -= node->t; /* make q->t relative to node->t */
}
node->next = q;
p->next = node;
return 1;
}
int
coap_delete_node(coap_queue_t *node) {
if ( !node )
return 0;
coap_delete_pdu(node->pdu);
coap_free_node(node);
return 1;
}
void
coap_delete_all(coap_queue_t *queue) {
if ( !queue )
return;
coap_delete_all( queue->next );
coap_delete_node( queue );
}
coap_queue_t *
coap_new_node() {
coap_queue_t *node;
node = coap_malloc_node();
if ( ! node ) {
#ifndef NDEBUG
coap_log(LOG_WARNING, "coap_new_node: malloc\n");
#endif
return NULL;
}
memset(node, 0, sizeof *node );
return node;
}
coap_queue_t *
coap_peek_next( coap_context_t *context ) {
if ( !context || !context->sendqueue )
return NULL;
return context->sendqueue;
}
coap_queue_t *
coap_pop_next( coap_context_t *context ) {
coap_queue_t *next;
if ( !context || !context->sendqueue )
return NULL;
next = context->sendqueue;
context->sendqueue = context->sendqueue->next;
if (context->sendqueue) {
context->sendqueue->t += next->t;
}
next->next = NULL;
return next;
}
#ifdef COAP_DEFAULT_WKC_HASHKEY
/** Checks if @p Key is equal to the pre-defined hash key for.well-known/core. */
#define is_wkc(Key) \
(memcmp((Key), COAP_DEFAULT_WKC_HASHKEY, sizeof(coap_key_t)) == 0)
#else
/* Implements a singleton to store a hash key for the .wellknown/core
* resources. */
int
is_wkc(coap_key_t k) {
static coap_key_t wkc;
static unsigned char _initialized = 0;
if (!_initialized) {
_initialized = coap_hash_path((unsigned char *)COAP_DEFAULT_URI_WELLKNOWN,
sizeof(COAP_DEFAULT_URI_WELLKNOWN) - 1, wkc);
}
return memcmp(k, wkc, sizeof(coap_key_t)) == 0;
}
#endif
coap_context_t *
coap_new_context(
const coap_address_t *listen_addr) {
#ifdef WITH_POSIX
coap_context_t *c = coap_malloc( sizeof( coap_context_t ) );
int reuse = 1;
#endif /* WITH_POSIX */
#ifdef WITH_LWIP
coap_context_t *c = memp_malloc(MEMP_COAP_CONTEXT);
#endif /* WITH_LWIP */
#ifdef WITH_CONTIKI
coap_context_t *c;
if (initialized)
return NULL;
#endif /* WITH_CONTIKI */
if (!listen_addr) {
coap_log(LOG_EMERG, "no listen address specified\n");
return NULL;
}
coap_clock_init();
#ifdef WITH_LWIP
prng_init(LWIP_RAND());
#else /* WITH_LWIP */
prng_init((unsigned long)listen_addr ^ clock_offset);
#endif /* WITH_LWIP */
#ifndef WITH_CONTIKI
if ( !c ) {
#ifndef NDEBUG
coap_log(LOG_EMERG, "coap_init: malloc:\n");
#endif
return NULL;
}
#endif /* not WITH_CONTIKI */
#ifdef WITH_CONTIKI
coap_resources_init();
coap_pdu_resources_init();
c = &the_coap_context;
initialized = 1;
#endif /* WITH_CONTIKI */
memset(c, 0, sizeof( coap_context_t ) );
/* initialize message id */
prng((unsigned char *)&c->message_id, sizeof(unsigned short));
/* register the critical options that we know */
coa