Mayank Gupta / Mbed OS pelion-example-frdm

Dependencies:   FXAS21002 FXOS8700Q

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers arm_uc_utilities.c Source File

arm_uc_utilities.c

00001 // ----------------------------------------------------------------------------
00002 // Copyright 2016-2017 ARM Ltd.
00003 //
00004 // SPDX-License-Identifier: Apache-2.0
00005 //
00006 // Licensed under the Apache License, Version 2.0 (the "License");
00007 // you may not use this file except in compliance with the License.
00008 // You may obtain a copy of the License at
00009 //
00010 //     http://www.apache.org/licenses/LICENSE-2.0
00011 //
00012 // Unless required by applicable law or agreed to in writing, software
00013 // distributed under the License is distributed on an "AS IS" BASIS,
00014 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015 // See the License for the specific language governing permissions and
00016 // limitations under the License.
00017 // ----------------------------------------------------------------------------
00018 
00019 #include "update-client-common/arm_uc_utilities.h"
00020 #include "update-client-common/arm_uc_error.h"
00021 #include "update-client-common/arm_uc_config.h"
00022 
00023 #include <string.h>
00024 #include <stdlib.h>
00025 #include <stdio.h>
00026 
00027 /* lookup table for printing hexadecimal values */
00028 const uint8_t arm_uc_hex_table[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
00029                                       '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
00030                                      };
00031 
00032 /**
00033  * @brief Parse a uri string to populate a arm_uc_uri_t struct
00034  * @detail Format of uri scheme:[//]host[:port]/path
00035  *         [] means optional, path will always start with a '/'
00036  *
00037  * @param str Pointer to string containing URI.
00038  * @param size String length.
00039  * @param uri The arm_uc_uri_t struct to be populated
00040  * @return Error code.
00041  */
00042 arm_uc_error_t arm_uc_str2uri(const uint8_t *buffer,
00043                               uint32_t buffer_size,
00044                               arm_uc_uri_t *uri)
00045 {
00046     arm_uc_error_t result = (arm_uc_error_t) { ERR_INVALID_PARAMETER };
00047 
00048     if (buffer &&
00049             uri &&
00050             uri->ptr &&
00051             (buffer_size < uri->size_max)) {
00052         const uint8_t *str = buffer;
00053         uint8_t *colon = NULL;
00054         uint8_t *slash = NULL;
00055         uint32_t len = 0;
00056         uint8_t slash_count = 0;
00057 
00058         /* find scheme by searching for first colon */
00059         colon = memchr(str, ':', buffer_size);
00060         len = colon - str;
00061 
00062         if (len < uri->size_max) {
00063             /* copy scheme to temporary uri buffer and convert to lower case.
00064             */
00065             for (uint32_t index = 0; index < len; index++) {
00066                 /* lower case characters have higher ASCII value */
00067                 if (str[index] < 'a') {
00068                     uri->ptr[index] = str[index] + ('a' - 'A');
00069                 } else {
00070                     uri->ptr[index] = str[index];
00071                 }
00072             }
00073 
00074             /* copy ':' */
00075             uri->ptr[len] = str[len];
00076 
00077             /* convert scheme string to scheme type */
00078             if (memcmp(uri->ptr, "http:", 5) == 0) {
00079                 uri->scheme = URI_SCHEME_HTTP;
00080 
00081                 /* set default port based on scheme - can be overwritten */
00082                 uri->port   = 80;
00083             } else if (memcmp(uri->ptr, "coaps:", 6) == 0) {
00084                 uri->scheme = URI_SCHEME_COAPS;
00085                 uri->port = 5683;
00086             } else if (memcmp(uri->ptr, "file:", 5) == 0) {
00087                 uri->scheme = URI_SCHEME_FILE;
00088             } else {
00089                 uri->scheme = URI_SCHEME_NONE;
00090             }
00091 
00092             /* only continue if scheme is supported */
00093             if (uri->scheme != URI_SCHEME_NONE) {
00094                 /* strip leading '/', but at most two of them, since 'file://' URIs
00095                    might have a third '/' when specifying absolute paths */
00096                 str = colon + 1;
00097                 for (str += 1;
00098                         (str[0] == '/') && (str < (buffer + buffer_size) && (slash_count < 1));
00099                         ++str, ++slash_count);
00100 
00101                 /* File URIs only have the 'path' component, so they need to
00102                    be handled separately */
00103                 if (uri->scheme == URI_SCHEME_FILE) {
00104                     /* host part will be empty */
00105                     uri->ptr[0] = '\0';
00106                     uri->host = (char *)uri->ptr;
00107 
00108                     /* path is the whole data after "file://" */
00109                     len = buffer_size - (str - buffer);
00110                     memcpy(uri->ptr + 1, str, len);
00111                     uri->ptr[len + 1] = '\0';
00112                     uri->path = (char *)uri->ptr + 1;
00113                     uri->size = len + 2;
00114 
00115                     result = (arm_uc_error_t) { ERR_NONE };
00116                 } else {
00117                     /* find separation between host and path */
00118                     slash = memchr(str, '/', buffer_size - (str - buffer));
00119 
00120                     if (slash != NULL) {
00121                         bool parsed = true;
00122 
00123                         /* find optional port */
00124                         colon = memchr(str, ':', buffer_size - (slash - buffer));
00125 
00126                         if (colon != NULL) {
00127                             uri->port = arm_uc_str2uint32(colon + 1,
00128                                                           buffer_size - (colon - buffer),
00129                                                           &parsed);
00130                             len = colon - str;
00131                         } else {
00132                             len = slash - str;
00133                         }
00134 
00135                         /* check */
00136                         if ((parsed == 1) && (len < uri->size_max)) {
00137                             /* copy host name to URI buffer */
00138                             memcpy(uri->ptr, str, len);
00139 
00140                             /* \0 terminate string */
00141                             uri->ptr[len] = '\0';
00142 
00143                             /* update length */
00144                             uri->size = len + 1;
00145 
00146                             /* set host pointer */
00147                             uri->host = (char *) uri->ptr;
00148 
00149                             /* find remaining path length */
00150                             str = slash;
00151                             len = arm_uc_strnlen(str, buffer_size - (str - buffer));
00152 
00153                             /* check */
00154                             if ((len > 0) && (len < (uri->size_max - uri->size))) {
00155                                 /* copy path to URI buffer */
00156                                 memcpy(&uri->ptr[uri->size], str, len);
00157 
00158                                 /* set path pointer */
00159                                 uri->path = (char *) &uri->ptr[uri->size];
00160 
00161                                 /* \0 terminate string */
00162                                 uri->ptr[uri->size + len] = '\0';
00163 
00164                                 /* update length after path pointer is set */
00165                                 uri->size += len + 1;
00166 
00167                                 /* parsing passed all checks */
00168                                 result = (arm_uc_error_t) { ERR_NONE };
00169                             }
00170                         }
00171                     }
00172                 }
00173             }
00174         }
00175     }
00176 
00177     return result;
00178 }
00179 
00180 /**
00181  * @brief Find substring inside string.
00182  * @details The size of both string and substring is explicitly passed so no
00183  *          assumptions are made about NULL termination.
00184  *
00185  * @param big_buffer Pointer to the string to be searched.
00186  * @param big_length Length of the string to be searched.
00187  * @param little_buffer Pointer to the substring being searched for.
00188  * @param little_length Length of the substring being searched for.
00189  * @return Index to where the substring was found inside the string. If the
00190  *         string doesn't contain the subtring, UINT32_MAX is returned.
00191  */
00192 uint32_t arm_uc_strnstrn(const uint8_t *big_buffer,
00193                          uint32_t big_length,
00194                          const uint8_t *little_buffer,
00195                          uint32_t little_length)
00196 {
00197     uint32_t result = UINT32_MAX;
00198 
00199     /* Sanity check. Pointers are not NULL. The little buffer is smaller than
00200        the big buffer. The little buffer is not empty.
00201     */
00202     if (big_buffer &&
00203             little_buffer &&
00204             (big_length >= little_length) &&
00205             (little_length > 0)) {
00206         uint8_t little_hash = 0;
00207         uint8_t big_hash = 0;
00208         uint32_t little_length_m1 = little_length - 1;
00209 
00210         /* Prepare hashes. The last byte for the big hash is added in the
00211            comparison loop.
00212         */
00213         for (uint32_t index = 0; index < little_length_m1; index++) {
00214             little_hash ^= little_buffer[index];
00215             big_hash ^= big_buffer[index];
00216         }
00217 
00218         /* Add the last byte for the little hash. */
00219         little_hash ^= little_buffer[little_length_m1];
00220 
00221         /* Comparison loop. In each loop the big hash is updated and compared
00222            to the little hash. If the hash matches, a more thorough byte-wise
00223            comparison is performed. The complexity of the hash determines how
00224            often a collision occures and how often a full comparison is done.
00225         */
00226         for (uint32_t index = 0;
00227                 index < (big_length - (little_length_m1));
00228                 index++) {
00229             /* update hash */
00230             big_hash ^= big_buffer[index + (little_length_m1)];
00231 
00232             /* cursory check */
00233             if (little_hash == big_hash) {
00234                 /* hash checks out do comprehensive check */
00235                 uint32_t checks = 0;
00236 
00237                 for (; checks < little_length; checks++) {
00238                     /* stop counting if bytes differ */
00239                     if (big_buffer[index + checks] != little_buffer[checks]) {
00240                         break;
00241                     }
00242                 }
00243 
00244                 /* check if all bytes matched */
00245                 if (checks == little_length) {
00246                     /* save pointer and break loop */
00247                     result = index;
00248                     break;
00249                 }
00250             }
00251 
00252             /* update hash - remove tail */
00253             big_hash ^= big_buffer[index];
00254         }
00255     }
00256 
00257     return result;
00258 }
00259 
00260 /**
00261  * @brief Find string length.
00262  * @details Custom implementation of strnlen which is a GNU extension.
00263  *          Returns either the string length or max_length.
00264  *
00265  * @param buffer Pointer to string.
00266  * @param max_length Maximum buffer length.
00267  *
00268  * @return String length or max_length.
00269  */
00270 uint32_t arm_uc_strnlen(const uint8_t *buffer, uint32_t max_length)
00271 {
00272     uint32_t length = 0;
00273 
00274     for (; length < max_length; length++) {
00275         if (buffer[length] == '\0') {
00276             break;
00277         }
00278     }
00279 
00280     return length;
00281 }
00282 
00283 /**
00284  * @brief Convert string to unsigned 32 bit integer.
00285  * @details Function tries to parse string as an unsigned 32 bit integer
00286  *          and return the value. The function expects the first byte to be an
00287  *          integer and will continue until:
00288  *           1. the buffer is empty
00289  *           2. the intermediate result is larger then UINT32_MAX
00290  *           3. the next byte is not a number
00291  *
00292  *          If a valid 32 bit unsigned integer is found the third parameter is
00293  *          set to true and the return value holds the parsed number. Otherwise,
00294  *          the third parameter will be false and the return value will be 0.
00295  *
00296  * @param buffer Pointer to string.
00297  * @param max_length Maximum buffer length.
00298  * @param success Pointer to boolean indicating whether the parsing was successful.
00299  * @return Parsed value. Only valid if success it true.
00300  */
00301 uint32_t arm_uc_str2uint32(const uint8_t *buffer,
00302                            uint32_t max_length,
00303                            bool *success)
00304 {
00305     uint64_t result = 0;
00306     bool found = false;
00307 
00308     /* default output and return status is 0 and false */
00309     uint32_t output = 0;
00310 
00311     if (success) {
00312         *success = false;
00313     }
00314 
00315     /* null pointer and length check */
00316     if (buffer && (max_length > 0)) {
00317         /* loop through string */
00318         for (uint32_t index = 0; index < max_length; index++) {
00319             /* check if character is a number */
00320             if (('0' <= buffer[index]) &&
00321                     (buffer[index] <= '9') &&
00322                     (result < UINT64_MAX)) {
00323                 /* shift one decimal position and append next digit */
00324                 result *= 10;
00325                 result += buffer[index] - '0';
00326 
00327                 /* found at least one integer, mark as found */
00328                 found = true;
00329             } else {
00330                 /* character is not a number, stop loop */
00331                 break;
00332             }
00333         }
00334 
00335         /* set output and return value only if a valid number was found */
00336         if (found && (result <= UINT64_MAX)) {
00337             output = result;
00338 
00339             if (success) {
00340                 *success = true;
00341             }
00342         }
00343     }
00344 
00345     return output;
00346 }
00347 
00348 static const uint8_t base64EncodeArray[65] = {MBED_CLOUD_UPDATE_BASE64_CHARSET};
00349 
00350 uint8_t *ARM_UC_Base64Enc(uint8_t *buf, const uint32_t size, const arm_uc_buffer_t *bin)
00351 {
00352     uint32_t partial = 0;
00353     const uint8_t *lastPos = buf + size;
00354     uint32_t i;
00355     uint32_t pad2 = (bin->size - bin->size % 3);
00356     uint32_t pad1 = (bin->size - bin->size % 3) + 1;
00357     for (i = 0; i < bin->size && buf <= lastPos - 4; i += 3) {
00358         partial = (bin->ptr[i] << 16);
00359         if (i < pad1) {
00360             partial = partial | (bin->ptr[i + 1] << 8);
00361         }
00362         if (i < pad2) {
00363             partial = partial | (bin->ptr[i + 2] << 0);
00364         }
00365         buf[0] = base64EncodeArray[(partial >> 18) & 0x3f];
00366         buf[1] = base64EncodeArray[(partial >> 12) & 0x3f];
00367         buf[2] = (i < pad1) ? base64EncodeArray[(partial >>  6) & 0x3f] : base64EncodeArray[64];
00368         buf[3] = (i < pad2) ? base64EncodeArray[(partial >>  0) & 0x3f] : base64EncodeArray[64];
00369         buf += 4;
00370     }
00371     buf[0] = 0;
00372     return buf;
00373 }
00374 
00375 uint32_t ARM_UC_Base64DecodeChar(uint8_t c)
00376 {
00377     if (c == MBED_CLOUD_UPDATE_BASE64_CHARSET[64] || c == MBED_CLOUD_UPDATE_BASE64_CHARSET[0]) {
00378         return 0;
00379     }
00380     uint32_t idx = 0;
00381     int32_t i;
00382     for (i = 5; i >= 0; i--) {
00383         uint32_t tmpidx = idx | 1 << i;
00384         uint8_t ct = MBED_CLOUD_UPDATE_BASE64_CHARSET[tmpidx];
00385         if (c == ct) {
00386             return tmpidx;
00387         } else if (c > ct) {
00388             idx = tmpidx;
00389         }
00390     }
00391     return (uint32_t) -1;
00392 }
00393 
00394 void ARM_UC_Base64Dec(arm_uc_buffer_t *bin, const uint32_t size, const uint8_t *buf)
00395 {
00396     uintptr_t optr = (uintptr_t)bin->ptr;
00397     const uint8_t  *iptr = buf;
00398     while ((uintptr_t)iptr + 4 < (uintptr_t) buf + size && optr + 1 < (uintptr_t)bin->ptr + bin->size_max) {
00399         uint8_t partial[3];
00400         uint8_t a = (ARM_UC_Base64DecodeChar(iptr[0]));
00401         uint8_t b = (ARM_UC_Base64DecodeChar(iptr[1]));
00402         uint8_t c = (ARM_UC_Base64DecodeChar(iptr[2]));
00403         uint8_t d = (ARM_UC_Base64DecodeChar(iptr[3]));
00404         uint8_t l = 3;
00405         if (d == MBED_CLOUD_UPDATE_BASE64_CHARSET[64]) {
00406             l--;
00407         }
00408         if (c == MBED_CLOUD_UPDATE_BASE64_CHARSET[64]) {
00409             l--;
00410         }
00411         partial[0] = ((a << 2) & 0xfc) | ((b >> 4) & 0x3);
00412         partial[1] = ((b << 4) & 0xf0) | ((c >> 2) & 0xf);
00413         partial[2] = ((c << 6) & 0xc0) | ((d >> 0) & 0x3f);
00414         memcpy((void *)optr, partial, l);
00415         iptr += 4;
00416         optr += l;
00417         if (d == MBED_CLOUD_UPDATE_BASE64_CHARSET[64]) {
00418             break;
00419         }
00420     }
00421     bin->size = optr - (uintptr_t)bin->ptr;
00422 }
00423 
00424 size_t arm_uc_calculate_full_uri_length(const arm_uc_uri_t *uri)
00425 {
00426 
00427     size_t scheme_length = 0;
00428 
00429     if (uri->scheme == URI_SCHEME_COAPS) {
00430         scheme_length = strlen(UC_COAPS_STRING) + 1;
00431     } else if (uri->scheme == URI_SCHEME_HTTP) {
00432         scheme_length = strlen(UC_HTTP_STRING) + 1;
00433     } else if (uri->scheme == URI_SCHEME_FILE) {
00434         scheme_length = strlen(UC_FILE_STRING) + 1;
00435     } else {
00436         return 0; // Not supported scheme
00437     }
00438 
00439     return (uri->size +
00440             strlen(uri->path) +
00441             scheme_length);
00442 }