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