1
2
3
4
5
6
7
8
9 package modfetch
10
11 import (
12 "bytes"
13 "errors"
14 "fmt"
15 "io"
16 "io/fs"
17 "net/url"
18 "os"
19 "path/filepath"
20 "strings"
21 "sync"
22 "time"
23
24 "cmd/go/internal/base"
25 "cmd/go/internal/cfg"
26 "cmd/go/internal/lockedfile"
27 "cmd/go/internal/web"
28
29 "golang.org/x/mod/module"
30 "golang.org/x/mod/sumdb"
31 "golang.org/x/mod/sumdb/note"
32 )
33
34
35 func useSumDB(mod module.Version) bool {
36 if mod.Path == "golang.org/toolchain" {
37 must := true
38
39
40
41
42
43
44 if strings.HasPrefix(cfg.GOPROXY, "file://") && !strings.ContainsAny(cfg.GOPROXY, ",|") {
45 must = false
46 }
47
48
49 if strings.Contains(os.Getenv("GIT_HTTP_USER_AGENT"), "proxy.golang.org") {
50 must = false
51 }
52
53
54
55
56
57
58
59
60
61 if must {
62 return true
63 }
64 }
65 return cfg.GOSUMDB != "off" && !module.MatchPrefixPatterns(cfg.GONOSUMDB, mod.Path)
66 }
67
68
69
70 func lookupSumDB(mod module.Version) (dbname string, lines []string, err error) {
71 dbOnce.Do(func() {
72 dbName, db, dbErr = dbDial()
73 })
74 if dbErr != nil {
75 return "", nil, dbErr
76 }
77 lines, err = db.Lookup(mod.Path, mod.Version)
78 return dbName, lines, err
79 }
80
81 var (
82 dbOnce sync.Once
83 dbName string
84 db *sumdb.Client
85 dbErr error
86 )
87
88 func dbDial() (dbName string, db *sumdb.Client, err error) {
89
90
91
92
93
94
95
96
97 gosumdb := cfg.GOSUMDB
98 if gosumdb == "sum.golang.google.cn" {
99 gosumdb = "sum.golang.org https://sum.golang.google.cn"
100 }
101
102 if gosumdb == "off" {
103 return "", nil, fmt.Errorf("checksum database disabled by GOSUMDB=off")
104 }
105
106 key := strings.Fields(gosumdb)
107 if len(key) >= 1 {
108 if k := knownGOSUMDB[key[0]]; k != "" {
109 key[0] = k
110 }
111 }
112 if len(key) == 0 {
113 return "", nil, fmt.Errorf("missing GOSUMDB")
114 }
115 if len(key) > 2 {
116 return "", nil, fmt.Errorf("invalid GOSUMDB: too many fields")
117 }
118 vkey, err := note.NewVerifier(key[0])
119 if err != nil {
120 return "", nil, fmt.Errorf("invalid GOSUMDB: %v", err)
121 }
122 name := vkey.Name()
123
124
125 direct, err := url.Parse("https://" + name)
126 if err != nil || strings.HasSuffix(name, "/") || *direct != (url.URL{Scheme: "https", Host: direct.Host, Path: direct.Path, RawPath: direct.RawPath}) || direct.RawPath != "" || direct.Host == "" {
127 return "", nil, fmt.Errorf("invalid sumdb name (must be host[/path]): %s %+v", name, *direct)
128 }
129
130
131 var base *url.URL
132 if len(key) >= 2 {
133
134
135 u, err := url.Parse(key[1])
136 if err != nil {
137 return "", nil, fmt.Errorf("invalid GOSUMDB URL: %v", err)
138 }
139 base = u
140 }
141
142 return name, sumdb.NewClient(&dbClient{key: key[0], name: name, direct: direct, base: base}), nil
143 }
144
145 type dbClient struct {
146 key string
147 name string
148 direct *url.URL
149
150 once sync.Once
151 base *url.URL
152 baseErr error
153 }
154
155 func (c *dbClient) ReadRemote(path string) ([]byte, error) {
156 c.once.Do(c.initBase)
157 if c.baseErr != nil {
158 return nil, c.baseErr
159 }
160
161 var data []byte
162 start := time.Now()
163 targ := web.Join(c.base, path)
164 data, err := web.GetBytes(targ)
165 if false {
166 fmt.Fprintf(os.Stderr, "%.3fs %s\n", time.Since(start).Seconds(), targ.Redacted())
167 }
168 return data, err
169 }
170
171
172
173
174
175
176 func (c *dbClient) initBase() {
177 if c.base != nil {
178 return
179 }
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200 err := TryProxies(func(proxy string) error {
201 switch proxy {
202 case "noproxy":
203 return errUseProxy
204 case "direct", "off":
205 return errProxyOff
206 default:
207 proxyURL, err := url.Parse(proxy)
208 if err != nil {
209 return err
210 }
211 if _, err := web.GetBytes(web.Join(proxyURL, "sumdb/"+c.name+"/supported")); err != nil {
212 return err
213 }
214
215 c.base = web.Join(proxyURL, "sumdb/"+c.name)
216 return nil
217 }
218 })
219 if errors.Is(err, fs.ErrNotExist) {
220
221
222 c.base = c.direct
223 } else if err != nil {
224 c.baseErr = err
225 }
226 }
227
228
229
230 func (c *dbClient) ReadConfig(file string) (data []byte, err error) {
231 if file == "key" {
232 return []byte(c.key), nil
233 }
234
235 if cfg.SumdbDir == "" {
236 return nil, fmt.Errorf("could not locate sumdb file: missing $GOPATH: %s",
237 cfg.GoPathError)
238 }
239 targ := filepath.Join(cfg.SumdbDir, file)
240 data, err = lockedfile.Read(targ)
241 if errors.Is(err, fs.ErrNotExist) {
242
243
244 return []byte{}, nil
245 }
246 return data, err
247 }
248
249
250 func (*dbClient) WriteConfig(file string, old, new []byte) error {
251 if file == "key" {
252
253 return fmt.Errorf("cannot write key")
254 }
255 if cfg.SumdbDir == "" {
256 return fmt.Errorf("could not locate sumdb file: missing $GOPATH: %s",
257 cfg.GoPathError)
258 }
259 targ := filepath.Join(cfg.SumdbDir, file)
260 os.MkdirAll(filepath.Dir(targ), 0777)
261 f, err := lockedfile.Edit(targ)
262 if err != nil {
263 return err
264 }
265 defer f.Close()
266 data, err := io.ReadAll(f)
267 if err != nil {
268 return err
269 }
270 if len(data) > 0 && !bytes.Equal(data, old) {
271 return sumdb.ErrWriteConflict
272 }
273 if _, err := f.Seek(0, 0); err != nil {
274 return err
275 }
276 if err := f.Truncate(0); err != nil {
277 return err
278 }
279 if _, err := f.Write(new); err != nil {
280 return err
281 }
282 return f.Close()
283 }
284
285
286
287
288 func (*dbClient) ReadCache(file string) ([]byte, error) {
289 targ := filepath.Join(cfg.GOMODCACHE, "cache/download/sumdb", file)
290 data, err := lockedfile.Read(targ)
291
292
293
294
295 if err == nil && len(data) == 0 {
296 err = &fs.PathError{Op: "read", Path: targ, Err: fs.ErrNotExist}
297 }
298 return data, err
299 }
300
301
302 func (*dbClient) WriteCache(file string, data []byte) {
303 targ := filepath.Join(cfg.GOMODCACHE, "cache/download/sumdb", file)
304 os.MkdirAll(filepath.Dir(targ), 0777)
305 lockedfile.Write(targ, bytes.NewReader(data), 0666)
306 }
307
308 func (*dbClient) Log(msg string) {
309
310 }
311
312 func (*dbClient) SecurityError(msg string) {
313 base.Fatalf("%s", msg)
314 }
315
View as plain text