#include #include #include #include /*for isxdigit*/ #include "contiki.h" #include "contiki-net.h" #include "buffer.h" #include "coap-server.h" #include "rest-util.h" #include "rest.h" /*added for periodic_resource*/ #include "dev/leds.h" #if !UIP_CONF_IPV6_RPL && !defined (CONTIKI_TARGET_MINIMAL_NET) #include "static-routing.h" #endif #define DEBUG 0 #if DEBUG #include #define PRINTF(...) printf(__VA_ARGS__) #define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15]) #define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) #else #define PRINTF(...) #define PRINT6ADDR(addr) #define PRINTLLADDR(addr) #endif #define MAX_PAYLOAD_LEN 120 #define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) #define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) static struct uip_udp_conn *server_conn; static uint16_t current_tid; static service_callback service_cbk = NULL; void coap_set_service_callback(service_callback callback) { service_cbk = callback; } void parse_message(coap_packet_t* packet, uint8_t* buf, uint16_t size) { int processed = 0; int i = 0; PRINTF("parse_message size %d-->\n",size); init_packet(packet); packet->ver = (buf[0] & COAP_HEADER_VERSION_MASK) >> COAP_HEADER_VERSION_POSITION; packet->type = (buf[0] & COAP_HEADER_TYPE_MASK) >> COAP_HEADER_TYPE_POSITION; packet->option_count = buf[0] & COAP_HEADER_OPTION_COUNT_MASK; packet->code = buf[1]; packet->tid = (buf[2] << 8) + buf[3]; processed += 4; if (packet->option_count) { int option_index = 0; uint8_t option_delta; uint16_t option_len; uint8_t* option_buf = buf + processed; packet->options = (header_option_t*)allocate_buffer(sizeof(header_option_t) * packet->option_count); if (packet->options) { header_option_t* current_option = packet->options; header_option_t* prev_option = NULL; while(option_index < packet->option_count) { /*FIXME : put boundary controls*/ option_delta = (option_buf[i] & COAP_HEADER_OPTION_DELTA_MASK) >> COAP_HEADER_OPTION_DELTA_POSITION; option_len = (option_buf[i] & COAP_HEADER_OPTION_SHORT_LENGTH_MASK); i++; if (option_len == 0xf) { option_len += option_buf[i]; i++; } current_option->option = option_delta; current_option->len = option_len; current_option->value = option_buf + i; if (option_index) { prev_option->next = current_option; /*This field defines the difference between the option Type of * this option and the previous option (or zero for the first option)*/ current_option->option += prev_option->option; } if (current_option->option == Option_Type_Uri_Path) { packet->url = (char*)current_option->value; packet->url_len = current_option->len; } else if (current_option->option == Option_Type_Uri_Query){ packet->query = (char*)current_option->value; packet->query_len = current_option->len; } PRINTF("OPTION %d %u %s \n", current_option->option, current_option->len, current_option->value); i += option_len; option_index++; prev_option = current_option++; } current_option->next = NULL; } else { PRINTF("MEMORY ERROR\n"); /*FIXME : add control here*/ return; } } processed += i; /**/ if (processed < size) { packet->payload = &buf[processed]; packet->payload_len = size - processed; } /*FIXME url is not decoded - is necessary?*/ /*If query is not already provided via Uri_Query option then check URL*/ if (packet->url && !packet->query) { if ((packet->query = strchr(packet->url, '?'))) { uint16_t total_url_len = packet->url_len; /*set query len and update url len so that it does not include query part now*/ packet->url_len = packet->query - packet->url; packet->query++; packet->query_len = packet->url + total_url_len - packet->query; PRINTF("url %s, url_len %u, query %s, query_len %u\n", packet->url, packet->url_len, packet->query, packet->query_len); } } PRINTF("PACKET ver:%d type:%d oc:%d \ncode:%d tid:%u url:%s len:%u payload:%s pay_len %u\n", (int)packet->ver, (int)packet->type, (int)packet->option_count, (int)packet->code, packet->tid, packet->url, packet->url_len, packet->payload, packet->payload_len); } int coap_get_query_variable(coap_packet_t* packet, const char *name, char* output, uint16_t output_size) { if (packet->query) { return get_variable(name, packet->query, packet->query_len, output, output_size, 0); } return 0; } int coap_get_post_variable(coap_packet_t* packet, const char *name, char* output, uint16_t output_size) { if (packet->payload) { return get_variable(name, packet->payload, packet->payload_len, output, output_size, 1); } return 0; } static header_option_t* allocate_header_option(uint16_t variable_len) { PRINTF("sizeof header_option_t %u variable size %u\n", sizeof(header_option_t), variable_len); uint8_t* buffer = allocate_buffer(sizeof(header_option_t) + variable_len); if (buffer){ header_option_t* option = (header_option_t*) buffer; option->next = NULL; option->len = 0; option->value = buffer + sizeof(header_option_t); return option; } return NULL; } /*FIXME : does not overwrite the same option yet.*/ int coap_set_option(coap_packet_t* packet, option_type option_type, uint16_t len, uint8_t* value) { PRINTF("coap_set_option len %u\n", len); header_option_t* option = allocate_header_option(len); if (option){ option->next = NULL; option->len = len; option->option = option_type; memcpy(option->value, value, len); header_option_t* option_current = packet->options; header_option_t* prev = NULL; while (option_current){ if (option_current->option > option->option){ break; } prev = option_current; option_current = option_current->next; } if (!prev){ if (option_current){ option->next = option_current; } packet->options = option; } else{ option->next = option_current; prev->next = option; } packet->option_count++; PRINTF("option->len %u option->option %u option->value %x next %x\n", option->len, option->option, (unsigned int) option->value, (unsigned int)option->next); int i = 0; for ( ; i < option->len ; i++ ){ PRINTF(" (%u)", option->value[i]); } PRINTF("\n"); return 1; } return 0; } header_option_t* coap_get_option(coap_packet_t* packet, option_type option_type) { PRINTF("coap_get_option count: %u--> \n", packet->option_count); int i = 0; header_option_t* current_option = packet->options; for (; i < packet->option_count; current_option = current_option->next, i++) { PRINTF("Current option: %u\n", current_option->option); if (current_option->option == option_type){ return current_option; } } return NULL; } static void fill_error_packet(coap_packet_t* packet, int error, uint16_t tid) { packet->ver=1; packet->option_count=0; packet->url=NULL; packet->options=NULL; switch (error){ case MEMORY_ALLOC_ERR: packet->code=INTERNAL_SERVER_ERROR_500; packet->tid=tid; packet->type=MESSAGE_TYPE_ACK; break; default: break; } } static void init_response(coap_packet_t* request, coap_packet_t* response) { init_packet(response); if(request->type == MESSAGE_TYPE_CON) { response->code = OK_200; response->tid = request->tid; response->type = MESSAGE_TYPE_ACK; } } uint16_t coap_get_payload(coap_packet_t* packet, uint8_t** payload) { if (packet->payload) { *payload = packet->payload; return packet->payload_len; } else { *payload = NULL; return 0; } } int coap_set_payload(coap_packet_t* packet, uint8_t* payload, uint16_t size) { packet->payload = copy_to_buffer(payload, size); if (packet->payload) { packet->payload_len = size; return 1; } return 0; } int coap_set_header_content_type(coap_packet_t* packet, content_type_t content_type) { uint16_t len = 1; return coap_set_option(packet, Option_Type_Content_Type, len, (uint8_t*) &content_type); } content_type_t coap_get_header_content_type(coap_packet_t* packet) { header_option_t* option = coap_get_option(packet, Option_Type_Content_Type); if (option){ return (uint8_t)(*(option->value)); } return DEFAULT_CONTENT_TYPE; } int coap_get_header_subscription_lifetime(coap_packet_t* packet, uint32_t* lifetime) { PRINTF("coap_get_header_subscription_lifetime --> \n"); header_option_t* option = coap_get_option(packet, Option_Type_Subscription_Lifetime); if (option){ PRINTF("Subs Found len %u (first byte %u)\n", option->len, (uint16_t)option->value[0]); *lifetime = read_int(option->value, option->len); return 1; } return 0; } int coap_set_header_subscription_lifetime(coap_packet_t* packet, uint32_t lifetime) { uint8_t temp[4]; uint16_t len = write_variable_int(temp, lifetime); return coap_set_option(packet, Option_Type_Subscription_Lifetime, len, temp); } int coap_get_header_block(coap_packet_t* packet, block_option_t* block) { uint32_t all_block; PRINTF("coap_get_header_block --> \n"); header_option_t* option = coap_get_option(packet, Option_Type_Block); if (option){ PRINTF("Block Found len %u (first byte %u)\n", option->len, (uint16_t)option->value[0]); all_block = read_int(option->value, option->len); block->number = all_block >> 4; block->more = (all_block & 0x8) >> 3; block->size = (all_block & 0x7); return 1; } return 0; } int coap_set_header_block(coap_packet_t* packet, uint32_t number, uint8_t more, uint8_t size) { uint8_t temp[4]; size = log_2(size/16); number = number << 4; number |= (more << 3) & 0x8; number |= size & 0x7; uint16_t len = write_variable_int(temp, number); PRINTF("number %lu, more %u, size %u block[0] %u block[1] %u block[2] %u block[3] %u\n", number, (uint16_t)more, (uint16_t)size, (uint16_t)temp[0], (uint16_t)temp[1], (uint16_t)temp[2], (uint16_t)temp[3]); return coap_set_option(packet, Option_Type_Block, len, temp); } int coap_set_header_uri(coap_packet_t* packet, char* uri) { return coap_set_option(packet, Option_Type_Uri_Path, strlen(uri), (uint8_t*) uri); } int coap_set_header_etag(coap_packet_t* packet, uint8_t* etag, uint8_t size) { return coap_set_option(packet, Option_Type_Etag, size, etag); } void coap_set_code(coap_packet_t* packet, status_code_t code) { packet->code = (uint8_t)code; } coap_method_t coap_get_method(coap_packet_t* packet) { return (coap_method_t)packet->code; } void coap_set_method(coap_packet_t* packet, coap_method_t method) { packet->code = (uint8_t)method; } static void send_request(coap_packet_t* request, struct uip_udp_conn *client_conn) { char buf[MAX_PAYLOAD_LEN]; int data_size = 0; data_size = serialize_packet(request, buf); PRINTF("Created a connection with the server "); PRINT6ADDR(&client_conn->ripaddr); PRINTF(" local/remote port %u/%u\n", uip_htons(client_conn->lport), uip_htons(client_conn->rport)); PRINTF("Sending to: "); PRINT6ADDR(&client_conn->ripaddr); uip_udp_packet_send(client_conn, buf, data_size); } static int handle_incoming_data(void) { int error=NO_ERROR; char buf[MAX_PAYLOAD_LEN]; PRINTF("uip_datalen received %u \n",(u16_t)uip_datalen()); char* data = uip_appdata + uip_ext_len; u16_t datalen = uip_datalen() - uip_ext_len; int data_size = 0; if (uip_newdata()) { ((char *)data)[datalen] = 0; PRINTF("Server received: '%s' (port:%u) from ", (char *)data, uip_htons(UIP_UDP_BUF->srcport)); PRINT6ADDR(&UIP_IP_BUF->srcipaddr); PRINTF("\n"); if (init_buffer(COAP_DATA_BUFF_SIZE)) { coap_packet_t* request = (coap_packet_t*)allocate_buffer(sizeof(coap_packet_t)); parse_message(request, (uint8_t*)data, datalen); uip_ipaddr_copy(&request->addr, &UIP_IP_BUF->srcipaddr); if (request->type != MESSAGE_TYPE_ACK) { coap_packet_t* response = (coap_packet_t*)allocate_buffer(sizeof(coap_packet_t)); init_response(request, response); if (service_cbk) { service_cbk(request, response); } data_size = serialize_packet(response, buf); } delete_buffer(); } else { PRINTF("Memory Alloc Error\n"); error = MEMORY_ALLOC_ERR; /*FIXME : Crappy way of accessing TID of the incoming packet, fix it!*/ coap_packet_t error_packet; fill_error_packet(&error_packet,error, (data[2] << 8) + data[3]); data_size = serialize_packet(&error_packet, buf); } uip_ipaddr_copy(&server_conn->ripaddr, &UIP_IP_BUF->srcipaddr); server_conn->rport = UIP_UDP_BUF->srcport; PRINTF("Responding with message size: %d\n",data_size); uip_udp_packet_send(server_conn, buf, data_size); /* Restore server connection to allow data from any node */ memset(&server_conn->ripaddr, 0, sizeof(server_conn->ripaddr)); server_conn->rport = 0; } return error; } process_event_t resource_changed_event; void resource_changed(struct periodic_resource_t* resource) { process_post(&coap_server, resource_changed_event, (process_data_t)resource); } /*---------------------------------------------------------------------------*/ PROCESS(coap_server, "Coap Server"); PROCESS_THREAD(coap_server, ev, data) { PROCESS_BEGIN(); PRINTF("COAP SERVER\n"); /* if static routes are used rather than RPL */ #if !UIP_CONF_IPV6_RPL && !defined (CONTIKI_TARGET_MINIMAL_NET) set_global_address(); configure_routing(); #endif current_tid = random_rand(); resource_changed_event = process_alloc_event(); /* new connection with remote host */ server_conn = udp_new(NULL, uip_htons(0), NULL); udp_bind(server_conn, uip_htons(MOTE_SERVER_LISTEN_PORT)); PRINTF("Local/remote port %u/%u\n", uip_htons(server_conn->lport), uip_htons(server_conn->rport)); while(1) { PROCESS_YIELD(); if(ev == tcpip_event) { handle_incoming_data(); } else if (ev == resource_changed_event) { periodic_resource_t* resource = (periodic_resource_t*)data; PRINTF("resource_changed_event \n"); if (init_buffer(COAP_DATA_BUFF_SIZE)) { coap_packet_t* request = (coap_packet_t*)allocate_buffer(sizeof(coap_packet_t)); init_packet(request); coap_set_code(request, COAP_GET); request->tid = current_tid++; coap_set_header_subscription_lifetime(request, resource->lifetime); coap_set_header_uri(request, (char *)resource->resource->url); if (resource->periodic_request_generator) { resource->periodic_request_generator(request); } if (!resource->client_conn) { /*FIXME send port is fixed for now to 61616*/ resource->client_conn = udp_new(&resource->addr, uip_htons(61616), NULL); udp_bind(resource->client_conn, uip_htons(MOTE_CLIENT_LISTEN_PORT)); } if (resource->client_conn) { send_request(request, resource->client_conn); } delete_buffer(); } } } PROCESS_END(); } /*---------------------------------------------------------------------------*/