001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.midp.io.j2me.ssl;
028:
029: import java.io.*;
030:
031: import javax.microedition.io.*;
032:
033: import javax.microedition.pki.*;
034:
035: import com.sun.cldc.io.*;
036:
037: import com.sun.midp.ssl.*;
038:
039: import com.sun.midp.io.*;
040:
041: import com.sun.midp.midlet.*;
042:
043: import com.sun.midp.publickeystore.*;
044:
045: import com.sun.midp.security.*;
046:
047: /**
048: * This class implements the necessary functionality
049: * for an SSL connection.
050: */
051: public class Protocol implements SecureConnection,
052: ConnectionBaseInterface {
053:
054: /**
055: * Inner class to request security token from SecurityInitializer.
056: * SecurityInitializer should be able to check this inner class name.
057: */
058: static private class SecurityTrusted implements
059: ImplicitlyTrustedClass {
060: };
061:
062: /** This class has a different security domain than the MIDlet suite */
063: private static SecurityToken classSecurityToken = SecurityInitializer
064: .requestToken(new SecurityTrusted());
065:
066: /** Underlying TCP connection. */
067: private com.sun.midp.io.j2me.socket.Protocol tcpConnection;
068:
069: /** Underlying SSL connection. */
070: private SSLStreamConnection sslConnection;
071:
072: /**
073: * Connect to the underlying secure socket transport.
074: *
075: * @param name the target of the connection
076: * @param mode a flag that is true if the caller
077: * intends to write to the connection, ignored
078: * @param timeouts a flag to indicate that the called
079: * wants timeout exceptions, ignored
080: *
081: * @return SSL/TCP stream connection
082: *
083: * @exception IOException is thrown if the connection cannot be opened
084: * @exception IllegalArgumentException if the name is bad
085: */
086: public Connection openPrim(String name, int mode, boolean timeouts)
087: throws IOException {
088: Scheduler scheduler = Scheduler.getScheduler();
089: MIDletSuite midletSuite = scheduler.getMIDletSuite();
090: HttpUrl url;
091: OutputStream tcpOutputStream;
092: InputStream tcpInputStream;
093:
094: if (tcpConnection != null) {
095: // This method should only be called once.
096: throw new RuntimeException("Illegal state for operation");
097: }
098:
099: try {
100: midletSuite.checkForPermission(Permissions.SSL, "ssl:"
101: + name);
102: } catch (InterruptedException ie) {
103: throw new InterruptedIOException(
104: "Interrupted while trying to ask the user permission");
105: }
106:
107: if (name.charAt(0) != '/' || name.charAt(1) != '/') {
108: throw new IllegalArgumentException(
109: "Protocol must start with \"//\"");
110: }
111:
112: url = new HttpUrl("ssl", name); // parse name into host and port
113:
114: /*
115: * Since we reused the HttpUrl parser, we must make sure that
116: * there was nothing past the authority in the URL.
117: */
118: if (url.path != null || url.query != null
119: || url.fragment != null) {
120: throw new IllegalArgumentException("Malformed address");
121: }
122:
123: /*
124: * JTWI security check, untrusted MIDlets cannot open port 443.
125: * This is so they cannot perform HTTPS
126: * requests on server without using the system code. The
127: * system HTTP code will add a "UNTRUSTED/1.0" to the user agent
128: * field for untrusted MIDlets.
129: */
130: if (!midletSuite.isTrusted() && url.port == 443) {
131: throw new SecurityException(
132: "Target port denied to untrusted applications");
133: }
134:
135: tcpConnection = new com.sun.midp.io.j2me.socket.Protocol();
136: tcpConnection
137: .openPrim(classSecurityToken, "//" + url.authority);
138: try {
139: tcpOutputStream = tcpConnection.openOutputStream();
140: try {
141: tcpInputStream = tcpConnection.openInputStream();
142:
143: /*
144: * Porting note: This would be the place to connect to a
145: * SOCKS proxy if desired.
146: */
147:
148: try {
149: // Get the SSLStreamConnection
150: sslConnection = new SSLStreamConnection(url.host,
151: url.port, tcpInputStream, tcpOutputStream,
152: WebPublicKeyStore.getTrustedKeyStore());
153: } catch (IOException e) {
154: tcpInputStream.close();
155: throw e;
156: }
157: } catch (IOException e) {
158: tcpOutputStream.close();
159: throw e;
160: }
161: } catch (IOException e) {
162: tcpConnection.close();
163: throw e;
164: }
165:
166: return this ;
167: }
168:
169: /**
170: * Close the connection to the target.
171: *
172: * @exception IOException If an I/O error occurs
173: */
174: public void close() throws IOException {
175: try {
176: sslConnection.close();
177: } finally {
178: tcpConnection.close();
179: }
180: }
181:
182: /**
183: * Returns an input stream.
184: *
185: * @return an input stream for writing bytes to this port.
186: * @exception IOException if an I/O error occurs when creating the
187: * output stream.
188: */
189: public InputStream openInputStream() throws IOException {
190: return sslConnection.openInputStream();
191: }
192:
193: /**
194: * Open and return a data input stream for a connection.
195: *
196: * @return An input stream
197: * @exception IOException If an I/O error occurs
198: */
199: public DataInputStream openDataInputStream() throws IOException {
200: return sslConnection.openDataInputStream();
201: }
202:
203: /**
204: * Returns an output stream.
205: *
206: * @return an output stream for writing bytes to this port.
207: * @exception IOException if an I/O error occurs when creating the
208: * output stream.
209: */
210: public OutputStream openOutputStream() throws IOException {
211: return sslConnection.openOutputStream();
212: }
213:
214: /**
215: * Open and return a data output stream for a connection.
216: *
217: * @return An input stream
218: * @exception IOException If an I/O error occurs
219: */
220: public DataOutputStream openDataOutputStream() throws IOException {
221: return sslConnection.openDataOutputStream();
222: }
223:
224: /**
225: * Set a socket option for the connection.
226: * <P>
227: * Options inform the low level networking code about intended
228: * usage patterns that the application will use in dealing with
229: * the socket connection.
230: * </P>
231: *
232: * @param option socket option identifier (KEEPALIVE, LINGER,
233: * SNDBUF, RCVBUF, or DELAY)
234: * @param value numeric value for specified option (must be positive)
235: * @exception IllegalArgumentException if the value is not
236: * valid (e.g. negative value)
237: * @exception IOException if the connection was closed
238: *
239: * @see #getSocketOption
240: */
241: public void setSocketOption(byte option, int value)
242: throws IllegalArgumentException, IOException {
243: tcpConnection.setSocketOption(option, value);
244: }
245:
246: /**
247: * Get a socket option for the connection.
248: *
249: * @param option socket option identifier (KEEPALIVE, LINGER,
250: * SNDBUF, RCVBUF, or DELAY)
251: * @return positive numeric value for specified option or -1 if the
252: * value is not available.
253: * @exception IllegalArgumentException if the option identifier is
254: * not valid
255: * @exception IOException if the connection was closed
256: * @see #setSocketOption
257: */
258: public int getSocketOption(byte option)
259: throws IllegalArgumentException, IOException {
260: return tcpConnection.getSocketOption(option);
261: }
262:
263: /**
264: * Gets the local address to which the socket is bound.
265: *
266: * <P>The host address(IP number) that can be used to connect to this
267: * end of the socket connection from an external system.
268: * Since IP addresses may be dynamically assigned a remote application
269: * will need to be robust in the face of IP number reassignment.</P>
270: * <P> The local hostname (if available) can be accessed from
271: * <code>System.getProperty("microedition.hostname")</code>
272: * </P>
273: *
274: * @return the local address to which the socket is bound.
275: * @exception IOException if the connection was closed
276: * @see ServerSocketConnection
277: */
278: public String getLocalAddress() throws IOException {
279: return tcpConnection.getLocalAddress();
280: }
281:
282: /**
283: * Returns the local port to which this socket is bound.
284: *
285: * @return the local port number to which this socket is connected.
286: * @exception IOException if the connection was closed
287: * @see ServerSocketConnection
288: */
289: public int getLocalPort() throws IOException {
290: return tcpConnection.getLocalPort();
291: }
292:
293: /**
294: * Gets the remote address to which the socket is bound.
295: * The address can be either the remote host name or the IP
296: * address(if available).
297: *
298: * @return the remote address to which the socket is bound.
299: * @exception IOException if the connection was closed
300: */
301: public String getAddress() throws IOException {
302: return tcpConnection.getAddress();
303: }
304:
305: /**
306: * Returns the remote port to which this socket is bound.
307: *
308: * @return the remote port number to which this socket is connected.
309: * @exception IOException if the connection was closed
310: */
311: public int getPort() throws IOException {
312: return tcpConnection.getPort();
313: }
314:
315: /**
316: * Return the security information associated with this connection.
317: * If the connection is still in <CODE>Setup</CODE> state then
318: * the connection is initiated to establish the secure connection
319: * to the server. The method returns when the connection is
320: * established and the <CODE>Certificate</CODE> supplied by the
321: * server has been validated.
322: * The <CODE>SecurityInfo</CODE> is only returned if the
323: * connection has been successfully made to the server.
324: *
325: * @return the security information associated with this open connection.
326: *
327: * @exception CertificateException if the <code>Certificate</code>
328: * supplied by the server cannot be validated.
329: * The <code>CertificateException</code> will contain
330: * the information about the error and indicate the certificate in the
331: * validation chain with the error.
332: * @exception IOException if an arbitrary connection failure occurs
333: */
334: public SecurityInfo getSecurityInfo() throws IOException {
335: return sslConnection.getSecurityInfo();
336: }
337: }
|