X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=resource%2Fcsdk%2Fstack%2Fsrc%2Focstack.c;h=da84aa0e595837235385473256fd2eecfff2be0f;hb=ecaf6a8df05eaa08400ae39a48c7bfe8634c5891;hp=e6080c9fcaddd8dccd5311e68ddadd07ada2aa97;hpb=f793b5859eb5e3e9abb187bdd74e23b8bb060d8e;p=platform%2Fupstream%2Fiotivity.git diff --git a/resource/csdk/stack/src/ocstack.c b/resource/csdk/stack/src/ocstack.c index e6080c9..da84aa0 100644 --- a/resource/csdk/stack/src/ocstack.c +++ b/resource/csdk/stack/src/ocstack.c @@ -22,24 +22,43 @@ //----------------------------------------------------------------------------- // Includes //----------------------------------------------------------------------------- +#define _POSIX_C_SOURCE 200112L +#include +#include + #include "ocstack.h" #include "ocstackinternal.h" #include "ocresourcehandler.h" #include "occlientcb.h" #include "ocobserve.h" #include "ocrandom.h" -#include "debug.h" -#include "occoap.h" #include "ocmalloc.h" #include "ocserverrequest.h" +#include "ocsecurityinternal.h" + +#include "cacommon.h" +#include "cainterface.h" + +#ifdef WITH_ARDUINO +#include "Time.h" +#else +#include +#endif +#include "coap_time.h" +#include "utlist.h" +#include "pdu.h" + +#ifndef ARDUINO +#include +#endif + //----------------------------------------------------------------------------- // Typedefs //----------------------------------------------------------------------------- typedef enum { - OC_STACK_UNINITIALIZED = 0, OC_STACK_INITIALIZED + OC_STACK_UNINITIALIZED = 0, OC_STACK_INITIALIZED, OC_STACK_UNINIT_IN_PROGRESS } OCStackState; - #ifdef WITH_PRESENCE typedef enum { OC_PRESENCE_UNINITIALIZED = 0, OC_PRESENCE_INITIALIZED @@ -61,20 +80,77 @@ uint32_t PresenceTimeOut[] = {50, 75, 85, 95, 100}; OCMode myStackMode; OCDeviceEntityHandler defaultDeviceHandler; +OCStackResult getQueryFromUri(const char * uri, unsigned char** resourceType, char ** newURI); //----------------------------------------------------------------------------- // Macros //----------------------------------------------------------------------------- #define TAG PCF("OCStack") +#define VERIFY_SUCCESS(op, successCode) { if (op != successCode) \ + {OC_LOG_V(FATAL, TAG, "%s failed!!", #op); goto exit;} } #define VERIFY_NON_NULL(arg, logLevel, retVal) { if (!(arg)) { OC_LOG((logLevel), \ TAG, PCF(#arg " is NULL")); return (retVal); } } +#define VERIFY_NON_NULL_V(arg) { if (!arg) {OC_LOG_V(FATAL, TAG, "%s is NULL", #arg);\ + goto exit;} } //TODO: we should allow the server to define this #define MAX_OBSERVE_AGE (0x2FFFFUL) -//----------------------------------------------------------------------------- -// Externs -//----------------------------------------------------------------------------- -extern void DeinitOCSecurityInfo(); + +//============================================================================= +// Helper Functions +//============================================================================= +static uint32_t GetTime(float afterSeconds) +{ + coap_tick_t now; + coap_ticks(&now); + return now + (uint32_t)(afterSeconds * COAP_TICKS_PER_SECOND); +} + +static OCStackResult FormOCResponse(OCResponse * * responseLoc, + ClientCB * cbNode, + uint32_t maxAge, + unsigned char * fullUri, + unsigned char * rcvdUri, + CAToken_t * rcvdToken, + OCClientResponse * clientResponse, + unsigned char * bufRes) +{ + OCResponse * response = (OCResponse *) OCMalloc(sizeof(OCResponse)); + if (!response) + { + return OC_STACK_NO_MEMORY; + } + response->cbNode = cbNode; + response->maxAge = maxAge; + response->fullUri = fullUri; + response->rcvdUri = rcvdUri; + response->rcvdToken = rcvdToken; + response->clientResponse = clientResponse; + response->bufRes = bufRes; + + *responseLoc = response; + return OC_STACK_OK; +} + +/// This method is used to create the IPv4 dev_addr structure. +/// TODO: Remove in future. Temporary helper function. +/// Builds a socket interface address using IP address and port number +static int32_t OCBuildIPv4Address(uint8_t a, uint8_t b, uint8_t c, uint8_t d, + uint16_t port, OCDevAddr *ipAddr) +{ + if ( !ipAddr ) { + OC_LOG(FATAL, TAG, "Invalid argument"); + return 1; + } + + ipAddr->addr[0] = a; + ipAddr->addr[1] = b; + ipAddr->addr[2] = c; + ipAddr->addr[3] = d; + *((uint16_t*)&(ipAddr->addr[4])) = port; + + return 0; +} //----------------------------------------------------------------------------- // Internal API function @@ -82,7 +158,7 @@ extern void DeinitOCSecurityInfo(); // This internal function is called to update the stack with the status of // observers and communication failures -OCStackResult OCStackFeedBack(OCCoAPToken * token, uint8_t status) +OCStackResult OCStackFeedBack(CAToken_t * token, uint8_t status) { OCStackResult result = OC_STACK_ERROR; ResourceObserver * observer = NULL; @@ -120,7 +196,7 @@ OCStackResult OCStackFeedBack(OCCoAPToken * token, uint8_t status) //observer is still interested OC_LOG(DEBUG, TAG, PCF("observer is interested in our \ notifications, reset the failedCount")); - observer = GetObserverUsingToken(token); + observer = GetObserverUsingToken (token); if(observer) { observer->forceHighQos = 0; @@ -135,7 +211,7 @@ OCStackResult OCStackFeedBack(OCCoAPToken * token, uint8_t status) case OC_OBSERVER_FAILED_COMM: //observer is not reachable OC_LOG(DEBUG, TAG, PCF("observer is unreachable")); - observer = GetObserverUsingToken(token); + observer = GetObserverUsingToken (token); if(observer) { if(observer->failedCommCount >= MAX_OBSERVER_FAILED_COMM) @@ -177,14 +253,636 @@ OCStackResult OCStackFeedBack(OCCoAPToken * token, uint8_t status) return result; } +OCStackResult CAToOCStackResult(CAResponseResult_t caCode) +{ + OCStackResult ret = OC_STACK_ERROR; + + switch(caCode) + { + case CA_SUCCESS: + ret = OC_STACK_OK; + break; + case CA_CREATED: + ret = OC_STACK_RESOURCE_CREATED; + break; + case CA_DELETED: + ret = OC_STACK_RESOURCE_DELETED; + break; + case CA_BAD_REQ: + ret = OC_STACK_INVALID_QUERY; + break; + case CA_BAD_OPT: + ret = OC_STACK_INVALID_OPTION; + break; + case CA_NOT_FOUND: + ret = OC_STACK_NO_RESOURCE; + break; + default: + break; + } + return ret; +} + +OCStackResult OCToCAConnectivityType(OCConnectivityType ocConType, CAConnectivityType_t* caConType) +{ + OCStackResult ret = OC_STACK_OK; + + switch(ocConType) + { + case OC_ETHERNET: + *caConType = CA_ETHERNET; + break; + case OC_WIFI: + *caConType = CA_WIFI; + break; + case OC_EDR: + *caConType = CA_EDR; + break; + case OC_LE: + *caConType = CA_LE; + break; + case OC_ALL: + // Currently OC_ALL represents WIFI and ETHERNET + // Add other connectivity types as they are enabled in future + *caConType = (CAConnectivityType_t) (CA_WIFI|CA_ETHERNET); + break; + default: + ret = OC_STACK_INVALID_PARAM; + break; + } + return ret; +} + +OCStackResult CAToOCConnectivityType(CAConnectivityType_t caConType, OCConnectivityType *ocConType) +{ + OCStackResult ret = OC_STACK_OK; + + switch(caConType) + { + case CA_ETHERNET: + *ocConType = OC_ETHERNET; + break; + case CA_WIFI: + *ocConType = OC_WIFI; + break; + case CA_EDR: + *ocConType = OC_EDR; + break; + case CA_LE: + *ocConType = OC_LE; + break; + default: + ret = OC_STACK_INVALID_PARAM; + break; + } + return ret; +} + +// update response.addr appropriately from endPoint.addressInfo +OCStackResult UpdateResponseAddr(OCClientResponse *response, const CARemoteEndpoint_t* endPoint) +{ + OCStackResult ret = OC_STACK_ERROR; + static OCDevAddr address = {0}; + char * tok = NULL; + char * savePtr = NULL; + char * cpAddress = (char *) OCMalloc(strlen(endPoint->addressInfo.IP.ipAddress) + 1); + if(!cpAddress) + { + ret = OC_STACK_NO_MEMORY; + goto exit; + } + memcpy(cpAddress, endPoint->addressInfo.IP.ipAddress, + strlen(endPoint->addressInfo.IP.ipAddress) + 1); + + // Grabs the first three numbers from the IPv4 address and replaces dots + for(int i=0; i<4; i++) + { + tok = strtok_r(i==0 ? cpAddress : NULL, ".", &savePtr); + + if(!tok) + { + ret = OC_STACK_ERROR; + goto exit; + } + address.addr[i] = atoi(tok); + } + + memcpy(&address.addr[4], &endPoint->addressInfo.IP.port, sizeof(uint32_t)); + + if(response) + { + response->addr = &address; + ret = CAToOCConnectivityType(endPoint->connectivityType, &(response->connType)); + } + else + { + OC_LOG(ERROR, TAG, PCF("OCClientResponse is NULL!")); + } +exit: + OCFree(cpAddress); + return ret; +} + +void parsePresencePayload(char* payload, uint32_t* seqNum, uint32_t* maxAge, char** resType) +{ + char * tok = NULL; + char * savePtr; + // The format of the payload is {"oc":[%u:%u:%s]} + // %u : sequence number, + // %u : max age + // %s : Resource Type (Optional) + tok = strtok_r(payload, "[:]}", &savePtr); + payload[strlen(payload)] = ':'; + tok = strtok_r(NULL, "[:]}", &savePtr); + payload[strlen((char *)payload)] = ':'; + *seqNum = (uint32_t) atoi(tok); + tok = strtok_r(NULL, "[:]}", &savePtr); + *maxAge = (uint32_t) atoi(tok); + tok = strtok_r(NULL, "[:]}",&savePtr); + + if(tok) + { + *resType = (char *)OCMalloc(strlen(tok)); + if(!*resType) + { + return; + } + payload[strlen((char *)payload)] = ':'; + strcpy(*resType, tok); + OC_LOG_V(DEBUG, TAG, "----------------resourceTypeName %s", *resType); + } + payload[strlen((char *)payload)] = ']'; +} + +OCStackResult HandlePresenceResponse(const CARemoteEndpoint_t* endPoint, + const CAResponseInfo_t* responseInfo) +{ + OCStackApplicationResult cbResult = OC_STACK_DELETE_TRANSACTION; + ClientCB * cbNode = NULL; + char *resourceTypeName = NULL; + OCClientResponse response; + OCStackResult result = OC_STACK_ERROR; + uint32_t lowerBound = 0; + uint32_t higherBound = 0; + uint32_t maxAge = 0; + + char *fullUri = NULL; + char *ipAddress = NULL; + int presenceSubscribe = 0; + int multicastPresenceSubscribe = 0; + + if (responseInfo->result != CA_SUCCESS) + { + OC_LOG_V(ERROR, TAG, "HandlePresenceResponse failed %d", responseInfo->result); + return OC_STACK_ERROR; + } + + fullUri = (char *) OCMalloc(MAX_URI_LENGTH ); + + if(NULL == fullUri) + { + OC_LOG(ERROR, TAG, PCF("Memory could not be allocated for fullUri")); + result = OC_STACK_NO_MEMORY; + goto exit; + } + + ipAddress = (char *) OCMalloc(strlen(endPoint->addressInfo.IP.ipAddress) + 1); + + if(NULL == ipAddress) + { + OC_LOG(ERROR, TAG, PCF("Memory could not be allocated for ipAddress")); + result = OC_STACK_NO_MEMORY; + goto exit; + } + + strncpy(ipAddress, endPoint->addressInfo.IP.ipAddress, + strlen(endPoint->addressInfo.IP.ipAddress)); + ipAddress[strlen(endPoint->addressInfo.IP.ipAddress)] = '\0'; + + snprintf(fullUri, MAX_URI_LENGTH, "coap://%s:%u%s", ipAddress, endPoint->addressInfo.IP.port, + OC_PRESENCE_URI); + + cbNode = GetClientCB(NULL, NULL, (unsigned char *) fullUri); + + if(cbNode) + { + presenceSubscribe = 1; + } + else + { + snprintf(fullUri, MAX_URI_LENGTH, "%s%s", OC_MULTICAST_IP, endPoint->resourceUri); + cbNode = GetClientCB(NULL, NULL, (unsigned char *) fullUri); + if(cbNode) + { + multicastPresenceSubscribe = 1; + } + } + + if(!presenceSubscribe && !multicastPresenceSubscribe) + { + OC_LOG(INFO, TAG, PCF("Received a presence notification, but I do not have callback \ + ------------ ignoring")); + goto exit; + } + + // No payload to the application in case of presence + response.resJSONPayload = NULL; + response.result = OC_STACK_OK; + + result = UpdateResponseAddr(&response, endPoint); + if(result != OC_STACK_OK) + { + goto exit; + } + + if(responseInfo->info.payload) + { + parsePresencePayload(responseInfo->info.payload, + &(response.sequenceNumber), + &maxAge, + &resourceTypeName); + } + + if(maxAge == 0) + { + OC_LOG(INFO, TAG, PCF("===============Stopping presence")); + response.result = OC_STACK_PRESENCE_STOPPED; + if(cbNode->presence) + { + OCFree(cbNode->presence->timeOut); + OCFree(cbNode->presence); + cbNode->presence = NULL; + } + } + else if(presenceSubscribe) + { + if(!cbNode->presence) + { + cbNode->presence = (OCPresence *) OCMalloc(sizeof(OCPresence)); + VERIFY_NON_NULL_V(cbNode->presence); + cbNode->presence->timeOut = NULL; + cbNode->presence->timeOut = (uint32_t *) + OCMalloc(PresenceTimeOutSize * sizeof(uint32_t)); + if(!(cbNode->presence->timeOut)){ + OCFree(cbNode->presence); + result = OC_STACK_NO_MEMORY; + } + } + + OC_LOG_V(INFO, TAG, "===============Update presence TTL, now time is %u", GetTime(0)); + cbNode->presence->TTL = maxAge; + for(int index = 0; index < PresenceTimeOutSize; index++) + { + lowerBound = GetTime(((float)(PresenceTimeOut[index]) + /(float)100)*(float)cbNode->presence->TTL); + higherBound = GetTime(((float)(PresenceTimeOut[index + 1]) + /(float)100)*(float)cbNode->presence->TTL); + cbNode->presence->timeOut[index] = OCGetRandomRange(lowerBound, higherBound); + OC_LOG_V(DEBUG, TAG, "----------------lowerBound timeout %d", lowerBound); + OC_LOG_V(DEBUG, TAG, "----------------higherBound timeout %d", higherBound); + OC_LOG_V(DEBUG, TAG, "----------------timeOut entry %d", + cbNode->presence->timeOut[index]); + } + cbNode->presence->TTLlevel = 0; + OC_LOG_V(DEBUG, TAG, "----------------this TTL level %d", cbNode->presence->TTLlevel); + if(cbNode->sequenceNumber == response.sequenceNumber) + { + OC_LOG(INFO, TAG, PCF("===============No presence change")); + goto exit; + } + OC_LOG(INFO, TAG, PCF("===============Presence changed, calling up the stack")); + cbNode->sequenceNumber = response.sequenceNumber; + + // Ensure that a filter is actually applied. + if(resourceTypeName && cbNode->filterResourceType) + { + if(!findResourceType(cbNode->filterResourceType, resourceTypeName)) + { + goto exit; + } + } + } + else + { + // This is the multicast case + + OCMulticastNode* mcNode = NULL; + mcNode = GetMCPresenceNode((const unsigned char *)fullUri); + + if(mcNode != NULL) + { + if(mcNode->nonce == response.sequenceNumber) + { + OC_LOG(INFO, TAG, PCF("===============No presence change (Multicast)")); + goto exit; + } + mcNode->nonce = response.sequenceNumber; + } + else + { + uint32_t uriLen = strlen((char*)fullUri); + unsigned char* uri = (unsigned char *) OCMalloc(uriLen + 1); + if(uri) + { + memcpy(uri, fullUri, (uriLen + 1)); + } + else + { + OC_LOG(INFO, TAG, + PCF("===============No Memory for URI to store in the presence node")); + result = OC_STACK_NO_MEMORY; + goto exit; + } + result = AddMCPresenceNode(&mcNode, (unsigned char*) uri, response.sequenceNumber); + if(result == OC_STACK_NO_MEMORY) + { + OC_LOG(INFO, TAG, + PCF("===============No Memory for Multicast Presence Node")); + result = OC_STACK_NO_MEMORY; + goto exit; + } + } + + // Ensure that a filter is actually applied. + if(resourceTypeName && cbNode->filterResourceType) + { + if(!findResourceType(cbNode->filterResourceType, resourceTypeName)) + { + goto exit; + } + } + } + + cbResult = cbNode->callBack(cbNode->context, cbNode->handle, &response); + + if (cbResult == OC_STACK_DELETE_TRANSACTION) + { + FindAndDeleteClientCB(cbNode); + } + +exit: +OCFree(fullUri); +OCFree(ipAddress); +OCFree(resourceTypeName); +return result; +} + + +//This function will be called back by CA layer when a response is received +void HandleCAResponses(const CARemoteEndpoint_t* endPoint, const CAResponseInfo_t* responseInfo) +{ + OC_LOG(INFO, TAG, PCF("Enter HandleCAResponses")); + + OCStackApplicationResult appResult = OC_STACK_DELETE_TRANSACTION; + + if(NULL == endPoint) + { + OC_LOG(ERROR, TAG, PCF("endPoint is NULL")); + return; + } + + if(NULL == responseInfo) + { + OC_LOG(ERROR, TAG, PCF("responseInfo is NULL")); + return; + } + + if(strcmp(endPoint->resourceUri, OC_PRESENCE_URI) == 0) + { + HandlePresenceResponse(endPoint, responseInfo); + return; + } + + ClientCB *cbNode = GetClientCB((CAToken_t *)&(responseInfo->info.token), NULL, NULL); + + if (cbNode) + { + OC_LOG(INFO, TAG, PCF("Calling into application address space")); + OCClientResponse response; + + OCStackResult result = UpdateResponseAddr(&response, endPoint); + if(result != OC_STACK_OK) + { + OC_LOG(ERROR, TAG, PCF("Invalid connectivity type in endpoint")); + return; + } + + response.result = CAToOCStackResult(responseInfo->result); + response.resJSONPayload = (unsigned char*)responseInfo->info.payload; + response.numRcvdVendorSpecificHeaderOptions = 0; + if(responseInfo->info.numOptions > 0) + { + int start = 0; + //First option always with option ID is OC_COAP_OPTION_OBSERVE if it is available. + if(responseInfo->info.options[0].optionID == COAP_OPTION_OBSERVE) + { + memcpy (&(response.sequenceNumber), + &(responseInfo->info.options[0].optionData), sizeof(uint32_t)); + response.numRcvdVendorSpecificHeaderOptions = responseInfo->info.numOptions - 1; + start = 1; + } + else + { + response.numRcvdVendorSpecificHeaderOptions = responseInfo->info.numOptions; + } + + if(response.numRcvdVendorSpecificHeaderOptions > MAX_HEADER_OPTIONS) + { + OC_LOG(ERROR, TAG, PCF("#header options are more than MAX_HEADER_OPTIONS")); + return; + } + + for (uint8_t i = start; i < responseInfo->info.numOptions; i++) + { + memcpy (&(response.rcvdVendorSpecificHeaderOptions[i-start]), + &(responseInfo->info.options[i]), sizeof(OCHeaderOption)); + } + } + appResult = cbNode->callBack(cbNode->context, + cbNode->handle, &response); + if (appResult == OC_STACK_DELETE_TRANSACTION) + { + FindAndDeleteClientCB(cbNode); + } + } + OC_LOG_V(INFO, TAG, PCF("Received payload: %s\n"), (char*)responseInfo->info.payload); + OC_LOG(INFO, TAG, PCF("Exit HandleCAResponses")); +} + +//This function will be called back by CA layer when a request is received +void HandleCARequests(const CARemoteEndpoint_t* endPoint, const CARequestInfo_t* requestInfo) +{ + OC_LOG(INFO, TAG, PCF("Enter HandleCARequests")); + if(!endPoint) + { + OC_LOG(ERROR, TAG, PCF("endPoint is NULL")); + return; + } + + if(!requestInfo) + { + OC_LOG(ERROR, TAG, PCF("requestInfo is NULL")); + return; + } + + OCStackResult requestResult = OC_STACK_ERROR; + + if(myStackMode == OC_CLIENT) + { + //TODO: should the client be responding to requests? + return; + } + + OCServerProtocolRequest serverRequest = {}; + + OC_LOG_V(INFO, TAG, PCF("***** Endpoint URI ***** : %s\n"), (char*)endPoint->resourceUri); + + char * newUri = (char *)endPoint->resourceUri; + unsigned char * query = NULL; + getQueryFromUri(endPoint->resourceUri, &query, &newUri); + OC_LOG_V(INFO, TAG, PCF("**********URI without query ****: %s\n"), newUri); + OC_LOG_V(INFO, TAG, PCF("**********Query ****: %s\n"), query); + if(strlen(newUri) < MAX_URI_LENGTH) + { + //copy URI + memcpy (&(serverRequest.resourceUrl), newUri, strlen(newUri)); + } + else + { + OC_LOG(ERROR, TAG, PCF("URI length exceeds MAX_URI_LENGTH.")); + return; + } + //copy query + if(query) + { + if(strlen((char*)query) < MAX_QUERY_LENGTH) + { + memcpy (&(serverRequest.query), query, strlen((char*)query)); + } + else + { + OC_LOG(ERROR, TAG, PCF("Query length exceeds MAX_QUERY_LENGTH.")); + return; + } + } + //copy request payload + if (requestInfo->info.payload) + { + serverRequest.reqTotalSize = strlen(requestInfo->info.payload) + 1; + memcpy (&(serverRequest.reqJSONPayload), requestInfo->info.payload, + strlen(requestInfo->info.payload)); + serverRequest.reqTotalSize = strlen((const char *)requestInfo->info.payload) + 1; + } + else + { + serverRequest.reqTotalSize = 1; + } + + switch (requestInfo->method) + { + case CA_GET: + { + serverRequest.method = OC_REST_GET; + break; + } + case CA_PUT: + { + serverRequest.method = OC_REST_PUT; + break; + } + case CA_POST: + { + serverRequest.method = OC_REST_POST; + break; + } + case CA_DELETE: + { + serverRequest.method = OC_REST_DELETE; + break; + } + default: + { + OC_LOG(ERROR, TAG, PCF("Received CA method %d not supported")); + return; + } + } + + OC_LOG_V(INFO, TAG, "HandleCARequests: CA token length = %d", CA_MAX_TOKEN_LEN); + OC_LOG_BUFFER(INFO, TAG, (const uint8_t *)requestInfo->info.token, CA_MAX_TOKEN_LEN); + + serverRequest.requestToken = (CAToken_t)OCCalloc(1, CA_MAX_TOKEN_LEN+1); + // Module Name + if (!serverRequest.requestToken) + { + OC_LOG(FATAL, TAG, "Server Request Token is NULL"); + return; + } + memcpy(serverRequest.requestToken, requestInfo->info.token, CA_MAX_TOKEN_LEN); + + if (requestInfo->info.type == CA_MSG_CONFIRM) + { + serverRequest.qos = OC_HIGH_QOS; + } + else if (requestInfo->info.type == CA_MSG_NONCONFIRM) + { + serverRequest.qos = OC_LOW_QOS; + } + else if (requestInfo->info.type == CA_MSG_ACKNOWLEDGE) + { + // TODO-CA: Need to handle this + } + else if (requestInfo->info.type == CA_MSG_RESET) + { + // TODO-CA: Need to handle this + } + // CA does not need the following 3 fields + serverRequest.coapID = 0; + serverRequest.delayedResNeeded = 0; + serverRequest.secured = endPoint->isSecured; + + // copy the address + serverRequest.addressInfo = endPoint->addressInfo; + serverRequest.connectivityType = endPoint->connectivityType; + + // copy vendor specific header options + // TODO-CA: CA is including non-vendor header options as well, like observe. + // Need to filter those out + uint8_t tempNum = (requestInfo->info.numOptions); + GetObserveHeaderOption(&serverRequest.observationOption, requestInfo->info.options, &tempNum); + if (requestInfo->info.numOptions > MAX_HEADER_OPTIONS) + { + OC_LOG(ERROR, TAG, + PCF("The request info numOptions is greater than MAX_HEADER_OPTIONS")); + OCFree(serverRequest.requestToken); + return; + } + serverRequest.numRcvdVendorSpecificHeaderOptions = tempNum; + if (serverRequest.numRcvdVendorSpecificHeaderOptions) + { + memcpy (&(serverRequest.rcvdVendorSpecificHeaderOptions), requestInfo->info.options, + sizeof(CAHeaderOption_t)*tempNum); + } + + requestResult = HandleStackRequests (&serverRequest); + if(requestResult != OC_STACK_OK) + { + OC_LOG(ERROR, TAG, PCF("HandleStackRequests failed")); + } + OC_LOG(INFO, TAG, PCF("Exit HandleCARequests")); +} + //This function will be called back by occoap layer when a request is received OCStackResult HandleStackRequests(OCServerProtocolRequest * protocolRequest) { OC_LOG(INFO, TAG, PCF("Entering HandleStackRequests (OCStack Layer)")); - OCStackResult result = OC_STACK_ERROR; ResourceHandling resHandling; OCResource *resource; + if(!protocolRequest) + { + OC_LOG(ERROR, TAG, PCF("protocolRequest is NULL")); + return OC_STACK_INVALID_PARAM; + } OCServerRequest * request = GetServerRequestUsingToken(protocolRequest->requestToken); if(!request) @@ -196,8 +894,8 @@ OCStackResult HandleStackRequests(OCServerProtocolRequest * protocolRequest) protocolRequest->observationOption, protocolRequest->qos, protocolRequest->query, protocolRequest->rcvdVendorSpecificHeaderOptions, protocolRequest->reqJSONPayload, &protocolRequest->requestToken, - &protocolRequest->requesterAddr, protocolRequest->resourceUrl, - protocolRequest->reqTotalSize); + protocolRequest->resourceUrl,protocolRequest->reqTotalSize, + &protocolRequest->addressInfo, protocolRequest->connectivityType); if (OC_STACK_OK != result) { OC_LOG(ERROR, TAG, PCF("Error adding server request")); @@ -237,23 +935,274 @@ OCStackResult HandleStackRequests(OCServerProtocolRequest * protocolRequest) } //This function will be called back by occoap layer when a response is received -void HandleStackResponses(OCResponse * response) +OCStackResult HandleStackResponses(OCResponse * response) { - OCStackApplicationResult result = OC_STACK_DELETE_TRANSACTION; OC_LOG(INFO, TAG, PCF("Entering HandleStackResponses (OCStack Layer)")); + OCStackResult result = OC_STACK_OK; + OCStackApplicationResult cbResult = OC_STACK_DELETE_TRANSACTION; + uint8_t isObserveNotification = 0; + ClientCB * cbNode = NULL; + if(!response) + { + OC_LOG(ERROR, TAG, PCF("response is NULL")); + return OC_STACK_INVALID_PARAM; + } +#ifdef WITH_PRESENCE + uint8_t isPresenceNotification = 0; + uint8_t isMulticastPresence = 0; + char * resourceTypeName = NULL; + uint32_t lowerBound = 0; + uint32_t higherBound = 0; + char * tok = NULL; + unsigned char * bufRes = response->bufRes; +#endif // WITH_PRESENCE - if (response->cbNode) + cbNode = response->cbNode; + if(!cbNode) { - OC_LOG(INFO, TAG, PCF("Calling into application address space")); - result = response->cbNode->callBack(response->cbNode->context, - response->cbNode->handle, response->clientResponse); - if (result == OC_STACK_DELETE_TRANSACTION || + cbNode = GetClientCB(response->rcvdToken, NULL, NULL); + } + + if(response->clientResponse->sequenceNumber >= OC_OFFSET_SEQUENCE_NUMBER) + { + isObserveNotification = 1; + OC_LOG(INFO, TAG, PCF("Received an observe notification")); + } + + OC_LOG_V(DEBUG, TAG, "The sequenceNumber/NONCE of this response %u", + response->clientResponse->sequenceNumber); + OC_LOG_V(DEBUG, TAG, "The maxAge/TTL of this response %u", response->maxAge); + OC_LOG_V(DEBUG, TAG, "The response received is %s", bufRes); + +#ifdef WITH_PRESENCE + if(!strcmp((char *)response->rcvdUri, (char *)OC_PRESENCE_URI)){ + isPresenceNotification = 1; + if(!bufRes) + { + result = OC_STACK_INVALID_PARAM; + goto exit; + } + char * savePtr; + tok = strtok_r((char *)bufRes, "[:]}", &savePtr); + bufRes[strlen((char *)bufRes)] = ':'; + tok = strtok_r(NULL, "[:]}", &savePtr); + bufRes[strlen((char *)bufRes)] = ':'; + response->clientResponse->sequenceNumber = (uint32_t )atoi(tok); + OC_LOG_V(DEBUG, TAG, "The received NONCE is %u", response->clientResponse->sequenceNumber); + tok = strtok_r(NULL, "[:]}", &savePtr); + response->maxAge = (uint32_t )atoi(tok); + OC_LOG_V(DEBUG, TAG, "The received TTL is %u", response->maxAge); + tok = strtok_r(NULL, "[:]}", &savePtr); + if(tok) + { + resourceTypeName = (char *)OCMalloc(strlen(tok)); + if(!resourceTypeName) + { + goto exit; + } + bufRes[strlen((char *)bufRes)] = ':'; + strcpy(resourceTypeName, tok); + OC_LOG_V(DEBUG, TAG, "----------------resourceTypeName %s", + resourceTypeName); + } + bufRes[strlen((char *)bufRes)] = ']'; + } + + // Check if the application subcribed for presence + if(!cbNode) + { + cbNode = GetClientCB(NULL, NULL, response->fullUri); + } + + // Check if application subscribed for multicast presence + if(!cbNode) + { + snprintf((char *)response->fullUri, MAX_URI_LENGTH, "%s%s", + OC_MULTICAST_IP, response->rcvdUri); + cbNode = GetClientCB(NULL, NULL, response->fullUri); + if(cbNode) + { + isMulticastPresence = 1; + isPresenceNotification = 0; + } + } + + if(cbNode && isPresenceNotification) + { + OC_LOG(INFO, TAG, PCF("Received a presence notification")); + if(!cbNode->presence) + { + cbNode->presence = (OCPresence *) OCMalloc(sizeof(OCPresence)); + VERIFY_NON_NULL_V(cbNode->presence); + cbNode->presence->timeOut = NULL; + cbNode->presence->timeOut = (uint32_t *) + OCMalloc(PresenceTimeOutSize * sizeof(uint32_t)); + if(!(cbNode->presence->timeOut)){ + OCFree(cbNode->presence); + result = OC_STACK_NO_MEMORY; + } + } + if(response->maxAge == 0) + { + OC_LOG(INFO, TAG, PCF("===============Stopping presence")); + response->clientResponse->result = OC_STACK_PRESENCE_STOPPED; + if(cbNode->presence) + { + OCFree(cbNode->presence->timeOut); + OCFree(cbNode->presence); + cbNode->presence = NULL; + } + } + else + { + OC_LOG_V(INFO, TAG, "===============Update presence TTL, now time is %d", GetTime(0)); + cbNode->presence->TTL = response->maxAge; + for(int index = 0; index < PresenceTimeOutSize; index++) + { + lowerBound = GetTime(((float)(PresenceTimeOut[index]) + /(float)100)*(float)cbNode->presence->TTL); + higherBound = GetTime(((float)(PresenceTimeOut[index + 1]) + /(float)100)*(float)cbNode->presence->TTL); + cbNode->presence->timeOut[index] = OCGetRandomRange(lowerBound, higherBound); + OC_LOG_V(DEBUG, TAG, "----------------lowerBound timeout %d", lowerBound); + OC_LOG_V(DEBUG, TAG, "----------------higherBound timeout %d", higherBound); + OC_LOG_V(DEBUG, TAG, "----------------timeOut entry %d", + cbNode->presence->timeOut[index]); + } + cbNode->presence->TTLlevel = 0; + OC_LOG_V(DEBUG, TAG, "----------------this TTL level %d", cbNode->presence->TTLlevel); + if(cbNode->sequenceNumber == response->clientResponse->sequenceNumber) + { + OC_LOG(INFO, TAG, PCF("===============No presence change")); + goto exit; + } + OC_LOG(INFO, TAG, PCF("===============Presence changed, calling up the stack")); + cbNode->sequenceNumber = response->clientResponse->sequenceNumber;; + } + + // Ensure that a filter is actually applied. + if(resourceTypeName && cbNode->filterResourceType) + { + if(!findResourceType(cbNode->filterResourceType, resourceTypeName)) + { + goto exit; + } + } + } + else if(cbNode && isMulticastPresence) + { + // Check if the same nonce for a given host + OCMulticastNode* mcNode = NULL; + mcNode = GetMCPresenceNode(response->fullUri); + + if(response->maxAge == 0) + { + OC_LOG(INFO, TAG, PCF("===============Stopping presence")); + response->clientResponse->result = OC_STACK_PRESENCE_STOPPED; + if(cbNode->presence) + { + OCFree(cbNode->presence->timeOut); + OCFree(cbNode->presence); + cbNode->presence = NULL; + } + } + else if(mcNode != NULL) + { + if(mcNode->nonce == response->clientResponse->sequenceNumber) + { + OC_LOG(INFO, TAG, PCF("===============No presence change (Multicast)")); + result = OC_STACK_NO_MEMORY; + goto exit; + } + mcNode->nonce = response->clientResponse->sequenceNumber; + } + else + { + uint32_t uriLen = strlen((char*)response->fullUri); + unsigned char* uri = (unsigned char *) OCMalloc(uriLen + 1); + if(uri) + { + memcpy(uri, response->fullUri, (uriLen + 1)); + } + else + { + OC_LOG(INFO, TAG, + PCF("===============No Memory for URI to store in the presence node")); + result = OC_STACK_NO_MEMORY; + goto exit; + } + result = AddMCPresenceNode(&mcNode, (unsigned char*) uri, + response->clientResponse->sequenceNumber); + if(result == OC_STACK_NO_MEMORY) + { + OC_LOG(INFO, TAG, + PCF("===============No Memory for Multicast Presence Node")); + result = OC_STACK_NO_MEMORY; + goto exit; + } + } + + // Ensure that a filter is actually applied. + if(resourceTypeName && cbNode->filterResourceType) + { + if(!findResourceType(cbNode->filterResourceType, resourceTypeName)) + { + goto exit; + } + } + } + + else if(!cbNode && isPresenceNotification) + { + OC_LOG(INFO, TAG, PCF("Received a presence notification, but I do not have callback \ + ------------ ignoring")); + } + #endif // WITH_PRESENCE + + if(cbNode) + { + if(isObserveNotification) + { + OC_LOG(INFO, TAG, PCF("Received an observe notification")); + //TODO: check the standard for methods to detect wrap around condition + if(cbNode->method == OC_REST_OBSERVE && + (response->clientResponse->sequenceNumber <= cbNode->sequenceNumber || + (response->clientResponse->sequenceNumber > cbNode->sequenceNumber && + response->clientResponse->sequenceNumber == + MAX_SEQUENCE_NUMBER))) + { + OC_LOG_V(DEBUG, TAG, "Observe notification came out of order. \ + Ignoring Incoming:%d Against Current:%d.", + response->clientResponse->sequenceNumber, cbNode->sequenceNumber); + goto exit; + } + if(response->clientResponse->sequenceNumber > cbNode->sequenceNumber){ + cbNode->sequenceNumber = response->clientResponse->sequenceNumber; + } + } + + response->clientResponse->resJSONPayload = bufRes; + + cbResult = cbNode->callBack(cbNode->context, cbNode->handle, response->clientResponse); + + if (cbResult == OC_STACK_DELETE_TRANSACTION || response->clientResponse->result == OC_STACK_COMM_ERROR || - response->clientResponse->result == OC_STACK_RESOURCE_DELETED) + (response->clientResponse->result == OC_STACK_RESOURCE_DELETED && + !isPresenceNotification && !isMulticastPresence)) { - FindAndDeleteClientCB(response->cbNode); + FindAndDeleteClientCB(cbNode); } } + else + { + result = OC_STACK_ERROR; + } + + exit: + #ifdef WITH_PRESENCE + OCFree(resourceTypeName); + #endif + return result; } int ParseIPv4Address(unsigned char * ipAddrStr, uint8_t * ipAddr, uint16_t * port) @@ -349,7 +1298,7 @@ static void incrementSequenceNumber(OCResource * resPtr); static OCStackResult verifyUriQueryLength(const char * inputUri, uint16_t uriLen); static uint8_t OCIsPacketTransferRequired(const char *request, const char *response, uint16_t size); -OCStackResult getResourceType(const char * uri, unsigned char** resourceType, char ** newURI); +OCStackResult getResourceType(const char * query, unsigned char** resourceType); //----------------------------------------------------------------------------- // Public APIs @@ -374,37 +1323,81 @@ OCStackResult OCInit(const char *ipAddr, uint16_t port, OCMode mode) OCStackResult result = OC_STACK_ERROR; OC_LOG(INFO, TAG, PCF("Entering OCInit")); + // Validate mode + if (!((mode == OC_CLIENT) || (mode == OC_SERVER) || (mode == OC_CLIENT_SERVER))) + { + OC_LOG(ERROR, TAG, PCF("Invalid mode")); + return OC_STACK_ERROR; + } + if (ipAddr) { OC_LOG_V(INFO, TAG, "IP Address = %s", ipAddr); } - switch (mode) + OCSeedRandom(); + CAInitialize(); + //It is ok to select network to CA_WIFI for now +#ifdef WITH_ARDUINO + CAResult_t caResult = CASelectNetwork(CA_ETHERNET); +#else + CAResult_t caResult = CASelectNetwork(CA_WIFI|CA_ETHERNET); +#endif + if(caResult == CA_STATUS_OK) { - case OC_CLIENT: - OC_LOG(INFO, TAG, PCF("Client mode")); - break; - case OC_SERVER: - OC_LOG(INFO, TAG, PCF("Server mode")); - break; - case OC_CLIENT_SERVER: - OC_LOG(INFO, TAG, PCF("Client-server mode")); - break; - default: - OC_LOG(ERROR, TAG, PCF("Invalid mode")); - return OC_STACK_ERROR; - break; + OC_LOG(INFO, TAG, PCF("CASelectNetwork to WIFI")); + CARegisterHandler(HandleCARequests, HandleCAResponses); + { + OC_LOG(INFO, TAG, PCF("CARegisterHandler...")); + stackState = OC_STACK_INITIALIZED; + result = OC_STACK_OK; + switch (mode) + { + case OC_CLIENT: + caResult = CAStartDiscoveryServer(); + OC_LOG(INFO, TAG, PCF("Client mode: CAStartDiscoveryServer")); + break; + case OC_SERVER: + caResult = CAStartListeningServer(); + OC_LOG(INFO, TAG, PCF("Server mode: CAStartListeningServer")); + break; + case OC_CLIENT_SERVER: + caResult = CAStartListeningServer(); + if(caResult == CA_STATUS_OK) + { + caResult = CAStartDiscoveryServer(); + } + OC_LOG(INFO, TAG, PCF("Client-server mode")); + break; + default: + OC_LOG(ERROR, TAG, PCF("Invalid mode")); + return OC_STACK_ERROR; + break; + } + + } + if (caResult == CA_STATUS_OK) + { + result = OC_STACK_OK; + } + else + { + result = OC_STACK_ERROR; + } } - myStackMode = mode; + myStackMode = mode; defaultDeviceHandler = NULL; +#if defined(__WITH_DTLS__) + caResult = CARegisterDTLSCredentialsHandler(GetDtlsPskCredentials); + result = (caResult == CA_STATUS_OK) ? OC_STACK_OK : OC_STACK_ERROR; +#endif // (__WITH_DTLS__) + #ifdef WITH_PRESENCE PresenceTimeOutSize = sizeof(PresenceTimeOut)/sizeof(PresenceTimeOut[0]) - 1; #endif // WITH_PRESENCE - // Make call to OCCoAP layer - result = OCInitCoAP(ipAddr, (uint16_t) port, myStackMode); if (result == OC_STACK_OK) { stackState = OC_STACK_INITIALIZED; @@ -433,12 +1426,19 @@ OCStackResult OCStop() OC_LOG(INFO, TAG, PCF("Entering OCStop")); - if (stackState != OC_STACK_INITIALIZED) + if (stackState == OC_STACK_UNINIT_IN_PROGRESS) + { + OC_LOG(DEBUG, TAG, PCF("Stack already stopping, exiting")); + return OC_STACK_OK; + } + else if (stackState != OC_STACK_INITIALIZED) { OC_LOG(ERROR, TAG, PCF("Stack not initialized")); return OC_STACK_ERROR; } + stackState = OC_STACK_UNINIT_IN_PROGRESS; + #ifdef WITH_PRESENCE // Ensure that the TTL associated with ANY and ALL presence notifications originating from // here send with the code "OC_STACK_PRESENCE_STOPPED" result. @@ -447,9 +1447,12 @@ OCStackResult OCStop() // Free memory dynamically allocated for resources deleteAllResources(); + DeleteDeviceInfo(); + CATerminate(); + //CATerminate does not return any error code. It is OK to assign result to OC_STACK_OK. + result = OC_STACK_OK; - // Make call to OCCoAP layer - if (OCStopCoAP() == OC_STACK_OK) + if (result == OC_STACK_OK) { // Remove all observers DeleteObserverList(); @@ -458,6 +1461,7 @@ OCStackResult OCStop() stackState = OC_STACK_UNINITIALIZED; result = OC_STACK_OK; } else { + stackState = OC_STACK_INITIALIZED; result = OC_STACK_ERROR; } @@ -472,6 +1476,27 @@ OCStackResult OCStop() } /** + * Map OCQualityOfService to CAMessageType + * + * @param OCQualityOfService - Input qos. + * + * Returns CA message type for a given qos. + */ +CAMessageType_t qualityOfServiceToMessageType(OCQualityOfService qos) +{ + switch (qos) + { + case OC_HIGH_QOS: + return CA_MSG_CONFIRM; + case OC_LOW_QOS: + case OC_MEDIUM_QOS: + case OC_NA_QOS: + default: + return CA_MSG_NONCONFIRM; + } +} + +/** * Verify the lengths of the URI and the query separately * * @param inputUri - Input URI and query. @@ -506,9 +1531,11 @@ OCStackResult verifyUriQueryLength(const char *inputUri, uint16_t uriLen) } /** - * Discover or Perform requests on a specified resource (specified by that Resource's respective URI). + * Discover or Perform requests on a specified resource + * (specified by that Resource's respective URI). * - * @param handle - @ref OCDoHandle to refer to the request sent out on behalf of calling this API. + * @param handle - @ref OCDoHandle to refer to the request sent out on behalf of + * calling this API. * @param method - @ref OCMethod to perform on the resource * @param requiredUri - URI of the resource to interact with * @param referenceUri - URI of the reference resource @@ -525,20 +1552,31 @@ OCStackResult verifyUriQueryLength(const char *inputUri, uint16_t uriLen) * OC_STACK_INVALID_CALLBACK - invalid callback function pointer * OC_STACK_INVALID_METHOD - invalid resource method * OC_STACK_INVALID_URI - invalid required or reference URI + * + * Note: IN case of CA, when using multicast, the required URI should not contain IP address. + * Instead, it just contains the URI to the resource such as "/oc/core". */ - OCStackResult OCDoResource(OCDoHandle *handle, OCMethod method, const char *requiredUri, - const char *referenceUri, const char *request, - OCQualityOfService qos, OCCallbackData *cbData, - OCHeaderOption * options, uint8_t numOptions) + const char *referenceUri, const char *request, OCConnectivityType conType, + OCQualityOfService qos, OCCallbackData *cbData, + OCHeaderOption * options, uint8_t numOptions) { OCStackResult result = OC_STACK_ERROR; - OCCoAPToken token; ClientCB *clientCB = NULL; unsigned char * requestUri = NULL; unsigned char * resourceType = NULL; + unsigned char * query = NULL; char * newUri = (char *)requiredUri; (void) referenceUri; + CARemoteEndpoint_t* endpoint = NULL; + CAResult_t caResult; + CAToken_t token = NULL; + CAInfo_t requestData; + CARequestInfo_t requestInfo; + CAGroupEndpoint_t grpEnd = {0}; + + // To track if memory is allocated for additional header options + uint8_t hdrOptionMemAlloc = 0; OC_LOG(INFO, TAG, PCF("Entering OCDoResource")); @@ -546,12 +1584,13 @@ OCStackResult OCDoResource(OCDoHandle *handle, OCMethod method, const char *requ VERIFY_NON_NULL(cbData, FATAL, OC_STACK_INVALID_CALLBACK); VERIFY_NON_NULL(cbData->cb, FATAL, OC_STACK_INVALID_CALLBACK); - TODO ("Need to form the final query by concatenating require and reference URI's"); + //TODO ("Need to form the final query by concatenating require and reference URI's"); VERIFY_NON_NULL(requiredUri, FATAL, OC_STACK_INVALID_URI); uint16_t uriLen = strlen(requiredUri); - // ToDo: We should also check if the requiredUri has a mutlicast address, then qos has to be OC_Low_QOS + // ToDo: We should also check if the requiredUri has a mutlicast address, + // then qos has to be OC_Low_QOS switch (method) { case OC_REST_GET: @@ -585,13 +1624,22 @@ OCStackResult OCDoResource(OCDoHandle *handle, OCMethod method, const char *requ #ifdef WITH_PRESENCE if(method == OC_REST_PRESENCE) { - result = getResourceType(requiredUri, &resourceType, &newUri); - if(resourceType) { - OC_LOG_V(DEBUG, TAG, "Got Resource Type: %s", resourceType); + result = getQueryFromUri(requiredUri, &query, &newUri); + if(query) + { + result = getResourceType((char *) query, &resourceType); + if(resourceType) + { + OC_LOG_V(DEBUG, TAG, "Got Resource Type: %s", resourceType); + } + else + { + OC_LOG(DEBUG, TAG, PCF("Resource type is NULL.")); + } } else { - OC_LOG(DEBUG, TAG, "Got Resource Type is NULL."); + OC_LOG(DEBUG, TAG, PCF("Query string is NULL.")); } if(result != OC_STACK_OK) { @@ -618,19 +1666,134 @@ OCStackResult OCDoResource(OCDoHandle *handle, OCMethod method, const char *requ goto exit; } - // Generate token which will be used by OCStack to match responses received - // with the request - OCGenerateCoAPToken(&token); + memset(&requestData, 0, sizeof(CAInfo_t)); + memset(&requestInfo, 0, sizeof(CARequestInfo_t)); + switch (method) + { + case OC_REST_GET: + case OC_REST_OBSERVE: + case OC_REST_OBSERVE_ALL: + case OC_REST_CANCEL_OBSERVE: + { + requestInfo.method = CA_GET; + break; + } + case OC_REST_PUT: + { + requestInfo.method = CA_PUT; + break; + } + case OC_REST_POST: + { + requestInfo.method = CA_POST; + break; + } + case OC_REST_DELETE: + { + requestInfo.method = CA_DELETE; + break; + } + #ifdef WITH_PRESENCE + case OC_REST_PRESENCE: + { + // Replacing method type with GET because "presence" + // is a stack layer only implementation. + requestInfo.method = CA_GET; + break; + } + #endif + default: + result = OC_STACK_INVALID_METHOD; + goto exit; + } + + //High QoS is not supported + if(qos == OC_HIGH_QOS) + { + result = OC_STACK_INVALID_PARAM; + goto exit; + } + + // create token + caResult = CAGenerateToken(&token); + if (caResult != CA_STATUS_OK) + { + OC_LOG(ERROR, TAG, PCF("CAGenerateToken error")); + CADestroyToken(token); + goto exit; + } - if((result = AddClientCB(&clientCB, cbData, &token, handle, method, requestUri, resourceType)) - != OC_STACK_OK) + requestData.type = qualityOfServiceToMessageType(qos); + requestData.token = token; + if ((method == OC_REST_OBSERVE) || (method == OC_REST_OBSERVE_ALL)) { - result = OC_STACK_NO_MEMORY; + result = CreateObserveHeaderOption (&(requestData.options), options, + numOptions, OC_OBSERVE_REGISTER); + if (result != OC_STACK_OK) + { + goto exit; + } + hdrOptionMemAlloc = 1; + requestData.numOptions = numOptions + 1; + } + else + { + requestData.options = (CAHeaderOption_t*)options; + requestData.numOptions = numOptions; + } + requestData.payload = (char *)request; + + requestInfo.info = requestData; + + CAConnectivityType_t caConType; + + result = OCToCAConnectivityType((OCConnectivityType) conType, &caConType); + if (result != OC_STACK_OK) + { + OC_LOG(ERROR, TAG, PCF("Invalid Connectivity Type")); + goto exit; + } + + // send request + if(conType == OC_ALL) + { + grpEnd.connectivityType = caConType; + + grpEnd.resourceUri = (CAURI_t) OCMalloc(uriLen + 1); + if(!grpEnd.resourceUri) + { + result = OC_STACK_NO_MEMORY; + goto exit; + } + strncpy(grpEnd.resourceUri, requiredUri, (uriLen + 1)); + + caResult = CASendRequestToAll(&grpEnd, &requestInfo); + } + else + { + caResult = CACreateRemoteEndpoint(newUri, caConType, &endpoint); + + if (caResult != CA_STATUS_OK) + { + OC_LOG(ERROR, TAG, PCF("CACreateRemoteEndpoint error")); + goto exit; + } + + caResult = CASendRequest(endpoint, &requestInfo); + } + + if (caResult != CA_STATUS_OK) + { + OC_LOG(ERROR, TAG, PCF("CASendRequest")); goto exit; } - // Make call to OCCoAP layer - result = OCDoCoAPResource(method, qos, &token, newUri, request, options, numOptions); + if((result = AddClientCB(&clientCB, cbData, &token, handle, method, + requestUri, resourceType)) != OC_STACK_OK) + { + result = OC_STACK_NO_MEMORY; + goto exit; + } exit: if(newUri != requiredUri) @@ -642,6 +1805,12 @@ exit: OC_LOG(ERROR, TAG, PCF("OCDoResource error")); FindAndDeleteClientCB(clientCB); } + CADestroyRemoteEndpoint(endpoint); + OCFree(grpEnd.resourceUri); + if (hdrOptionMemAlloc) + { + OCFree(requestData.options); + } return result; } @@ -659,7 +1828,8 @@ exit: * OC_STACK_INVALID_PARAM - The handle provided is invalid. */ OCStackResult OCCancel(OCDoHandle handle, OCQualityOfService qos, OCHeaderOption * options, - uint8_t numOptions) { + uint8_t numOptions) +{ /* * This ftn is implemented one of two ways in the case of observation: * @@ -678,6 +1848,12 @@ OCStackResult OCCancel(OCDoHandle handle, OCQualityOfService qos, OCHeaderOption * Remove the callback associated on client side. */ OCStackResult ret = OC_STACK_OK; + CARemoteEndpoint_t* endpoint = NULL; + CAResult_t caResult; + CAInfo_t requestData; + CARequestInfo_t requestInfo; + // Track if memory is allocated for additional header options + uint8_t hdrOptionMemAlloc = 0; if(!handle) { return OC_STACK_INVALID_PARAM; @@ -692,17 +1868,38 @@ OCStackResult OCCancel(OCDoHandle handle, OCQualityOfService qos, OCHeaderOption { case OC_REST_OBSERVE: case OC_REST_OBSERVE_ALL: - if(qos == OC_HIGH_QOS) + //TODO-CA : Why CA_WIFI alone? + caResult = CACreateRemoteEndpoint((char *)clientCB->requestUri, CA_WIFI, + &endpoint); + if (caResult != CA_STATUS_OK) { - ret = OCDoCoAPResource(OC_REST_CANCEL_OBSERVE, qos, - &(clientCB->token), (const char *) clientCB->requestUri, NULL, options, - numOptions); + OC_LOG(ERROR, TAG, PCF("CACreateRemoteEndpoint error")); + return OC_STACK_ERROR; } - else + + memset(&requestData, 0, sizeof(CAInfo_t)); + requestData.type = qualityOfServiceToMessageType(qos); + requestData.token = clientCB->token; + if (CreateObserveHeaderOption (&(requestData.options), + options, numOptions, OC_OBSERVE_DEREGISTER) != OC_STACK_OK) + { + return OC_STACK_ERROR; + } + hdrOptionMemAlloc = 1; + requestData.numOptions = numOptions + 1; + memset(&requestInfo, 0, sizeof(CARequestInfo_t)); + requestInfo.method = CA_GET; + requestInfo.info = requestData; + // send request + caResult = CASendRequest(endpoint, &requestInfo); + if (caResult != CA_STATUS_OK) { - FindAndDeleteClientCB(clientCB); + OC_LOG(ERROR, TAG, PCF("CASendRequest error")); + } + if(caResult == CA_STATUS_OK) + { + ret = OC_STACK_OK; } - break; #ifdef WITH_PRESENCE case OC_REST_PRESENCE: FindAndDeleteClientCB(clientCB); @@ -712,8 +1909,15 @@ OCStackResult OCCancel(OCDoHandle handle, OCQualityOfService qos, OCHeaderOption return OC_STACK_INVALID_METHOD; } } + CADestroyRemoteEndpoint(endpoint); + if (hdrOptionMemAlloc) + { + OCFree(requestData.options); + } + return ret; } + #ifdef WITH_PRESENCE OCStackResult OCProcessPresence() { @@ -726,6 +1930,7 @@ OCStackResult OCProcessPresence() OCDevAddr dst; OCClientResponse clientResponse; OCResponse * response = NULL; + OCStackApplicationResult cbResult = OC_STACK_DELETE_TRANSACTION; LL_FOREACH(cbList, cbNode) { if(OC_REST_PRESENCE == cbNode->method) @@ -733,7 +1938,8 @@ OCStackResult OCProcessPresence() if(cbNode->presence) { uint32_t now = GetTime(0); - OC_LOG_V(DEBUG, TAG, "----------------this TTL level %d", cbNode->presence->TTLlevel); + OC_LOG_V(DEBUG, TAG, "----------------this TTL level %d", + cbNode->presence->TTLlevel); OC_LOG_V(DEBUG, TAG, "----------------current ticks %d", now); @@ -754,13 +1960,14 @@ OCStackResult OCProcessPresence() { OCBuildIPv4Address(ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3], port, &dst); - result = FormOCClientResponse(&clientResponse, OC_STACK_PRESENCE_TIMEOUT, - (OCDevAddr *) &dst, 0, NULL); - if(result != OC_STACK_OK) - { - goto exit; - } - result = FormOCResponse(&response, cbNode, 0, &clientResponse); + + clientResponse.sequenceNumber = 0; + clientResponse.result = OC_STACK_PRESENCE_TIMEOUT; + clientResponse.addr = (OCDevAddr *) &dst; + clientResponse.resJSONPayload = NULL; + + result = FormOCResponse(&response, cbNode, 0, NULL, NULL, + &cbNode->token, &clientResponse, NULL); if(result != OC_STACK_OK) { goto exit; @@ -777,21 +1984,52 @@ OCStackResult OCProcessPresence() result = OC_STACK_INVALID_IP; goto exit; } - HandleStackResponses(response); + + cbResult = cbNode->callBack(cbNode->context, cbNode->handle, &clientResponse); + if (cbResult == OC_STACK_DELETE_TRANSACTION) + { + FindAndDeleteClientCB(cbNode); + } } + if(now >= cbNode->presence->timeOut[cbNode->presence->TTLlevel]) { + CAResult_t caResult; + CARemoteEndpoint_t* endpoint = NULL; + CAInfo_t requestData; + CARequestInfo_t requestInfo; + OC_LOG(DEBUG, TAG, PCF("time to test server presence ==========")); - OCCoAPToken token; - OCGenerateCoAPToken(&token); - result = OCDoCoAPResource(OC_REST_GET, OC_LOW_QOS, - &token, (const char *)cbNode->requestUri, NULL, NULL, 0); - if(result != OC_STACK_OK) + + //TODO-CA : Why CA_WIFI alone? + caResult = CACreateRemoteEndpoint((char *)cbNode->requestUri, CA_WIFI, + &endpoint); + + if (caResult != CA_STATUS_OK) + { + OC_LOG(ERROR, TAG, PCF("CACreateRemoteEndpoint error")); + goto exit; + } + + memset(&requestData, 0, sizeof(CAInfo_t)); + requestData.type = CA_MSG_NONCONFIRM; + requestData.token = cbNode->token; + + memset(&requestInfo, 0, sizeof(CARequestInfo_t)); + requestInfo.method = CA_GET; + requestInfo.info = requestData; + + caResult = CASendRequest(endpoint, &requestInfo); + + if (caResult != CA_STATUS_OK) { + OC_LOG(ERROR, TAG, PCF("CASendRequest error")); goto exit; } + cbNode->presence->TTLlevel++; - OC_LOG_V(DEBUG, TAG, "----------------moving to TTL level %d", cbNode->presence->TTLlevel); + OC_LOG_V(DEBUG, TAG, "----------------moving to TTL level %d", + cbNode->presence->TTLlevel); } } } @@ -803,7 +2041,7 @@ exit: } return result; } -#endif +#endif // WITH_PRESENCE /** * Called in main loop of OC client or server. Allows low-level processing of @@ -813,13 +2051,12 @@ exit: * OC_STACK_OK - no errors * OC_STACK_ERROR - stack process error */ -OCStackResult OCProcess() { - - OC_LOG(INFO, TAG, PCF("Entering OCProcess")); +OCStackResult OCProcess() +{ #ifdef WITH_PRESENCE OCProcessPresence(); #endif - OCProcessCoAP(); + CAHandleRequestResponse(); return OC_STACK_OK; } @@ -852,15 +2089,26 @@ OCStackResult OCStartPresence(const uint32_t ttl) if(OC_PRESENCE_UNINITIALIZED == presenceState) { - OCDevAddr multiCastAddr; - OCCoAPToken token; - presenceState = OC_PRESENCE_INITIALIZED; - OCGenerateCoAPToken(&token); - OCBuildIPv4Address(224, 0, 1, 187, 5683, &multiCastAddr); - //add the presence observer - AddObserver(OC_PRESENCE_URI, NULL, 0, &token, &multiCastAddr, - (OCResource *)presenceResource.handle, OC_LOW_QOS); + + CAAddress_t addressInfo; + strncpy(addressInfo.IP.ipAddress, "224.0.1.187", CA_IPADDR_SIZE); + addressInfo.IP.port = 5683; + + //TODO make sure there is no memory leak here since another copy + //of token is being created inside AddObserver + CAToken_t caToken = NULL; + CAResult_t caResult = CAGenerateToken(&caToken); + if (caResult != CA_STATUS_OK) + { + OC_LOG(ERROR, TAG, PCF("CAGenerateToken error")); + CADestroyToken(caToken); + return OC_STACK_ERROR; + } + + AddObserver(OC_PRESENCE_URI, NULL, 0, &caToken, + (OCResource *)presenceResource.handle, OC_LOW_QOS, + &addressInfo, CA_WIFI); } // Each time OCStartPresence is called @@ -902,16 +2150,30 @@ OCStackResult OCSetDefaultDeviceEntityHandler(OCDeviceEntityHandler entityHandle return OC_STACK_OK; } +OCStackResult OCSetDeviceInfo(OCDeviceInfo deviceInfo) +{ + OC_LOG(INFO, TAG, PCF("Entering OCSetDeviceInfo")); + + if(myStackMode == OC_CLIENT) + { + return OC_STACK_ERROR; + } + + return SaveDeviceInfo(deviceInfo); +} + /** * Create a resource * - * @param handle - pointer to handle to newly created resource. Set by ocstack. Used to refer to resource + * @param handle - pointer to handle to newly created resource. Set by ocstack. + * Used to refer to resource * @param resourceTypeName - name of resource type. Example: "core.led" * @param resourceInterfaceName - name of resource interface. Example: "core.rw" * @param uri - URI of the resource. Example: "/a/led" * @param entityHandler - entity handler function that is called by ocstack to handle requests, etc * NULL for default entity handler - * @param resourceProperties - properties supported by resource. Example: OC_DISCOVERABLE|OC_OBSERVABLE + * @param resourceProperties - properties supported by resource. + * Example: OC_DISCOVERABLE|OC_OBSERVABLE * * @return * OC_STACK_OK - no errors @@ -935,8 +2197,13 @@ OCStackResult OCCreateResource(OCResourceHandle *handle, return result; } // Validate parameters + if(!uri || (strlen(uri) == 0)) + { + OC_LOG(ERROR, TAG, PCF("URI is invalid")); + return OC_STACK_INVALID_URI; + } // Is it presented during resource discovery? - if (!handle || !resourceTypeName || !uri) { + if (!handle || !resourceTypeName) { OC_LOG(ERROR, TAG, PCF("Input parameter is NULL")); return OC_STACK_INVALID_PARAM; } @@ -966,11 +2233,10 @@ OCStackResult OCCreateResource(OCResourceHandle *handle, } } // Create the pointer and insert it into the resource list - pointer = (OCResource *) OCMalloc(sizeof(OCResource)); + pointer = (OCResource *) OCCalloc(1, sizeof(OCResource)); if (!pointer) { goto exit; } - memset(pointer, 0, sizeof(OCResource)); pointer->sequenceNum = OC_OFFSET_SEQUENCE_NUMBER; insertResource(pointer); @@ -1214,14 +2480,13 @@ OCStackResult BindResourceTypeToResource(OCResource* resource, // TODO: Does resource attribute resentation really have to be maintained in stack? // Is it presented during resource discovery? - TODO ("Make sure that the resourcetypename doesn't already exist in the resource"); + //TODO ("Make sure that the resourcetypename doesn't already exist in the resource"); // Create the resourcetype and insert it into the resource list - pointer = (OCResourceType *) OCMalloc(sizeof(OCResourceType)); + pointer = (OCResourceType *) OCCalloc(1, sizeof(OCResourceType)); if (!pointer) { goto exit; } - memset(pointer, 0, sizeof(OCResourceType)); // Set the resourceTypeName size = strlen(resourceTypeName) + 1; @@ -1256,14 +2521,13 @@ OCStackResult BindResourceInterfaceToResource(OCResource* resource, // Validate parameters VERIFY_NON_NULL(resourceInterfaceName, ERROR, OC_STACK_INVALID_PARAM); - TODO ("Make sure that the resourceinterface name doesn't already exist in the resource"); + //TODO ("Make sure that the resourceinterface name doesn't already exist in the resource"); // Create the resourceinterface and insert it into the resource list - pointer = (OCResourceInterface *) OCMalloc(sizeof(OCResourceInterface)); + pointer = (OCResourceInterface *) OCCalloc(1, sizeof(OCResourceInterface)); if (!pointer) { goto exit; } - memset(pointer, 0, sizeof(OCResourceInterface)); // Set the resourceinterface name size = strlen(resourceInterfaceName) + 1; @@ -1696,7 +2960,6 @@ void incrementSequenceNumber(OCResource * resPtr) return; } -#ifdef WITH_PRESENCE /** * Notify Presence subscribers that a resource has been modified * @@ -1705,6 +2968,7 @@ void incrementSequenceNumber(OCResource * resPtr) * @param qos - Quality Of Service * */ +#ifdef WITH_PRESENCE OCStackResult SendPresenceNotification(OCResourceType *resourceType) { OCResource *resPtr = NULL; @@ -1726,10 +2990,10 @@ OCStackResult SendPresenceNotification(OCResourceType *resourceType) } result = SendAllObserverNotification(method, resPtr, maxAge, resourceType, OC_LOW_QOS); + return result; } -#endif - +#endif // WITH_PRESENCE /** * Notify observers that an observed value has changed. * @@ -1857,7 +3121,6 @@ OCStackResult OCDoResponse(OCEntityHandlerResponse *ehResponse) else { // Normal response - // Get pointer to request info serverRequest = GetServerRequestUsingHandle((OCServerRequest *)ehResponse->requestHandle); if(serverRequest) @@ -1899,10 +3162,10 @@ static OCDoHandle GenerateInvocationHandle() { OCDoHandle handle = NULL; // Generate token here, it will be deleted when the transaction is deleted - handle = (OCDoHandle) OCMalloc(sizeof(uint8_t[MAX_TOKEN_LENGTH])); + handle = (OCDoHandle) OCMalloc(sizeof(uint8_t[CA_MAX_TOKEN_LEN])); if (handle) { - OCFillRandomMem((uint8_t*)handle, sizeof(uint8_t[MAX_TOKEN_LENGTH])); + OCFillRandomMem((uint8_t*)handle, sizeof(uint8_t[CA_MAX_TOKEN_LEN])); } return handle; @@ -2323,29 +3586,50 @@ uint8_t OCIsPacketTransferRequired(const char *request, const char *response, ui } /** - * Retrieves a resource type based upon a uri string if the uri string contains only just one + * Retrieves a resource type based upon a query ontains only just one * resource attribute (and that has to be of type "rt"). * - * @remark This API malloc's memory for the resource type and newURI. Do not malloc resourceType - * or newURI before passing in. + * @remark This API malloc's memory for the resource type. Do not malloc resourceType + * before passing in. * - * @param uri - Valid URI for "requiredUri" parameter to OCDoResource API. + * @param query - The quert part of the URI * @param resourceType - The resource type to be populated; pass by reference. - * @param newURI - Return URI without resourceType appended to the end of it. This is used to - * ensure that the uri parameter is not modified; pass by reference. * * @return - * OC_STACK_INVALID_URI - Returns this if the URI is invalid/NULL. * OC_STACK_INVALID_PARAM - Returns this if the resourceType parameter is invalid/NULL. * OC_STACK_OK - Success */ -OCStackResult getResourceType(const char * uri, unsigned char** resourceType, char ** newURI) +OCStackResult getResourceType(const char * query, unsigned char** resourceType) +{ + if(!query) + { + return OC_STACK_INVALID_PARAM; + } + + OCStackResult result = OC_STACK_ERROR; + + if(strncmp(query, "rt=", 3) == 0) + { + *resourceType = (unsigned char *) OCMalloc(strlen(query)-3); + if(!*resourceType) + { + result = OC_STACK_NO_MEMORY; + } + + strcpy((char *)*resourceType, ((const char *)&query[3])); + result = OC_STACK_OK; + } + + return result; +} + +OCStackResult getQueryFromUri(const char * uri, unsigned char** query, char ** newURI) { if(!uri) { return OC_STACK_INVALID_URI; } - if(!resourceType || !newURI) + if(!query || !newURI) { return OC_STACK_INVALID_PARAM; } @@ -2356,21 +3640,24 @@ OCStackResult getResourceType(const char * uri, unsigned char** resourceType, ch goto exit; } strcpy(tempURI, uri); - leftToken = strtok((char *)tempURI, "?"); + char* strTokPtr; + leftToken = strtok_r((char *)tempURI, "?", &strTokPtr); + //TODO-CA: This could be simplified. Clean up required. while(leftToken != NULL) { - if(strncmp(leftToken, "rt=", 3) == 0) + if(strncmp(leftToken, "rt=", 3) == 0 || strncmp(leftToken, "if=", 3) == 0) { - *resourceType = (unsigned char *) OCMalloc(strlen(leftToken)-3); - if(!*resourceType) + *query = (unsigned char *) OCMalloc(strlen(leftToken) + 1); + if(!*query) { + OCFree(tempURI); goto exit; } - strcpy((char *)*resourceType, ((const char *)&leftToken[3])); + strcpy((char *)*query, ((const char *)&leftToken[0])); break; } - leftToken = strtok(NULL, "?"); + leftToken = strtok_r(NULL, "?", &strTokPtr); } *newURI = tempURI; @@ -2380,3 +3667,66 @@ OCStackResult getResourceType(const char * uri, unsigned char** resourceType, ch exit: return OC_STACK_NO_MEMORY; } + +const ServerID OCGetServerInstanceID(void) +{ + static bool generated = false; + static ServerID sid; + + if(generated) + { + return sid; + } + + sid = OCGetRandom(); + generated = true; + return sid; +} + +const char* OCGetServerInstanceIDString(void) +{ + // max printed length of a base 10 + // uint32 is 10 characters, so 11 includes null. + // This will change as the representation gets switched + // to another value + static char buffer[11]; + int n = sprintf(buffer, "%u", OCGetServerInstanceID()); + if (n < 0) + { + buffer[0]='\0'; + } + + return buffer; +} + +/// Retrieve the IPv4 address embedded inside OCDev address data structure +int32_t OCDevAddrToIPv4Addr(OCDevAddr *ipAddr, uint8_t *a, uint8_t *b, + uint8_t *c, uint8_t *d ) +{ + if ( !ipAddr || !a || !b || !c || !d ) { + OC_LOG(FATAL, TAG, "Invalid argument"); + return OC_STACK_INVALID_PARAM; + } + + *a = ipAddr->addr[0]; + *b = ipAddr->addr[1]; + *c = ipAddr->addr[2]; + *d = ipAddr->addr[3]; + + return OC_STACK_OK; +} + + +/// Retrieve the IPv4 address embedded inside OCDev address data structure +int32_t OCDevAddrToPort(OCDevAddr *ipAddr, uint16_t *port) +{ + if ( !ipAddr || !port ) { + OC_LOG(FATAL, TAG, "Invalid argument"); + return OC_STACK_INVALID_PARAM; + } + + *port = *((uint16_t*)&ipAddr->addr[4]); + + return OC_STACK_OK; +} +