1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package tls 6 7 import ( 8 "bufio" 9 "encoding/hex" 10 "errors" 11 "flag" 12 "fmt" 13 "io" 14 "io/ioutil" 15 "net" 16 "strconv" 17 "strings" 18 "sync" 19 ) 20 21 // TLS reference tests run a connection against a reference implementation 22 // (OpenSSL) of TLS and record the bytes of the resulting connection. The Go 23 // code, during a test, is configured with deterministic randomness and so the 24 // reference test can be reproduced exactly in the future. 25 // 26 // In order to save everyone who wishes to run the tests from needing the 27 // reference implementation installed, the reference connections are saved in 28 // files in the testdata directory. Thus running the tests involves nothing 29 // external, but creating and updating them requires the reference 30 // implementation. 31 // 32 // Tests can be updated by running them with the -update flag. This will cause 33 // the test files. Generally one should combine the -update flag with -test.run 34 // to updated a specific test. Since the reference implementation will always 35 // generate fresh random numbers, large parts of the reference connection will 36 // always change. 37 38 var update = flag.Bool("update", false, "update golden files on disk") 39 40 // recordingConn is a net.Conn that records the traffic that passes through it. 41 // WriteTo can be used to produce output that can be later be loaded with 42 // ParseTestData. 43 type recordingConn struct { 44 net.Conn 45 sync.Mutex 46 flows [][]byte 47 reading bool 48 } 49 50 func (r *recordingConn) Read(b []byte) (n int, err error) { 51 if n, err = r.Conn.Read(b); n == 0 { 52 return 53 } 54 b = b[:n] 55 56 r.Lock() 57 defer r.Unlock() 58 59 if l := len(r.flows); l == 0 || !r.reading { 60 buf := make([]byte, len(b)) 61 copy(buf, b) 62 r.flows = append(r.flows, buf) 63 } else { 64 r.flows[l-1] = append(r.flows[l-1], b[:n]...) 65 } 66 r.reading = true 67 return 68 } 69 70 func (r *recordingConn) Write(b []byte) (n int, err error) { 71 if n, err = r.Conn.Write(b); n == 0 { 72 return 73 } 74 b = b[:n] 75 76 r.Lock() 77 defer r.Unlock() 78 79 if l := len(r.flows); l == 0 || r.reading { 80 buf := make([]byte, len(b)) 81 copy(buf, b) 82 r.flows = append(r.flows, buf) 83 } else { 84 r.flows[l-1] = append(r.flows[l-1], b[:n]...) 85 } 86 r.reading = false 87 return 88 } 89 90 // WriteTo writes Go source code to w that contains the recorded traffic. 91 func (r *recordingConn) WriteTo(w io.Writer) { 92 // TLS always starts with a client to server flow. 93 clientToServer := true 94 95 for i, flow := range r.flows { 96 source, dest := "client", "server" 97 if !clientToServer { 98 source, dest = dest, source 99 } 100 fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, source, dest) 101 dumper := hex.Dumper(w) 102 dumper.Write(flow) 103 dumper.Close() 104 clientToServer = !clientToServer 105 } 106 } 107 108 func parseTestData(r io.Reader) (flows [][]byte, err error) { 109 var currentFlow []byte 110 111 scanner := bufio.NewScanner(r) 112 for scanner.Scan() { 113 line := scanner.Text() 114 // If the line starts with ">>> " then it marks the beginning 115 // of a new flow. 116 if strings.HasPrefix(line, ">>> ") { 117 if len(currentFlow) > 0 || len(flows) > 0 { 118 flows = append(flows, currentFlow) 119 currentFlow = nil 120 } 121 continue 122 } 123 124 // Otherwise the line is a line of hex dump that looks like: 125 // 00000170 fc f5 06 bf (...) |.....X{&?......!| 126 // (Some bytes have been omitted from the middle section.) 127 128 if i := strings.IndexByte(line, ' '); i >= 0 { 129 line = line[i:] 130 } else { 131 return nil, errors.New("invalid test data") 132 } 133 134 if i := strings.IndexByte(line, '|'); i >= 0 { 135 line = line[:i] 136 } else { 137 return nil, errors.New("invalid test data") 138 } 139 140 hexBytes := strings.Fields(line) 141 for _, hexByte := range hexBytes { 142 val, err := strconv.ParseUint(hexByte, 16, 8) 143 if err != nil { 144 return nil, errors.New("invalid hex byte in test data: " + err.Error()) 145 } 146 currentFlow = append(currentFlow, byte(val)) 147 } 148 } 149 150 if len(currentFlow) > 0 { 151 flows = append(flows, currentFlow) 152 } 153 154 return flows, nil 155 } 156 157 // tempFile creates a temp file containing contents and returns its path. 158 func tempFile(contents string) string { 159 file, err := ioutil.TempFile("", "go-tls-test") 160 if err != nil { 161 panic("failed to create temp file: " + err.Error()) 162 } 163 path := file.Name() 164 file.WriteString(contents) 165 file.Close() 166 return path 167 } 168