private void doOpen(string hostName)
{
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);
}
string password = null;
if (parsedConnectionString.ContainsKey(NuoDbConnectionStringBuilder.PasswordKey))
{
password = parsedConnectionString.Password;
}
string cipher = DEFAULT_CIPHER;
if (parsedConnectionString.ContainsKey(NuoDbConnectionStringBuilder.SchemaKey))
{
tag.addAttribute("Schema", parsedConnectionString.Schema);
properties["Schema"] = parsedConnectionString.Schema;
}
// 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();
// 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)
/*
String timeZone = properties.getProperty(TIMEZONE_NAME);
if (timeZone == null)
{
// Save the default at the time the connection was opened
TimeZone tz = TimeZone.getDefault();
sqlContext.setTimeZone(tz);
sqlContext.setTimeZoneId(tz.getID());
properties.setProperty(TIMEZONE_NAME, tz.getID());
}
else
{
TimeZone tz = TimeZone.getTimeZone(timeZone);
sqlContext.setTimeZone(tz);
sqlContext.setTimeZoneId(tz.getID());
}
*/
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 accpeted 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 + "\"", e);
}
throw e;
}
catch (IOException 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());
}
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());
}
}