@@ -588,26 +588,101 @@ int upscli_split_authconf_section(const char *sect_name,
588588 /* Take raw sect_name as input (e.g. a user-written string from config files).
589589 * Normalize it by splitting into user, host, and port components (populating absent values).
590590 * Return normalized components and reconstructed section name in output parameters (if not NULL).
591+ * This looks similar to upscli_splitname() but reserves the option to evolve
592+ * the supported section name syntax differently for the different purpose.
593+ * TOTHINK: Combine the `host:port` logic with upscli_splitaddr() to
594+ * de-duplicate IPv6 nuance handling etc.?
591595 */
592- const char * at = NULL , * colon = NULL ;
596+ const char * at = NULL , * colon = NULL , * bracket_open = NULL , * bracket_close = NULL ;
593597 char * sect_user = NULL , * sect_host = NULL , * sect_port = NULL ;
594598 int fixed_sect_user = 0 ;
595599
596600 if (!sect_name ) {
597601 upsdebugx (1 , "%s: sect_name is NULL" , __func__ );
602+ errno = EINVAL ;
598603 return -1 ;
599604 }
600605
601606 if (!(* sect_name )) {
602607 /* TOTHINK: Should this mean `localhost@NUT_PORT`? Or global? Probably neither. */
603608 upsdebugx (1 , "%s: sect_name is empty" , __func__ );
609+ errno = EINVAL ;
604610 return -1 ;
605611 }
606612
607613 at = strchr (sect_name , '@' );
608614 colon = strchr (sect_name , ':' );
615+ bracket_open = strchr (sect_name , '[' );
616+ bracket_close = strchr (sect_name , ']' );
617+
618+ /* Sanity checks */
609619 if (at && colon && colon < at ) {
610620 upsdebugx (1 , "%s: Invalid section header: colon ':' before at '@': '%s'" , __func__ , sect_name );
621+ errno = EINVAL ;
622+ return -1 ;
623+ }
624+
625+ /* IPv6 numeric addresses are a series of colon-separated hex digits wrapped in square brackets */
626+ if (bracket_open && colon && colon < bracket_open ) {
627+ upsdebugx (1 , "%s: Invalid section header: colon ':' before opening bracket '[': '%s'" , __func__ , sect_name );
628+ errno = EINVAL ;
629+ return -1 ;
630+ }
631+
632+ if ( (bracket_open && !bracket_close )
633+ || (!bracket_open && bracket_close )
634+ ) {
635+ upsdebugx (1 , "%s: Invalid section header: single bracket '[' or ']' in text: '%s'" , __func__ , sect_name );
636+ errno = EINVAL ;
637+ return -1 ;
638+ }
639+
640+ if (bracket_close && colon && colon > bracket_close ) {
641+ upsdebugx (1 , "%s: Invalid section header: first colon ':' is after closing bracket ']': '%s'" , __func__ , sect_name );
642+ errno = EINVAL ;
643+ return -1 ;
644+ }
645+
646+ if (bracket_close && !colon ) {
647+ upsdebugx (1 , "%s: Invalid section header: brackets '[...]' present but no colon ':' inside: '%s'" , __func__ , sect_name );
648+ errno = EINVAL ;
649+ return -1 ;
650+ }
651+
652+ /* For valid IPv6 spelling, consider there should be several colons inside brackets */
653+ if (bracket_open && bracket_close && colon && colon > bracket_open && colon < bracket_close ) {
654+ /* There should be at most one more colon inside brackets
655+ * Technically: up to 8 hex sections split by 7 colons,
656+ * but long stretches of zeroes may collapse into "::" ONCE.
657+ */
658+ colon = strchr (colon + 1 , ':' );
659+ if (!colon || colon > bracket_close ) {
660+ upsdebugx (1 , "%s: Invalid section header: withh numeric IPv6 there must be multiple colons ':' inside brackets: '%s'" , __func__ , sect_name );
661+ errno = EINVAL ;
662+ return -1 ;
663+ }
664+ }
665+
666+ /* For valid IPv6 spelling, consider the colon after brackets as the port separator (if any, NULL otherwise) */
667+ if (bracket_close ) {
668+ /* There should be at most one colon after brackets (may also be none) */
669+ colon = strchr (bracket_close + 1 , ':' );
670+ }
671+
672+ if (colon && strchr (colon + 1 , ':' )) {
673+ upsdebugx (1 , "%s: Invalid section header: multiple colons ':' in text: '%s'" , __func__ , sect_name );
674+ return -1 ;
675+ }
676+
677+ if (bracket_open && strchr (bracket_open + 1 , '[' )) {
678+ upsdebugx (1 , "%s: Invalid section header: multiple opening brackets '[' in text: '%s'" , __func__ , sect_name );
679+ errno = EINVAL ;
680+ return -1 ;
681+ }
682+
683+ if (bracket_close && strchr (bracket_close + 1 , ']' )) {
684+ upsdebugx (1 , "%s: Invalid section header: multiple closing brackets ']' in text: '%s'" , __func__ , sect_name );
685+ errno = EINVAL ;
611686 return -1 ;
612687 }
613688
@@ -722,8 +797,10 @@ static void handle_authconf_args(size_t numargs, char **arg, int global_scope)
722797
723798 current_section_ignored = 0 ;
724799
725- sect_name = xstrdup (& arg [0 ][1 ]); /* forget leading '[' */
726- end_bracket = strchr (sect_name , ']' );
800+ /* forget leading '[' */
801+ sect_name = xstrdup (& arg [0 ][1 ]);
802+ /* we would remove the LAST seen bracket (there may be some in text, in case of IPv6 addresses) */
803+ end_bracket = strrchr (sect_name , ']' );
727804 if (!end_bracket || !strcmp (sect_name , "_global_defaults" )) {
728805 free (sect_name );
729806
@@ -740,7 +817,9 @@ static void handle_authconf_args(size_t numargs, char **arg, int global_scope)
740817 return ;
741818 }
742819
743- * (char * )(end_bracket ) = '\0' ; /* forget trailing ']' and any characters after it (comments etc.) */
820+ /* forget trailing ']' and any characters after it (comments etc.)...
821+ * although those should be separate parseconf arguments */
822+ * (char * )(end_bracket ) = '\0' ;
744823
745824 if (upscli_split_authconf_section (sect_name , & normalized_sect_name ,
746825 & sect_user , & current_section_with_fixed_username ,
0 commit comments