config.go (2880B)
1 package main 2 3 import ( 4 "crypto/tls" 5 "crypto/x509" 6 "fmt" 7 "io" 8 "os" 9 "strings" 10 ) 11 12 var lookupSinkChan chan lookupSinkMsg 13 var lookupCertChan chan lookupCertMsg 14 15 func init() { 16 lookupSinkChan = make(chan lookupSinkMsg, 16) 17 lookupCertChan = make(chan lookupCertMsg, 16) 18 } 19 20 type sink struct { 21 pattern pattern 22 network string 23 address string 24 } 25 26 type cert struct { 27 pattern pattern 28 crtPath string 29 keyPath string 30 cert *tls.Certificate 31 } 32 33 type lookupSinkMsg struct { 34 hostname hostname 35 reply chan<- *sink 36 } 37 38 type lookupCertMsg struct { 39 hostname hostname 40 reply chan<- *cert 41 } 42 43 func lookupSink(hostname hostname) (*sink, error) { 44 reply := make(chan *sink, 1) 45 lookupSinkChan <- lookupSinkMsg{ 46 hostname: hostname, 47 reply: reply, 48 } 49 50 sink, ok := <-reply 51 if !ok { 52 return nil, fmt.Errorf("no sink for hostname %s", hostname) 53 } 54 55 return sink, nil 56 } 57 58 func lookupCert(hostname hostname) (*cert, error) { 59 reply := make(chan *cert, 1) 60 lookupCertChan <- lookupCertMsg{ 61 hostname: hostname, 62 reply: reply, 63 } 64 65 cert, ok := <-reply 66 if !ok { 67 return nil, fmt.Errorf("no cert for hostname %s", hostname) 68 } 69 70 return cert, nil 71 } 72 73 type config struct { 74 sinks []*sink 75 certs []*cert 76 } 77 78 func loadTLSCert(crtPath, keyPath string) (*tls.Certificate, error) { 79 tlsCert, err := tls.LoadX509KeyPair(crtPath, keyPath) 80 if err != nil { 81 return nil, err 82 } 83 84 // Parsing the leaf certificate in advance is recommended by crypto/tls to 85 // "reduce per-handshake processing". 86 tlsCert.Leaf, err = x509.ParseCertificate(tlsCert.Certificate[0]) 87 if err != nil { 88 return nil, err 89 } 90 91 return &tlsCert, nil 92 } 93 94 func loadConfig(path string) (*config, error) { 95 file, err := os.Open(path) 96 if err != nil { 97 return nil, err 98 } 99 defer file.Close() 100 101 data, err := io.ReadAll(file) 102 if err != nil { 103 return nil, err 104 } 105 106 var cfg config 107 108 lines := strings.Split(string(data), "\n") 109 for _, line := range lines { 110 fields := strings.Fields(line) 111 if len(fields) == 0 { 112 continue // Empty line 113 } 114 if len(fields) < 3 { 115 return nil, fmt.Errorf("illegal config: line with fewer than 3 fields") 116 } 117 118 switch fields[0] { 119 case "sink": 120 pattern, err := parsePattern(fields[3:]) 121 if err != nil { 122 return nil, err 123 } 124 125 sink := sink{ 126 pattern: pattern, 127 network: fields[1], 128 address: fields[2], 129 } 130 cfg.sinks = append(cfg.sinks, &sink) 131 132 case "cert": 133 crtPath := fields[1] 134 keyPath := fields[2] 135 136 tlsCert, err := loadTLSCert(crtPath, keyPath) 137 if err != nil { 138 return nil, err 139 } 140 141 pattern, err := parsePattern(fields[3:]) 142 if err != nil { 143 return nil, err 144 } 145 146 cert := cert{ 147 pattern: pattern, 148 crtPath: crtPath, 149 keyPath: keyPath, 150 cert: tlsCert, 151 } 152 cfg.certs = append(cfg.certs, &cert) 153 154 default: 155 return nil, fmt.Errorf("illegal config: expected \"sink\" or \"cert\" as first field") 156 } 157 } 158 159 return &cfg, nil 160 }