@@ -955,6 +955,163 @@ bootstrapDns:
955955 Expect (hook .Messages ).ShouldNot (ContainElement (ContainSubstring ("deprecated" )))
956956 })
957957 })
958+
959+ Describe ("DNS Stamp Upstreams" , func () {
960+ When ("Config contains DNS stamps" , func () {
961+ It ("should parse DNS stamp upstreams correctly" , func () {
962+ confFile := tmpDir .CreateStringFile ("config.yml" ,
963+ "upstreams:" ,
964+ " groups:" ,
965+ " default:" ,
966+ " - sdns://AgcAAAAAAAAABzEuMC4wLjGgENk8mGSlIfMGXMOlIlCcKvq7AVgcrZxtjon911-ep0cg63Ul-I8NlFj4GplQGb_TTLiczclX57DvMV8Q-JdjgRgSZG5zLmNsb3VkZmxhcmUuY29tCi9kbnMtcXVlcnk" , // Cloudflare DoH
967+ " - sdns://AAcAAAAAAAAABzguOC44Ljg" , // Google DNS Plain
968+ )
969+
970+ c , err = LoadConfig (confFile .Path , true )
971+ Expect (err ).Should (Succeed ())
972+
973+ // Verify default group
974+ defaultUpstreams := c .Upstreams .Groups ["default" ]
975+ Expect (defaultUpstreams ).Should (HaveLen (2 ))
976+
977+ // Verify Cloudflare DoH
978+ Expect (defaultUpstreams [0 ].Net ).Should (Equal (NetProtocolHttps ))
979+ Expect (defaultUpstreams [0 ].Host ).Should (Equal ("dns.cloudflare.com" ))
980+ Expect (defaultUpstreams [0 ].Port ).Should (Equal (uint16 (443 )))
981+ Expect (defaultUpstreams [0 ].Path ).Should (Equal ("/dns-query" ))
982+ Expect (defaultUpstreams [0 ].CommonName ).Should (Equal ("dns.cloudflare.com" ))
983+ Expect (defaultUpstreams [0 ].CertificateFingerprints ).ShouldNot (BeEmpty ())
984+
985+ // Verify Google Plain DNS
986+ Expect (defaultUpstreams [1 ].Net ).Should (Equal (NetProtocolTcpUdp ))
987+ Expect (defaultUpstreams [1 ].Host ).Should (Equal ("8.8.8.8" ))
988+ Expect (defaultUpstreams [1 ].Port ).Should (Equal (uint16 (53 )))
989+ })
990+
991+ It ("should support mixed DNS stamp and traditional format" , func () {
992+ confFile := tmpDir .CreateStringFile ("config.yml" ,
993+ "upstreams:" ,
994+ " groups:" ,
995+ " default:" ,
996+ " - 8.8.8.8" , // Traditional
997+ " - https://dns.google/dns-query" , // Traditional DoH
998+ " - sdns://AAcAAAAAAAAABzguOC44Ljg" , // DNS Stamp
999+ " - sdns://AgcAAAAAAAAABzEuMC4wLjGgENk8mGSlIfMGXMOlIlCcKvq7AVgcrZxtjon911-ep0cg63Ul-I8NlFj4GplQGb_TTLiczclX57DvMV8Q-JdjgRgSZG5zLmNsb3VkZmxhcmUuY29tCi9kbnMtcXVlcnk" , // DNS Stamp DoH
1000+ )
1001+
1002+ c , err = LoadConfig (confFile .Path , true )
1003+ Expect (err ).Should (Succeed ())
1004+
1005+ defaultUpstreams := c .Upstreams .Groups ["default" ]
1006+ Expect (defaultUpstreams ).Should (HaveLen (4 ))
1007+
1008+ // All should be parsed correctly
1009+ Expect (defaultUpstreams [0 ].Host ).Should (Equal ("8.8.8.8" ))
1010+ Expect (defaultUpstreams [0 ].Net ).Should (Equal (NetProtocolTcpUdp ))
1011+
1012+ Expect (defaultUpstreams [1 ].Host ).Should (Equal ("dns.google" ))
1013+ Expect (defaultUpstreams [1 ].Net ).Should (Equal (NetProtocolHttps ))
1014+
1015+ Expect (defaultUpstreams [2 ].Host ).Should (Equal ("8.8.8.8" ))
1016+ Expect (defaultUpstreams [2 ].Net ).Should (Equal (NetProtocolTcpUdp ))
1017+
1018+ Expect (defaultUpstreams [3 ].Host ).Should (Equal ("dns.cloudflare.com" ))
1019+ Expect (defaultUpstreams [3 ].Net ).Should (Equal (NetProtocolHttps ))
1020+ })
1021+
1022+ It ("should reject unsupported protocol in DNS stamp" , func () {
1023+ confFile := tmpDir .CreateStringFile ("config.yml" ,
1024+ "upstreams:" ,
1025+ " groups:" ,
1026+ " default:" ,
1027+ " - sdns://AQMAAAAAAAAAETk0Ljc2Ljc2LjE6ODQ0MyAK-Y3YBV0rO9yqiOWp6OMQNvPPRMfOqCvQV7C8BmOW6hnSZG5zY3J5cHQuZGU" , // DNSCrypt
1028+ )
1029+
1030+ _ , err = LoadConfig (confFile .Path , true )
1031+ Expect (err ).Should (HaveOccurred ())
1032+ // May fail with various error messages depending on stamp validity
1033+ })
1034+
1035+ It ("should reject invalid DNS stamp" , func () {
1036+ confFile := tmpDir .CreateStringFile ("config.yml" ,
1037+ "upstreams:" ,
1038+ " groups:" ,
1039+ " default:" ,
1040+ " - sdns://invalid!!!" , // Invalid stamp
1041+ )
1042+
1043+ _ , err = LoadConfig (confFile .Path , true )
1044+ Expect (err ).Should (HaveOccurred ())
1045+ })
1046+
1047+ It ("should preserve certificate fingerprints from DNS stamps" , func () {
1048+ confFile := tmpDir .CreateStringFile ("config.yml" ,
1049+ "upstreams:" ,
1050+ " groups:" ,
1051+ " default:" ,
1052+ " - sdns://AgcAAAAAAAAABzEuMC4wLjGgENk8mGSlIfMGXMOlIlCcKvq7AVgcrZxtjon911-ep0cg63Ul-I8NlFj4GplQGb_TTLiczclX57DvMV8Q-JdjgRgSZG5zLmNsb3VkZmxhcmUuY29tCi9kbnMtcXVlcnk" , // Cloudflare with certs
1053+ )
1054+
1055+ c , err = LoadConfig (confFile .Path , true )
1056+ Expect (err ).Should (Succeed ())
1057+
1058+ upstream := c .Upstreams .Groups ["default" ][0 ]
1059+ Expect (upstream .CertificateFingerprints ).Should (HaveLen (2 ))
1060+
1061+ // Each fingerprint should be 32 bytes (SHA256)
1062+ for _ , fp := range upstream .CertificateFingerprints {
1063+ Expect (fp ).Should (HaveLen (32 ))
1064+ }
1065+ })
1066+
1067+ It ("should handle IPv6 in DNS stamps" , func () {
1068+ confFile := tmpDir .CreateStringFile ("config.yml" ,
1069+ "upstreams:" ,
1070+ " groups:" ,
1071+ " default:" ,
1072+ " - sdns://AAcAAAAAAAAAKVsyMDAxOjBkYjg6ODVhMzowMDAwOjAwMDA6OGEyZTowMzcwOjczMzRd" ,
1073+ )
1074+
1075+ c , err = LoadConfig (confFile .Path , true )
1076+ Expect (err ).Should (Succeed ())
1077+
1078+ upstream := c .Upstreams .Groups ["default" ][0 ]
1079+
1080+ // All should be parsed correctly
1081+ Expect (upstream .Host ).Should (Equal ("2001:0db8:85a3:0000:0000:8a2e:0370:7334" ))
1082+ Expect (upstream .Net ).Should (Equal (NetProtocolTcpUdp ))
1083+ })
1084+ })
1085+
1086+ When ("Config uses conditional upstream with DNS stamps" , func () {
1087+ It ("should parse DNS stamps in conditional mapping" , func () {
1088+ confFile := tmpDir .CreateStringFile ("config.yml" ,
1089+ "upstreams:" ,
1090+ " groups:" ,
1091+ " default:" ,
1092+ " - 8.8.8.8" ,
1093+ "conditional:" ,
1094+ " mapping:" ,
1095+ " example.com: sdns://AgcAAAAAAAAABzEuMC4wLjGgENk8mGSlIfMGXMOlIlCcKvq7AVgcrZxtjon911-ep0cg63Ul-I8NlFj4GplQGb_TTLiczclX57DvMV8Q-JdjgRgSZG5zLmNsb3VkZmxhcmUuY29tCi9kbnMtcXVlcnk" ,
1096+ " test.com: 1.1.1.1" , // Traditional format
1097+ )
1098+
1099+ c , err = LoadConfig (confFile .Path , true )
1100+ Expect (err ).Should (Succeed ())
1101+
1102+ // Verify conditional mapping parsed DNS stamp
1103+ exampleUpstreams := c .Conditional .Mapping .Upstreams ["example.com" ]
1104+ Expect (exampleUpstreams ).Should (HaveLen (1 ))
1105+ Expect (exampleUpstreams [0 ].Host ).Should (Equal ("dns.cloudflare.com" ))
1106+ Expect (exampleUpstreams [0 ].Net ).Should (Equal (NetProtocolHttps ))
1107+
1108+ // Verify traditional format still works
1109+ testUpstreams := c .Conditional .Mapping .Upstreams ["test.com" ]
1110+ Expect (testUpstreams ).Should (HaveLen (1 ))
1111+ Expect (testUpstreams [0 ].Host ).Should (Equal ("1.1.1.1" ))
1112+ })
1113+ })
1114+ })
9581115})
9591116
9601117func defaultTestFileConfig (config * Config ) {
0 commit comments