简体   繁体   中英

How to start PostgreSQL message flow protocol using node.js net.Socket

I am able to send a Startup Message to the PostgreSQL server I have running and get a response from that server. I get ParameterStatus messages. The problem is I never get any type of Authentication message. My question is this: Why is it that the server never sends any type of Authentication message back to me?

Below I will show you my code snippet for understanding how the startup part of the protocol works, a couple lines of what it outputs for debugging (so that hopefully you won't have to even read my code), what I think is useful information from the PostgreSQL Documentation for understanding my question and another resource I have found to be useful for visualizing the protocol.

This is my code:

var net = require('net');
var BlueBird = require('bluebird');
var Buffer = require('buffer').Buffer;


var createStartupMessage = function(user_name, database_name){

    var buffer_size = 22 + user_name.length + 1 + database_name.length + 1 + 1;
    var StartUpMessage = new Buffer(buffer_size);
    var position_in_buffer = 0;

    StartUpMessage.writeUInt32BE(buffer_size, 0);
    position_in_buffer += 4;

    StartUpMessage.writeUInt32BE(196608, position_in_buffer); //version 3.0
    position_in_buffer += 4;

    position_in_buffer = addMessageSegment(StartUpMessage, "user", position_in_buffer);
    position_in_buffer = addMessageSegment(StartUpMessage, user_name, position_in_buffer);

    position_in_buffer = addMessageSegment(StartUpMessage, "database", position_in_buffer);
    position_in_buffer = addMessageSegment(StartUpMessage, database_name, position_in_buffer);

    //Add the last null terminator to the buffer
    addNullTerminatorToMessageSegment(StartUpMessage, position_in_buffer);

    console.log("The StartUpMessage looks like this in Hexcode: " + StartUpMessage.toString('hex'));
    console.log("The length of the StartupMessage in Hexcode is: " + StartUpMessage.toString('hex').length);

    return StartUpMessage;

    };


var addMessageSegment = function(StartUpMessage, message_segment, position_in_buffer){

    var bytes_in_message_segment = Buffer.byteLength(message_segment);

    StartUpMessage.write(message_segment, position_in_buffer, StartUpMessage - position_in_buffer, 'utf8');
    position_in_buffer = position_in_buffer + bytes_in_message_segment;

    position_in_buffer = addNullTerminatorToMessageSegment(StartUpMessage, position_in_buffer);

    return position_in_buffer;

};


var addNullTerminatorToMessageSegment = function(StartUpMessage, position_in_buffer){

    StartUpMessage.writeUInt8(0, position_in_buffer);
    position_in_buffer = position_in_buffer + 1;

    return position_in_buffer;

};

//Here is where everything starts. The functions above are called within this BlueBird Promise.
BlueBird.coroutine(function* () {

    var host = "127.0.0.1";
    var port = "5432";
    var idle_timeout = 10000;

    var MySocket = new net.Socket();
    MySocket.setTimeout(idle_timeout);

    var StartUpMessage = createStartupMessage("testusertwo", "testdatabasetwo");

    var data = yield new Promise(

        function resolver(resolve, reject) {

            var number_of_responses = 0;
            var number_of_responses_to_wait_for = 2;

            MySocket.on('connect', function () {
                var message = StartUpMessage.toString("utf8");
                var flushed = MySocket.write(message, "utf8");
                console.log("Message flushed to kernel: " + flushed);
            });

            MySocket.on('data', function (data) {  

                console.log("The response from the server is: " + data.toString('utf8'));
                console.log("----This Line Divides the Response Below from the Response Above----");

                if( number_of_responses !== number_of_responses_to_wait_for){
                    number_of_responses += 1;
                } else {
                    resolve(data);
                }

            });

            MySocket.on('error', function (error) {
                reject(error);
            });

            MySocket.connect(port, host);

        }

        );

    return data;

})()
    .then(function (data) {

        return data;

  })
    .catch(function (error) {

        console.error(error);

  });

These are a couple lines from what my code outputs for debugging purposes. It shows the hexcode representation of the initial utf-8 encoded message I send to the server (startup message format is shown on slide 9 via the link at the bottom). Then it shows the servers response.

After this my program hangs waiting where I am waiting to see it send an Authentication class of message. In the Startup Message I have bolded the first two 32 bit Big Endian integers and all the null terminators for convenience. Also, the ? marks at the end (in ?M2\\??ZI) are really those diamond question marks from utf-8 and this ending part changes on every run as well. I do not know why.

Some output from my code:

The StartUpMessage looks like this in Hexcode:

 **0000003300030000**75736572**00**746573747573657274776f**00**6461746162617365**00**74657374646174616261736574776f**0000**

The response from the server is:

Sapplication_nameSclient_encodingUTF8SDateStyleISO, MDYSinteger_datetimesonSntervalStylepostgresSis_superuseroffSserver_encodingUTF8Sserver_version9.5.0S&session_authorizationtestusertwoS#standard_conforming_stringsonSTimeZoneUS/EasternK ?M2\\??ZI

This is what I think is relevant Information from the Postgresql Documentation:

50.2.Message Flow.1.Start-up:

To begin a session, a frontend opens a connection to the server and sends a startup message.

The authentication cycle ends with the server either rejecting the connection attempt (ErrorResponse), or sending AuthenticationOk.

This section says some other things as well that make it sound like I should either get one of the many Authentication messages listed (such as AuthenticationCleartextPassword message) or an AuthenticationOk if a password is not needed and everything happens without an error. If there is an error, then I should get an ErrorResponse message.

50.5.Message Formats:

In this section it is indicated that if the first Byte in the server response is 'S', then the Message is classified as a ParameterStatus message.

In this section it also indicates that if the first Byte in the server response is 'R', then the Message is classified as an Authentication message.

The useful resource I found:

I think this is a very good resource for visualizing the message flow protocol. The authors name is Jan Urban ́ski. On slide 9, the startup packet is shown. The only thing I've found (with node.js anyway) is there needs to be another null terminator box before the . . . box.

https://www.pgcon.org/2014/schedule/attachments/330_postgres-for-the-wire.pdf

After looking on Wireshark, I realized that I was getting an Authentication message ('R' type Message). The problem was that I was parsing the data from the server incorrectly. I immediately converted it to a UTF8 string. The data needs to be parsed according to the message formats before any of it can be converted to UTF8. This is because the formats are not just a bunch of chars strung together. They include 32 bit big endian ints and 16 big endian ints.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM