3535 {flag , {atom (), term ()}, {atom (), term ()}} |
3636 {enum , [atom ()]} |
3737 ip |
38+ fqdn |
3839 {duration , cuttlefish_duration :time_unit () } |
3940 bytesize |
4041 {percent , integer } |
@@ -75,6 +76,7 @@ is_supported({flag, {On, _}, {Off, _}}) when is_atom(On), is_atom(Off) -> true;
7576is_supported (atom ) -> true ;
7677is_supported ({enum , E }) when is_list (E ) -> true ;
7778is_supported (ip ) -> true ;
79+ is_supported (fqdn ) -> true ;
7880is_supported ({duration , f }) -> true ;
7981is_supported ({duration , w }) -> true ;
8082is_supported ({duration , d }) -> true ;
@@ -96,6 +98,8 @@ is_extended({file, F}) when is_list(F) -> true;
9698is_extended ({directory , D }) when is_list (D ) -> true ;
9799is_extended ({ip , {IP , Port }}) when is_list (IP ) andalso is_integer (Port ) -> true ;
98100is_extended ({ip , StringIP }) when is_list (StringIP ) -> true ;
101+ is_extended ({fqdn , {FQDN , Port }}) when is_list (FQDN ) andalso is_integer (Port ) -> true ;
102+ is_extended ({fqdn , StringFQDN }) when is_list (StringFQDN ) -> true ;
99103is_extended ({{duration , f }, D }) when is_list (D ) -> true ;
100104is_extended ({{duration , w }, D }) when is_list (D ) -> true ;
101105is_extended ({{duration , d }, D }) when is_list (D ) -> true ;
@@ -116,6 +120,7 @@ extended_from({atom, _}) -> atom;
116120extended_from ({file , _ }) -> file ;
117121extended_from ({directory , _ }) -> directory ;
118122extended_from ({ip , _ }) -> ip ;
123+ extended_from ({fqdn , _ }) -> fqdn ;
119124extended_from ({{duration , Unit }, _ }) -> {duration , Unit };
120125extended_from ({bytesize , _ }) -> bytesize ;
121126extended_from ({{percent , integer }, _ }) -> {percent , integer };
@@ -149,6 +154,9 @@ to_string(Integer, integer) when is_list(Integer) -> Integer;
149154to_string ({IP , Port }, ip ) when is_list (IP ), is_integer (Port ) -> IP ++ " :" ++ integer_to_list (Port );
150155to_string (IPString , ip ) when is_list (IPString ) -> IPString ;
151156
157+ to_string ({FQDN , Port }, fqdn ) when is_list (FQDN ), is_integer (Port ) -> FQDN ++ " :" ++ integer_to_list (Port );
158+ to_string (FQDNString , fqdn ) when is_list (FQDNString ) -> FQDNString ;
159+
152160to_string (Enum , {enum , _ }) when is_list (Enum ) -> Enum ;
153161to_string (Enum , {enum , _ }) when is_atom (Enum ) -> atom_to_list (Enum );
154162
@@ -211,6 +219,10 @@ from_string({IP, Port}, ip) when is_list(IP), is_integer(Port) -> {IP, Port};
211219from_string (String , ip ) when is_list (String ) ->
212220 from_string_to_ip (String , lists :split (string :rchr (String , $: ), String ));
213221
222+ from_string ({FQDN , Port }, fqdn ) when is_list (FQDN ), is_integer (Port ) -> {FQDN , Port };
223+ from_string (String , fqdn ) when is_list (String ) ->
224+ from_string_to_fqdn (String , lists :split (string :rchr (String , $: ), String ));
225+
214226from_string (Duration , {duration , _ }) when is_integer (Duration ) -> Duration ;
215227from_string (Duration , {duration , Unit }) when is_list (Duration ) -> cuttlefish_duration :parse (Duration , Unit );
216228
@@ -282,6 +294,17 @@ ip_conversions(String, _IPStr, _IP, undefined) ->
282294ip_conversions (_String , IPStr , {ok , _ }, Port ) ->
283295 {IPStr , Port }.
284296
297+ fqdn_conversions (String , _FQDNStr , nomatch , _Port ) ->
298+ {error , {conversion , {String , 'FQDN' }}};
299+ fqdn_conversions (String , _FQDNStr , _ , undefined ) ->
300+ {error , {conversion , {String , 'FQDN' }}};
301+ fqdn_conversions (_String , FQDNStr , {match , _ }, Port ) ->
302+ {FQDNStr , Port }.
303+
304+ validate_fqdn (Str ) ->
305+ % % inspired by https://regexr.com/3g5j0, amended to disallow [:space:]
306+ re :run (Str , " ^(?!:\/\/ )(?=[^[:space:]]{1,255}$)((.{1,63}\. ){1,127}(?![0-9]*$)[a-z0-9-]+\. ?)$" ).
307+
285308droplast (List ) ->
286309 lists :sublist (List , length (List )- 1 ).
287310
@@ -294,6 +317,12 @@ from_string_to_ip(String, {IpPlusColon, PortString}) ->
294317 IP = droplast (IpPlusColon ),
295318 ip_conversions (String , IP , inet :parse_address (IP ), port_to_integer (PortString )).
296319
320+ from_string_to_fqdn (String , {[], String }) ->
321+ {error , {conversion , {String , 'FQDN' }}};
322+ from_string_to_fqdn (String , {FQDNPlusColon , PortString }) ->
323+ FQDN = droplast (FQDNPlusColon ),
324+ fqdn_conversions (String , FQDN , validate_fqdn (FQDN ), port_to_integer (PortString )).
325+
297326
298327-ifdef (TEST ).
299328
@@ -348,6 +377,8 @@ to_string_extended_type_test() ->
348377 ? assertEqual (" 32" , to_string (" 32" , {integer , 32 })),
349378 ? assertEqual (" 127.0.0.1:8098" , to_string (" 127.0.0.1:8098" , {ip , " 127.0.0.1:8098" })),
350379 ? assertEqual (" 127.0.0.1:8098" , to_string ({" 127.0.0.1" , 8098 }, {ip , {" 127.0.0.1" , 8098 }})),
380+ ? assertEqual (" example.com:8098" , to_string (" example.com:8098" , {fqdn , " example.com:8098" })),
381+ ? assertEqual (" example.com:8098" , to_string ({" example.com" , 8098 }, {fqdn , {" example.com" , 8098 }})),
351382 ? assertEqual (" string" , to_string (" string" , {string , " string" })),
352383 ? assertEqual (" 1w" , to_string (" 1w" , {{duration , s }, " 1w" })),
353384 ? assertEqual (" 1w" , to_string (604800000 , {{duration , ms }, " 1w" })),
@@ -405,6 +436,38 @@ from_string_ip_test() ->
405436 BadIPs ),
406437 ok .
407438
439+ from_string_fqdn_test () ->
440+ ? assertEqual ({" fqdn.com" , 8098 }, from_string (" fqdn.com:8098" , fqdn )),
441+ ? assertEqual (
442+ {" f.q.d.n.com" , 8098 },
443+ from_string (" f.q.d.n.com:8098" , fqdn )),
444+ ? assertEqual (
445+ {" fqdn.com." , 8098 },
446+ from_string (" fqdn.com.:8098" , fqdn )),
447+ ? assertEqual (
448+ {" FqDn.com" , 8098 },
449+ from_string (" FqDn.com:8098" , fqdn )),
450+ ? assertEqual (
451+ {" ec2-35-160-210-253.us-west-2-.compute.amazonaws.com" , 1 },
452+ from_string (" ec2-35-160-210-253.us-west-2-.compute.amazonaws.com:1" , fqdn )),
453+
454+ BadFQDNs = [
455+ " This is not an fqdn:80" ,
456+ " This.is not.an.fqdn:80" ,
457+ " " ,
458+ " 127.0.0.1:80" ,
459+ " fqdn.com" , % % No port
460+ " fqdn.com:-5" ,
461+ " fqdn.com:80:81"
462+ ],
463+
464+ lists :foreach (fun (Bad ) ->
465+ ? assertEqual ({error , {conversion , {Bad , 'FQDN' }}},
466+ from_string (Bad , fqdn ))
467+ end ,
468+ BadFQDNs ),
469+ ok .
470+
408471from_string_enum_test () ->
409472 ? assertEqual (" \" a\" is not a valid enum value, acceptable values are: b, c" , ? XLATE (from_string (a , {enum , [b , c ]}))),
410473 ? assertEqual (true , from_string (" true" , {enum , [true , false ]})),
0 commit comments