View Javadoc

1   //========================================================================
2   //Copyright 2011-2012 Mort Bay Consulting Pty. Ltd.
3   //------------------------------------------------------------------------
4   //All rights reserved. This program and the accompanying materials
5   //are made available under the terms of the Eclipse Public License v1.0
6   //and Apache License v2.0 which accompanies this distribution.
7   //The Eclipse Public License is available at
8   //http://www.eclipse.org/legal/epl-v10.html
9   //The Apache License v2.0 is available at
10  //http://www.opensource.org/licenses/apache2.0.php
11  //You may elect to redistribute this code under either of these licenses.
12  //========================================================================
13  
14  package org.eclipse.jetty.spdy.parser;
15  
16  import java.io.ByteArrayInputStream;
17  import java.nio.ByteBuffer;
18  import java.security.cert.Certificate;
19  import java.security.cert.CertificateException;
20  import java.security.cert.CertificateFactory;
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.List;
24  
25  import org.eclipse.jetty.spdy.SessionException;
26  import org.eclipse.jetty.spdy.api.SessionStatus;
27  import org.eclipse.jetty.spdy.frames.ControlFrameType;
28  import org.eclipse.jetty.spdy.frames.CredentialFrame;
29  
30  public class CredentialBodyParser extends ControlFrameBodyParser
31  {
32      private final List<Certificate> certificates = new ArrayList<>();
33      private final ControlFrameParser controlFrameParser;
34      private State state = State.SLOT;
35      private int totalLength;
36      private int cursor;
37      private short slot;
38      private int proofLength;
39      private byte[] proof;
40      private int certificateLength;
41      private byte[] certificate;
42  
43      public CredentialBodyParser(ControlFrameParser controlFrameParser)
44      {
45          this.controlFrameParser = controlFrameParser;
46      }
47  
48      @Override
49      public boolean parse(ByteBuffer buffer)
50      {
51          while (buffer.hasRemaining())
52          {
53              switch (state)
54              {
55                  case SLOT:
56                  {
57                      if (buffer.remaining() >= 2)
58                      {
59                          slot = buffer.getShort();
60                          checkSlotValid();
61                          state = State.PROOF_LENGTH;
62                      }
63                      else
64                      {
65                          state = State.SLOT_BYTES;
66                          cursor = 2;
67                      }
68                      break;
69                  }
70                  case SLOT_BYTES:
71                  {
72                      byte currByte = buffer.get();
73                      --cursor;
74                      slot += (currByte & 0xFF) << 8 * cursor;
75                      if (cursor == 0)
76                      {
77                          checkSlotValid();
78                          state = State.PROOF_LENGTH;
79                      }
80                      break;
81                  }
82                  case PROOF_LENGTH:
83                  {
84                      if (buffer.remaining() >= 4)
85                      {
86                          proofLength = buffer.getInt() & 0x7F_FF_FF_FF;
87                          state = State.PROOF;
88                      }
89                      else
90                      {
91                          state = State.PROOF_LENGTH_BYTES;
92                          cursor = 4;
93                      }
94                      break;
95                  }
96                  case PROOF_LENGTH_BYTES:
97                  {
98                      byte currByte = buffer.get();
99                      --cursor;
100                     proofLength += (currByte & 0xFF) << 8 * cursor;
101                     if (cursor == 0)
102                     {
103                         proofLength &= 0x7F_FF_FF_FF;
104                         state = State.PROOF;
105                     }
106                     break;
107                 }
108                 case PROOF:
109                 {
110                     totalLength = controlFrameParser.getLength() - 2 - 4 - proofLength;
111                     proof = new byte[proofLength];
112                     if (buffer.remaining() >= proofLength)
113                     {
114                         buffer.get(proof);
115                         state = State.CERTIFICATE_LENGTH;
116                         if (totalLength == 0)
117                         {
118                             onCredential();
119                             return true;
120                         }
121                     }
122                     else
123                     {
124                         state = State.PROOF_BYTES;
125                         cursor = proofLength;
126                     }
127                     break;
128                 }
129                 case PROOF_BYTES:
130                 {
131                     proof[proofLength - cursor] = buffer.get();
132                     --cursor;
133                     if (cursor == 0)
134                     {
135                         state = State.CERTIFICATE_LENGTH;
136                         if (totalLength == 0)
137                         {
138                             onCredential();
139                             return true;
140                         }
141                     }
142                     break;
143                 }
144                 case CERTIFICATE_LENGTH:
145                 {
146                     if (buffer.remaining() >= 4)
147                     {
148                         certificateLength = buffer.getInt() & 0x7F_FF_FF_FF;
149                         state = State.CERTIFICATE;
150                     }
151                     else
152                     {
153                         state = State.CERTIFICATE_LENGTH_BYTES;
154                         cursor = 4;
155                     }
156                     break;
157                 }
158                 case CERTIFICATE_LENGTH_BYTES:
159                 {
160                     byte currByte = buffer.get();
161                     --cursor;
162                     certificateLength += (currByte & 0xFF) << 8 * cursor;
163                     if (cursor == 0)
164                     {
165                         certificateLength &= 0x7F_FF_FF_FF;
166                         state = State.CERTIFICATE;
167                     }
168                     break;
169                 }
170                 case CERTIFICATE:
171                 {
172                     totalLength -= 4 + certificateLength;
173                     certificate = new byte[certificateLength];
174                     if (buffer.remaining() >= certificateLength)
175                     {
176                         buffer.get(certificate);
177                         if (onCertificate())
178                             return true;
179                     }
180                     else
181                     {
182                         state = State.CERTIFICATE_BYTES;
183                         cursor = certificateLength;
184                     }
185                     break;
186                 }
187                 case CERTIFICATE_BYTES:
188                 {
189                     certificate[certificateLength - cursor] = buffer.get();
190                     --cursor;
191                     if (cursor == 0)
192                     {
193                         if (onCertificate())
194                             return true;
195                     }
196                     break;
197                 }
198                 default:
199                 {
200                     throw new IllegalStateException();
201                 }
202             }
203         }
204         return false;
205     }
206 
207     private void checkSlotValid()
208     {
209         if (slot <= 0)
210             throw new SessionException(SessionStatus.PROTOCOL_ERROR,
211                     "Invalid slot " + slot + " for " + ControlFrameType.CREDENTIAL + " frame");
212     }
213 
214     private boolean onCertificate()
215     {
216         certificates.add(deserializeCertificate(certificate));
217         if (totalLength == 0)
218         {
219             onCredential();
220             return true;
221         }
222         else
223         {
224             certificateLength = 0;
225             state = State.CERTIFICATE_LENGTH;
226         }
227         return false;
228     }
229 
230     private Certificate deserializeCertificate(byte[] bytes)
231     {
232         try
233         {
234             CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
235             return certificateFactory.generateCertificate(new ByteArrayInputStream(bytes));
236         }
237         catch (CertificateException x)
238         {
239             throw new SessionException(SessionStatus.PROTOCOL_ERROR, x);
240         }
241     }
242 
243     private void onCredential()
244     {
245         CredentialFrame frame = new CredentialFrame(controlFrameParser.getVersion(), slot,
246                 Arrays.copyOf(proof, proof.length), certificates.toArray(new Certificate[certificates.size()]));
247         controlFrameParser.onControlFrame(frame);
248         reset();
249     }
250 
251     private void reset()
252     {
253         state = State.SLOT;
254         totalLength = 0;
255         cursor = 0;
256         slot = 0;
257         proofLength = 0;
258         proof = null;
259         certificateLength = 0;
260         certificate = null;
261         certificates.clear();
262     }
263 
264     public enum State
265     {
266         SLOT, SLOT_BYTES, PROOF_LENGTH, PROOF_LENGTH_BYTES, PROOF, PROOF_BYTES,
267         CERTIFICATE_LENGTH, CERTIFICATE_LENGTH_BYTES, CERTIFICATE, CERTIFICATE_BYTES
268     }
269 }