1
+ /**
2
+ * Copyright (C) 2003 Alexander Kout
3
+ * Originally from the jFxp project (http://jfxp.sourceforge.net/).
4
+ * Copied with permission June 11, 2012 by Femi Omojola (fomojola@ideasynthesis.com).
5
+ */
6
+ package org .java_websocket ;
7
+
8
+ import java .io .IOException ;
9
+ import java .net .Socket ;
10
+ import java .net .SocketAddress ;
11
+ import java .nio .ByteBuffer ;
12
+ import java .nio .channels .ByteChannel ;
13
+ import java .nio .channels .SelectableChannel ;
14
+ import java .nio .channels .SocketChannel ;
15
+
16
+ import javax .net .ssl .SSLEngine ;
17
+ import javax .net .ssl .SSLEngineResult ;
18
+ import javax .net .ssl .SSLException ;
19
+ import javax .net .ssl .SSLSession ;
20
+
21
+ /**
22
+ * Implements the relevant portions of the SocketChannel interface with the SSLEngine wrapper.
23
+ */
24
+ public class SSLSocketChannel implements ByteChannel
25
+ {
26
+ private ByteBuffer clientIn , clientOut , cTOs , sTOc , wbuf ;
27
+ private SocketChannel sc ;
28
+ private SSLEngineResult res ;
29
+ private SSLEngine sslEngine ;
30
+ private int SSL ;
31
+
32
+ public SSLSocketChannel (SocketChannel sc , SSLEngine sslEngine ) throws IOException
33
+ {
34
+ this .sc = sc ;
35
+ this .sslEngine = sslEngine ;
36
+ SSL = 1 ;
37
+ try {
38
+ sslEngine .setEnableSessionCreation (true );
39
+ SSLSession session = sslEngine .getSession ();
40
+ createBuffers (session );
41
+ // wrap
42
+ clientOut .clear ();
43
+ sc .write (wrap (clientOut ));
44
+ while (res .getHandshakeStatus () !=
45
+ SSLEngineResult .HandshakeStatus .FINISHED ) {
46
+ if (res .getHandshakeStatus () ==
47
+ SSLEngineResult .HandshakeStatus .NEED_UNWRAP ) {
48
+ // unwrap
49
+ sTOc .clear ();
50
+ while (sc .read (sTOc ) < 1 )
51
+ Thread .sleep (20 );
52
+ sTOc .flip ();
53
+ unwrap (sTOc );
54
+ if (res .getHandshakeStatus () != SSLEngineResult .HandshakeStatus .FINISHED ) {
55
+ clientOut .clear ();
56
+ sc .write (wrap (clientOut ));
57
+ }
58
+ } else if (res .getHandshakeStatus () ==
59
+ SSLEngineResult .HandshakeStatus .NEED_WRAP ) {
60
+ // wrap
61
+ clientOut .clear ();
62
+ sc .write (wrap (clientOut ));
63
+ } else {Thread .sleep (1000 );}
64
+ }
65
+ clientIn .clear ();
66
+ clientIn .flip ();
67
+ SSL = 4 ;
68
+ } catch (Exception e ) {
69
+ e .printStackTrace (System .out );
70
+ SSL = 0 ;
71
+ }
72
+ }
73
+
74
+ private synchronized ByteBuffer wrap (ByteBuffer b ) throws SSLException {
75
+ cTOs .clear ();
76
+ res = sslEngine .wrap (b , cTOs );
77
+ cTOs .flip ();
78
+ return cTOs ;
79
+ }
80
+
81
+ private synchronized ByteBuffer unwrap (ByteBuffer b ) throws SSLException {
82
+ clientIn .clear ();
83
+ int pos ;
84
+ while (b .hasRemaining ()) {
85
+ res = sslEngine .unwrap (b , clientIn );
86
+ if (res .getHandshakeStatus () ==
87
+ SSLEngineResult .HandshakeStatus .NEED_TASK ) {
88
+ // Task
89
+ Runnable task ;
90
+ while ((task =sslEngine .getDelegatedTask ()) != null )
91
+ {
92
+ task .run ();
93
+ }
94
+ } else if (res .getHandshakeStatus () ==
95
+ SSLEngineResult .HandshakeStatus .FINISHED ) {
96
+ return clientIn ;
97
+ } else if (res .getStatus () ==
98
+ SSLEngineResult .Status .BUFFER_UNDERFLOW ) {
99
+ return clientIn ;
100
+ }
101
+ }
102
+ return clientIn ;
103
+ }
104
+
105
+ private void createBuffers (SSLSession session ) {
106
+
107
+ int appBufferMax = session .getApplicationBufferSize ();
108
+ int netBufferMax = session .getPacketBufferSize ();
109
+
110
+ clientIn = ByteBuffer .allocate (65536 );
111
+ clientOut = ByteBuffer .allocate (appBufferMax );
112
+ wbuf = ByteBuffer .allocate (65536 );
113
+
114
+ cTOs = ByteBuffer .allocate (netBufferMax );
115
+ sTOc = ByteBuffer .allocate (netBufferMax );
116
+
117
+ }
118
+
119
+ public int write (ByteBuffer src ) throws IOException {
120
+ if (SSL == 4 ) {
121
+ return sc .write (wrap (src ));
122
+ }
123
+ return sc .write (src );
124
+ }
125
+
126
+ public int read (ByteBuffer dst ) throws IOException {
127
+ int amount = 0 , limit ;
128
+ if (SSL == 4 ) {
129
+ // test if there was a buffer overflow in dst
130
+ if (clientIn .hasRemaining ()) {
131
+ limit = Math .min (clientIn .remaining (), dst .remaining ());
132
+ for (int i = 0 ; i < limit ; i ++) {
133
+ dst .put (clientIn .get ());
134
+ amount ++;
135
+ }
136
+ return amount ;
137
+ }
138
+ // test if some bytes left from last read (e.g. BUFFER_UNDERFLOW)
139
+ if (sTOc .hasRemaining ()) {
140
+ unwrap (sTOc );
141
+ clientIn .flip ();
142
+ limit = Math .min (clientIn .limit (), dst .remaining ());
143
+ for (int i = 0 ; i < limit ; i ++) {
144
+ dst .put (clientIn .get ());
145
+ amount ++;
146
+ }
147
+ if (res .getStatus () != SSLEngineResult .Status .BUFFER_UNDERFLOW ) {
148
+ sTOc .clear ();
149
+ sTOc .flip ();
150
+ return amount ;
151
+ }
152
+ }
153
+ if (!sTOc .hasRemaining ())
154
+ sTOc .clear ();
155
+ else
156
+ sTOc .compact ();
157
+
158
+ if (sc .read (sTOc ) == -1 ) {
159
+ sTOc .clear ();
160
+ sTOc .flip ();
161
+ return -1 ;
162
+ }
163
+ sTOc .flip ();
164
+ unwrap (sTOc );
165
+ // write in dst
166
+ clientIn .flip ();
167
+ limit = Math .min (clientIn .limit (), dst .remaining ());
168
+ for (int i = 0 ; i < limit ; i ++) {
169
+ dst .put (clientIn .get ());
170
+ amount ++;
171
+ }
172
+ return amount ;
173
+ }
174
+ return sc .read (dst );
175
+ }
176
+
177
+ public boolean isConnected () {
178
+ return sc .isConnected ();
179
+ }
180
+
181
+ public void close () throws IOException {
182
+ if (SSL == 4 ) {
183
+ sslEngine .closeOutbound ();
184
+ sslEngine .getSession ().invalidate ();
185
+ clientOut .clear ();
186
+ sc .write (wrap (clientOut ));
187
+ sc .close ();
188
+ } else
189
+ sc .close ();
190
+ }
191
+
192
+ public SelectableChannel configureBlocking (boolean b ) throws IOException {
193
+ return sc .configureBlocking (b );
194
+ }
195
+
196
+ public boolean connect (SocketAddress remote ) throws IOException {
197
+ return sc .connect (remote );
198
+ }
199
+
200
+ public boolean finishConnect () throws IOException {
201
+ return sc .finishConnect ();
202
+ }
203
+
204
+ public Socket socket () {
205
+ return sc .socket ();
206
+ }
207
+
208
+ public boolean isInboundDone () {
209
+ return sslEngine .isInboundDone ();
210
+ }
211
+
212
+ @ Override
213
+ public boolean isOpen () {
214
+ return sc .isOpen ();
215
+ }
216
+ }
0 commit comments