#endif // _di_f_network_from_ip_string_
#ifndef _di_f_network_is_ip_address_
- void f_network_is_ip_address(const f_string_static_t address, f_number_unsigned_t * const port, f_state_t * const state) {
+ void f_network_is_ip_address(const f_string_static_t address, f_string_range_double_t * const where, f_state_t * const state) {
if (!state) return;
uint8_t flag = 0x0; // 0x1 == is IPv6.
uint8_t set = 0;
uint8_t count = 0;
+ f_string_range_double_t at = f_string_range_double_t_initialize;
for (; i < address.used; ++i) {
if (isxdigit(address.string[i])) {
if (address.string[i] < f_string_ascii_0_s.string[0] || address.string[i] > f_string_ascii_9_s.string[0]) {
- flag = 0x1;
+ flag |= 0x1;
+ }
+
+ if (!where) {
+ if (at.start_1 > at.stop_1) {
+ at.start_1 = at.stop_1 = i;
+ }
}
++count;
break;
}
- else if (address.string[i] == f_string_ascii_colon_s.string[0]) {
+
+ if (address.string[i] == f_string_ascii_colon_s.string[0]) {
flag = 0x1;
break;
}
- else if (address.string[i] == f_string_ascii_bracket_open_s.string[0]) {
+
+ if (address.string[i] == f_string_ascii_bracket_open_s.string[0]) {
if (flag) {
state->status = F_false;
return;
}
- flag = 0x3;
+ flag |= 0x3;
break;
}
return;
}
- f_number_unsigned_t j = 0;
+ at.start_1 = i;
if (flag & 0x1) {
state->status = F_network_version_six_not;
- if (count > 4) return;
+ if (count > 4 || i == address.used) return;
if (!count) {
- for (j = i; i < address.used && !address.string[j]; ++j) {
+
+ // Skip past NULL characters.
+ for (; i < address.used && !address.string[i]; ++i) {
if (state->interrupt) {
state->interrupt((void *) state, 0);
if (F_status_set_fine(state->status) == F_interrupt) return;
+
+ // Restore status in case the interrupt callback changed it.
state->status = F_network_version_six_not;
}
} // for
- if (j == address.used) return;
+ if (i == address.used) return;
if (!(flag & 0x2)) flag |= 0x10;
+
+ at.start_1 = i;
}
+ // Process address, counting all sets.
for (; i < address.used; ++i) {
if (state->interrupt) {
state->interrupt((void *) state, 0);
if (F_status_set_fine(state->status) == F_interrupt) return;
+
+ // Restore status in case the interrupt callback changed it.
state->status = F_network_version_six_not;
}
else if (address.string[i] == f_string_ascii_colon_s.string[0]) {
if (flag & 0x4) return;
- j = i + 1;
+ at.stop_1 = i;
+ at.start_2 = i + 1;
+
+ if (at.start_2 < address.used) {
- if (j < address.used) {
- for (; j < address.used && !address.string[j]; ++j) {
+ // Skip past NULL characters.
+ for (; at.start_2 < address.used && !address.string[at.start_2]; ++at.start_2) {
if (state->interrupt) {
state->interrupt((void *) state, 0);
if (F_status_set_fine(state->status) == F_interrupt) return;
+
+ // Restore status in case the interrupt callback changed it.
state->status = F_network_version_six_not;
}
} // for
- if (address.string[j] == f_string_ascii_colon_s.string[0]) {
+ if (at.start_2 == address.used) return;
+
+ if (address.string[at.start_2] == f_string_ascii_colon_s.string[0]) {
if (flag & 0x8) return;
- i = j;
flag |= 0x8;
- for (; i < address.used && !address.string[i]; ++i) {
+ // Skip past NULL characters.
+ for (; at.start_2 < address.used && !address.string[at.start_2]; ++at.start_2) {
if (state->interrupt) {
state->interrupt((void *) state, 0);
if (F_status_set_fine(state->status) == F_interrupt) return;
+
+ // Restore status in case the interrupt callback changed it.
state->status = F_network_version_six_not;
}
} // for
// Must not end on a double-colon.
- if (i + 1 == address.used) return;
+ if (at.start_2 == address.used) return;
}
// Colons must be followed by a hexidecimal digit.
- if (!isxdigit(address.string[i + 1])) return;
+ if (!isxdigit(address.string[at.start_2])) return;
+
+ i = at.stop_1 = at.start_2;
}
else {
else if (address.string[i] == f_string_ascii_bracket_close_s.string[0]) {
if (!(flag & 0x2)) return;
- for (j = i + 1; j < address.used && !address.string[j]; ++j) {
+ if (!where) {
+ at.stop_1 = i - 1;
+ }
+
+ // Skip past NULL characters.
+ for (at.start_2 = i + 1; at.start_2 < address.used && !address.string[at.start_2]; ++at.start_2) {
if (state->interrupt) {
state->interrupt((void *) state, 0);
if (F_status_set_fine(state->status) == F_interrupt) return;
+
+ // Restore status in case the interrupt callback changed it.
state->status = F_network_version_six_not;
}
} // for
- if (j < address.used) {
- if (address.string[j] == f_string_ascii_colon_s.string[0]) {
- while (++j < address.used && !address.string[j]) {
+ if (at.start_2 < address.used) {
+ if (address.string[at.start_2] == f_string_ascii_colon_s.string[0]) {
+
+ // Skip past NULL characters.
+ while (++at.start_2 < address.used && !address.string[at.start_2]) {
if (state->interrupt) {
state->interrupt((void *) state, 0);
if (F_status_set_fine(state->status) == F_interrupt) return;
+
+ // Restore status in case the interrupt callback changed it.
state->status = F_network_version_six_not;
}
} // while
// The colon used to designate the port number must be followed by a (base-10) number.
- for (i = j; j < address.used; ++j) {
+ for (at.stop_2 = at.start_2; at.stop_2 < address.used; ++at.stop_2) {
if (state->interrupt) {
state->interrupt((void *) state, 0);
if (F_status_set_fine(state->status) == F_interrupt) return;
+
+ // Restore status in case the interrupt callback changed it.
state->status = F_network_version_six_not;
}
- if (!address.string[j]) continue;
- if (!isdigit(address.string[j])) return;
+ if (!address.string[at.stop_2]) continue;
+ if (!isdigit(address.string[at.stop_2])) return;
} // for
// The double colon either must exist when set is smaller than 7 or the double colon must not exist at all.
if (set < 7 && !(flag & 0x8) || set == 7 && (flag & 0x8)) return;
state->status = F_network_version_six;
-
- if (port) *port = i;
}
else {
state->status = F_network_version_six;
- if (port) *port = 0;
+ at.start_2 = 1;
+ at.stop_2 = 0;
+
+ if (where) {
+ *where = at;
+ }
}
return;
if (!(flag & 0x10) || set || count) {
state->status = F_network_version_six;
- if (port) *port = 0;
+ at.start_2 = 1;
+ at.stop_2 = 0;
+
+ if (where) {
+ *where = at;
+ }
}
return;
if (state->interrupt) {
state->interrupt((void *) state, 0);
if (F_status_set_fine(state->status) == F_interrupt) return;
+
+ // Restore status in case the interrupt callback changed it.
state->status = F_network_version_four_not;
}
if (isdigit(address.string[i])) {
if (flag & 0x4) {
if (!(flag & 0x8) && ++count > 3) return;
+
+ if (flag & 0x2) {
+ at.stop_2 = i;
+ }
+ }
+ else if (flag & 0x2) {
+ at.stop_2 = i;
}
- else if (!(flag & 0x2)) {
+ else {
if (++count > 3) return;
}
}
if (state->interrupt) {
state->interrupt((void *) state, 0);
if (F_status_set_fine(state->status) == F_interrupt) return;
+
+ // Restore status in case the interrupt callback changed it.
state->status = F_network_version_four_not;
}
} // for
+ if (i == address.used) return;
+
flag |= 0x2;
- j = i + 1; // Save the position that might represent the start of the port number.
+ at.start_2 = i + 1; // Save the position that might represent the start of the port number.
}
else if (address.string[i] == f_string_ascii_slash_forward_s.string[0]) {
if ((flag & 0x4) || set != 3) return;
} // for
if (set == 3) {
- state->status = F_network_version_four;
+ if (where) {
+ *where = at;
+ }
- if (port) *port = j;
+ state->status = F_network_version_four;
}
else {
state->status = F_network_version_four_not;
* The human-friendly IP address string.
* @param to
* The converted IP version 4 or version 6 family integer.
+ * For IPv6, the use of a port number requires the address to be encases in brackets, like: [::1]:80.
*
* @return
* F_okay on success.
*
* For IPv6, this only accepts port numbers when the IPv6 address is wrapped in brackets ('[' (U+005B) and ']' (U+005D)).
*
+ * This allows NULL characters to exist within the IP address, but the standard POSIX/libc does not.
+ * The address from this string should be sanitized to not have NULL characters before passing to a POSIX/libc function.
+ *
* @param address
* The string to parse.
- * @param port
- * (optional) This gets updated with the location where the first digit of the port number begins.
- * This is set to 0 if there is no port number.
+ * @param where
+ * (optional) This utilizes a strange range where the first set (start_1 to stop_1) represents the address range and the second set (start_2 to stop_2) represents the port range.
+ * For IPv4, an address of '127.0.0.1:123' would have the first set represent '127.0.0.1' and the second set represent '123'.
+ * For IPv6, an address of '[::1]:123' would have the first set represent '::1' and the second set represent '123'.
+ *
+ * Notice that for IPv6, the address of '::1's does not include the open and close brackets.
+ * Notice that for both the IPv4 and the IPv6, the address and port number do not include the colon separating the address from the port.
+ *
+ * The goal here is to be able to present the strings to functions like inet_pton().
+ *
+ * The start_1 to stop_1 range represents the address part.
+ * The start_2 to stop_2 range represents the port part (start_2 will be greater than stop_2 if there is no port).
+ *
* On any error, this value is not changed.
+ * If the given address is not valid, then this value is not changed.
*
* Set to NULL to disable.
* @param state
* F_interrupt (with or without error bit) if stopping due to an interrupt.
*/
#ifndef _di_f_network_is_ip_address_
- extern void f_network_is_ip_address(const f_string_static_t address, f_number_unsigned_t * const port, f_state_t * const state);
+ extern void f_network_is_ip_address(const f_string_static_t address, f_string_range_double_t * const where, f_state_t * const state);
#endif // _di_f_network_is_ip_address_
/**