| 
					
				 | 
			
			
				@@ -398,7 +398,7 @@ static config_var_t option_vars_[] = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   V(MaxOnionQueueDelay,          MSEC_INTERVAL, "1750 msec"), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   V(MaxUnparseableDescSizeToLog, MEMUNIT, "10 MB"), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   V(MinMeasuredBWsForAuthToIgnoreAdvertised, INT, "500"), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  V(MyFamily,                    STRING,   NULL), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  VAR("MyFamily",                LINELIST, MyFamily_lines,       NULL), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   V(NewCircuitPeriod,            INTERVAL, "30 seconds"), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   OBSOLETE("NamingAuthoritativeDirectory"), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   OBSOLETE("NATDListenAddress"), 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -685,7 +685,9 @@ static int options_transition_affects_workers( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       const or_options_t *old_options, const or_options_t *new_options); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static int options_transition_affects_descriptor( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       const or_options_t *old_options, const or_options_t *new_options); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-static int check_nickname_list(char **lst, const char *name, char **msg); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int normalize_nickname_list(config_line_t **normalized_out, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                   const config_line_t *lst, const char *name, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                   char **msg); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static char *get_bindaddr_from_transport_listen_line(const char *line, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                                                      const char *transport); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static int parse_ports(or_options_t *options, int validate_only, 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -893,6 +895,7 @@ or_options_free(or_options_t *options) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   tor_free(options->BridgePassword_AuthDigest_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   tor_free(options->command_arg); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   tor_free(options->master_key_fname); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  config_free_lines(options->MyFamily); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   config_free(&options_format, options); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -3817,13 +3820,14 @@ options_validate(or_options_t *old_options, or_options_t *options, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				              "have it group-readable."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (options->MyFamily && options->BridgeRelay) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (options->MyFamily_lines && options->BridgeRelay) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     log_warn(LD_CONFIG, "Listing a family for a bridge relay is not " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				              "supported: it can reveal bridge fingerprints to censors. " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				              "You should also make sure you aren't listing this bridge's " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				              "fingerprint in any other MyFamily."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (check_nickname_list(&options->MyFamily, "MyFamily", msg)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (normalize_nickname_list(&options->MyFamily, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                              options->MyFamily_lines, "MyFamily", msg)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   for (cl = options->NodeFamilies; cl; cl = cl->next) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     routerset_t *rs = routerset_new(); 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -4524,7 +4528,7 @@ options_transition_affects_descriptor(const or_options_t *old_options, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       get_effective_bwburst(old_options) != 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         get_effective_bwburst(new_options) || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       !opt_streq(old_options->ContactInfo, new_options->ContactInfo) || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      !opt_streq(old_options->MyFamily, new_options->MyFamily) || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      !config_lines_eq(old_options->MyFamily, new_options->MyFamily) || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       !opt_streq(old_options->AccountingStart, new_options->AccountingStart) || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       old_options->AccountingMax != new_options->AccountingMax || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       old_options->AccountingRule != new_options->AccountingRule || 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -4620,27 +4624,36 @@ get_default_conf_file(int defaults_file) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #endif 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-/** Verify whether lst is a string containing valid-looking comma-separated 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- * nicknames, or NULL. Will normalise <b>lst</b> to prefix '$' to any nickname 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- * or fingerprint that needs it. Return 0 on success. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Verify whether lst is a list of strings containing valid-looking 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * comma-separated nicknames, or NULL. Will normalise <b>lst</b> to prefix '$' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * to any nickname or fingerprint that needs it. Also splits comma-separated 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * list elements into multiple elements. Return 0 on success. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  * Warn and return -1 on failure. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 static int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-check_nickname_list(char **lst, const char *name, char **msg) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+normalize_nickname_list(config_line_t **normalized_out, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        const config_line_t *lst, const char *name, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        char **msg) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  int r = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  smartlist_t *sl; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  int changes = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (!*lst) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!lst) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  sl = smartlist_new(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  smartlist_split_string(sl, *lst, ",", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK|SPLIT_STRIP_SPACE, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  config_line_t *new_nicknames = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  config_line_t **new_nicknames_next = &new_nicknames; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  SMARTLIST_FOREACH_BEGIN(sl, char *, s) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const config_line_t *cl; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  for (cl = lst; cl; cl = cl->next) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const char *line = cl->value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!line) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    int valid_line = 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    smartlist_t *sl = smartlist_new(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    smartlist_split_string(sl, line, ",", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK|SPLIT_STRIP_SPACE, 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    SMARTLIST_FOREACH_BEGIN(sl, char *, s) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      char *normalized = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       if (!is_legal_nickname_or_hexdigest(s)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         // check if first char is dollar 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if (s[0] != '$') { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -4649,36 +4662,45 @@ check_nickname_list(char **lst, const char *name, char **msg) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				           tor_asprintf(&prepended, "$%s", s); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				           if (is_legal_nickname_or_hexdigest(prepended)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // The nickname is valid when it's prepended, swap the current 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // version with a prepended one 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            tor_free(s); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            SMARTLIST_REPLACE_CURRENT(sl, s, prepended); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            changes = 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // The nickname is valid when it's prepended, set it as the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // normalized version 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            normalized = prepended; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // Still not valid, free and fallback to error message 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            tor_free(prepended); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				           } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          // Still not valid, free and fallback to error message 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          tor_free(prepended); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        tor_asprintf(msg, "Invalid nickname '%s' in %s line", s, name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        r = -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!normalized) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          tor_asprintf(msg, "Invalid nickname '%s' in %s line", s, name); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          valid_line = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        normalized = tor_strdup(s); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  SMARTLIST_FOREACH_END(s); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  // Replace the caller's nickname list with a fixed one 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  if (changes && r == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    char *newNicknames = smartlist_join_strings(sl, ", ", 0, NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    tor_free(*lst); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    *lst = newNicknames; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      config_line_t *next = tor_malloc_zero(sizeof(*next)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      next->key = tor_strdup(cl->key); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      next->value = normalized; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      next->next = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      *new_nicknames_next = next; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      new_nicknames_next = &next->next; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } SMARTLIST_FOREACH_END(s); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    SMARTLIST_FOREACH(sl, char *, s, tor_free(s)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    smartlist_free(sl); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!valid_line) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      config_free_lines(new_nicknames); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  SMARTLIST_FOREACH(sl, char *, s, tor_free(s)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  smartlist_free(sl); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  *normalized_out = new_nicknames; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  return r; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 /** Learn config file name from command line arguments, or use the default. 
			 |