1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 package main
32
33 import (
34 "flag"
35 "fmt"
36 "io"
37 "log"
38 "net/http"
39 "os"
40 "os/exec"
41 "strings"
42 "sync"
43 "time"
44
45 "golang.org/x/mod/sumdb"
46 )
47
48 func usage() {
49 fmt.Fprintf(os.Stderr, "usage: gosumcheck [-h H] [-k key] [-u url] [-v] go.sum...\n")
50 os.Exit(2)
51 }
52
53 var (
54 height = flag.Int("h", 8, "tile height")
55 vkey = flag.String("k", "sum.golang.org+033de0ae+Ac4zctda0e5eza+HJyk9SxEdh+s3Ux18htTTAD8OuAn8", "key")
56 url = flag.String("u", "", "url to server (overriding name)")
57 vflag = flag.Bool("v", false, "enable verbose output")
58 )
59
60 func main() {
61 log.SetPrefix("notecheck: ")
62 log.SetFlags(0)
63
64 flag.Usage = usage
65 flag.Parse()
66 if flag.NArg() < 1 {
67 usage()
68 }
69
70 client := sumdb.NewClient(new(clientOps))
71
72
73
74
75 env := os.Getenv("GONOSUMDB")
76 if env == "" {
77 out, err := exec.Command("go", "env", "GONOSUMDB").CombinedOutput()
78 if err != nil {
79 log.Fatalf("go env GONOSUMDB: %v\n%s", err, out)
80 }
81 env = strings.TrimSpace(string(out))
82 }
83 client.SetGONOSUMDB(env)
84
85 for _, arg := range flag.Args() {
86 data, err := os.ReadFile(arg)
87 if err != nil {
88 log.Fatal(err)
89 }
90 checkGoSum(client, arg, data)
91 }
92 }
93
94 func checkGoSum(client *sumdb.Client, name string, data []byte) {
95 lines := strings.Split(string(data), "\n")
96 if lines[len(lines)-1] != "" {
97 log.Printf("error: final line missing newline")
98 return
99 }
100 lines = lines[:len(lines)-1]
101
102 errs := make([]string, len(lines))
103 var wg sync.WaitGroup
104 for i, line := range lines {
105 wg.Add(1)
106 go func(i int, line string) {
107 defer wg.Done()
108 f := strings.Fields(line)
109 if len(f) != 3 {
110 errs[i] = "invalid number of fields"
111 return
112 }
113
114 dbLines, err := client.Lookup(f[0], f[1])
115 if err != nil {
116 if err == sumdb.ErrGONOSUMDB {
117 errs[i] = fmt.Sprintf("%s@%s: %v", f[0], f[1], err)
118 } else {
119
120 errs[i] = err.Error()
121 }
122 return
123 }
124 hashAlgPrefix := f[0] + " " + f[1] + " " + f[2][:strings.Index(f[2], ":")+1]
125 for _, dbLine := range dbLines {
126 if dbLine == line {
127 return
128 }
129 if strings.HasPrefix(dbLine, hashAlgPrefix) {
130 errs[i] = fmt.Sprintf("%s@%s hash mismatch: have %s, want %s", f[0], f[1], line, dbLine)
131 return
132 }
133 }
134 errs[i] = fmt.Sprintf("%s@%s hash algorithm mismatch: have %s, want one of:\n\t%s", f[0], f[1], line, strings.Join(dbLines, "\n\t"))
135 }(i, line)
136 }
137 wg.Wait()
138
139 for i, err := range errs {
140 if err != "" {
141 fmt.Printf("%s:%d: %s\n", name, i+1, err)
142 }
143 }
144 }
145
146 type clientOps struct{}
147
148 func (*clientOps) ReadConfig(file string) ([]byte, error) {
149 if file == "key" {
150 return []byte(*vkey), nil
151 }
152 if strings.HasSuffix(file, "/latest") {
153
154
155 return []byte{}, nil
156 }
157 return nil, fmt.Errorf("unknown config %s", file)
158 }
159
160 func (*clientOps) WriteConfig(file string, old, new []byte) error {
161
162 return nil
163 }
164
165 func (*clientOps) ReadCache(file string) ([]byte, error) {
166 return nil, fmt.Errorf("no cache")
167 }
168
169 func (*clientOps) WriteCache(file string, data []byte) {
170
171 }
172
173 func (*clientOps) Log(msg string) {
174 log.Print(msg)
175 }
176
177 func (*clientOps) SecurityError(msg string) {
178 log.Fatal(msg)
179 }
180
181 func init() {
182 http.DefaultClient.Timeout = 1 * time.Minute
183 }
184
185 func (*clientOps) ReadRemote(path string) ([]byte, error) {
186 name := *vkey
187 if i := strings.Index(name, "+"); i >= 0 {
188 name = name[:i]
189 }
190 start := time.Now()
191 target := "https://" + name + path
192 if *url != "" {
193 target = *url + path
194 }
195 resp, err := http.Get(target)
196 if err != nil {
197 return nil, err
198 }
199 defer resp.Body.Close()
200 if resp.StatusCode != 200 {
201 return nil, fmt.Errorf("GET %v: %v", target, resp.Status)
202 }
203 data, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
204 if err != nil {
205 return nil, err
206 }
207 if *vflag {
208 fmt.Fprintf(os.Stderr, "%.3fs %s\n", time.Since(start).Seconds(), target)
209 }
210 return data, nil
211 }
212
View as plain text