示例#1
0
        /// <summary>
        /// Construct a backend error exception based on a list of one or more
        /// backend errors.  The basic Exception.Message will be built from the
        /// first (usually the only) error in the list.
        /// </summary>
        internal NpgsqlException(IList <NpgsqlError> errors)
            : base(errors[0].ToString())
        {
            NpgsqlEventLog.LogMsg(resman, "Log_ExceptionOccured", LogLevel.Normal, Message);

            this.errors = new NpgsqlError[errors.Count];
            errors.CopyTo(this.errors, 0);
        }
示例#2
0
        /// <summary>
        /// This method writes a set of bytes to the stream. It also enables logging of them.
        /// </summary>
        public static void WriteBytes(byte[] the_bytes, Stream network_stream)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteBytes");
            NpgsqlEventLog.LogMsg(resman, "Log_BytesWritten", LogLevel.Debug, the_bytes);

            network_stream.Write(the_bytes, 0, the_bytes.Length);
            network_stream.Write(new byte[1], 0, 1);
        }
示例#3
0
        ///<summary>
        /// This method writes a C NULL terminated string to the network stream.
        /// It appends a NULL terminator to the end of the String.
        /// </summary>
        public static void WriteString(String the_string, Stream network_stream, Encoding encoding)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteString");

            NpgsqlEventLog.LogMsg(resman, "Log_StringWritten", LogLevel.Debug, the_string);

            network_stream.Write(encoding.GetBytes(the_string + '\x00'), 0, encoding.GetByteCount(the_string) + 1);
        }
示例#4
0
        /// <summary>
        /// This method writes a set of bytes to the stream. It also enables logging of them.
        /// </summary>
        public static Stream WriteBytes(this Stream stream, byte[] the_bytes)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteBytes");
            NpgsqlEventLog.LogMsg(resman, "Log_BytesWritten", LogLevel.Debug, the_bytes);

            stream.Write(the_bytes, 0, the_bytes.Length);

            return(stream);
        }
示例#5
0
        ///<summary>
        /// This method writes a C NULL terminated string to the network stream.
        /// It appends a NULL terminator to the end of the String.
        /// </summary>
        ///<summary>
        /// This method writes a C NULL terminated string to the network stream.
        /// It appends a NULL terminator to the end of the String.
        /// </summary>
        public static void WriteString(String the_string, Stream network_stream)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteString");

            NpgsqlEventLog.LogMsg(resman, "Log_StringWritten", LogLevel.Debug, the_string);

            byte[] bytes = ENCODING_UTF8.GetBytes(the_string + '\x00');

            network_stream.Write(bytes, 0, bytes.Length);
        }
示例#6
0
        /// <summary>
        /// This method writes a byte to the stream. It also enables logging of them.
        /// </summary>
        public static Stream WriteBytesNullTerminated(this Stream stream, byte the_byte)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteByte");
            NpgsqlEventLog.LogMsg(resman, "Log_BytesWritten", LogLevel.Debug, the_byte);

            stream.WriteByte(the_byte);
            stream.WriteByte(0);

            return(stream);
        }
示例#7
0
 /// <summary>
 /// Write each key/value pair in the connection string to the log.
 /// </summary>
 private void LogConnectionString()
 {
     if (NpgsqlEventLog.Level < LogLevel.Debug)
     {
         return;
     }
     foreach (string key in settings.Keys)
     {
         NpgsqlEventLog.LogMsg(resman, "Log_ConnectionStringValues", LogLevel.Debug, key, settings[key]);
     }
 }
示例#8
0
        ///<summary>
        /// This method writes a string to the network stream.
        /// </summary>
        public static Stream WriteString(this Stream stream, String theString)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteString");

            NpgsqlEventLog.LogMsg(resman, "Log_StringWritten", LogLevel.Debug, theString);

            byte[] bytes = BackendEncoding.UTF8Encoding.GetBytes(theString);

            stream.Write(bytes, 0, bytes.Length);

            return(stream);
        }
示例#9
0
        ///<summary>
        /// This method writes a C NULL terminated string to the network stream.
        /// It appends a NULL terminator to the end of the String.
        /// </summary>
        public static Stream WriteStringNullTerminated(this Stream stream, String format, params object[] parameters)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "WriteStringNullTerminated");

            string theString = string.Format(format, parameters);

            NpgsqlEventLog.LogMsg(resman, "Log_StringWritten", LogLevel.Debug, theString);

            byte[] bytes = BackendEncoding.UTF8Encoding.GetBytes(theString);

            stream.Write(bytes, 0, bytes.Length);
            stream.WriteByte(0);

            return(stream);
        }
示例#10
0
        ///<summary>
        /// This method gets a length terminated string from a network stream.
        /// It returns the resultant string of bytes read.
        /// This string is sent from backend.
        /// </summary>
        public static String ReadString(Stream network_stream, Encoding encoding, Int32 length)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ReadString");

            ArrayList buffer = new ArrayList();
            Byte      b;
            String    string_read;

            for (int C = 0; C < length; C++)
            {
                // [FIXME] Is this cast always safe?
                b = (Byte)network_stream.ReadByte();
                buffer.Add(b);
            }

            string_read = encoding.GetString((Byte[])buffer.ToArray(typeof(Byte)));
            NpgsqlEventLog.LogMsg(resman, "Log_StringRead", LogLevel.Debug, string_read);

            return(string_read);
        }
        /// <summary>
        /// Releases all resources used by the
        /// <see cref="Npgsql.NpgsqlConnection">NpgsqlConnection</see>.
        /// </summary>
        /// <param name="disposing"><b>true</b> when called from Dispose();
        /// <b>false</b> when being called from the finalizer.</param>
        protected override void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Dispose");
                    Close();
                }
                else
                {
                    if (FullState != ConnectionState.Closed)
                    {
                        NpgsqlEventLog.LogMsg(resman, "Log_ConnectionLeaking", LogLevel.Debug);
                        NpgsqlConnectorPool.ConnectorPoolMgr.FixPoolCountBecauseOfConnectionDisposeFalse(this);
                    }
                }

                base.Dispose(disposing);
                disposed = true;
            }
        }
示例#12
0
        public override void Open(NpgsqlConnector context, Int32 timeout)
        {
            try
            {
                NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open");

                IAsyncResult result;
                // Keep track of time remaining; Even though there may be multiple timeout-able calls,
                // this allows us to still respect the caller's timeout expectation.
                DateTime attemptStart;

                attemptStart = DateTime.Now;

                result = Dns.BeginGetHostAddresses(context.Host, null, null);

                if (!result.AsyncWaitHandle.WaitOne(timeout, true))
                {
                    // Timeout was used up attempting the Dns lookup
                    throw new TimeoutException(resman.GetString("Exception_DnsLookupTimeout"));
                }

                timeout -= Convert.ToInt32((DateTime.Now - attemptStart).TotalMilliseconds);

                IPAddress[] ips    = Dns.EndGetHostAddresses(result);
                Socket      socket = null;
                Exception   lastSocketException = null;

                // try every ip address of the given hostname, use the first reachable one
                // make sure not to exceed the caller's timeout expectation by splitting the
                // time we have left between all the remaining ip's in the list.
                for (int i = 0; i < ips.Length; i++)
                {
                    NpgsqlEventLog.LogMsg(resman, "Log_ConnectingTo", LogLevel.Debug, ips[i]);

                    IPEndPoint ep = new IPEndPoint(ips[i], context.Port);
                    socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

                    attemptStart = DateTime.Now;

                    try
                    {
                        result = socket.BeginConnect(ep, null, null);

                        if (!result.AsyncWaitHandle.WaitOne(timeout / (ips.Length - i), true))
                        {
                            throw new TimeoutException(resman.GetString("Exception_ConnectionTimeout"));
                        }

                        socket.EndConnect(result);

                        // connect was successful, leave the loop
                        break;
                    }
                    catch (Exception e)
                    {
                        NpgsqlEventLog.LogMsg(resman, "Log_FailedConnection", LogLevel.Normal, ips[i]);

                        timeout            -= Convert.ToInt32((DateTime.Now - attemptStart).TotalMilliseconds);
                        lastSocketException = e;

                        socket.Close();
                        socket = null;
                    }
                }

                if (socket == null)
                {
                    throw lastSocketException;
                }

                NpgsqlNetworkStream baseStream = new NpgsqlNetworkStream(socket, true);
                Stream sslStream = null;

                // If the PostgreSQL server has SSL connectors enabled Open SslClientStream if (response == 'S') {
                if (context.SSL || (context.SslMode == SslMode.Require) || (context.SslMode == SslMode.Prefer))
                {
                    baseStream
                    .WriteInt32(8)
                    .WriteInt32(80877103);

                    // Receive response
                    Char response = (Char)baseStream.ReadByte();

                    if (response == 'S')
                    {
                        //create empty collection
                        X509CertificateCollection clientCertificates = new X509CertificateCollection();

                        //trigger the callback to fetch some certificates
                        context.DefaultProvideClientCertificatesCallback(clientCertificates);

                        //if (context.UseMonoSsl)
                        if (!NpgsqlConnector.UseSslStream)
                        {
                            SslClientStream sslStreamPriv;

                            sslStreamPriv = new SslClientStream(
                                baseStream,
                                context.Host,
                                true,
                                SecurityProtocolType.Default,
                                clientCertificates);

                            sslStreamPriv.ClientCertSelectionDelegate =
                                new CertificateSelectionCallback(context.DefaultCertificateSelectionCallback);
                            sslStreamPriv.ServerCertValidationDelegate =
                                new CertificateValidationCallback(context.DefaultCertificateValidationCallback);
                            sslStreamPriv.PrivateKeyCertSelectionDelegate =
                                new PrivateKeySelectionCallback(context.DefaultPrivateKeySelectionCallback);
                            sslStream = sslStreamPriv;
                        }
                        else
                        {
                            SslStream sslStreamPriv;

                            sslStreamPriv = new SslStream(baseStream, true, context.DefaultValidateRemoteCertificateCallback);

                            sslStreamPriv.AuthenticateAsClient(context.Host, clientCertificates, System.Security.Authentication.SslProtocols.Default, false);
                            sslStream = sslStreamPriv;
                        }
                    }
                    else if (context.SslMode == SslMode.Require)
                    {
                        throw new InvalidOperationException(resman.GetString("Exception_Ssl_RequestError"));
                    }
                }

                context.Socket     = socket;
                context.BaseStream = baseStream;
                context.Stream     = new BufferedStream(sslStream == null ? baseStream : sslStream, 8192);

                NpgsqlEventLog.LogMsg(resman, "Log_ConnectedTo", LogLevel.Normal, context.Host, context.Port);
                ChangeState(context, NpgsqlConnectedState.Instance);
            }
            catch (Exception e)
            {
                throw new NpgsqlException(string.Format(resman.GetString("Exception_FailedConnection"), context.Host), e);
            }
        }
示例#13
0
 /// <summary>
 /// Construct a backend error exception based on a list of one or more
 /// backend errors.  The basic Exception.Message will be built from the
 /// first (usually the only) error in the list.
 /// </summary>
 internal NpgsqlException(IList errors) : base(((NpgsqlError)errors[0]).ToString())
 {
     NpgsqlEventLog.LogMsg(resman, "Log_ExceptionOccured", LogLevel.Normal, Message);
     this.errors = new ArrayList(errors);
 }
        protected IEnumerable <IServerResponseObject> ProcessBackendResponses_Ver_3(NpgsqlConnector context)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");

            using (new ContextResetter(context))
            {
                Stream         stream   = context.Stream;
                NpgsqlMediator mediator = context.Mediator;

                List <NpgsqlError> errors = new List <NpgsqlError>();

                for (;;)
                {
                    // Check the first Byte of response.
                    BackEndMessageCode message = (BackEndMessageCode)stream.ReadByte();
                    switch (message)
                    {
                    case BackEndMessageCode.ErrorResponse:

                        NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion, stream);
                        error.ErrorSql = mediator.SqlSent;

                        errors.Add(error);

                        NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message);

                        // Return imediately if it is in the startup state or connected state as
                        // there is no more messages to consume.
                        // Possible error in the NpgsqlStartupState:
                        //        Invalid password.
                        // Possible error in the NpgsqlConnectedState:
                        //        No pg_hba.conf configured.

                        if (!context.RequireReadyForQuery)
                        {
                            throw new NpgsqlException(errors);
                        }

                        break;

                    case BackEndMessageCode.AuthenticationRequest:

                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");

                        // Get the length in case we're getting AuthenticationGSSContinue
                        int authDataLength = PGUtil.ReadInt32(stream) - 8;

                        AuthenticationRequestType authType = (AuthenticationRequestType)PGUtil.ReadInt32(stream);
                        switch (authType)
                        {
                        case AuthenticationRequestType.AuthenticationOk:
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);
                            break;

                        case AuthenticationRequestType.AuthenticationClearTextPassword:
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);

                            // Send the PasswordPacket.

                            ChangeState(context, NpgsqlStartupState.Instance);
                            context.Authenticate(context.Password);

                            break;

                        case AuthenticationRequestType.AuthenticationMD5Password:
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
                            // Now do the "MD5-Thing"
                            // for this the Password has to be:
                            // 1. md5-hashed with the username as salt
                            // 2. md5-hashed again with the salt we get from the backend

                            MD5 md5 = MD5.Create();

                            // 1.
                            byte[] passwd       = context.Password;
                            byte[] saltUserName = BackendEncoding.UTF8Encoding.GetBytes(context.UserName);

                            byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length];

                            passwd.CopyTo(crypt_buf, 0);
                            saltUserName.CopyTo(crypt_buf, passwd.Length);

                            StringBuilder sb         = new StringBuilder();
                            byte[]        hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }

                            String prehash = sb.ToString();

                            byte[] prehashbytes = BackendEncoding.UTF8Encoding.GetBytes(prehash);
                            crypt_buf = new byte[prehashbytes.Length + 4];

                            stream.Read(crypt_buf, prehashbytes.Length, 4);
                            // Send the PasswordPacket.
                            ChangeState(context, NpgsqlStartupState.Instance);

                            // 2.
                            prehashbytes.CopyTo(crypt_buf, 0);

                            sb         = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5"
                            hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }

                            context.Authenticate(BackendEncoding.UTF8Encoding.GetBytes(sb.ToString()));

                            break;

#if WINDOWS && UNMANAGED
                        case AuthenticationRequestType.AuthenticationSSPI:
                        {
                            if (context.IntegratedSecurity)
                            {
                                // For SSPI we have to get the IP-Address (hostname doesn't work)
                                string ipAddressString = ((IPEndPoint)context.Socket.RemoteEndPoint).Address.ToString();
                                context.SSPI = new SSPIHandler(ipAddressString, "POSTGRES");
                                ChangeState(context, NpgsqlStartupState.Instance);
                                context.Authenticate(context.SSPI.Continue(null));
                                break;
                            }
                            else
                            {
                                // TODO: correct exception
                                throw new Exception();
                            }
                        }

                        case AuthenticationRequestType.AuthenticationGSSContinue:
                        {
                            byte[] authData = new byte[authDataLength];
                            PGUtil.CheckedStreamRead(stream, authData, 0, authDataLength);
                            byte[] passwd_read = context.SSPI.Continue(authData);
                            if (passwd_read.Length != 0)
                            {
                                context.Authenticate(passwd_read);
                            }
                            break;
                        }
#endif

                        default:
                            // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now.
                            errors.Add(
                                new NpgsqlError(context.BackendProtocolVersion,
                                                String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType)));
                            throw new NpgsqlException(errors);
                        }
                        break;

                    case BackEndMessageCode.RowDescription:
                        yield return(new NpgsqlRowDescriptionV3(stream, context.OidToNameMapping, context.CompatVersion));

                        break;

                    case BackEndMessageCode.ParameterDescription:

                        // Do nothing,for instance,  just read...
                        int lenght   = PGUtil.ReadInt32(stream);
                        int nb_param = PGUtil.ReadInt16(stream);
                        for (int i = 0; i < nb_param; i++)
                        {
                            int typeoid = PGUtil.ReadInt32(stream);
                        }

                        break;

                    case BackEndMessageCode.DataRow:
                        yield return(new StringRowReaderV3(stream));

                        break;

                    case BackEndMessageCode.ReadyForQuery:

//                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery");

                        // Possible status bytes returned:
                        //   I = Idle (no transaction active).
                        //   T = In transaction, ready for more.
                        //   E = Error in transaction, queries will fail until transaction aborted.
                        // Just eat the status byte, we have no use for it at this time.
                        PGUtil.ReadInt32(stream);
                        stream.ReadByte();

                        ChangeState(context, NpgsqlReadyState.Instance);

                        if (errors.Count != 0)
                        {
                            throw new NpgsqlException(errors);
                        }

                        yield break;

                    case BackEndMessageCode.BackendKeyData:

                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData");
                        // BackendKeyData message.
                        NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion, stream);
                        context.BackEndKeyData = backend_keydata;

                        // Wait for ReadForQuery message
                        break;

                    case BackEndMessageCode.NoticeResponse:
                        // Notices and errors are identical except that we
                        // just throw notices away completely ignored.
                        context.FireNotice(new NpgsqlError(context.BackendProtocolVersion, stream));
                        break;

                    case BackEndMessageCode.CompletedResponse:
                        PGUtil.ReadInt32(stream);
                        yield return(new CompletedResponse(stream));

                        break;

                    case BackEndMessageCode.ParseComplete:
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParseComplete");
                        // Just read up the message length.
                        PGUtil.ReadInt32(stream);
                        break;

                    case BackEndMessageCode.BindComplete:
//                            NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BindComplete");
                        // Just read up the message length.
                        PGUtil.ReadInt32(stream);
                        break;

                    case BackEndMessageCode.EmptyQueryResponse:
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
                        PGUtil.ReadInt32(stream);
                        break;

                    case BackEndMessageCode.NotificationResponse:
                        // Eat the length
                        PGUtil.ReadInt32(stream);
                        context.FireNotification(new NpgsqlNotificationEventArgs(stream, true));
                        if (context.IsNotificationThreadRunning)
                        {
                            yield break;
                        }
                        break;

                    case BackEndMessageCode.ParameterStatus:
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus");
                        NpgsqlParameterStatus parameterStatus = new NpgsqlParameterStatus(stream);

                        NpgsqlEventLog.LogMsg(resman, "Log_ParameterStatus", LogLevel.Debug, parameterStatus.Parameter,
                                              parameterStatus.ParameterValue);

                        context.AddParameterStatus(parameterStatus);

                        if (parameterStatus.Parameter == "server_version")
                        {
                            // Deal with this here so that if there are
                            // changes in a future backend version, we can handle it here in the
                            // protocol handler and leave everybody else put of it.
                            string versionString = parameterStatus.ParameterValue.Trim();
                            for (int idx = 0; idx != versionString.Length; ++idx)
                            {
                                char c = parameterStatus.ParameterValue[idx];
                                if (!char.IsDigit(c) && c != '.')
                                {
                                    versionString = versionString.Substring(0, idx);
                                    break;
                                }
                            }
                            context.ServerVersion = new Version(versionString);
                        }
                        break;

                    case BackEndMessageCode.NoData:
                        // This nodata message may be generated by prepare commands issued with queries which doesn't return rows
                        // for example insert, update or delete.
                        // Just eat the message.
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus");
                        PGUtil.ReadInt32(stream);
                        break;

                    case BackEndMessageCode.CopyInResponse:
                        // Enter COPY sub protocol and start pushing data to server
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyInResponse");
                        ChangeState(context, NpgsqlCopyInState.Instance);
                        PGUtil.ReadInt32(stream);     // length redundant
                        context.CurrentState.StartCopy(context, ReadCopyHeader(stream));
                        yield break;
                    // Either StartCopy called us again to finish the operation or control should be passed for user to feed copy data

                    case BackEndMessageCode.CopyOutResponse:
                        // Enter COPY sub protocol and start pulling data from server
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyOutResponse");
                        ChangeState(context, NpgsqlCopyOutState.Instance);
                        PGUtil.ReadInt32(stream);     // length redundant
                        context.CurrentState.StartCopy(context, ReadCopyHeader(stream));
                        yield break;
                    // Either StartCopy called us again to finish the operation or control should be passed for user to feed copy data

                    case BackEndMessageCode.CopyData:
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyData");
                        Int32  len = PGUtil.ReadInt32(stream) - 4;
                        byte[] buf = new byte[len];
                        PGUtil.ReadBytes(stream, buf, 0, len);
                        context.Mediator.ReceivedCopyData = buf;
                        yield break;     // read data from server one chunk at a time while staying in copy operation mode

                    case BackEndMessageCode.CopyDone:
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CopyDone");
                        PGUtil.ReadInt32(stream);     // CopyDone can not have content so this is always 4
                        // This will be followed by normal CommandComplete + ReadyForQuery so no op needed
                        break;

                    case BackEndMessageCode.IO_ERROR:
                        // Connection broken. Mono returns -1 instead of throwing an exception as ms.net does.
                        throw new IOException();

                    default:
                        // This could mean a number of things
                        //   We've gotten out of sync with the backend?
                        //   We need to implement this type?
                        //   Backend has gone insane?
                        // FIXME
                        // what exception should we really throw here?
                        throw new NotSupportedException(String.Format("Backend sent unrecognized response type: {0}", (Char)message));
                    }
                }
            }
        }
        protected IEnumerable <IServerResponseObject> ProcessBackendResponses_Ver_2(NpgsqlConnector context)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");

            using (new ContextResetter(context))
            {
                Stream             stream   = context.Stream;
                NpgsqlMediator     mediator = context.Mediator;
                List <NpgsqlError> errors   = new List <NpgsqlError>();

                for (;;)
                {
                    // Check the first Byte of response.
                    switch ((BackEndMessageCode)stream.ReadByte())
                    {
                    case BackEndMessageCode.ErrorResponse:

                    {
                        NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion, stream);
                        error.ErrorSql = mediator.GetSqlSent();

                        errors.Add(error);

                        NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message);
                    }

                        // Return imediately if it is in the startup state or connected state as
                        // there is no more messages to consume.
                        // Possible error in the NpgsqlStartupState:
                        //        Invalid password.
                        // Possible error in the NpgsqlConnectedState:
                        //        No pg_hba.conf configured.

                        if (!context.RequireReadyForQuery)
                        {
                            throw new NpgsqlException(errors);
                        }

                        break;

                    case BackEndMessageCode.AuthenticationRequest:
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");
                        AuthenticationRequestType authType = (AuthenticationRequestType)PGUtil.ReadInt32(stream);
                        switch (authType)
                        {
                        case AuthenticationRequestType.AuthenticationOk:
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);
                            break;

                        case AuthenticationRequestType.AuthenticationClearTextPassword:
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);
                            // Send the PasswordPacket.
                            ChangeState(context, NpgsqlStartupState.Instance);
                            context.Authenticate(context.Password);
                            context.Stream.Flush();

                            break;

                        case AuthenticationRequestType.AuthenticationMD5Password:
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
                            // Now do the "MD5-Thing"
                            // for this the Password has to be:
                            // 1. md5-hashed with the username as salt
                            // 2. md5-hashed again with the salt we get from the backend

                            MD5 md5 = MD5.Create();

                            // 1.
                            byte[] passwd       = context.Password;
                            byte[] saltUserName = BackendEncoding.UTF8Encoding.GetBytes(context.UserName);

                            byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length];

                            passwd.CopyTo(crypt_buf, 0);
                            saltUserName.CopyTo(crypt_buf, passwd.Length);

                            StringBuilder sb         = new StringBuilder();
                            byte[]        hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }

                            String prehash = sb.ToString();

                            byte[] prehashbytes = BackendEncoding.UTF8Encoding.GetBytes(prehash);

                            byte[] saltServer = new byte[4];
                            stream.Read(saltServer, 0, 4);
                            // Send the PasswordPacket.
                            ChangeState(context, NpgsqlStartupState.Instance);

                            // 2.

                            crypt_buf = new byte[prehashbytes.Length + saltServer.Length];
                            prehashbytes.CopyTo(crypt_buf, 0);
                            saltServer.CopyTo(crypt_buf, prehashbytes.Length);

                            sb         = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5"
                            hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }

                            context.Authenticate(BackendEncoding.UTF8Encoding.GetBytes(sb.ToString()));
                            context.Stream.Flush();

                            break;

                        default:
                            // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now.
                            errors.Add(
                                new NpgsqlError(context.BackendProtocolVersion,
                                                String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType)));
                            throw new NpgsqlException(errors);
                        }
                        break;

                    case BackEndMessageCode.RowDescription:
                        yield return(new NpgsqlRowDescriptionV2(stream, context.OidToNameMapping, context.CompatVersion));

                        break;

                    case BackEndMessageCode.DataRow:
                        yield return(new StringRowReaderV2(stream));

                        break;

                    case BackEndMessageCode.BinaryRow:
                        throw new NotSupportedException();

                    case BackEndMessageCode.ReadyForQuery:
                        ChangeState(context, NpgsqlReadyState.Instance);
                        if (errors.Count != 0)
                        {
                            throw new NpgsqlException(errors);
                        }
                        yield break;

                    case BackEndMessageCode.BackendKeyData:
                        context.BackEndKeyData = new NpgsqlBackEndKeyData(context.BackendProtocolVersion, stream);
                        break;

                    case BackEndMessageCode.NoticeResponse:
                        context.FireNotice(new NpgsqlError(context.BackendProtocolVersion, stream));
                        break;

                    case BackEndMessageCode.CompletedResponse:
                        yield return(new CompletedResponse(stream));

                        break;

                    case BackEndMessageCode.CursorResponse:
                        // This is the cursor response message.
                        // It is followed by a C NULL terminated string with the name of
                        // the cursor in a FETCH case or 'blank' otherwise.
                        // In this case it should be always 'blank'.
                        // [FIXME] Get another name for this function.
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CursorResponse");

                        String cursorName = PGUtil.ReadString(stream);
                        // Continue waiting for ReadyForQuery message.
                        break;

                    case BackEndMessageCode.EmptyQueryResponse:
                        NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
                        PGUtil.ReadString(stream);
                        break;

                    case BackEndMessageCode.NotificationResponse:
                        context.FireNotification(new NpgsqlNotificationEventArgs(stream, false));
                        if (context.IsNotificationThreadRunning)
                        {
                            yield break;
                        }
                        break;

                    case BackEndMessageCode.IO_ERROR:
                        // Connection broken. Mono returns -1 instead of throw an exception as ms.net does.
                        throw new IOException();

                    default:
                        // This could mean a number of things
                        //   We've gotten out of sync with the backend?
                        //   We need to implement this type?
                        //   Backend has gone insane?
                        throw new DataException("Backend sent unrecognized response type");
                    }
                }
            }
        }
示例#16
0
        public override void Open(NpgsqlConnector context)
        {
            try
            {
                NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open");

                IPAddress[] ips    = ResolveIPHost(context.Host);
                Socket      socket = null;

                // try every ip address of the given hostname, use the first reachable one
                foreach (IPAddress ip in ips)
                {
                    NpgsqlEventLog.LogMsg(resman, "Log_ConnectingTo", LogLevel.Debug, ip);

                    IPEndPoint ep = new IPEndPoint(ip, context.Port);
                    socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

                    try
                    {
                        IAsyncResult result = socket.BeginConnect(ep, null, null);

                        if (!result.AsyncWaitHandle.WaitOne(context.ConnectionTimeout * 1000, true))
                        {
                            socket.Close();
                            throw new Exception(resman.GetString("Exception_ConnectionTimeout"));
                        }

                        socket.EndConnect(result);

                        // connect was successful, leave the loop
                        break;
                    }
                    catch (Exception)
                    {
                        NpgsqlEventLog.LogMsg(resman, "Log_FailedConnection", LogLevel.Normal, ip);
                        socket.Close();
                    }
                }

                if (socket == null || !socket.Connected)
                {
                    throw new Exception(string.Format(resman.GetString("Exception_FailedConnection"), context.Host));
                }

                Stream stream = new NpgsqlNetworkStream(context, socket, true);

                // If the PostgreSQL server has SSL connectors enabled Open SslClientStream if (response == 'S') {
                if (context.SSL || (context.SslMode == SslMode.Require) || (context.SslMode == SslMode.Prefer))
                {
                    PGUtil.WriteInt32(stream, 8);
                    PGUtil.WriteInt32(stream, 80877103);
                    // Receive response

                    Char response = (Char)stream.ReadByte();
                    if (response == 'S')
                    {
                        //create empty collection
                        X509CertificateCollection clientCertificates = new X509CertificateCollection();

                        //trigger the callback to fetch some certificates
                        context.DefaultProvideClientCertificatesCallback(clientCertificates);

                        stream = new SslClientStream(
                            stream,
                            context.Host,
                            true,
                            SecurityProtocolType.Default,
                            clientCertificates);

                        ((SslClientStream)stream).ClientCertSelectionDelegate =
                            new CertificateSelectionCallback(context.DefaultCertificateSelectionCallback);
                        ((SslClientStream)stream).ServerCertValidationDelegate =
                            new CertificateValidationCallback(context.DefaultCertificateValidationCallback);
                        ((SslClientStream)stream).PrivateKeyCertSelectionDelegate =
                            new PrivateKeySelectionCallback(context.DefaultPrivateKeySelectionCallback);
                    }
                    else if (context.SslMode == SslMode.Require)
                    {
                        throw new InvalidOperationException(resman.GetString("Exception_Ssl_RequestError"));
                    }
                }

                context.Stream = new NpgsqlBufferedStream(stream);
                context.Socket = socket;

                NpgsqlEventLog.LogMsg(resman, "Log_ConnectedTo", LogLevel.Normal, context.Host, context.Port);
                ChangeState(context, NpgsqlConnectedState.Instance);
            }
            //FIXME: Exceptions that come from what we are handling should be wrapped - e.g. an error connecting to
            //the server should definitely be presented to the uesr as an NpgsqlError. Exceptions from userland should
            //be passed untouched - e.g. ThreadAbortException because the user started this in a thread they created and
            //then aborted should be passed through.
            //Are there any others that should be pass through? Alternatively, are there a finite number that should
            //be wrapped?
            catch (ThreadAbortException)
            {
                throw;
            }
            catch (Exception e)
            {
                throw new NpgsqlException(e.Message, e);
            }
        }
        public override void Open(NpgsqlConnector context)
        {
            try
            {
                NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open");

                /*TcpClient tcpc = new TcpClient();
                *  tcpc.Connect(new IPEndPoint(ResolveIPHost(context.Host), context.Port));
                *  Stream stream = tcpc.GetStream();*/

                /*socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.SendTimeout, context.ConnectionTimeout*1000);*/

                //socket.Connect(new IPEndPoint(ResolveIPHost(context.Host), context.Port));


                /*Socket socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
                 *
                 * IAsyncResult result = socket.BeginConnect(new IPEndPoint(ResolveIPHost(context.Host), context.Port), null, null);
                 *
                 * if (!result.AsyncWaitHandle.WaitOne(context.ConnectionTimeout*1000, true))
                 * {
                 * socket.Close();
                 * throw new Exception(resman.GetString("Exception_ConnectionTimeout"));
                 * }
                 *
                 * try
                 * {
                 * socket.EndConnect(result);
                 * }
                 * catch (Exception)
                 * {
                 * socket.Close();
                 * throw;
                 * }
                 */

                IPAddress[] ips    = ResolveIPHost(context.Host);
                Socket      socket = null;

                // try every ip address of the given hostname, use the first reachable one
                foreach (IPAddress ip in ips)
                {
                    NpgsqlEventLog.LogMsg(resman, "Log_ConnectingTo", LogLevel.Debug, ip);

                    IPEndPoint ep = new IPEndPoint(ip, context.Port);
                    socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

                    try
                    {
                        IAsyncResult result = socket.BeginConnect(ep, null, null);

                        if (!result.AsyncWaitHandle.WaitOne(context.ConnectionTimeout * 1000, true))
                        {
                            socket.Close();
                            throw new Exception(resman.GetString("Exception_ConnectionTimeout"));
                        }

                        socket.EndConnect(result);

                        // connect was successful, leave the loop
                        break;
                    }
                    catch (Exception)
                    {
                        NpgsqlEventLog.LogMsg(resman, "Log_FailedConnection", LogLevel.Normal, ip);
                        socket.Close();
                    }
                }

                if (socket == null || !socket.Connected)
                {
                    throw new Exception(string.Format(resman.GetString("Exception_FailedConnection"), context.Host));
                }

                Stream stream = new NetworkStream(socket, true);



                context.Stream = new BufferedStream(stream);
                context.Socket = socket;


                NpgsqlEventLog.LogMsg(resman, "Log_ConnectedTo", LogLevel.Normal, context.Host, context.Port);
                ChangeState(context, NpgsqlConnectedState.Instance);
            }
            //FIXME: Exceptions that come from what we are handling should be wrapped - e.g. an error connecting to
            //the server should definitely be presented to the uesr as an NpgsqlError. Exceptions from userland should
            //be passed untouched - e.g. ThreadAbortException because the user started this in a thread they created and
            //then aborted should be passed through.
            //Are there any others that should be pass through? Alternatively, are there a finite number that should
            //be wrapped?
            catch (ThreadAbortException)
            {
                throw;
            }
            catch (Exception e)
            {
                throw new NpgsqlException(e.Message, e);
            }
        }
示例#18
0
 public NpgsqlException(String message, Exception inner)
     : base(message, inner)
 {
     NpgsqlEventLog.LogMsg("An NpgsqlException occured: " + message + " (" + inner.Message + ")", LogLevel.Normal);
 }
示例#19
0
        protected virtual void ProcessBackendResponses_Ver_2(NpgsqlConnector context)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");

            Stream         stream   = context.Stream;
            NpgsqlMediator mediator = context.Mediator;

            // Often used buffer
            Byte[] inputBuffer = new Byte[4];

            Boolean readyForQuery = false;

            byte[] asciiRowBytes = new byte[300];
            char[] asciiRowChars = new char[300];

            while (!readyForQuery)
            {
                // Check the first Byte of response.
                switch (stream.ReadByte())
                {
                case NpgsqlMessageTypes_Ver_2.ErrorResponse:

                {
                    NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion);
                    error.ReadFromStream(stream, context.Encoding);
                    error.ErrorSql = mediator.SqlSent;

                    mediator.Errors.Add(error);

                    NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message);
                }

                    // Return imediately if it is in the startup state or connected state as
                    // there is no more messages to consume.
                    // Possible error in the NpgsqlStartupState:
                    //		Invalid password.
                    // Possible error in the NpgsqlConnectedState:
                    //		No pg_hba.conf configured.

                    if (!mediator.RequireReadyForQuery)
                    {
                        return;
                    }

                    break;


                case NpgsqlMessageTypes_Ver_2.AuthenticationRequest:

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");

                    {
                        Int32 authType = PGUtil.ReadInt32(stream, inputBuffer);

                        if (authType == NpgsqlMessageTypes_Ver_2.AuthenticationOk)
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);

                            break;
                        }

                        if (authType == NpgsqlMessageTypes_Ver_2.AuthenticationClearTextPassword)
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);

                            // Send the PasswordPacket.

                            ChangeState(context, NpgsqlStartupState.Instance);
                            context.Authenticate(context.Password);

                            break;
                        }


                        if (authType == NpgsqlMessageTypes_Ver_2.AuthenticationMD5Password)
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
                            // Now do the "MD5-Thing"
                            // for this the Password has to be:
                            // 1. md5-hashed with the username as salt
                            // 2. md5-hashed again with the salt we get from the backend


                            MD5 md5 = MD5.Create();


                            // 1.
                            byte[] passwd       = context.Encoding.GetBytes(context.Password);
                            byte[] saltUserName = context.Encoding.GetBytes(context.UserName);

                            byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length];

                            passwd.CopyTo(crypt_buf, 0);
                            saltUserName.CopyTo(crypt_buf, passwd.Length);



                            StringBuilder sb         = new StringBuilder();
                            byte[]        hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }


                            String prehash = sb.ToString();

                            byte[] prehashbytes = context.Encoding.GetBytes(prehash);



                            byte[] saltServer = new byte[4];
                            stream.Read(saltServer, 0, 4);
                            // Send the PasswordPacket.
                            ChangeState(context, NpgsqlStartupState.Instance);


                            // 2.

                            crypt_buf = new byte[prehashbytes.Length + saltServer.Length];
                            prehashbytes.CopyTo(crypt_buf, 0);
                            saltServer.CopyTo(crypt_buf, prehashbytes.Length);

                            sb         = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5"
                            hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }

                            context.Authenticate(sb.ToString());

                            break;
                        }

                        // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now.

                        mediator.Errors.Add(new NpgsqlError(context.BackendProtocolVersion, String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType)));
                    }

                    return;

                case NpgsqlMessageTypes_Ver_2.RowDescription:
                    // This is the RowDescription message.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription");

                    {
                        NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion);
                        rd.ReadFromStream(stream, context.Encoding, context.OidToNameMapping);

                        // Initialize the array list which will contain the data from this rowdescription.
                        mediator.AddRowDescription(rd);
                    }

                    // Now wait for the AsciiRow messages.
                    break;

                case NpgsqlMessageTypes_Ver_2.AsciiRow:
                    // This is the AsciiRow message.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AsciiRow");

                    {
                        NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion, asciiRowBytes, asciiRowChars);
                        asciiRow.ReadFromStream(stream, context.Encoding);

                        // Add this row to the rows array.
                        mediator.AddAsciiRow(asciiRow);
                    }

                    // Now wait for CompletedResponse message.
                    break;

                case NpgsqlMessageTypes_Ver_2.BinaryRow:
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BinaryRow");

                    {
                        NpgsqlBinaryRow binaryRow = new NpgsqlBinaryRow(context.Mediator.LastRowDescription);
                        binaryRow.ReadFromStream(stream, context.Encoding);

                        mediator.AddBinaryRow(binaryRow);
                    }

                    break;

                case NpgsqlMessageTypes_Ver_2.ReadyForQuery:

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery");
                    readyForQuery = true;
                    ChangeState(context, NpgsqlReadyState.Instance);
                    break;

                case NpgsqlMessageTypes_Ver_2.BackendKeyData:

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData");
                    // BackendKeyData message.
                    NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion);
                    backend_keydata.ReadFromStream(stream);
                    mediator.SetBackendKeydata(backend_keydata);


                    // Wait for ReadForQuery message
                    break;
                    ;

                case NpgsqlMessageTypes_Ver_2.NoticeResponse:

                {
                    NpgsqlError notice = new NpgsqlError(context.BackendProtocolVersion);
                    notice.ReadFromStream(stream, context.Encoding);

                    mediator.Notices.Add(notice);

                    NpgsqlEventLog.LogMsg(resman, "Log_NoticeResponse", LogLevel.Debug, notice.Message);
                }

                    // Wait for ReadForQuery message
                    break;

                case NpgsqlMessageTypes_Ver_2.CompletedResponse:
                    // This is the CompletedResponse message.
                    // Get the string returned.


                    String result = PGUtil.ReadString(stream, context.Encoding);

                    NpgsqlEventLog.LogMsg(resman, "Log_CompletedResponse", LogLevel.Debug, result);
                    // Add result from the processing.

                    mediator.AddCompletedResponse(result);

                    // Now wait for ReadyForQuery message.
                    break;

                case NpgsqlMessageTypes_Ver_2.CursorResponse:
                    // This is the cursor response message.
                    // It is followed by a C NULL terminated string with the name of
                    // the cursor in a FETCH case or 'blank' otherwise.
                    // In this case it should be always 'blank'.
                    // [FIXME] Get another name for this function.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "CursorResponse");

                    String cursorName = PGUtil.ReadString(stream, context.Encoding);
                    // Continue waiting for ReadyForQuery message.
                    break;

                case NpgsqlMessageTypes_Ver_2.EmptyQueryResponse:
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
                    PGUtil.ReadString(stream, context.Encoding);
                    break;

                case NpgsqlMessageTypes_Ver_2.NotificationResponse:

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse");

                    Int32  PID = PGUtil.ReadInt32(stream, inputBuffer);
                    String notificationResponse = PGUtil.ReadString(stream, context.Encoding);
                    mediator.AddNotification(new NpgsqlNotificationEventArgs(PID, notificationResponse));

                    if (context.IsNotificationThreadRunning)
                    {
                        readyForQuery = true;
                    }

                    // Wait for ReadForQuery message
                    break;

                case -1:
                    // Connection broken. Mono returns -1 instead of throw an exception as ms.net does.
                    throw new IOException();

                default:
                    // This could mean a number of things
                    //   We've gotten out of sync with the backend?
                    //   We need to implement this type?
                    //   Backend has gone insane?
                    // FIXME
                    // what exception should we really throw here?
                    throw new NotSupportedException("Backend sent unrecognized response type");
                }
            }
        }
示例#20
0
 public NpgsqlException(String message) : base(message)
 {
     NpgsqlEventLog.LogMsg("An NpgsqlException occured: " + message, LogLevel.Normal);
 }
示例#21
0
        protected virtual void ProcessBackendResponses_Ver_3(NpgsqlConnector context)
        {
            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ProcessBackendResponses");

            Stream         stream   = context.Stream;
            NpgsqlMediator mediator = context.Mediator;

            // Often used buffers
            Byte[] inputBuffer = new Byte[4];
            String Str;

            Boolean readyForQuery = false;

            byte[] asciiRowBytes = new byte[300];
            char[] asciiRowChars = new char[300];

            while (!readyForQuery)
            {
                // Check the first Byte of response.
                Int32 message = stream.ReadByte();
                switch (message)
                {
                case NpgsqlMessageTypes_Ver_3.ErrorResponse:

                {
                    NpgsqlError error = new NpgsqlError(context.BackendProtocolVersion);
                    error.ReadFromStream(stream, context.Encoding);
                    error.ErrorSql = mediator.SqlSent;

                    mediator.Errors.Add(error);

                    NpgsqlEventLog.LogMsg(resman, "Log_ErrorResponse", LogLevel.Debug, error.Message);
                }

                    // Return imediately if it is in the startup state or connected state as
                    // there is no more messages to consume.
                    // Possible error in the NpgsqlStartupState:
                    //		Invalid password.
                    // Possible error in the NpgsqlConnectedState:
                    //		No pg_hba.conf configured.

                    if (!mediator.RequireReadyForQuery)
                    {
                        return;
                    }

                    break;


                case NpgsqlMessageTypes_Ver_3.AuthenticationRequest:

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "AuthenticationRequest");

                    // Eat length
                    PGUtil.ReadInt32(stream, inputBuffer);

                    {
                        Int32 authType = PGUtil.ReadInt32(stream, inputBuffer);

                        if (authType == NpgsqlMessageTypes_Ver_3.AuthenticationOk)
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationOK", LogLevel.Debug);

                            break;
                        }

                        if (authType == NpgsqlMessageTypes_Ver_3.AuthenticationClearTextPassword)
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationClearTextRequest", LogLevel.Debug);

                            // Send the PasswordPacket.

                            ChangeState(context, NpgsqlStartupState.Instance);
                            context.Authenticate(context.Password);

                            break;
                        }


                        if (authType == NpgsqlMessageTypes_Ver_3.AuthenticationMD5Password)
                        {
                            NpgsqlEventLog.LogMsg(resman, "Log_AuthenticationMD5Request", LogLevel.Debug);
                            // Now do the "MD5-Thing"
                            // for this the Password has to be:
                            // 1. md5-hashed with the username as salt
                            // 2. md5-hashed again with the salt we get from the backend


                            MD5 md5 = MD5.Create();


                            // 1.
                            byte[] passwd       = context.Encoding.GetBytes(context.Password);
                            byte[] saltUserName = context.Encoding.GetBytes(context.UserName);

                            byte[] crypt_buf = new byte[passwd.Length + saltUserName.Length];

                            passwd.CopyTo(crypt_buf, 0);
                            saltUserName.CopyTo(crypt_buf, passwd.Length);



                            StringBuilder sb         = new StringBuilder();
                            byte[]        hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }


                            String prehash = sb.ToString();

                            byte[] prehashbytes = context.Encoding.GetBytes(prehash);



                            stream.Read(inputBuffer, 0, 4);
                            // Send the PasswordPacket.
                            ChangeState(context, NpgsqlStartupState.Instance);


                            // 2.

                            crypt_buf = new byte[prehashbytes.Length + 4];
                            prehashbytes.CopyTo(crypt_buf, 0);
                            inputBuffer.CopyTo(crypt_buf, prehashbytes.Length);

                            sb         = new StringBuilder("md5"); // This is needed as the backend expects md5 result starts with "md5"
                            hashResult = md5.ComputeHash(crypt_buf);
                            foreach (byte b in hashResult)
                            {
                                sb.Append(b.ToString("x2"));
                            }

                            context.Authenticate(sb.ToString());

                            break;
                        }

                        // Only AuthenticationClearTextPassword and AuthenticationMD5Password supported for now.
                        mediator.Errors.Add(new NpgsqlError(context.BackendProtocolVersion, String.Format(resman.GetString("Exception_AuthenticationMethodNotSupported"), authType)));
                    }

                    return;

                case NpgsqlMessageTypes_Ver_3.RowDescription:
                    // This is the RowDescription message.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "RowDescription");
                    {
                        NpgsqlRowDescription rd = new NpgsqlRowDescription(context.BackendProtocolVersion);
                        rd.ReadFromStream(stream, context.Encoding, context.OidToNameMapping);

                        mediator.AddRowDescription(rd);
                    }

                    // Now wait for the AsciiRow messages.
                    break;

                case NpgsqlMessageTypes_Ver_3.ParameterDescription:

                    // Do nothing,for instance,  just read...
                    int lenght   = PGUtil.ReadInt32(stream, inputBuffer);
                    int nb_param = PGUtil.ReadInt16(stream, inputBuffer);
                    for (int i = 0; i < nb_param; i++)
                    {
                        int typeoid = PGUtil.ReadInt32(stream, inputBuffer);
                    }

                    break;

                case NpgsqlMessageTypes_Ver_3.DataRow:
                    // This is the AsciiRow message.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "DataRow");
                    {
                        NpgsqlAsciiRow asciiRow = new NpgsqlAsciiRow(context.Mediator.LastRowDescription, context.BackendProtocolVersion, asciiRowBytes, asciiRowChars);
                        asciiRow.ReadFromStream(stream, context.Encoding);

                        // Add this row to the rows array.
                        mediator.AddAsciiRow(asciiRow);
                    }

                    // Now wait for CompletedResponse message.
                    break;

                case NpgsqlMessageTypes_Ver_3.ReadyForQuery:

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ReadyForQuery");

                    // Possible status bytes returned:
                    //   I = Idle (no transaction active).
                    //   T = In transaction, ready for more.
                    //   E = Error in transaction, queries will fail until transaction aborted.
                    // Just eat the status byte, we have no use for it at this time.
                    PGUtil.ReadInt32(stream, inputBuffer);
                    PGUtil.ReadString(stream, context.Encoding, 1);

                    readyForQuery = true;
                    ChangeState(context, NpgsqlReadyState.Instance);

                    break;

                case NpgsqlMessageTypes_Ver_3.BackendKeyData:

                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BackendKeyData");
                    // BackendKeyData message.
                    NpgsqlBackEndKeyData backend_keydata = new NpgsqlBackEndKeyData(context.BackendProtocolVersion);
                    backend_keydata.ReadFromStream(stream);
                    mediator.SetBackendKeydata(backend_keydata);


                    // Wait for ReadForQuery message
                    break;

                case NpgsqlMessageTypes_Ver_3.NoticeResponse:

                    // Notices and errors are identical except that we
                    // just throw notices away completely ignored.
                {
                    NpgsqlError notice = new NpgsqlError(context.BackendProtocolVersion);
                    notice.ReadFromStream(stream, context.Encoding);

                    mediator.Notices.Add(notice);

                    NpgsqlEventLog.LogMsg(resman, "Log_NoticeResponse", LogLevel.Debug, notice.Message);
                }

                    // Wait for ReadForQuery message
                    break;

                case NpgsqlMessageTypes_Ver_3.CompletedResponse:
                    // This is the CompletedResponse message.
                    // Get the string returned.

                    PGUtil.ReadInt32(stream, inputBuffer);
                    Str = PGUtil.ReadString(stream, context.Encoding);

                    NpgsqlEventLog.LogMsg(resman, "Log_CompletedResponse", LogLevel.Debug, Str);

                    // Add result from the processing.
                    mediator.AddCompletedResponse(Str);

                    break;

                case NpgsqlMessageTypes_Ver_3.ParseComplete:
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParseComplete");
                    // Just read up the message length.
                    PGUtil.ReadInt32(stream, inputBuffer);
                    readyForQuery = true;
                    break;

                case NpgsqlMessageTypes_Ver_3.BindComplete:
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "BindComplete");
                    // Just read up the message length.
                    PGUtil.ReadInt32(stream, inputBuffer);
                    readyForQuery = true;
                    break;

                case NpgsqlMessageTypes_Ver_3.EmptyQueryResponse:
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "EmptyQueryResponse");
                    PGUtil.ReadInt32(stream, inputBuffer);
                    break;

                case NpgsqlMessageTypes_Ver_3.NotificationResponse:
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "NotificationResponse");

                    // Eat the length
                    PGUtil.ReadInt32(stream, inputBuffer);
                    {
                        // Process ID sending notification
                        Int32 PID = PGUtil.ReadInt32(stream, inputBuffer);
                        // Notification string
                        String notificationResponse = PGUtil.ReadString(stream, context.Encoding);
                        // Additional info, currently not implemented by PG (empty string always), eat it
                        PGUtil.ReadString(stream, context.Encoding);
                        mediator.AddNotification(new NpgsqlNotificationEventArgs(PID, notificationResponse));
                    }

                    if (context.IsNotificationThreadRunning)
                    {
                        readyForQuery = true;
                    }

                    // Wait for ReadForQuery message
                    break;

                case NpgsqlMessageTypes_Ver_3.ParameterStatus:
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus");
                    NpgsqlParameterStatus parameterStatus = new NpgsqlParameterStatus();
                    parameterStatus.ReadFromStream(stream, context.Encoding);

                    NpgsqlEventLog.LogMsg(resman, "Log_ParameterStatus", LogLevel.Debug, parameterStatus.Parameter, parameterStatus.ParameterValue);

                    mediator.AddParameterStatus(parameterStatus.Parameter, parameterStatus);

                    if (parameterStatus.Parameter == "server_version")
                    {
                        // Add this one under our own name so that if the parameter name
                        // changes in a future backend version, we can handle it here in the
                        // protocol handler and leave everybody else put of it.
                        mediator.AddParameterStatus("__npgsql_server_version", parameterStatus);
                        //                        context.ServerVersionString = parameterStatus.ParameterValue;
                    }

                    break;

                case NpgsqlMessageTypes_Ver_3.NoData:
                    // This nodata message may be generated by prepare commands issued with queries which doesn't return rows
                    // for example insert, update or delete.
                    // Just eat the message.
                    NpgsqlEventLog.LogMsg(resman, "Log_ProtocolMessage", LogLevel.Debug, "ParameterStatus");
                    PGUtil.ReadInt32(stream, inputBuffer);
                    break;


                case -1:
                    // Connection broken. Mono returns -1 instead of throw an exception as ms.net does.
                    throw new IOException();

                default:
                    // This could mean a number of things
                    //   We've gotten out of sync with the backend?
                    //   We need to implement this type?
                    //   Backend has gone insane?
                    // FIXME
                    // what exception should we really throw here?
                    throw new NotSupportedException(String.Format("Backend sent unrecognized response type: {0}", (Char)message));
                }
            }
        }
        public override void Open(NpgsqlConnector context)
        {
            try
            {
                NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Open");

                /*TcpClient tcpc = new TcpClient();
                *  tcpc.Connect(new IPEndPoint(ResolveIPHost(context.Host), context.Port));
                *  Stream stream = tcpc.GetStream();*/

                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                /*socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.SendTimeout, context.ConnectionTimeout*1000);*/

                //socket.Connect(new IPEndPoint(ResolveIPHost(context.Host), context.Port));

                IAsyncResult result = socket.BeginConnect(new IPEndPoint(ResolveIPHost(context.Host), context.Port), null, null);

                if (!result.AsyncWaitHandle.WaitOne(context.ConnectionTimeout * 1000, true))
                {
                    socket.Close();
                    throw new Exception(resman.GetString("Exception_ConnectionTimeout"));
                }

                try
                {
                    socket.EndConnect(result);
                }
                catch (Exception ex)
                {
                    socket.Close();
                    throw;
                }

                Stream stream = new NetworkStream(socket, true);



                // If the PostgreSQL server has SSL connectors enabled Open SslClientStream if (response == 'S') {
                if (context.SSL || (context.SslMode == SslMode.Require) || (context.SslMode == SslMode.Prefer))
                {
                    PGUtil.WriteInt32(stream, 8);
                    PGUtil.WriteInt32(stream, 80877103);
                    // Receive response

                    Char response = (Char)stream.ReadByte();
                    if (response == 'S')
                    {
                        stream = new SslClientStream(
                            stream,
                            context.Host,
                            true,
                            Mono.Security.Protocol.Tls.SecurityProtocolType.Default
                            );

                        ((SslClientStream)stream).ClientCertSelectionDelegate     = new CertificateSelectionCallback(context.DefaultCertificateSelectionCallback);
                        ((SslClientStream)stream).ServerCertValidationDelegate    = new CertificateValidationCallback(context.DefaultCertificateValidationCallback);
                        ((SslClientStream)stream).PrivateKeyCertSelectionDelegate = new PrivateKeySelectionCallback(context.DefaultPrivateKeySelectionCallback);
                    }
                    else if (context.SslMode == SslMode.Require)
                    {
                        throw new InvalidOperationException(resman.GetString("Exception_Ssl_RequestError"));
                    }
                }

                context.Stream = new BufferedStream(stream);
                context.Socket = socket;


                NpgsqlEventLog.LogMsg(resman, "Log_ConnectedTo", LogLevel.Normal, context.Host, context.Port);
                ChangeState(context, NpgsqlConnectedState.Instance);
            }
            catch (Exception e)
            {
                throw new NpgsqlException(e.Message, e);
            }
        }
示例#23
0
        internal static void LogStringWritten(string theString)

        {
            NpgsqlEventLog.LogMsg(resman, "Log_StringWritten", LogLevel.Debug, theString);
        }
示例#24
0
 public NpgsqlException()
 {
     NpgsqlEventLog.LogMsg("An NpgsqlException occured: <no message>", LogLevel.Normal);
 }