private void doOpen(string hostName) { networkErrorOccurred = false; string databaseName = parsedConnectionString.Database; int index = hostName.IndexOf(':'); int port = PORT; if (index != -1) { try { port = Int32.Parse(hostName.Substring(index + 1)); } catch (FormatException e) { throw new ArgumentException("Invalid port number in connection string", "ConnectionString", e); } hostName = hostName.Substring(0, index); } dataStream = new EncodedDataStream(); authenticating = false; try { StringDictionary properties = new StringDictionary(); Tag tag = new Tag("Connection"); tag.addAttribute("Service", "SQL2"); tag.addAttribute("Database", databaseName); if (parsedConnectionString.ContainsKey(NuoDbConnectionStringBuilder.ServerKey)) { tag.addAttribute("Server", parsedConnectionString.Server); properties["Server"] = parsedConnectionString.Server; } string userName = null; if (parsedConnectionString.ContainsKey(NuoDbConnectionStringBuilder.UserKey)) { properties["User"] = userName = parsedConnectionString.User; tag.addAttribute("User", userName); } else { throw new ArgumentException("Username is missing in connection string", "ConnectionString"); } string password = ""; if (parsedConnectionString.ContainsKey(NuoDbConnectionStringBuilder.PasswordKey)) { password = parsedConnectionString.Password; } string cipher = DEFAULT_CIPHER; if (parsedConnectionString.ContainsKey(NuoDbConnectionStringBuilder.CipherKey)) { properties["Cipher"] = cipher = parsedConnectionString.Cipher; } if (parsedConnectionString.ContainsKey(NuoDbConnectionStringBuilder.SchemaKey)) { tag.addAttribute("Schema", parsedConnectionString.Schema); properties["Schema"] = parsedConnectionString.Schema; } if (parsedConnectionString.ContainsKey(NuoDbConnectionStringBuilder.LBTagKey)) { properties["LBTag"] = parsedConnectionString.LBTag; } if (parsedConnectionString.ContainsKey(NuoDbConnectionStringBuilder.ClientInfoKey)) { properties["clientInfo"] = parsedConnectionString.ClientInfo; } properties["clientProcessID"] = Process.GetCurrentProcess().Id.ToString(); // see comment below ... for now these are the only two types that // we can support in the client code if ((!cipher.Equals("RC4")) && (!cipher.Equals("None"))) throw new NuoDbSqlException("Unknown cipher: " + cipher); tag.addAttribute("Cipher", cipher); string xml = tag.ToString(); CryptoSocket brokerSocket = new CryptoSocket(hostName, port); inputStream = brokerSocket.InputStream; outputStream = brokerSocket.OutputStream; dataStream.write(xml); dataStream.send(outputStream); dataStream.getMessage(inputStream); string response = dataStream.readString(); brokerSocket.Close(); Tag responseTag = new Tag(); responseTag.parse(response); if (responseTag.Name.Equals("Error")) { throw new NuoDbSqlException(responseTag.getAttribute("text", "error text not found")); } serverAddress = responseTag.getAttribute("Address", null); serverPort = responseTag.getIntAttribute("Port", 0); if (serverAddress == null || serverPort == 0) { throw new NuoDbSqlException("no NuoDB nodes are available for database \"" + databaseName + "\""); } socket = new CryptoSocket(serverAddress, serverPort); //socket.TcpNoDelay = true; inputStream = socket.InputStream; outputStream = socket.OutputStream; dataStream.reset(); dataStream.write(xml); dataStream.send(outputStream); RemotePassword remotePassword = new RemotePassword(); string userKey = remotePassword.genClientKey(); dataStream.startMessage(Protocol.OpenDatabase); dataStream.encodeInt(Protocol.PROTOCOL_VERSION); dataStream.encodeString(databaseName); getProcessConnection(databaseName); string dbUUId = processConnection.DatabaseUUId.ToString(); #if __MonoCS__ // On Mono, timezone support is too much platform dependent sqlContext.TimeZone = TimeZoneInfo.Local; #else // see if the app set the TimeZone. If so, it will be sent to the server // so set the local TZ to be the same. If not, send the current default // TZ to the server. (Of course, this affects this connection only) if (parsedConnectionString.ContainsKey(NuoDbConnectionStringBuilder.TimeZoneKey)) { string tzone = parsedConnectionString.TimeZone; properties["TimeZone"] = tzone; sqlContext.TimeZone = OlsonDatabase.FindWindowsTimeZone(tzone); } else { // Save the default at the time the connection was opened string tzone = TimeZoneInfo.Local.Id; properties["TimeZone"] = OlsonDatabase.FindOlsonTimeZone(tzone); // As described in http://msdn.microsoft.com/en-us/library/system.timezoneinfo.local.aspx TimeZoneInfo.Local // always applies the DST setting of the current time, even if the DST settings of the tested date used different // rules; so we fetch the complete definition from the database sqlContext.TimeZone = TimeZoneInfo.FindSystemTimeZoneById(tzone); } #endif int count = properties.Count + 1 + ((dbUUId == null) ? 0 : 1); // Add LastCommitInfo and DatabaseUUId dataStream.encodeInt(count); foreach (DictionaryEntry property in properties) { string name = (string)property.Key; string value = (string)property.Value; dataStream.encodeString(name); dataStream.encodeString(value); } // LastCommitInfo and DatabaseUUId are sent as properties. This avoids sending another // message and keeps them from being protocol version sensitive string lastCommitParam = getProcessConnection(databaseName).getLastCommitInfo(); dataStream.encodeString("LastCommitInfo"); dataStream.encodeString(lastCommitParam); if (dbUUId != null) { dataStream.encodeString("DatabaseUUId"); dataStream.encodeString(dbUUId); } // This would have been the last commit txn id if that scheme was ever fully // implemented but it wasn't and this is now obsolete. keep it for compatibility with older servers dataStream.encodeLong(0); dataStream.encodeString(userKey); sendAndReceive(dataStream); protocolVersion = dataStream.getInt(); string serverKey = dataStream.getString(); string salt = dataStream.getString(); dataStream.ProtocolVersion = protocolVersion; if (protocolVersion >= Protocol.PROTOCOL_VERSION5) { processConnection.DatabaseUUId = dataStream.getUUId(); } string upperUserName = userName.ToUpper(); byte[] key = remotePassword.computeSessionKey(upperUserName, password, salt, serverKey); // NOTE: unlike the C++ implementation we only support RC4 in .NET // and it's a hard-coded class (instead of the factory interface // on the C++ CryptoSocket) so there's no checking to see which // cipher was requested here inputStream.encrypt(new CipherRC4(key)); outputStream.encrypt(new CipherRC4(key)); dataStream.startMessage(Protocol.Authentication); dataStream.encodeString("Success!"); authenticating = true; sendAndReceive(dataStream); // if the caller requested a cipher of None and we got here then the // server accepted it and expects us to disable crypto now if (cipher.Equals("None")) { inputStream.encrypt(null); outputStream.encrypt(null); } } catch (NuoDbSqlException e) { #if DEBUG System.Diagnostics.Trace.WriteLine("NuoDBConnection::doOpen(): exception " + e.ToString()); #endif if (authenticating) { throw new NuoDbSqlException("Authentication failed for database \"" + databaseName + "\""); } throw e; } catch (IOException exception) { #if DEBUG System.Diagnostics.Trace.WriteLine("NuoDBConnection::doOpen(): exception " + exception.ToString()); #endif networkErrorOccurred = true; if (socket != null && socket.Connected) { try { socket.Close(); socket = null; } catch (IOException) { // just ignore } } throw new NuoDbSqlException(exception.ToString()); } catch (XmlException exception) { #if DEBUG System.Diagnostics.Trace.WriteLine("NuoDBConnection::doOpen(): exception " + exception.ToString()); #endif if (socket != null && socket.Connected) { try { socket.Close(); socket = null; } catch (IOException) { // just ignore } } throw new NuoDbSqlException(exception.ToString()); } if (parsedConnectionString.ContainsKey(NuoDbConnectionStringBuilder.ApplicationIntentKey)) { string applicationIntent = parsedConnectionString.ApplicationIntent; if (applicationIntent.Equals("ReadOnly", StringComparison.InvariantCultureIgnoreCase)) { dataStream.startMessage(Protocol.SetReadOnly); dataStream.encodeBoolean(true); sendAsync(dataStream); } } if (parsedConnectionString.ContainsKey(NuoDbConnectionStringBuilder.IsolationLevelKey)) { string isolationLevel = parsedConnectionString.IsolationLevel; int level = 0; if (isolationLevel.Equals("ReadCommitted", StringComparison.InvariantCultureIgnoreCase)) { // static const int TRANSACTION_READ_COMMITTED = 2; level = 2; } else if (isolationLevel.Equals("Serializable", StringComparison.InvariantCultureIgnoreCase)) { // static const int TRANSACTION_SERIALIZABLE = 8; level = 8; } else if (isolationLevel.Equals("WriteCommitted", StringComparison.InvariantCultureIgnoreCase)) { // static const int TRANSACTION_WRITE_COMMITTED = 5; level = 5; } else if (isolationLevel.Equals("ConsistentRead", StringComparison.InvariantCultureIgnoreCase)) { // static const int TRANSACTION_CONSISTENT_READ = 7; level = 7; } else { throw new NuoDbSqlException("\"" + isolationLevel + "\" is not a valid isolation level"); } dataStream.startMessage(Protocol.SetTransactionIsolation); dataStream.encodeInt(level); sendAndReceive(dataStream); } }
internal override void encodeValue(EncodedDataStream dataStream) { dataStream.encodeBoolean(value); }