我的c WebSocket实现在尝试连接到Deribit时不工作

hgtggwj0  于 2024-01-09  发布在  其他
关注(0)|答案(1)|浏览(111)

主要是为了好玩,我写了一个非常简单的c应用程序,它实现了一个WebSocket,试图通过SSL连接Deribit。应用程序连接Deribit(正常工作),握手(正常工作),发送消息(可能工作),然后接收消息(不工作)。
程序如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define PRINTERR() fprintf(stderr, "%s:L%i: error\n", __FILE__, __LINE__)

int main()
{
    /* connection*/
    const char *host = "www.deribit.com";
    const char *path = "/ws/api/v2";
    int port = 443;

    struct sockaddr_in server_addr;
    struct hostent *server;

    // Create socket
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        PRINTERR();
        return -1;
    }

    // Configure server address
    server = gethostbyname(host);
    if (server == NULL) {
        PRINTERR();
        return -1;
    }

    bzero((char *) &server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr,
          (char *)&server_addr.sin_addr.s_addr,
          server->h_length);
    server_addr.sin_port = htons(port);

    // Connect to server
    if (connect(sockfd,(struct sockaddr *) &server_addr,sizeof(server_addr)) < 0) {
        PRINTERR();
        return -1;
    }

    // Initialize SSL context
    SSL_CTX *ssl_context;
    SSL *ssl;
    SSL_library_init();
    SSL_load_error_strings();
    ssl_context = SSL_CTX_new(TLS_client_method());
    if (!ssl_context) {
        PRINTERR();
        return -1;
    }

    SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, NULL);

    // NOTE: assuming Ubuntu
    if (SSL_CTX_load_verify_locations(ssl_context, "/etc/ssl/certs/ca-certificates.crt", NULL) != 1) {
        PRINTERR();
        return -1;
    }

    // Connect SSL over the socket
    ssl = SSL_new(ssl_context);
    SSL_set_fd(ssl, sockfd);

    int ret = SSL_connect(ssl);
    if (ret != 1) {
        PRINTERR();
        return -1;
    } else {
        long ret = SSL_get_verify_result(ssl);
        if (ret != X509_V_OK) {
            PRINTERR();
            return -1;
        }
    }

    printf("connected to deribit (sockfd: %i)\n", sockfd);

    /* handshake */
    char request[1024];
    int request_len;
    char response[1024];
    int response_len;

    // Prepare the WebSocket handshake request
    request_len = sprintf(request, "GET %s HTTP/1.1\r\n"
                                   "Host: %s\r\n"
                                   "Upgrade: websocket\r\n"
                                   "Connection: Upgrade\r\n"
                                   "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
                                   "Sec-WebSocket-Version: 13\r\n\r\n",
                                   path, host);
    // Send the WebSocket handshake request
    if (SSL_write(ssl, request, request_len) < 0) {
        PRINTERR();
        return -1;
    }

    // Receive the WebSocket handshake response
    response_len = SSL_read(ssl, response, sizeof(response));
    if (response_len < 0) {
        PRINTERR();
        return -1;
    }

    // Check if the connection has been upgraded to a WebSocket connection
    if (strstr(response, "HTTP/1.1 101 Switching Protocols") &&
        strstr(response, "upgrade: websocket") &&
        strstr(response, "Connection: upgrade")) {
            printf("WebSocket handshake successful\n");
    } else {
        printf("WebSocket handshake failed\n");
        return -1;
    }

    /* send message */
    char req[1024];
    snprintf(
        req,
        sizeof(req),
        "{\"jsonrpc\":\"2.0\",\"id\":8212,\"method\":\"public/test\",\"params\":{}}"
    );

    int message_length = strlen(req);
    int sent_bytes = 0;
    char frame[10 + message_length];
    int frame_length = 2 + message_length;

    // Set the frame header
    frame[0] = 0x81; // Fin + Text opcode
    if (message_length <= 125)
    {
        frame[1] = (char) message_length;
        frame_length = 2 + message_length;
    }
    else if (message_length <= 65535)
    {
        frame[1] = 126;
        frame[2] = (char) (message_length >> 8);
        frame[3] = (char) (message_length & 0xFF);
        frame_length = 4 + message_length;
    }
    else
    {
        frame[1] = 127;
        frame[2] = (char) (message_length >> 56);
        frame[3] = (char) (message_length >> 48);
        frame[4] = (char) (message_length >> 40);
        frame[5] = (char) (message_length >> 32);
        frame[6] = (char) (message_length >> 24);
        frame[7] = (char) (message_length >> 16);
        frame[8] = (char) (message_length >> 8);
        frame[9] = (char) (message_length & 0xFF);
        frame_length = 10 + message_length;
    }

    // Copy the message into the frame
    memcpy(frame + 2, req, message_length);

    // Send the frame over SSL
    sent_bytes = SSL_write(ssl, frame, frame_length);

    /* receive message */
    unsigned char buf[4096];
    unsigned char mask[4];
    int bytes_received;
    int payload_length;
    unsigned char frame_header[2];
    bytes_received = SSL_read(ssl, frame_header, 2);

    if (bytes_received < 0) {
        PRINTERR();
        return -1;
    }

    if (frame_header[0] == 0x88) {
        printf("connection closed by deribit\n");
    }

    payload_length = (frame_header[1] & 0x7F);
    if (payload_length == 126) {
        bytes_received = SSL_read(ssl, &payload_length, 2);
        if (bytes_received < 0) {
            PRINTERR();
            return -1;
        }
    } else if (payload_length == 127) {
        bytes_received = SSL_read(ssl, &payload_length, 8);
        if (bytes_received < 0) {
            PRINTERR();
            return -1;
        }
    }

   if (frame_header[1] & 0x80) {
        bytes_received = SSL_read(ssl, mask, 4);
        if (bytes_received < 0) {
            PRINTERR();
            return -1;
        }
    }

    bytes_received = SSL_read(ssl, buf, payload_length);
    if (bytes_received < 0) {
        PRINTERR();
        return -1;
    }
    if (frame_header[1] & 0x80) {
        //unmask_message(buf, mask, bytes_received);
    }

    printf("frame header received: [0]: %d, [1]: %d\n", frame_header[0], frame_header[1]);
    printf("payload_length: %i\n", (frame_header[1] & 0x7F));
    printf("mask:           %i\n", (frame_header[1] & 0x80));
    printf("buffer: %s\n", buf);

    return 0;
}

字符串
如果你运行这段代码,你会看到在第一条消息发送之后(无论消息的内容是什么),应用程序从deribit接收到连接关闭消息(frame_header[1] & 0x80为true)。问题是从哪里来的?从处理SSL?还是WebSocket实现有问题?
我尝试直接使用sockfd而不使用SSL。我试图检查我的证书和Deribit接受的证书之间的潜在不一致性。我尝试向Deribit发送不同的消息。

mw3dktmi

mw3dktmi1#

晚了10个月,但我刚刚在我自己的一个项目中解决了这个问题,并认为我会与未来的谷歌员工分享。
由于此应用程序充当Deribit服务器的“客户端”,因此它需要屏蔽其有效负载。(紧跟在长度字节之后)来存储掩码密钥,以及要设置的帧的第二字节的MSB。注意,帧的第二字节中只有7位用于存储有效载荷长度。第一位(MSB)始终用于指示有效载荷是否被屏蔽,以及是否期望在帧报头中为屏蔽键增加4个字节。
然后,必须使用RFC 6455中描述的掩码密钥对有效载荷数据进行 * 掩码 *:
变换数据的八位组i(“transformed-octet-i”)是原始数据的八位组i(“original-octet-i”)与掩码密钥的索引i处的八位组(“masking-key-octet-j”)模4的XOR:
j = i MOD 4 transformed-octet-i = original-octet-iXOR masking-key-octet-j
一个可能的代码实现:

int _mask = 0x12345678;  // Should be randomly generated (securely)
char* mask_bytes = (char*)&_mask;
for (int i = 0; i < message_length; i++) {
    *((char*)frame + 6 + i) ^= mask_bytes[i % 4];  // frame + 6 is the beginning of the payload
}

字符串
我得到了与服务器正确通信的原始代码:
1.设置掩码位(frame[1] |= 0x80;
1.在帧头中为mask添加4个字节,并向其中复制一个随机的4字节值
1.如上所述,使用这个随机的4字节值来屏蔽负载(我粘贴了我使用的代码片段)
回顾一下:从客户端发送的帧必须被屏蔽,从服务器端发送的帧不能被屏蔽。OP忘记屏蔽从客户端发送的数据。

相关问题