Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: FXAS21002 FXOS8700Q
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 }
Generated on Tue Jul 12 2022 20:20:57 by
