terence zhang / wakaama
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers transaction.c Source File

transaction.c

00001 /*******************************************************************************
00002  *
00003  * Copyright (c) 2013, 2014 Intel Corporation and others.
00004  * All rights reserved. This program and the accompanying materials
00005  * are made available under the terms of the Eclipse Public License v1.0
00006  * and Eclipse Distribution License v1.0 which accompany this distribution.
00007  *
00008  * The Eclipse Public License is available at
00009  *    http://www.eclipse.org/legal/epl-v10.html
00010  * The Eclipse Distribution License is available at
00011  *    http://www.eclipse.org/org/documents/edl-v10.php.
00012  *
00013  * Contributors:
00014  *    David Navarro, Intel Corporation - initial API and implementation
00015  *    Simon Bernard - Please refer to git log
00016  *    Toby Jaffey - Please refer to git log
00017  *    Pascal Rieux - Please refer to git log
00018  *    Bosch Software Innovations GmbH - Please refer to git log
00019  *
00020  *******************************************************************************/
00021 
00022 /*
00023  Copyright (c) 2013, 2014 Intel Corporation
00024 
00025  Redistribution and use in source and binary forms, with or without modification,
00026  are permitted provided that the following conditions are met:
00027 
00028      * Redistributions of source code must retain the above copyright notice,
00029        this list of conditions and the following disclaimer.
00030      * Redistributions in binary form must reproduce the above copyright notice,
00031        this list of conditions and the following disclaimer in the documentation
00032        and/or other materials provided with the distribution.
00033      * Neither the name of Intel Corporation nor the names of its contributors
00034        may be used to endorse or promote products derived from this software
00035        without specific prior written permission.
00036 
00037  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00038  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00039  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00040  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00041  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00042  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00043  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00044  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00045  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
00046  THE POSSIBILITY OF SUCH DAMAGE.
00047 
00048  David Navarro <david.navarro@intel.com>
00049 
00050 */
00051 
00052 /*
00053 Contains code snippets which are:
00054 
00055  * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
00056  * All rights reserved.
00057  *
00058  * Redistribution and use in source and binary forms, with or without
00059  * modification, are permitted provided that the following conditions
00060  * are met:
00061  * 1. Redistributions of source code must retain the above copyright
00062  *    notice, this list of conditions and the following disclaimer.
00063  * 2. Redistributions in binary form must reproduce the above copyright
00064  *    notice, this list of conditions and the following disclaimer in the
00065  *    documentation and/or other materials provided with the distribution.
00066  * 3. Neither the name of the Institute nor the names of its contributors
00067  *    may be used to endorse or promote products derived from this software
00068  *    without specific prior written permission.
00069  *
00070  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
00071  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00072  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00073  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
00074  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00075  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00076  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00077  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00078  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00079  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00080  * SUCH DAMAGE.
00081 
00082 */
00083 
00084 /************************************************************************
00085  *  Function for communications transactions.
00086  *
00087  *  Basic specification: rfc7252
00088  *
00089  *  Transaction implements processing of piggybacked and separate response communication
00090  *  dialogs specified in section 2.2 of the above specification.
00091  *  The caller registers a callback function, which is called, when either the result is
00092  *  received or a timeout occurs.
00093  *
00094  *  Supported dialogs:
00095  *  Requests (GET - DELETE):
00096  *  - CON with mid, without token => regular finished with corresponding ACK.MID
00097  *  - CON with mid, with token => regular finished with corresponding ACK.MID and response containing
00098  *                  the token. Supports both versions, with piggybacked ACK and separate ACK/response.
00099  *                  Though the ACK.MID may be lost in the separate version, a matching response may
00100  *                  finish the transaction even without the ACK.MID.
00101  *  - NON without token => no transaction, no result expected!
00102  *  - NON with token => regular finished with response containing the token.
00103  *  Responses (COAP_201_CREATED - ?):
00104  *  - CON with mid => regular finished with corresponding ACK.MID
00105  */
00106 
00107 #include "internals.h"
00108 
00109 
00110 /*
00111  * Modulo mask (+1 and +0.5 for rounding) for a random number to get the tick number for the random
00112  * retransmission time between COAP_RESPONSE_TIMEOUT and COAP_RESPONSE_TIMEOUT*COAP_RESPONSE_RANDOM_FACTOR.
00113  */
00114 #define COAP_RESPONSE_TIMEOUT_TICKS         (CLOCK_SECOND * COAP_RESPONSE_TIMEOUT)
00115 #define COAP_RESPONSE_TIMEOUT_BACKOFF_MASK  ((CLOCK_SECOND * COAP_RESPONSE_TIMEOUT * (COAP_RESPONSE_RANDOM_FACTOR - 1)) + 1.5)
00116 
00117 static int prv_checkFinished(lwm2m_transaction_t * transacP,
00118                              coap_packet_t * receivedMessage)
00119 {
00120     int len;
00121     const uint8_t* token;
00122     coap_packet_t * transactionMessage = transacP->message;
00123 
00124     if (COAP_DELETE < transactionMessage->code)
00125     {
00126         // response
00127         return transacP->ack_received ? 1 : 0;
00128     }
00129     if (!IS_OPTION(transactionMessage, COAP_OPTION_TOKEN))
00130     {
00131         // request without token
00132         return transacP->ack_received ? 1 : 0;
00133     }
00134 
00135     len = coap_get_header_token(receivedMessage, &token);
00136     if (transactionMessage->token_len == len)
00137     {
00138         if (memcmp(transactionMessage->token, token, len)==0) return 1;
00139     }
00140 
00141     return 0;
00142 }
00143 
00144 lwm2m_transaction_t * transaction_new(void * sessionH,
00145                                       coap_method_t method,
00146                                       char * altPath,
00147                                       lwm2m_uri_t * uriP,
00148                                       uint16_t mID,
00149                                       uint8_t token_len,
00150                                       uint8_t* token)
00151 {
00152     lwm2m_transaction_t * transacP;
00153     int result;
00154 
00155     LOG_ARG("method: %d, altPath: \"%s\", mID: %d, token_len: %d",
00156             method, altPath, mID, token_len);
00157     LOG_URI(uriP);
00158 
00159     // no transactions without peer
00160     if (NULL == sessionH) return NULL;
00161 
00162     transacP = (lwm2m_transaction_t *)lwm2m_malloc(sizeof(lwm2m_transaction_t));
00163 
00164     if (NULL == transacP) return NULL;
00165     memset(transacP, 0, sizeof(lwm2m_transaction_t));
00166 
00167     transacP->message = lwm2m_malloc(sizeof(coap_packet_t));
00168     if (NULL == transacP->message) goto error;
00169 
00170     coap_init_message(transacP->message, COAP_TYPE_CON, method, mID);
00171 
00172     transacP->peerH = sessionH;
00173 
00174     transacP->mID = mID;
00175 
00176     if (altPath != NULL)
00177     {
00178         // TODO: Support multi-segment alternative path
00179         coap_set_header_uri_path_segment(transacP->message, altPath + 1);
00180     }
00181     if (NULL != uriP)
00182     {
00183         result = utils_intCopy(transacP->objStringID, LWM2M_STRING_ID_MAX_LEN, uriP->objectId);
00184         if (result < 0) goto error;
00185 
00186         coap_set_header_uri_path_segment(transacP->message, transacP->objStringID);
00187 
00188         if (LWM2M_URI_IS_SET_INSTANCE(uriP))
00189         {
00190             result = utils_intCopy(transacP->instanceStringID, LWM2M_STRING_ID_MAX_LEN, uriP->instanceId);
00191             if (result < 0) goto error;
00192             coap_set_header_uri_path_segment(transacP->message, transacP->instanceStringID);
00193         }
00194         else
00195         {
00196             if (LWM2M_URI_IS_SET_RESOURCE(uriP))
00197             {
00198                 coap_set_header_uri_path_segment(transacP->message, NULL);
00199             }
00200         }
00201         if (LWM2M_URI_IS_SET_RESOURCE(uriP))
00202         {
00203             result = utils_intCopy(transacP->resourceStringID, LWM2M_STRING_ID_MAX_LEN, uriP->resourceId);
00204             if (result < 0) goto error;
00205             coap_set_header_uri_path_segment(transacP->message, transacP->resourceStringID);
00206         }
00207     }
00208     if (0 < token_len)
00209     {
00210         if (NULL != token)
00211         {
00212             coap_set_header_token(transacP->message, token, token_len);
00213         }
00214         else {
00215             // generate a token
00216             uint8_t temp_token[COAP_TOKEN_LEN];
00217             time_t tv_sec = lwm2m_gettime();
00218 
00219             // initialize first 6 bytes, leave the last 2 random
00220             temp_token[0] = mID;
00221             temp_token[1] = mID >> 8;
00222             temp_token[2] = tv_sec;
00223             temp_token[3] = tv_sec >> 8;
00224             temp_token[4] = tv_sec >> 16;
00225             temp_token[5] = tv_sec >> 24;
00226             // use just the provided amount of bytes
00227             coap_set_header_token(transacP->message, temp_token, token_len);
00228         }
00229     }
00230 
00231     LOG("Exiting on success");
00232     return transacP;
00233 
00234 error:
00235     LOG("Exiting on failure");
00236     lwm2m_free(transacP);
00237     return NULL;
00238 }
00239 
00240 void transaction_free(lwm2m_transaction_t * transacP)
00241 {
00242     LOG("Entering");
00243     if (transacP->message) lwm2m_free(transacP->message);
00244     if (transacP->buffer) lwm2m_free(transacP->buffer);
00245     lwm2m_free(transacP);
00246 }
00247 
00248 void transaction_remove(lwm2m_context_t * contextP,
00249                         lwm2m_transaction_t * transacP)
00250 {
00251     LOG("Entering");
00252     contextP->transactionList = (lwm2m_transaction_t *) LWM2M_LIST_RM(contextP->transactionList, transacP->mID, NULL);
00253     transaction_free(transacP);
00254 }
00255 
00256 bool transaction_handleResponse(lwm2m_context_t * contextP,
00257                                  void * fromSessionH,
00258                                  coap_packet_t * message,
00259                                  coap_packet_t * response)
00260 {
00261     bool found = false;
00262     bool reset = false;
00263     lwm2m_transaction_t * transacP;
00264 
00265     LOG("Entering");
00266     transacP = contextP->transactionList;
00267 
00268     while (NULL != transacP)
00269     {
00270         if (lwm2m_session_is_equal(fromSessionH, transacP->peerH, contextP->userData) == true)
00271         {
00272             if (!transacP->ack_received)
00273             {
00274                 if ((COAP_TYPE_ACK == message->type) || (COAP_TYPE_RST == message->type))
00275                 {
00276                     if (transacP->mID == message->mid)
00277                     {
00278                         found = true;
00279                         transacP->ack_received = true;
00280                         reset = COAP_TYPE_RST == message->type;
00281                     }
00282                 }
00283             }
00284 
00285             if (reset || prv_checkFinished(transacP, message))
00286             {
00287                 // HACK: If a message is sent from the monitor callback,
00288                 // it will arrive before the registration ACK.
00289                 // So we resend transaction that were denied for authentication reason.
00290                 if (!reset)
00291                 {
00292                     if (COAP_TYPE_CON == message->type && NULL != response)
00293                     {
00294                         coap_init_message(response, COAP_TYPE_ACK, 0, message->mid);
00295                         message_send(contextP, response, fromSessionH);
00296                     }
00297                 
00298                     if ((COAP_401_UNAUTHORIZED == message->code) && (COAP_MAX_RETRANSMIT > transacP->retrans_counter))
00299                     {
00300                         transacP->ack_received = false;
00301                         transacP->retrans_time += COAP_RESPONSE_TIMEOUT;
00302                         return true;
00303                     }
00304                 }       
00305                 if (transacP->callback != NULL)
00306                 {
00307                     transacP->callback(transacP, message);
00308                 }
00309                 transaction_remove(contextP, transacP);
00310                 return true;
00311             }
00312             // if we found our guy, exit
00313             if (found)
00314             {
00315                 time_t tv_sec = lwm2m_gettime();
00316                 if (0 <= tv_sec)
00317                 {
00318                     transacP->retrans_time = tv_sec;
00319                 }
00320                 if (transacP->response_timeout)
00321                 {
00322                     transacP->retrans_time += transacP->response_timeout;
00323                 }
00324                 else
00325                 {
00326                     transacP->retrans_time += COAP_RESPONSE_TIMEOUT * transacP->retrans_counter;
00327                 }
00328                 return true;
00329             }
00330         }
00331 
00332         transacP = transacP->next;
00333     }
00334     return false;
00335 }
00336 
00337 int transaction_send(lwm2m_context_t * contextP,
00338                      lwm2m_transaction_t * transacP)
00339 {
00340     bool maxRetriesReached = false;
00341 
00342     LOG("Entering");
00343     if (transacP->buffer == NULL)
00344     {
00345         transacP->buffer_len = coap_serialize_get_size(transacP->message);
00346         if (transacP->buffer_len == 0) return COAP_500_INTERNAL_SERVER_ERROR;
00347 
00348         transacP->buffer = (uint8_t*)lwm2m_malloc(transacP->buffer_len);
00349         if (transacP->buffer == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
00350 
00351         transacP->buffer_len = coap_serialize_message(transacP->message, transacP->buffer);
00352         if (transacP->buffer_len == 0)
00353         {
00354             lwm2m_free(transacP->buffer);
00355             transacP->buffer = NULL;
00356             transaction_remove(contextP, transacP);
00357             return COAP_500_INTERNAL_SERVER_ERROR;
00358         }
00359     }
00360 
00361     if (!transacP->ack_received)
00362     {
00363         long unsigned timeout;
00364 
00365         if (0 == transacP->retrans_counter)
00366         {
00367             time_t tv_sec = lwm2m_gettime();
00368             if (0 <= tv_sec)
00369             {
00370                 transacP->retrans_time = tv_sec + COAP_RESPONSE_TIMEOUT;
00371                 transacP->retrans_counter = 1;
00372                 timeout = 0;
00373             }
00374             else
00375             {
00376                 maxRetriesReached = true;
00377             }
00378         }
00379         else
00380         {
00381             timeout = COAP_RESPONSE_TIMEOUT << (transacP->retrans_counter - 1);
00382         }
00383 
00384         if (COAP_MAX_RETRANSMIT + 1 >= transacP->retrans_counter)
00385         {
00386             (void)lwm2m_buffer_send(transacP->peerH, transacP->buffer, transacP->buffer_len, contextP->userData);
00387 
00388             transacP->retrans_time += timeout;
00389             transacP->retrans_counter += 1;
00390         }
00391         else
00392         {
00393             maxRetriesReached = true;
00394         }
00395     }
00396 
00397     if (transacP->ack_received || maxRetriesReached)
00398     {
00399         if (transacP->callback)
00400         {
00401             transacP->callback(transacP, NULL);
00402         }
00403         transaction_remove(contextP, transacP);
00404         return -1;
00405     }
00406 
00407     return 0;
00408 }
00409 
00410 void transaction_step(lwm2m_context_t * contextP,
00411                       time_t currentTime,
00412                       time_t * timeoutP)
00413 {
00414     lwm2m_transaction_t * transacP;
00415 
00416     LOG("Entering");
00417     transacP = contextP->transactionList;
00418     while (transacP != NULL)
00419     {
00420         // transaction_send() may remove transaction from the linked list
00421         lwm2m_transaction_t * nextP = transacP->next;
00422         int removed = 0;
00423 
00424         if (transacP->retrans_time <= currentTime)
00425         {
00426             removed = transaction_send(contextP, transacP);
00427         }
00428 
00429         if (0 == removed)
00430         {
00431             time_t interval;
00432 
00433             if (transacP->retrans_time > currentTime)
00434             {
00435                 interval = transacP->retrans_time - currentTime;
00436             }
00437             else
00438             {
00439                 interval = 1;
00440             }
00441 
00442             if (*timeoutP > interval)
00443             {
00444                 *timeoutP = interval;
00445             }
00446         }
00447         else
00448         {
00449             *timeoutP = 1;
00450         }
00451 
00452         transacP = nextP;
00453     }
00454 }