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