Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers stoip6.c Source File

stoip6.c

00001 /*
00002  * Copyright (c) 2014-2015 ARM Limited. All rights reserved.
00003  * SPDX-License-Identifier: Apache-2.0
00004  * Licensed under the Apache License, Version 2.0 (the License); you may
00005  * not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  * http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
00012  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 #include <string.h>
00017 #include <stdlib.h>
00018 #include <stdint.h>
00019 #include "common_functions.h"
00020 #include "ip6string.h"
00021 
00022 static uint16_t hex(const char *p);
00023 static bool is_hex(char c);
00024 
00025 /**
00026  * Convert numeric IPv6 address string to a binary.
00027  * IPv4 tunnelling addresses are not covered.
00028  * \param ip6addr IPv6 address in string format.
00029  * \param len Length of ipv6 string.
00030  * \param dest buffer for address. MUST be 16 bytes.
00031  * \return boolean set to true if conversion succeed, false if it didn't
00032  */
00033 bool stoip6(const char *ip6addr, size_t len, void *dest)
00034 {
00035     uint8_t *addr;
00036     const char *p, *q;
00037     int_fast8_t field_no, coloncolon = -1;
00038 
00039     addr = dest;
00040 
00041     if (len > 39) { // Too long, not possible. We do not support IPv4-mapped IPv6 addresses
00042         goto error;
00043     }
00044 
00045     // First go forward the string, until end, noting :: position if any
00046     // We're decrementing `len` as we go forward, and stop when it reaches 0
00047     for (field_no = 0, p = ip6addr; len && *p; p = q + 1) {
00048 
00049         for (q = p; len && *q && (*q != ':'); len -= 1) { // Seek for ':' or end
00050             if (!is_hex(*q++)) { // There must only be hex characters besides ':'
00051                 goto error;
00052             }
00053         }
00054 
00055         if ((q - p) > 4) { // We can't have more than 4 hex digits per segment
00056             goto error;
00057         }
00058 
00059         if (field_no == 8) { // If the address goes farther than 8 segments
00060             goto error;
00061         }
00062 
00063         // Convert and write this part, (high-endian AKA network byte order)
00064         addr = common_write_16_bit(hex(p), addr);
00065         field_no++;
00066 
00067         // We handle the colons
00068         if (len) {
00069             // Check if we reached "::"
00070             if (q[0] == ':' && q[1] == ':') {
00071                 if (coloncolon != -1) { // We are not supposed to see "::" more than once per address
00072                     goto error;
00073                 }
00074                 coloncolon = field_no;
00075                 q++;
00076                 len -= 2;
00077             } else {
00078                 len -= 1;
00079             }
00080         }
00081     }
00082 
00083     if (coloncolon != -1) {
00084         /* Insert zeros in the appropriate place */
00085         uint_fast8_t head_size = 2 * coloncolon;
00086         uint_fast8_t inserted_size = 2 * (8 - field_no);
00087         uint_fast8_t tail_size = 16 - head_size - inserted_size;
00088         addr = dest;
00089         memmove(addr + head_size + inserted_size, addr + head_size, tail_size);
00090         memset(addr + head_size, 0, inserted_size);
00091     } else if (field_no != 8) { // Report an error if we didn't get 8 fields
00092         goto error;
00093     }
00094     return true;
00095 
00096 error:
00097     // Fill the output buffer with 0 so we stick to the old failure behavior.
00098     // We are however more agressive and wipe the entire address, and do so more often.
00099     memset(dest, 0, 16);
00100     return false;
00101 }
00102 
00103 unsigned char sipv6_prefixlength(const char *ip6addr)
00104 {
00105     char *ptr = strchr(ip6addr, '/');
00106     if (ptr) {
00107         return (unsigned char)strtoul(ptr + 1, 0, 10);
00108     }
00109     return 0;
00110 }
00111 
00112 int stoip6_prefix(const char *ip6addr, void *dest, int_fast16_t *prefix_len_out)
00113 {
00114     size_t addr_len, total_len;
00115     int_fast16_t prefix_length;
00116 
00117     if (prefix_len_out) {
00118         *prefix_len_out = -1;
00119     }
00120 
00121     total_len = addr_len = strlen(ip6addr);
00122     const char *ptr = strchr(ip6addr, '/');
00123     if (ptr) {
00124         addr_len = ptr - ip6addr;
00125         if (prefix_len_out) {
00126             if (total_len - addr_len > 3) {
00127                 /* too many digits in prefix */
00128                 return -1;
00129             }
00130 
00131             prefix_length = strtoul(ptr + 1, 0, 10);
00132             if (prefix_length <  0 || prefix_length > 128) {
00133                 /* prefix value illegal */
00134                 return -1;
00135             }
00136 
00137             *prefix_len_out = prefix_length;
00138         }
00139     }
00140 
00141     if (!stoip6(ip6addr, addr_len, dest)) {
00142         /* parser failure */
00143         return -1;
00144     }
00145 
00146     return 0;
00147 }
00148 
00149 static bool is_hex(char c)
00150 {
00151     // 'A' (0x41) and 'a' (0x61) are mapped in the ASCII table in such a way that masking the 0x20 bit turn 'a' in 'A'
00152     if ((c & ~0x20) >= 'A' && (c & ~0x20) <= 'F') {
00153         return true;
00154     }
00155 
00156     if (c >= '0' && c <= '9') {
00157         return true;
00158     }
00159 
00160     return false;
00161 }
00162 
00163 static uint16_t hex(const char *p)
00164 {
00165     uint16_t val = 0;
00166 
00167     for (;;) {
00168         char c = *p++;
00169         if ((c >= '0') && (c <= '9')) {
00170             val = (val << 4) | (c - '0');
00171         } else if ((c >= 'A') && (c <= 'F')) {
00172             val = (val << 4) | (10 + (c - 'A'));
00173         } else if ((c >= 'a') && (c <= 'f')) {
00174             val = (val << 4) | (10 + (c - 'a'));
00175         } else {
00176             break; // Non hex character
00177         }
00178     }
00179     return val;
00180 }