@@ -27,83 +27,146 @@ type ctSubmissionRequest struct {
2727
2828type integrationSrv struct {
2929 sync.Mutex
30- submissions map [string ]int64
30+ submissions map [string ]int64
31+ // Hostnames where we refuse to provide an SCT. This is to exercise the code
32+ // path where all CT servers fail.
33+ rejectHosts map [string ]bool
34+ // A list of entries that we rejected based on rejectHosts.
35+ rejected []string
3136 key * ecdsa.PrivateKey
3237 latencySchedule []float64
3338 latencyItem int
3439}
3540
36- func (is * integrationSrv ) handler (w http.ResponseWriter , r * http.Request ) {
37- switch r .URL .Path {
38- case "/ct/v1/add-pre-chain" :
39- fallthrough
40- case "/ct/v1/add-chain" :
41- if r .Method != "POST" {
42- http .NotFound (w , r )
43- return
44- }
45- bodyBytes , err := ioutil .ReadAll (r .Body )
46- if err != nil {
47- http .Error (w , err .Error (), http .StatusBadRequest )
48- }
41+ func readJSON (w http.ResponseWriter , r * http.Request , output interface {}) error {
42+ if r .Method != "POST" {
43+ return fmt .Errorf ("incorrect method; only POST allowed" )
44+ }
45+ bodyBytes , err := ioutil .ReadAll (r .Body )
46+ if err != nil {
47+ return err
48+ }
4949
50- var addChainReq ctSubmissionRequest
51- err = json .Unmarshal (bodyBytes , & addChainReq )
52- if err != nil {
53- http .Error (w , err .Error (), http .StatusBadRequest )
54- }
55- if len (addChainReq .Chain ) == 0 {
56- w .WriteHeader (400 )
57- return
58- }
50+ err = json .Unmarshal (bodyBytes , output )
51+ if err != nil {
52+ return err
53+ }
54+ return nil
55+ }
5956
60- precert := false
61- if r .URL .Path == "/ct/v1/add-pre-chain" {
62- precert = true
63- }
57+ func (is * integrationSrv ) addChain (w http.ResponseWriter , r * http.Request ) {
58+ is .addChainOrPre (w , r , false )
59+ }
6460
65- b , err := base64 . StdEncoding . DecodeString ( addChainReq . Chain [ 0 ])
66- if err != nil {
67- w . WriteHeader ( 400 )
68- return
69- }
70- cert , err := x509 . ParseCertificate ( b )
71- if err != nil {
72- w . WriteHeader ( 400 )
73- return
74- }
75- hostnames := strings . Join ( cert . DNSNames , "," )
61+ // addRejectHost takes a JSON POST with a "host" field; any subsequent
62+ // submissions for that host will get a 400 error.
63+ func ( is * integrationSrv ) addRejectHost ( w http. ResponseWriter , r * http. Request ) {
64+ var rejectHostReq struct {
65+ Host string
66+ }
67+ err := readJSON ( w , r , & rejectHostReq )
68+ if err != nil {
69+ http . Error ( w , err . Error (), http . StatusBadRequest )
70+ return
71+ }
7672
77- is .Lock ()
78- is .submissions [hostnames ]++
79- is .Unlock ()
73+ is .Lock ()
74+ defer is .Unlock ()
75+ is .rejectHosts [rejectHostReq .Host ] = true
76+ w .Write ([]byte {})
77+ }
78+
79+ // getRejections returns a JSON array containing strings; those strings are
80+ // base64 encodings of certificates or precertificates that were rejected due to
81+ // the rejectHosts mechanism.
82+ func (is * integrationSrv ) getRejections (w http.ResponseWriter , r * http.Request ) {
83+ is .Lock ()
84+ defer is .Unlock ()
85+ output , err := json .Marshal (is .rejected )
86+ if err != nil {
87+ http .Error (w , err .Error (), http .StatusBadRequest )
88+ return
89+ }
8090
81- if is .latencySchedule != nil {
82- is .Lock ()
83- sleepTime := time .Duration (is .latencySchedule [is .latencyItem % len (is .latencySchedule )]) * time .Second
84- is .latencyItem ++
91+ w .WriteHeader (http .StatusOK )
92+ w .Write (output )
93+ }
94+
95+ func (is * integrationSrv ) addPreChain (w http.ResponseWriter , r * http.Request ) {
96+ is .addChainOrPre (w , r , true )
97+ }
98+
99+ func (is * integrationSrv ) addChainOrPre (w http.ResponseWriter , r * http.Request , precert bool ) {
100+ if r .Method != "POST" {
101+ http .NotFound (w , r )
102+ return
103+ }
104+ bodyBytes , err := ioutil .ReadAll (r .Body )
105+ if err != nil {
106+ http .Error (w , err .Error (), http .StatusBadRequest )
107+ return
108+ }
109+
110+ var addChainReq ctSubmissionRequest
111+ err = json .Unmarshal (bodyBytes , & addChainReq )
112+ if err != nil {
113+ http .Error (w , err .Error (), http .StatusBadRequest )
114+ return
115+ }
116+ if len (addChainReq .Chain ) == 0 {
117+ w .WriteHeader (400 )
118+ return
119+ }
120+
121+ b , err := base64 .StdEncoding .DecodeString (addChainReq .Chain [0 ])
122+ if err != nil {
123+ w .WriteHeader (400 )
124+ return
125+ }
126+ cert , err := x509 .ParseCertificate (b )
127+ if err != nil {
128+ w .WriteHeader (400 )
129+ return
130+ }
131+ hostnames := strings .Join (cert .DNSNames , "," )
132+
133+ is .Lock ()
134+ for _ , h := range cert .DNSNames {
135+ if is .rejectHosts [h ] {
85136 is .Unlock ()
86- time .Sleep (sleepTime )
87- }
88- w .WriteHeader (http .StatusOK )
89- w .Write (publisher .CreateTestingSignedSCT (addChainReq .Chain , is .key , precert , time .Now ()))
90- case "/submissions" :
91- if r .Method != "GET" {
92- http .NotFound (w , r )
137+ is .rejected = append (is .rejected , addChainReq .Chain [0 ])
138+ w .WriteHeader (400 )
93139 return
94140 }
141+ }
142+
143+ is .submissions [hostnames ]++
144+ is .Unlock ()
95145
146+ if is .latencySchedule != nil {
96147 is .Lock ()
97- hostnames := r . URL . Query (). Get ( "hostnames" )
98- submissions := is .submissions [ hostnames ]
148+ sleepTime := time . Duration ( is . latencySchedule [ is . latencyItem % len ( is . latencySchedule )]) * time . Second
149+ is .latencyItem ++
99150 is .Unlock ()
151+ time .Sleep (sleepTime )
152+ }
153+ w .WriteHeader (http .StatusOK )
154+ w .Write (publisher .CreateTestingSignedSCT (addChainReq .Chain , is .key , precert , time .Now ()))
155+ }
100156
101- w .WriteHeader (http .StatusOK )
102- fmt .Fprintf (w , "%d" , submissions )
103- default :
157+ func (is * integrationSrv ) getSubmissions (w http.ResponseWriter , r * http.Request ) {
158+ if r .Method != "GET" {
104159 http .NotFound (w , r )
105160 return
106161 }
162+
163+ is .Lock ()
164+ hostnames := r .URL .Query ().Get ("hostnames" )
165+ submissions := is .submissions [hostnames ]
166+ is .Unlock ()
167+
168+ w .WriteHeader (http .StatusOK )
169+ fmt .Fprintf (w , "%d" , submissions )
107170}
108171
109172type config struct {
@@ -139,10 +202,17 @@ func runPersonality(p Personality) {
139202 key : key ,
140203 latencySchedule : p .LatencySchedule ,
141204 submissions : make (map [string ]int64 ),
205+ rejectHosts : make (map [string ]bool ),
142206 }
207+ m := http .NewServeMux ()
208+ m .HandleFunc ("/submissions" , is .getSubmissions )
209+ m .HandleFunc ("/ct/v1/add-pre-chain" , is .addPreChain )
210+ m .HandleFunc ("/ct/v1/add-chain" , is .addChain )
211+ m .HandleFunc ("/add-reject-host" , is .addRejectHost )
212+ m .HandleFunc ("/get-rejections" , is .getRejections )
143213 srv := & http.Server {
144214 Addr : p .Addr ,
145- Handler : http . HandlerFunc ( is . handler ) ,
215+ Handler : m ,
146216 }
147217 log .Printf ("ct-test-srv on %s with pubkey %s" , p .Addr ,
148218 base64 .StdEncoding .EncodeToString (pubKeyBytes ))
0 commit comments