+ /*
+ * Handle IPv6 address inside square brackets as mention by RFC 2732. IPv6
+ * address that does not start AND end with brackets will be rejected even
+ * if valid.
+ *
+ * proto://[<addr>]...
+ * ^
+ */
+ if (*purl == '[') {
+ /* Address begins after '[' */
+ addr_b = purl + 1;
+ addr_e = strchr(addr_b, ']');
+ if (addr_e == NULL || addr_b == addr_e) {
+ ERR("Broken IPv6 address %s", addr_b);
+ goto free_error;
+ }
+
+ /* Moving parsed URL pointer after the final bracket ']' */
+ purl = addr_e + 1;
+
+ /*
+ * The closing bracket must be followed by a seperator or NULL char.
+ */
+ if (strchr(seps, *purl) == NULL) {
+ ERR("Unknown symbol after IPv6 address: %s", purl);
+ goto free_error;
+ }
+ } else {
+ purl = strpbrk_or_eos(purl, seps);
+ addr_e = purl;
+ }
+
+ /* Check if we at least have a char for the addr or hostname. */
+ if (addr_b == addr_e) {
+ ERR("No address or hostname detected.");
+ goto free_error;
+ }
+
+ addr_f = utils_strdupdelim(addr_b, addr_e);
+ if (addr_f == NULL) {
+ goto free_error;
+ }
+
+ /*
+ * Detect PORT after address. The net/net6 protocol allows up to two port
+ * so we can define the control and data port.
+ */
+ while (*purl == ':') {
+ int port;
+ const char *port_b, *port_e;
+ char *port_f;
+
+ /* Update pass counter */
+ i++;
+
+ /*
+ * Maximum of two ports is possible if P_NET/NET6. Bigger than that,
+ * two much stuff.
+ */
+ if ((i == 2 && (proto->code != P_NET && proto->code != P_NET6))
+ || i > 2) {
+ break;
+ }
+
+ /*
+ * Move parsed URL to port value.
+ * proto://addr_host:PORT1:PORT2/foo/bar
+ * ^
+ */
+ ++purl;
+ port_b = purl;
+ purl = strpbrk_or_eos(purl, seps);
+ port_e = purl;
+
+ if (port_b != port_e) {
+ port_f = utils_strdupdelim(port_b, port_e);
+ if (port_f == NULL) {
+ goto free_error;
+ }
+
+ port = atoi(port_f);
+ if (port > 0xffff || port <= 0x0) {
+ ERR("Invalid port number %d", port);
+ free(port_f);
+ goto free_error;
+ }
+ free(port_f);
+
+ if (i == 1) {
+ ctrl_port = port;
+ } else {
+ data_port = port;
+ }
+ }
+ };
+
+ /* Check for a valid subdir or trailing garbage */
+ if (*purl == '/') {
+ /*
+ * Move to subdir value.
+ * proto://addr_host:PORT1:PORT2/foo/bar
+ * ^
+ */
+ ++purl;
+ subdir_b = purl;
+ } else if (*purl != '\0') {
+ ERR("Trailing characters not recognized: %s", purl);
+ goto free_error;