NOTE: This mirror repository may be out of date, check the main repo for changes. If there are any remind me to update this mirror.

This is an unstable alpha of the Official Exosite library, there are known issues with the port to this platform. You probably shouldn't use this library yet if you just want to get things done.

This version uses CoAP for the application protocol.

picocoap/examples/posix/client_dtls.c

Committer:
Patrick Barrett
Date:
2015-01-07
Revision:
29:004c318e63fa
Parent:
7:f9df43829cea

File content as of revision 29:004c318e63fa:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <stdlib.h>
#include <time.h>

#include "coap.h"
#include "dtls.h"

#define COAPS_SRV "173.255.243.158"
#define COAPS_SRV_PORT 20220

#define BUFSIZE 2048
#define MAX_DOWNLOAD_SIZE 102400 // 100k
#define MAX_RETRY 3
#define TIMEOUT 5

#define PRINTF(...) printf(__VA_ARGS__);fflush(stdout);

#define TEST_READ 0x02
#define TEST_WRITE 0x04
#define TEST_UPLOAD_FILE 0x08
#define TEST_ACTIVATE 0x10
#define TEST_DOWNLOAD_CONTENT 0x20

#define block_size 5 // Range: 1 - 6
 
void hex_dump(uint8_t* bytes, size_t len);
void coap_pretty_print(uint8_t* pkt, size_t len);

int main(void)
{
  char alias[] = "temp";
  char cik[] = "a32c85ba9dda45823be416246cf8b433baa068d7";

  char host[] = "coap.exosite.com";
  char port[] = "5683";

  srand(time(NULL));

  // CoAP Message Setup
  #define MSG_BUF_LEN 64
  uint8_t msg_send[MSG_BUF_LEN];
  size_t  msg_send_len = 0;
  uint8_t msg_recv[MSG_BUF_LEN];
  size_t  msg_recv_len = 0;

  uint16_t message_id_counter = rand();

  // Socket to Exosite
  int localsock, remotesock;
  size_t bytes_sent;
  int rv;

  struct addrinfo exohints, *servinfo, *p, *q;

  memset(&exohints, 0, sizeof exohints);
  exohints.ai_family = AF_UNSPEC;
  exohints.ai_socktype = SOCK_DGRAM;
  exohints.ai_flags = AI_PASSIVE;

  if ((rv = getaddrinfo(NULL, port, &exohints, &servinfo)) != 0) {
    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
    return 1;
  }

  // loop through all the results and make a socket
  for(p = servinfo; p != NULL; p = p->ai_next) {
    if ((localsock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
      perror("bad socket");
      continue;
    }

    if (bind(localsock, p->ai_addr, p->ai_addrlen) == -1) {
      close(localsock);
      perror("bad bind");
      continue;
    }

    break;
  }

  if (p == NULL) {
      fprintf(stderr, "Failed to Bind Socket\n");
      return 2;
  }

  if ((rv = getaddrinfo(host, port, &exohints, &servinfo)) != 0) {
    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
    return 1;
  }


  // loop through all the results and make a socket
  for(q = servinfo; q != NULL; q = q->ai_next) {
    if ((remotesock = socket(q->ai_family, q->ai_socktype, q->ai_protocol)) == -1) {
      perror("bad socket");
      continue;
    }

    break;
  }

  if (q == NULL) {
      fprintf(stderr, "Failed to Bind Socket\n");
      return 2;
  }
 
  for (;;) 
  {
    printf("--------------------------------------------------------------------------------\n");

    // Build Message
    msg_send_len = 0; // Clear Message Buffer
    memset(msg_send, 0, msg_send_len);
    coap_set_version(msg_send, &msg_send_len, MSG_BUF_LEN, COAP_V1);
    coap_set_type(msg_send, &msg_send_len, MSG_BUF_LEN, CT_CON);
    coap_set_code(msg_send, &msg_send_len, MSG_BUF_LEN, CC_GET); //or POST
    coap_set_mid(msg_send, &msg_send_len, MSG_BUF_LEN, message_id_counter++);
    coap_set_token(msg_send, &msg_send_len, MSG_BUF_LEN, rand(), 2);
    coap_add_option(msg_send, &msg_send_len, MSG_BUF_LEN, CON_URI_PATH, (uint8_t*)"1a", 2);
    coap_add_option(msg_send, &msg_send_len, MSG_BUF_LEN, CON_URI_PATH, (uint8_t*)alias, strlen(alias));
    coap_add_option(msg_send, &msg_send_len, MSG_BUF_LEN, CON_URI_QUERY, (uint8_t*)cik, strlen(cik));
    //coap_set_payload(msg_send, &msg_send_len, MSG_BUF_LEN, (uint8_t*)"99", 2);

    // Send Message
    if ((bytes_sent = sendto(remotesock, msg_send, msg_send_len, 0, q->ai_addr, q->ai_addrlen)) == -1){
      fprintf(stderr, "Failed to Send Message\n");
      return 2;
    }

    printf("Sent.\n");
    coap_pretty_print(msg_send, msg_send_len);

    // Wait for Response
    msg_recv_len = recvfrom(remotesock, (void *)msg_recv, sizeof(msg_recv), 0, q->ai_addr, &q->ai_addrlen);
    if (msg_recv_len < 0) {
      fprintf(stderr, "%s\n", strerror(errno));
      exit(EXIT_FAILURE);
    }


    if(coap_validate_pkt(msg_recv, msg_recv_len) == CS_OK)
    {
      printf("Got Valid CoAP Packet\n");
      if(coap_get_mid(msg_recv, msg_recv_len) == coap_get_mid(msg_send, msg_send_len) &&
         coap_get_token(msg_recv, msg_recv_len, 0) == coap_get_token(msg_send, msg_send_len, 0)) //this is only actually checking token length, should check token.
      {
        printf("Is Response to Last Message\n");
        coap_pretty_print(msg_recv, msg_recv_len);
      }
    }else{
      printf("Received %zi Bytes, Not Valid CoAP\n", msg_recv_len);
      hex_dump(msg_recv, msg_recv_len);
    }

    usleep(1000000); // One Second
  }
}

// TEST_READ , TEST_WRITE
static char alias[] = "temp";
static char cik[] = "a32c85ba9dda45823be416246cf8b433baa068d7";
static char value_to_write[] = "56";

// dtls setup
static size_t dtls_connected = 0;
static size_t ready_to_recv = 0;
static dtls_context_t *dtls_context = NULL;
static session_t session;

typedef struct trans_data {
    int out;
    int ack;
    uint8* data;
    int retry;
} trans_data;

struct trans_data last_sent = {.retry=0};

#define SET_SEND_TIMESTAMP() {last_sent.out = (int)time(NULL); \
                              last_sent.data = (uint8*)sent_pdu->hdr; \
                              last_sent.ack = 0; \
                              last_sent.retry++;}
#define SET_ACK_TIMESTAMP() {last_sent.ack = (int)time(NULL); last_sent.retry = 0;}

int order_opts(void *a, void *b)
{
    if (!a || !b)
        return a < b ? -1 : 1;

    if (COAP_OPTION_KEY(*(coap_option *)a) < COAP_OPTION_KEY(*(coap_option *)b))
        return -1;

    return COAP_OPTION_KEY(*(coap_option *)a) == COAP_OPTION_KEY(*(coap_option *)b);
}

coap_pdu_t* coap_new_request(coap_context_t *ctx,  unsigned char method, coap_list_t *options, str payload )
{
    coap_pdu_t *pdu;
    coap_list_t *opt;

    if ( ! ( pdu = coap_new_pdu() ) )
        return NULL;

    pdu->hdr->type = COAP_MESSAGE_CON;
    pdu->hdr->id = coap_new_message_id(ctx);
    pdu->hdr->code = method;
    pdu->hdr->token_length = the_token.length;
    if ( !coap_add_token(pdu, the_token.length, the_token.s)) {
        debug("cannot add token to request\n");
    }
    for (opt = options; opt; opt = opt->next) {
        coap_add_option(pdu, COAP_OPTION_KEY(*(coap_option *)opt->data),
                        COAP_OPTION_LENGTH(*(coap_option *)opt->data),
                        COAP_OPTION_DATA(*(coap_option *)opt->data));
    }
    if (payload.length) {
        if (block_flag)
            coap_add_block(pdu, payload.length, payload.s, block.num, block.szx);
        else
            coap_add_data(pdu, payload.length, payload.s);
    }

    //coap_show_pdu(pdu);

    return pdu;
}

coap_list_t* new_option_node(unsigned short key, unsigned int length, unsigned char *data)
{
    coap_option *option;
    coap_list_t *node;

    option = coap_malloc(sizeof(coap_option) + length);
    if ( !option )
        goto error;

    COAP_OPTION_KEY(*option) = key;
    COAP_OPTION_LENGTH(*option) = length;
    memcpy(COAP_OPTION_DATA(*option), data, length);
    /* we can pass NULL here as delete function since option is released automatically  */
    node = coap_new_listnode(option, NULL);

    if ( node )
        return node;

error:
    perror("new_option_node: malloc");
    coap_free( option );
    return NULL;
}

coap_context_t* get_context(const char *node, const char *port)
{
    coap_context_t *ctx = NULL;
    int s;
    struct addrinfo hints;
    struct addrinfo *result, *rp;

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
    hints.ai_socktype = SOCK_DGRAM; /* Coap uses UDP */
    hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV | AI_ALL;

    s = getaddrinfo(node, port, &hints, &result);
    if ( s != 0 ) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        return NULL;
    }
    /* iterate through results until success */
    for (rp = result; rp != NULL; rp = rp->ai_next) {
        coap_address_t addr;
        if (rp->ai_addrlen <= sizeof(addr.addr)) {
            coap_address_init(&addr);
            addr.size = rp->ai_addrlen;
            memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen);

            ctx = coap_new_context(&addr);
            if (ctx) {
                goto finish;
            }
        }
    }

    fprintf(stderr, "no context available for interface '%s'\n", node);

finish:
    freeaddrinfo(result);
    return ctx;
}

coap_pdu_t* get_pdu(char* url, unsigned char method, str payload)
{
    coap_pdu_t  *pdu;
    size_t buflen;
    int res;
    optlist=NULL;

    coap_split_uri((unsigned char *)url, strlen(url), &uri );
    if (uri.path.length) {
        buflen = BUFSIZE;
        unsigned char _buf[BUFSIZE];
        unsigned char *buf = _buf;
        res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
        while (res--) {
            coap_insert(&optlist,
                        new_option_node(
                            COAP_OPTION_URI_PATH,
                            COAP_OPT_LENGTH(buf),
                            COAP_OPT_VALUE(buf)),
                        order_opts);
            buf += COAP_OPT_SIZE(buf);
        }
    }
    if (uri.query.length) {
        buflen = BUFSIZE;
        unsigned char _buf[BUFSIZE];
        unsigned char *buf = _buf;
        res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
        while (res--) {
            coap_insert(&optlist,
                        new_option_node(
                            COAP_OPTION_URI_QUERY,
                            COAP_OPT_LENGTH(buf),
                            COAP_OPT_VALUE(buf)),
                        order_opts);
            buf += COAP_OPT_SIZE(buf);
        }
    }
    if (NULL == coap_ctx)
        coap_ctx = get_context("0.0.0.0", "0");

    if (block_flag) {
        static unsigned char tmp[4];
        unsigned short opt;
        opt = method == COAP_REQUEST_GET ? COAP_OPTION_BLOCK2 : COAP_OPTION_BLOCK1;
        char more;
        if (coap_more_blocks(payload.length, block.num, block.szx))
            more = 0x08;
        else
            more = 0x00;
        //printf(" more:%d block num:%d\n", more, block.num);
        coap_insert(&optlist,
                    new_option_node(
                        opt,
                        coap_encode_var_bytes(tmp, (block.num << 4 | block.szx | more)),
                        tmp),
                    order_opts);
    }
    if (! (pdu = coap_new_request(coap_ctx, method, optlist, payload)))
        return NULL;
    return pdu;

}

coap_pdu_t* get_pdu_activate(char* vendor, char* model, char* serial_number)
{
    char dest[BUFSIZE];
    strcpy(dest, "coap://0.0.0.0/provision/activate/"); //address is virtual
    strcat(dest, vendor);
    strcat(dest, "/");
    strcat(dest, model);
    strcat(dest, "/");
    strcat(dest, serial_number);
    str payload = { 0, NULL };
    return get_pdu(dest, COAP_REQUEST_POST, payload);


    msg_send_len = 0; // Clear Message Buffer
    memset(msg_send, 0, msg_send_len);
    coap_set_version(msg_send, &msg_send_len, MSG_BUF_LEN, COAP_V1);
    coap_set_type(msg_send, &msg_send_len, MSG_BUF_LEN, CT_CON);
    coap_set_code(msg_send, &msg_send_len, MSG_BUF_LEN, CC_POST);
    coap_set_mid(msg_send, &msg_send_len, MSG_BUF_LEN, message_id_counter++);
    coap_set_token(msg_send, &msg_send_len, MSG_BUF_LEN, rand(), 2);
    coap_add_option(msg_send, &msg_send_len, MSG_BUF_LEN, CON_URI_PATH, (uint8_t*)"provision", 9);
    coap_add_option(msg_send, &msg_send_len, MSG_BUF_LEN, CON_URI_PATH, (uint8_t*)"activate", 8);
    coap_add_option(msg_send, &msg_send_len, MSG_BUF_LEN, CON_URI_PATH, (uint8_t*)vendor, strlen(vendor));
    coap_add_option(msg_send, &msg_send_len, MSG_BUF_LEN, CON_URI_PATH, (uint8_t*)model, strlen(model));
    coap_add_option(msg_send, &msg_send_len, MSG_BUF_LEN, CON_URI_PATH, (uint8_t*)serial_number, strlen(serial_number));
}

coap_pdu_t* get_pdu_download_content()
{
    char dest[BUFSIZE];
    strcpy(dest, "coap://0.0.0.0/provision/download/"); //address is virtual
    strcat(dest, vendor);
    strcat(dest, "/");
    strcat(dest, model);
    strcat(dest, "/");
    strcat(dest, content_id);
    strcat(dest, "?");
    strcat(dest, client_model_cik);
    str payload = { 0, NULL };
    block_flag = 1;
    return get_pdu(dest, COAP_REQUEST_GET, payload);

    msg_send_len = 0; // Clear Message Buffer
    memset(msg_send, 0, msg_send_len);
    coap_set_version(msg_send, &msg_send_len, MSG_BUF_LEN, COAP_V1);
    coap_set_type(msg_send, &msg_send_len, MSG_BUF_LEN, CT_CON);
    coap_set_code(msg_send, &msg_send_len, MSG_BUF_LEN, CC_POST);
    coap_set_mid(msg_send, &msg_send_len, MSG_BUF_LEN, message_id_counter++);
    coap_set_token(msg_send, &msg_send_len, MSG_BUF_LEN, rand(), 2);
    coap_add_option(msg_send, &msg_send_len, MSG_BUF_LEN, CON_URI_PATH, (uint8_t*)"provision", 9);
    coap_add_option(msg_send, &msg_send_len, MSG_BUF_LEN, CON_URI_PATH, (uint8_t*)"download", 8);
    coap_add_option(msg_send, &msg_send_len, MSG_BUF_LEN, CON_URI_PATH, (uint8_t*)vendor, strlen(vendor));
    coap_add_option(msg_send, &msg_send_len, MSG_BUF_LEN, CON_URI_PATH, (uint8_t*)model, strlen(model));
    coap_add_option(msg_send, &msg_send_len, MSG_BUF_LEN, CON_URI_PATH, (uint8_t*)serial_number, strlen(serial_number));
    coap_add_option(msg_send, &msg_send_len, MSG_BUF_LEN, CON_URI_PATH, (uint8_t*)alias, strlen(alias));
    coap_add_option(msg_send, &msg_send_len, MSG_BUF_LEN, CON_URI_QUERY, (uint8_t*)cik, strlen(cik));

    return EXO_OK;
}

coap_pdu_t* get_pdu_read1p(char* cik, char* alias)
{
    char dest[BUFSIZE];
    strcpy(dest, "coap://0.0.0.0/1a/"); //address is virtual
    strcat(dest, alias);
    strcat(dest, "?");
    strcat(dest, cik);
    str payload = { 0, NULL };
    return get_pdu(dest, COAP_REQUEST_GET, payload);
}

coap_pdu_t* get_pdu_write1p(char* cik, char* alias, char* value)
{
    char dest[BUFSIZE];
    strcpy(dest, "coap://0.0.0.0/1a/"); //address is virtual
    strcat(dest, alias);
    strcat(dest, "?");
    strcat(dest, cik);
    str payload = { strlen(value), (unsigned char*)value };
    return get_pdu(dest, COAP_REQUEST_POST, payload);
}

coap_pdu_t* get_pdu_block_upload(char* cik, char* alias, int length, unsigned char* data)
{
    char dest[BUFSIZE];
    strcpy(dest, "coap://0.0.0.0/1a/"); //address is virtual
    strcat(dest, alias);
    strcat(dest, "?");
    strcat(dest, cik);
    str payload = { length, data };
    block_flag = 1;
    return get_pdu(dest, COAP_REQUEST_PUT, payload);
}

static inline coap_opt_t * get_block(coap_pdu_t *pdu, coap_opt_iterator_t *opt_iter)
{
    coap_opt_filter_t f;

    assert(pdu);
    memset(f, 0, sizeof(coap_opt_filter_t));
    coap_option_setb(f, COAP_OPTION_BLOCK1);
    coap_option_setb(f, COAP_OPTION_BLOCK2);

    coap_option_iterator_init(pdu, opt_iter, f);
    return coap_option_next(opt_iter);
}

inline int check_token(coap_pdu_t *received)
{
    return received->hdr->token_length == the_token.length &&
           memcmp(received->hdr->token, the_token.s, the_token.length) == 0;
}

void message_handler(coap_pdu_t *received)
{
    size_t len;
    unsigned char *databuf = NULL;
    coap_opt_t *block_opt;
    coap_opt_iterator_t opt_iter;

    PRINTF("Receive %d.%02d response.\n\n",
           (received->hdr->code >> 5), received->hdr->code & 0x1F);
    /* check if this is a response to our original request */
    if (!check_token(received)) {
        return;
    }

    coap_get_data(received, &len, &databuf);
    block_opt = get_block(received, &opt_iter);
    coap_check_option(received, COAP_OPTION_SUBSCRIPTION, &opt_iter);

    if ( received->hdr->code == COAP_RESPONSE_CODE(205) ) {
        if (block_opt) {  // block option
            download_size +=  len;
            if (COAP_OPT_BLOCK_MORE(block_opt)) { // has more
                PRINTF("found the M bit, block size is %u, block nr. %u\n",
                       COAP_OPT_BLOCK_SZX(block_opt), coap_opt_block_num(block_opt));
                strcat(download_content, (char*)databuf);
                SET_ACK_TIMESTAMP();
                block.num += 1;
                sent_pdu = get_pdu_download_content(vendor, model, content_id);
                dtls_write(dtls_context, &session, (uint8*)sent_pdu->hdr, sent_pdu->length);
                SET_SEND_TIMESTAMP();
            } else {
                strcat(download_content, (char*)databuf);
                FILE *fp;
                fp=fopen(download_save_file, "wb");
                fwrite(download_content, 1, download_size, fp);
                fclose(fp);
                PRINTF("Downloaded content is saved to file '%s'.\n", download_save_file);
                ready_to_recv = 0;
            }
        } else {   // no block option
            if (databuf)
                PRINTF("payload: '%s'\n", databuf);
            ready_to_recv = 0;
            return;
        }
    } else if ( received->hdr->code == COAP_RESPONSE_CODE(204)) {
        if (databuf)
            PRINTF("payload: '%s'\n", databuf);
        ready_to_recv = 0;
    } else {                   //  other than 2.05, 2.04//
        if (block_opt && received->hdr->code == COAP_RESPONSE_CODE(231)) { // block option
            if (COAP_OPT_BLOCK_MORE(block_opt)) { // has more
                PRINTF("found the M bit, block size is %u, block nr. %u\n",
                       COAP_OPT_BLOCK_SZX(block_opt), coap_opt_block_num(block_opt));
                SET_ACK_TIMESTAMP();
                block.num += 1;
                sent_pdu = get_pdu_block_upload(cik, alias, file_size, file_data);
                dtls_write(dtls_context, &session, (uint8*)sent_pdu->hdr, sent_pdu->length);
                SET_SEND_TIMESTAMP();
            }
        } else { //not block option
            if (databuf)
                PRINTF("payload: '%s'\n", databuf);
            ready_to_recv = 0;
        }
    }
}

/* This function is the "key store" for tinyDTLS. It is called to
 * retrieve a key for the given identiy within this particular
 * session. */
int get_key(struct dtls_context_t *ctx,
            const session_t *session,
            const unsigned char *id, size_t id_len,
            const dtls_key_t **result)
{
    static const dtls_key_t psk = {
        .type = DTLS_KEY_PSK,
        .key.psk.id = (unsigned char *)"Client_identity",
        .key.psk.id_length = 15,
        .key.psk.key = (unsigned char *)"ex0CoAPs",
        .key.psk.key_length = 9
    };
    *result = &psk;
    return 0;
}

int handle_event(struct dtls_context_t *ctx, session_t *session,
                 dtls_alert_level_t level, unsigned short code)
{
    if  (code == DTLS_EVENT_CONNECTED)
        dtls_connected = 1;
    return 0;
}

int read_from_server(struct dtls_context_t *ctx,
                     session_t *session, uint8 *data, size_t len)
{
    //printf ("Server message:\n");
    //size_t i;
    //for (i = 0; i < len; i++)
    //    printf("%02x ", data[i]);
    //printf("\n");
    coap_pdu_t* received_pdu = coap_new_pdu();
    if (coap_pdu_parse(data, len, received_pdu)) {
        message_handler(received_pdu);
    } else
        PRINTF("Invalid CoAP message!");

    return 0;
}

int send_to_peer(struct dtls_context_t *ctx,
                 session_t *session, uint8 *data, size_t len)
{
    int fd = *(int *)dtls_get_app_data(ctx);
    return sendto(fd, data, len, MSG_DONTWAIT,
                  &session->addr.sa, session->size);
}

int dtls_handle_read(struct dtls_context_t *ctx)
{
    int fd;
    session_t session;
    static uint8 buf[BUFSIZE];
    int len;

    fd = *(int *)dtls_get_app_data(ctx);

    if (!fd)
        return -1;

    memset(&session, 0, sizeof(session_t));
    session.size = sizeof(session.addr);
    len = recvfrom(fd, buf, BUFSIZE, 0,
                   &session.addr.sa, &session.size);
    if (len < 0) {
        perror("recvfrom");
        return -1;
    } else {
    }
    return dtls_handle_message(ctx, &session, buf, len);
}

int resolve_address(const char *server, struct sockaddr *dst)
{
    struct addrinfo *res, *ainfo;
    struct addrinfo hints;
    static char addrstr[256];
    int error;

    memset(addrstr, 0, sizeof(addrstr));
    if (server && strlen(server) > 0)
        memcpy(addrstr, server, strlen(server));
    else
        memcpy(addrstr, "localhost", 9);

    memset ((char *)&hints, 0, sizeof(hints));
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_family = AF_UNSPEC;

    error = getaddrinfo(addrstr, "", &hints, &res);

    if (error != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error));
        return error;
    }
    for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) {

        switch (ainfo->ai_family) {
        case AF_INET6:
        case AF_INET:
            memcpy(dst, ainfo->ai_addr, ainfo->ai_addrlen);
            return ainfo->ai_addrlen;
        default:
            ;
        }
    }
    freeaddrinfo(res);
    return -1;
}

/*---------------------------------------------------------------------------*/

static dtls_handler_t cb = {
    .write = send_to_peer,
    .read  = read_from_server,
    .event = handle_event,
    .get_key = get_key
};
void get_file_content(char* file_name, unsigned int* size, unsigned char** content)
{
    FILE *fh = fopen(file_name, "rw");
    if ( fh == NULL ) {
        perror(file_name);
        return;
    }
    fseek(fh, 0, SEEK_END);
    *size = ftell(fh);
    rewind(fh);
    *content = NULL;
    *content = (unsigned char*)malloc(*size);
    if ( *size != fread(*content, *size, 1, fh) )
        return;
    fclose(fh);
    return;
}

int main(int argc, char **argv)
{
    //dtls_context_t *dtls_context = NULL;
    fd_set rfds;
    struct timeval timeout;
    int sock, result;
    int on = 1;
    int res;

    dtls_init();
    memset(&session, 0, sizeof(session_t));

    /* resolve destination address where server should be sent */
    res = resolve_address(COAPS_SRV, &session.addr.sa);

    if (res < 0) {
        PRINTF("failed to resolve address\n");
        exit(-1);
    }
    session.size = res;
    session.addr.sin.sin_port = htons(COAPS_SRV_PORT);

    sock = socket(session.addr.sa.sa_family, SOCK_DGRAM, 0);
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    dtls_context = dtls_new_context(&sock);

    if (!dtls_context) {
        PRINTF("cannot create context\n");
        exit(-1);
    }

    dtls_set_handler(dtls_context, &cb);
    dtls_connect(dtls_context, &session);

    while (1) {
        FD_ZERO(&rfds);
        FD_SET(sock, &rfds);

        timeout.tv_sec = 2;
        timeout.tv_usec = 0;

        result = select(sock+1, &rfds, 0, 0, &timeout);

        if (result < 0) {   // error
            if (errno != EINTR)
                perror("select");
        } else if (result == 0) { // timeout
            if (last_sent.out > 0 && last_sent.ack == 0) {
                if ((int)time(NULL) - last_sent.out > TIMEOUT) { // TIMEOUT
                    if (last_sent.retry <= MAX_RETRY) {
                        dtls_write(dtls_context, &session, last_sent.data, sent_pdu->length);
                        SET_SEND_TIMESTAMP();
                    } else {
                        PRINTF("Failed: reach retry limit");
                        break;
                    }
                }
            }
        } else {      // ok
            if (FD_ISSET(sock, &rfds)) {
                dtls_handle_read(dtls_context);
            }
        }
        if (dtls_connected && !ready_to_recv) {
            if (test_cases & TEST_WRITE) {
                sent_pdu = get_pdu_write1p(cik ,alias, value_to_write);
                PRINTF("Write data '%s' to '%s'..\n", value_to_write, alias);
                dtls_write(dtls_context, &session, (uint8*)sent_pdu->hdr, sent_pdu->length);
                test_cases -= TEST_WRITE;
                ready_to_recv = 1;
                continue;
            }
            if (test_cases & TEST_READ) {
                sent_pdu = get_pdu_read1p(cik ,alias);
                PRINTF("Read data from alias '%s'..\n", alias);
                dtls_write(dtls_context, &session, (uint8*)sent_pdu->hdr, sent_pdu->length);
                test_cases -= TEST_READ;
                ready_to_recv = 1;
                continue;
            }
        }
        if (!test_cases && !ready_to_recv)
            break;
    }
    dtls_free_context(dtls_context);
    exit(0);
}


void hex_dump(uint8_t* bytes, size_t len)
{
  size_t i, j;
  for (i = 0; i < len; i+=16){
    printf("  0x%.3zx    ", i);
    for (j = 0; j < 16; j++){
      if (i+j < len)
        printf("%02hhx ", bytes[i+j]);
      else
        printf("%s ", "--");
    }
    printf("   %.*s\n", (int)(16 > len-i ? len-i : 16), bytes+i);
  }
}

void coap_pretty_print(uint8_t* pkt, size_t len)
{
  size_t i;
  uint8_t *ptr;
  int32_t opt_num, j, k;

  if(coap_validate_pkt(pkt, len) == CS_OK){
      printf(" ------ Valid CoAP Packet (%zi) ------ \n", len);
      printf("Type: %i\n",coap_get_type(pkt, len));
      printf("Code: %i.%02i\n", coap_get_code_class(pkt, len), coap_get_code_detail(pkt, len));
      j = coap_get_option_count(pkt, len);
      for(i = 0; i < j; i++){
        k = coap_get_option(pkt, len, i, &opt_num, &ptr);

        printf("Option: %i\n", opt_num);
        printf(" Value: %.*s (%i)\n", k, ptr, k);
      }
      j = coap_get_payload(pkt, len, &ptr);
      printf("Value: %.*s (%i)\n", j, ptr, j);
    }else{
      printf(" ------ Non-CoAP Message (%zi) ------ \n", len);
      hex_dump(pkt, len);
    }
}