private PGStream enableSSL(PGStream pgStream, boolean requireSSL, Properties info, Logger logger) throws IOException, SQLException { if (logger.logDebug()) logger.debug(" FE=> SSLRequest"); // Send SSL request packet pgStream.SendInteger4(8); pgStream.SendInteger2(1234); pgStream.SendInteger2(5679); pgStream.flush(); // Now get the response from the backend, one of N, E, S. int beresp = pgStream.ReceiveChar(); switch (beresp) { case 'E': if (logger.logDebug()) logger.debug(" <=BE SSLError"); // Server doesn't even know about the SSL handshake protocol if (requireSSL) throw new PSQLException( GT.tr("The server does not support SSL."), PSQLState.CONNECTION_FAILURE); // We have to reconnect to continue. pgStream.close(); return new PGStream(pgStream.getHost(), pgStream.getPort()); case 'N': if (logger.logDebug()) logger.debug(" <=BE SSLRefused"); // Server does not support ssl if (requireSSL) throw new PSQLException( GT.tr("The server does not support SSL."), PSQLState.CONNECTION_FAILURE); return pgStream; case 'S': if (logger.logDebug()) logger.debug(" <=BE SSLOk"); // Server supports ssl Driver.makeSSL(pgStream, info, logger); return pgStream; default: throw new PSQLException( GT.tr("An error occured while setting up the SSL connection."), PSQLState.CONNECTION_FAILURE); } }
private void sendStartupPacket(PGStream pgStream, String[][] params, Logger logger) throws IOException { if (logger.logDebug()) { String details = ""; for (int i = 0; i < params.length; ++i) { if (i != 0) details += ", "; details += params[i][0] + "=" + params[i][1]; } logger.debug(" FE=> StartupPacket(" + details + ")"); } /* * Precalculate message length and encode params. */ int length = 4 + 4; byte[][] encodedParams = new byte[params.length * 2][]; for (int i = 0; i < params.length; ++i) { encodedParams[i * 2] = params[i][0].getBytes("US-ASCII"); encodedParams[i * 2 + 1] = params[i][1].getBytes("US-ASCII"); length += encodedParams[i * 2].length + 1 + encodedParams[i * 2 + 1].length + 1; } length += 1; // Terminating \0 /* * Send the startup message. */ pgStream.SendInteger4(length); pgStream.SendInteger2(3); // protocol major pgStream.SendInteger2(0); // protocol minor for (int i = 0; i < encodedParams.length; ++i) { pgStream.Send(encodedParams[i]); pgStream.SendChar(0); } pgStream.SendChar(0); pgStream.flush(); }
public ProtocolConnection openConnectionImpl( String host, int port, String user, String database, Properties info, Logger logger) throws SQLException { // Extract interesting values from the info properties: // - the SSL setting boolean requireSSL = (info.getProperty("ssl") != null); boolean trySSL = requireSSL; // XXX temporary until we revisit the ssl property values // NOTE: To simplify this code, it is assumed that if we are // using the V3 protocol, then the database is at least 7.4. That // eliminates the need to check database versions and maintain // backward-compatible code here. // // Change by Chris Smith <*****@*****.**> if (logger.logDebug()) logger.debug("Trying to establish a protocol version 3 connection to " + host + ":" + port); if (!Driver.sslEnabled()) { if (requireSSL) throw new PSQLException( GT.tr("The driver does not support SSL."), PSQLState.CONNECTION_FAILURE); trySSL = false; } // // Establish a connection. // PGStream newStream = null; try { newStream = new PGStream(host, port); // Construct and send an ssl startup packet if requested. if (trySSL) newStream = enableSSL(newStream, requireSSL, info, logger); // Construct and send a startup packet. String[][] params = { {"user", user}, {"database", database}, {"client_encoding", "UNICODE"}, {"DateStyle", "ISO"}, {"extra_float_digits", "2"} }; sendStartupPacket(newStream, params, logger); // Do authentication (until AuthenticationOk). doAuthentication(newStream, user, info.getProperty("password"), logger); // Do final startup. ProtocolConnectionImpl protoConnection = new ProtocolConnectionImpl(newStream, user, database, info, logger); readStartupMessages(newStream, protoConnection, logger); // And we're done. return protoConnection; } catch (UnsupportedProtocolException upe) { // Swallow this and return null so ConnectionFactory tries the next protocol. if (logger.logDebug()) logger.debug("Protocol not supported, abandoning connection."); try { newStream.close(); } catch (IOException e) { } return null; } catch (ConnectException cex) { // Added by Peter Mount <*****@*****.**> // ConnectException is thrown when the connection cannot be made. // we trap this an return a more meaningful message for the end user throw new PSQLException( GT.tr( "Connection refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections."), PSQLState.CONNECTION_REJECTED, cex); } catch (IOException ioe) { if (newStream != null) { try { newStream.close(); } catch (IOException e) { } } throw new PSQLException( GT.tr("The connection attempt failed."), PSQLState.CONNECTION_UNABLE_TO_CONNECT, ioe); } catch (SQLException se) { if (newStream != null) { try { newStream.close(); } catch (IOException e) { } } throw se; } }
private void readStartupMessages( PGStream pgStream, ProtocolConnectionImpl protoConnection, Logger logger) throws IOException, SQLException { while (true) { int beresp = pgStream.ReceiveChar(); switch (beresp) { case 'Z': // Ready For Query; we're done. if (pgStream.ReceiveInteger4() != 5) throw new IOException("unexpected length of ReadyForQuery packet"); char tStatus = (char) pgStream.ReceiveChar(); if (logger.logDebug()) logger.debug(" <=BE ReadyForQuery(" + tStatus + ")"); // Update connection state. switch (tStatus) { case 'I': protoConnection.setTransactionState(ProtocolConnection.TRANSACTION_IDLE); break; case 'T': protoConnection.setTransactionState(ProtocolConnection.TRANSACTION_OPEN); break; case 'E': protoConnection.setTransactionState(ProtocolConnection.TRANSACTION_FAILED); break; default: // Huh? break; } return; case 'K': // BackendKeyData int l_msgLen = pgStream.ReceiveInteger4(); if (l_msgLen != 12) throw new PSQLException( GT.tr("Protocol error. Session setup failed."), PSQLState.CONNECTION_UNABLE_TO_CONNECT); int pid = pgStream.ReceiveInteger4(); int ckey = pgStream.ReceiveInteger4(); if (logger.logDebug()) logger.debug(" <=BE BackendKeyData(pid=" + pid + ",ckey=" + ckey + ")"); protoConnection.setBackendKeyData(pid, ckey); break; case 'E': // Error int l_elen = pgStream.ReceiveInteger4(); ServerErrorMessage l_errorMsg = new ServerErrorMessage(pgStream.ReceiveString(l_elen - 4), logger.getLogLevel()); if (logger.logDebug()) logger.debug(" <=BE ErrorMessage(" + l_errorMsg + ")"); throw new PSQLException(l_errorMsg); case 'N': // Warning int l_nlen = pgStream.ReceiveInteger4(); ServerErrorMessage l_warnMsg = new ServerErrorMessage(pgStream.ReceiveString(l_nlen - 4), logger.getLogLevel()); if (logger.logDebug()) logger.debug(" <=BE NoticeResponse(" + l_warnMsg + ")"); protoConnection.addWarning(new PSQLWarning(l_warnMsg)); break; case 'S': // ParameterStatus int l_len = pgStream.ReceiveInteger4(); String name = pgStream.ReceiveString(); String value = pgStream.ReceiveString(); if (logger.logDebug()) logger.debug(" <=BE ParameterStatus(" + name + " = " + value + ")"); if (name.equals("server_version")) protoConnection.setServerVersion(value); else if (name.equals("client_encoding")) { if (!value.equals("UNICODE")) throw new PSQLException( GT.tr("Protocol error. Session setup failed."), PSQLState.CONNECTION_UNABLE_TO_CONNECT); pgStream.setEncoding(Encoding.getDatabaseEncoding("UNICODE")); } else if (name.equals("standard_conforming_strings")) { if (value.equals("on")) protoConnection.setStandardConformingStrings(true); else if (value.equals("off")) protoConnection.setStandardConformingStrings(false); else throw new PSQLException( GT.tr("Protocol error. Session setup failed."), PSQLState.CONNECTION_UNABLE_TO_CONNECT); } break; default: if (logger.logDebug()) logger.debug("invalid message type=" + (char) beresp); throw new PSQLException( GT.tr("Protocol error. Session setup failed."), PSQLState.CONNECTION_UNABLE_TO_CONNECT); } } }
private void doAuthentication(PGStream pgStream, String user, String password, Logger logger) throws IOException, SQLException { // Now get the response from the backend, either an error message // or an authentication request while (true) { int beresp = pgStream.ReceiveChar(); switch (beresp) { case 'E': // An error occured, so pass the error message to the // user. // // The most common one to be thrown here is: // "User authentication failed" // int l_elen = pgStream.ReceiveInteger4(); if (l_elen > 30000) { // if the error length is > than 30000 we assume this is really a v2 protocol // server, so trigger fallback. throw new UnsupportedProtocolException(); } ServerErrorMessage errorMsg = new ServerErrorMessage(pgStream.ReceiveString(l_elen - 4), logger.getLogLevel()); if (logger.logDebug()) logger.debug(" <=BE ErrorMessage(" + errorMsg + ")"); throw new PSQLException(errorMsg); case 'R': // Authentication request. // Get the message length int l_msgLen = pgStream.ReceiveInteger4(); // Get the type of request int areq = pgStream.ReceiveInteger4(); // Process the request. switch (areq) { case AUTH_REQ_CRYPT: { byte[] rst = new byte[2]; rst[0] = (byte) pgStream.ReceiveChar(); rst[1] = (byte) pgStream.ReceiveChar(); String salt = new String(rst, 0, 2, "US-ASCII"); if (logger.logDebug()) logger.debug(" <=BE AuthenticationReqCrypt(salt='" + salt + "')"); if (password == null) throw new PSQLException( GT.tr( "The server requested password-based authentication, but no password was provided."), PSQLState.CONNECTION_REJECTED); String result = UnixCrypt.crypt(salt, password); byte[] encodedResult = result.getBytes("US-ASCII"); if (logger.logDebug()) logger.debug(" FE=> Password(crypt='" + result + "')"); pgStream.SendChar('p'); pgStream.SendInteger4(4 + encodedResult.length + 1); pgStream.Send(encodedResult); pgStream.SendChar(0); pgStream.flush(); break; } case AUTH_REQ_MD5: { byte[] md5Salt = pgStream.Receive(4); if (logger.logDebug()) { logger.debug( " <=BE AuthenticationReqMD5(salt=" + Utils.toHexString(md5Salt) + ")"); } if (password == null) throw new PSQLException( GT.tr( "The server requested password-based authentication, but no password was provided."), PSQLState.CONNECTION_REJECTED); byte[] digest = MD5Digest.encode(user, password, md5Salt); if (logger.logDebug()) { logger.debug(" FE=> Password(md5digest=" + new String(digest, "US-ASCII") + ")"); } pgStream.SendChar('p'); pgStream.SendInteger4(4 + digest.length + 1); pgStream.Send(digest); pgStream.SendChar(0); pgStream.flush(); break; } case AUTH_REQ_PASSWORD: { if (logger.logDebug()) { logger.debug(" <=BE AuthenticationReqPassword"); logger.debug(" FE=> Password(password=<not shown>)"); } if (password == null) throw new PSQLException( GT.tr( "The server requested password-based authentication, but no password was provided."), PSQLState.CONNECTION_REJECTED); byte[] encodedPassword = password.getBytes("US-ASCII"); pgStream.SendChar('p'); pgStream.SendInteger4(4 + encodedPassword.length + 1); pgStream.Send(encodedPassword); pgStream.SendChar(0); pgStream.flush(); break; } case AUTH_REQ_OK: if (logger.logDebug()) logger.debug(" <=BE AuthenticationOk"); return; // We're done. default: if (logger.logDebug()) logger.debug(" <=BE AuthenticationReq (unsupported type " + ((int) areq) + ")"); throw new PSQLException( GT.tr( "The authentication type {0} is not supported. Check that you have configured the pg_hba.conf file to include the client''s IP address or subnet, and that it is using an authentication scheme supported by the driver.", new Integer(areq)), PSQLState.CONNECTION_REJECTED); } break; default: throw new PSQLException( GT.tr("Protocol error. Session setup failed."), PSQLState.CONNECTION_UNABLE_TO_CONNECT); } } }