首页 > 解决方案 > MySQL命令阶段没有来自服务器的响应

问题描述

我正在开发自己的 mysql 客户端。连接阶段运行良好。但是在随后的每个命令/数据包上,服务器都没有响应并在 30 秒后关闭连接。作为我的 mysql 服务器,我使用默认配置的 xampp。(我知道 C++ 有很好的 mysql 库,但我想创建自己的)。

这是我的代码(它只是用于测试,我知道它远非完美)

#include <string.h>
#include <iostream>

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#include <bitset>
#include <unistd.h>
#include <openssl/sha.h>


int main(int argc, char *argv[])
{
    int socketDescriptor;
    //server setup
    {
        bool preparation = true ;
        //prepare socket address information
        struct addrinfo hints = {0}, *addressInfo;
        hints.ai_flags = AI_PASSIVE;
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM;
        if (getaddrinfo(NULL, (const char *) "3306", &hints, &addressInfo))
            preparation = false;

        //initialize socket and prepare for accepting connections
        socketDescriptor = socket(addressInfo->ai_family, addressInfo->ai_socktype, addressInfo->ai_protocol);
        if (-1 == socketDescriptor)
            preparation = false;
        struct timeval timeout;
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
        if (-1 == setsockopt(socketDescriptor, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)))
            preparation = false;
        if (-1 == connect(socketDescriptor, addressInfo->ai_addr, addressInfo->ai_addrlen))
            preparation = false;
        freeaddrinfo(addressInfo);

        if (!preparation)
        {
            std::cerr << "Server initialization failed" << std::endl;
            exit(1);
        }
    }

    //get handshakev10
    //fetch header
    char header[4] = {0};
    if(4 > recv(socketDescriptor, header, 4, 0))
        exit(1);
    uint32_t payloadLength = 0;
    for(int i = 0; i < 3; i++)
        payloadLength |= (header[i] << (8 * i)) & 0b11111111;
    uint8_t sequenceID = header[3];

    std::string serverVersion = "";
    uint32_t threadID = 0;
    char authData1[8] = {0};
    uint32_t capabilityFlags = 0;
    uint8_t characterSet = 0;
    uint16_t statusFlags = 0;
    std::string pluginName = "";

    //fetch payload
    char payload[payloadLength] = {0};
    int offset = 0;
    if(payloadLength > recv(socketDescriptor, payload, payloadLength, 0))
        exit(1);
    if(10 != payload[offset])
        exit(1);
    offset++;

    //parse payload
    while('\0' != payload[offset])
    {
        serverVersion += payload[offset];
        offset++;
    }
    offset++;

    for(int i = 0; i < 4; i++)
    {
        threadID |= (payload[offset] & 0b11111111) << (8 * i);
        offset++;
    }
    for(int i = 0; i < 8; i++)
    {
        authData1[i] = payload[offset];
        offset++;
    }
    offset++;
    capabilityFlags |= payload[offset] & 0b11111111;
    offset++;
    capabilityFlags |= (payload[offset] & 0b11111111) << 8;
    offset++;
    characterSet = payload[offset];
    offset++;
    statusFlags |= payload[offset] & 0b11111111;
    offset++;
    statusFlags |= (payload[offset] & 0b11111111) << 8;
    offset++;
    capabilityFlags |= (payload[offset] & 0b11111111) << 16;
    offset++;
    capabilityFlags |= (payload[offset] & 0b11111111) << 24;
    offset++;
    uint8_t authData2Len = payload[offset];
    offset++;
    offset += 10;
    if(!authData2Len) authData2Len = 13;
    else authData2Len -= 9;
    char authData2[authData2Len] = {0};
    for(int i = 0; i < authData2Len; i++)
    {
        authData2[i] = payload[offset];
        offset++;
    }
    offset++;
    while('\0' != payload[offset])
    {
        pluginName += payload[offset];
        offset++;
    }

    //generate authData with native password method
    unsigned char *password = (unsigned char *)"test";

    if("mysql_native_password" != pluginName)
        exit(1);
    
    unsigned char hashedPW[20] = {0};
    unsigned char doubleHashedPW[20] = {0};
    unsigned char hashable[40] = {0};
    SHA1(password, 4, hashedPW);
    SHA1(hashedPW, 20, doubleHashedPW);
    for(int i = 0; i < 8; i++)
        hashable[i] = authData1[i];
    for(int i = 0; i < 12; i++)
        hashable[i + 8] = authData2[i];
    for(int i = 0; i < 20; i++)
        hashable[i + 20] = doubleHashedPW[i];
    SHA1(hashable, 40, doubleHashedPW);
    for(int i = 0; i < 20; i++)
        hashedPW[i] = hashedPW[i] ^ doubleHashedPW[i];

    //prepare handhshakeResponse41
    uint32_t maxPacketSize = 2048;
    char* username = "MyCustomUser";
    char *database = "accounts";
    char *responsePluginName = "mysql_native_password";
    capabilityFlags &= 0b11111011111011111111111111111111;

    uint32_t responsePayloadLen = 4+4+4+1+23+strlen(username)+1+21+strlen(database)+1+strlen(responsePluginName)+1+1;
    char responsePayload[responsePayloadLen] = {0};
    for(int i = 0; i < 3; i++)
        responsePayload[i] = (responsePayloadLen-4 >> (i * 8)) & 0b11111111;
    responsePayload[3] = 1;
    for(int i = 0; i < 4; i++)
        responsePayload[i+4] = (capabilityFlags >> (i * 8)) & 0b11111111;
    for(int i = 0; i < 4; i++)
        responsePayload[i+8] = (maxPacketSize >> (i * 8)) & 0b11111111;
    responsePayload[12] = characterSet;
    for(int i = 0; i < strlen(username) + 1; i++)
        responsePayload[36 + i] = username[i];
    responsePayload[49] = 20;
    for(int i = 0; i < 20; i++) 
        responsePayload[50 + i] = hashedPW[i];
    for(int i = 0; i < strlen(database) + 1; i++)
        responsePayload[70 + i] = database[i];
    for(int i = 0; i < strlen(responsePluginName) + 1; i++)
        responsePayload[79 + i] = responsePluginName[i];
    responsePayload[101] = 0;

    send(socketDescriptor, responsePayload, responsePayloadLen, 0);

    //send COM_PING packet
    char a = 1;
    while(1 == recv(socketDescriptor, &a, 1, 0))
        std::cout << std::bitset<8>(a) << " : " << a << std::endl;

    char b[5] = {0};
    b[0] = 1;
    b[4] = 0x0E;
    send(socketDescriptor, b, 5, 0);

    std::cout << "send: " << std::endl;
    for(int i = 0; i < 5; i++)
        std::cout << std::bitset<8>(b[i]) << " : " << b[i] << std::endl;

    std::cout << "recieved: " << std::endl;
    while(0 != recv(socketDescriptor, &a, 1, 0))
        std::cout << std::bitset<8>(a) << " : " << a << std::endl;
}   

标签: c++mysql

解决方案


我发现问题在于在我的 handshakeResponse41 中设置正确的功能标志。我忘记设置标志以指示不应使用压缩协议。


推荐阅读