github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/parameters/frontingSpec.go (about) 1 /* 2 * Copyright (c) 2021, Psiphon Inc. 3 * All rights reserved. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package parameters 21 22 import ( 23 "net" 24 25 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors" 26 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng" 27 regen "github.com/zach-klippenstein/goregen" 28 ) 29 30 // FrontingSpecs is a list of domain fronting specs. 31 type FrontingSpecs []*FrontingSpec 32 33 // FrontingSpec specifies a domain fronting configuration, to be used with 34 // MeekConn and MeekModePlaintextRoundTrip. In MeekModePlaintextRoundTrip, the 35 // fronted origin is an arbitrary web server, not a Psiphon server. This 36 // MeekConn mode requires HTTPS and server certificate validation: 37 // VerifyServerName is required; VerifyPins is recommended. See also 38 // psiphon.MeekConfig and psiphon.MeekConn. 39 // 40 // FrontingSpec.Addresses supports the functionality of both 41 // ServerEntry.MeekFrontingAddressesRegex and 42 // ServerEntry.MeekFrontingAddresses: multiple candidates are supported, and 43 // each candidate may be a regex, or a static value (with regex syntax). 44 type FrontingSpec struct { 45 FrontingProviderID string 46 Addresses []string 47 DisableSNI bool 48 VerifyServerName string 49 VerifyPins []string 50 Host string 51 } 52 53 // SelectParameters selects fronting parameters from the given FrontingSpecs, 54 // first selecting a spec at random. SelectParameters is similar to 55 // psiphon.selectFrontingParameters, which operates on server entries. 56 // 57 // The return values are: 58 // - Dial Address (domain or IP address) 59 // - SNI (which may be transformed; unless it is "", which indicates omit SNI) 60 // - VerifyServerName (see psiphon.CustomTLSConfig) 61 // - VerifyPins (see psiphon.CustomTLSConfig) 62 // - Host (Host header value) 63 func (specs FrontingSpecs) SelectParameters() ( 64 string, string, string, string, []string, string, error) { 65 66 if len(specs) == 0 { 67 return "", "", "", "", nil, "", errors.TraceNew("missing fronting spec") 68 } 69 70 spec := specs[prng.Intn(len(specs))] 71 72 if len(spec.Addresses) == 0 { 73 return "", "", "", "", nil, "", errors.TraceNew("missing fronting address") 74 } 75 76 frontingDialAddr, err := regen.Generate( 77 spec.Addresses[prng.Intn(len(spec.Addresses))]) 78 if err != nil { 79 return "", "", "", "", nil, "", errors.Trace(err) 80 } 81 82 SNIServerName := frontingDialAddr 83 if spec.DisableSNI || net.ParseIP(frontingDialAddr) != nil { 84 SNIServerName = "" 85 } 86 87 return spec.FrontingProviderID, 88 frontingDialAddr, 89 SNIServerName, 90 spec.VerifyServerName, 91 spec.VerifyPins, 92 spec.Host, 93 nil 94 } 95 96 // Validate checks that the JSON values are well-formed. 97 func (specs FrontingSpecs) Validate() error { 98 99 // An empty FrontingSpecs is allowed as a tactics setting, but 100 // SelectParameters will fail at runtime: code that uses FrontingSpecs must 101 // provide some mechanism -- or check for an empty FrontingSpecs -- to 102 // enable/disable features that use FrontingSpecs. 103 104 for _, spec := range specs { 105 if len(spec.FrontingProviderID) == 0 { 106 return errors.TraceNew("empty fronting provider ID") 107 } 108 if len(spec.Addresses) == 0 { 109 return errors.TraceNew("missing fronting addresses") 110 } 111 for _, addr := range spec.Addresses { 112 if len(addr) == 0 { 113 return errors.TraceNew("empty fronting address") 114 } 115 } 116 if len(spec.VerifyServerName) == 0 { 117 return errors.TraceNew("empty verify server name") 118 } 119 // An empty VerifyPins is allowed. 120 for _, pin := range spec.VerifyPins { 121 if len(pin) == 0 { 122 return errors.TraceNew("empty verify pin") 123 } 124 } 125 if len(spec.Host) == 0 { 126 return errors.TraceNew("empty fronting host") 127 } 128 } 129 return nil 130 }