1 package com.ericsson.research.transport.ws.spi;
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
32
33
34
35
36 import java.io.IOException;
37 import java.io.OutputStream;
38 import java.io.UnsupportedEncodingException;
39 import java.security.MessageDigest;
40 import java.security.NoSuchAlgorithmException;
41
42 import com.ericsson.research.transport.ws.WSException;
43
44 public class WSHixie76Handshake extends WSAbstractHandshake implements WSConstants {
45
46 private int num1,num2;
47 private String key3;
48
49 public WSHixie76Handshake(WSAbstractProtocol protocol) {
50 super(protocol);
51 if(protocol.client)
52 expectedHandshakeBodyLength = 16;
53 else
54 expectedHandshakeBodyLength = 8;
55 }
56
57 public void sendHandshake(OutputStream os) throws IOException {
58 StringBuffer h = new StringBuffer();
59 if(protocol.client) {
60 h.append(GET);
61 h.append(" ");
62 h.append(protocol.resource);
63 h.append(" ");
64 h.append(HTTP11);
65 h.append(WSHixie75Handshake.COMMON_PART);
66 h.append(HOST_HEADER);
67 h.append(COLON_SPACE);
68 h.append(protocol.host);
69 if ((protocol.securityContext==null && protocol.port!=80) || (protocol.securityContext!=null && protocol.port!=443)) {
70 h.append(":");
71 h.append(protocol.port);
72 }
73 h.append(CRLF);
74 h.append(ORIGIN_HEADER);
75 h.append(COLON_SPACE);
76 h.append(protocol.origin);
77 int spaces1 = (int) (Math.random()*12+1);
78 int spaces2 = (int) (Math.random()*12+1);
79 int max1 = Integer.MAX_VALUE / spaces1;
80 int max2 = Integer.MAX_VALUE / spaces2;
81 num1 = (int) (Math.random() * max1 + 1);
82 num2 = (int) (Math.random() * max2 + 1);
83 String spc1 = generateString(spaces1, 0x20, 0x20);
84 String spc2 = generateString(spaces2, 0x20, 0x20);
85 String gbg1 = generateString((int) (Math.random()*12+1), 0x3A, 0x7E);
86 String gbg2 = generateString((int) (Math.random()*12+1), 0x3A, 0x7E);
87 String key1 = splice(splice(String.valueOf(num1*spaces1), spc1), gbg1);
88 String key2 = splice(splice(String.valueOf(num2*spaces2), spc2), gbg2);
89 h.append(CRLF);
90 h.append(SEC_WEBSOCKET_KEY1_HEADER);
91 h.append(COLON_SPACE);
92 h.append(key1);
93 h.append(CRLF);
94 h.append(SEC_WEBSOCKET_KEY2_HEADER);
95 h.append(COLON_SPACE);
96 h.append(key2);
97 h.append(CRLFCRLF);
98 key3 = generateString(8, 0x00, 0xFF);
99 h.append(key3);
100 } else {
101 h.append(WSHixie75Handshake.SERVER_PREAMBLE);
102 h.append(WSHixie75Handshake.COMMON_PART);
103 h.append(SEC_WEBSOCKET_ORIGIN_HEADER);
104 h.append(COLON_SPACE);
105 h.append(protocol.origin);
106 h.append(CRLF);
107 h.append(SEC_WEBSOCKET_LOCATION_HEADER);
108 h.append(COLON_SPACE);
109 h.append(protocol.location);
110 h.append(CRLFCRLF);
111 }
112 synchronized(os) {
113 os.write(h.toString().getBytes(ISO_8859_1));
114 if(!protocol.client)
115 os.write(body);
116 os.flush();
117 }
118 }
119
120 private String splice(String src1, String src2) {
121 StringBuffer sb = new StringBuffer(src1);
122 for (int i = 0; i < src2.length(); i++) {
123 int pos = (int) Math.round(Math.random() * sb.length());
124 sb.insert(pos, src2.charAt(i));
125 }
126 return sb.toString();
127 }
128
129 private String generateString(int length, int startChar, int endChar) {
130 StringBuffer sb = new StringBuffer();
131 for (int i = 0; i < length; i++) {
132 int j = (int) (Math.floor(Math.random() * (endChar - startChar)) + startChar);
133 sb.append((char)j);
134 }
135 return sb.toString();
136 }
137
138 public void headersRead() throws WSException {
139 if(protocol.client) {
140 if (!protocol.origin.equals(getHeader(SEC_WEBSOCKET_ORIGIN_HEADER)))
141 throw new WSException("Failed to match origin ("+protocol.origin+" != "+getHeader(SEC_WEBSOCKET_ORIGIN_HEADER));
142 StringBuffer location = new StringBuffer();
143 if(protocol.securityContext!=null)
144 location.append(WSS_SCHEMA);
145 else
146 location.append(WS_SCHEMA);
147 location.append(protocol.host);
148 if((protocol.securityContext==null && protocol.port != 80) || (protocol.securityContext!=null && protocol.port != 443)) {
149 location.append(":");
150 location.append(protocol.port);
151 }
152 location.append(protocol.resource);
153 if (!location.toString().equals(getHeader(SEC_WEBSOCKET_LOCATION_HEADER)))
154 throw new WSException("Failed to match location ("+location+" != "+getHeader(SEC_WEBSOCKET_LOCATION_HEADER)+")");
155 } else {
156 if (getHeader(SEC_WEBSOCKET_KEY1_HEADER) == null || getHeader(SEC_WEBSOCKET_KEY2_HEADER) == null)
157 throw new WSException("One of Sec-WebSocket-Key* headers was null");
158 protocol.origin = getHeader(ORIGIN_HEADER);
159 StringBuffer loc = new StringBuffer();
160 if (protocol.securityContext!=null)
161 loc.append(WSS_SCHEMA);
162 else
163 loc.append(WS_SCHEMA);
164 loc.append(getHeader(HOST_HEADER));
165 loc.append(protocol.resource);
166 protocol.location = loc.toString();
167 }
168 }
169
170 public void bodyRead() throws WSException {
171 if(protocol.client) {
172 try {
173 byte[] bs = key3.getBytes("ISO-8859-1");
174 bs = createDigest(num1, num2, bs);
175 for(int i = 0; i<bs.length; i++)
176 if(body.length < i+1 || bs[i] != body[i])
177 throw new WSException("Handshake result doesn't match");
178 } catch (UnsupportedEncodingException e) {
179 throw new WSException(e);
180 }
181 } else {
182
183 int key1Spaces = countSpaces(getHeader(SEC_WEBSOCKET_KEY1_HEADER));
184 int key2Spaces = countSpaces(getHeader(SEC_WEBSOCKET_KEY2_HEADER));
185 if (key1Spaces <= 0 || key2Spaces <= 0)
186 throw new WSException("Invalid number of spaces in handshake");
187
188 long key1Num = Long.parseLong(getHeader(SEC_WEBSOCKET_KEY1_HEADER).replaceAll("[^0-9]", ""));
189 long key2Num = Long.parseLong(getHeader(SEC_WEBSOCKET_KEY2_HEADER).replaceAll("[^0-9]", ""));
190 long maxUint = Integer.MAX_VALUE;
191 maxUint <<= 1;
192 maxUint += 2;
193 if (key1Num > maxUint || key2Num > maxUint)
194 throw new WSException("Input values exceed permitted buffer size");
195
196 key1Num = key1Num / key1Spaces;
197 key2Num = key2Num / key2Spaces;
198 body = createDigest(key1Num, key2Num, body);
199 }
200 }
201
202 private byte[] createDigest(long n1, long n2, byte[] n3) throws WSException {
203 try {
204 byte[] q = new byte[16];
205 q[0] = (byte)((n1 & 0xFF000000) >> 24);
206 q[1] = (byte)((n1 & 0xFF0000) >> 16);
207 q[2] = (byte)((n1 & 0xFF00) >> 8);
208 q[3] = (byte)((n1 & 0xFF));
209 q[4] = (byte)((n2 & 0xFF000000) >> 24);
210 q[5] = (byte)((n2 & 0xFF0000) >> 16);
211 q[6] = (byte)((n2 & 0xFF00) >> 8);
212 q[7] = (byte)((n2 & 0xFF));
213 System.arraycopy(n3, 0, q, 8, 8);
214 MessageDigest md = MessageDigest.getInstance("MD5");
215 return md.digest(q);
216 } catch(NoSuchAlgorithmException e) {
217 throw new WSException(e);
218 }
219 }
220
221 private int countSpaces(String source) {
222 int j = 0;
223 for (int i = 0; i < source.length(); i++)
224 if (source.charAt(i) == ' ')
225 j++;
226 return j;
227 }
228
229 }