linux MBEDTLS_ERR_NET_INVALID_CONTEXT mbedtls中的错误,当没有数据可供TLS读取时,Epoll会发出EPOLLIN事件通知

vxf3dgd4  于 2024-01-06  发布在  Linux
关注(0)|答案(2)|浏览(284)

我用mbedtls做了一个epoll HTTPS服务器。它监听两个端口:一个用于HTTP,另一个用于HTTPS。HTTP端口工作得很好。但是对于HTTPS,当没有数据可供mbedtls_ssl_read()读取时,我似乎会收到EPOLLIN通知。
可能数据是可用的,但问题出在SSL上下文上,因为mbedtls_ssl_read()返回MBEDTLS_ERR_NET_INVALID_CONTEXT。但我看不出问题出在哪里。我已经正确地初始化了上下文,甚至与它进行了TLS握手。
下面是代码的重要部分:请注意,我知道这段代码在很多情况下会失败。我只是做了一个例子来重现错误。这段代码只是描述了当我接受我的单一客户端时发生的一切,然后,在接收EPOLLIN后,尝试从它读取数据。

  1. #define REQUEST_SIZE 8192
  2. struct HTTP_server
  3. {
  4. mbedtls_net_context https_context;
  5. mbedtls_entropy_context entropy;
  6. mbedtls_ctr_drbg_context ctr_drbg;
  7. mbedtls_ssl_config conf;
  8. mbedtls_x509_crt srvcert;
  9. mbedtls_pk_context pkey;
  10. const char *pers;
  11. };
  12. typedef struct client_data
  13. {
  14. char *request;
  15. proto_t protocol;
  16. union
  17. {
  18. int client_fd;
  19. mbedtls_net_context https_context;
  20. };
  21. mbedtls_ssl_context ssl;
  22. } client_data_t;
  23. struct HTTP_server server;
  24. server->pers = "ssl_server";
  25. mbedtls_net_init(&server->https_context);
  26. mbedtls_ssl_config_init(&server->conf);
  27. mbedtls_x509_crt_init(&server->srvcert);
  28. mbedtls_pk_init(&server->pkey);
  29. mbedtls_entropy_init(&server->entropy);
  30. mbedtls_ctr_drbg_init(&server->ctr_drbg);
  31. if (mbedtls_ctr_drbg_seed(&server->ctr_drbg, mbedtls_entropy_func, &server->entropy,
  32. (const unsigned char *) server->pers, strlen(server->pers)) != 0) {
  33. return 0;
  34. }
  35. if (mbedtls_x509_crt_parse_file(&server->srvcert, "server.crt") != 0)
  36. {
  37. return 0;
  38. }
  39. if (mbedtls_pk_parse_keyfile(&server->pkey, "server.key", "pass_phrase", mbedtls_ctr_drbg_random, &server->ctr_drbg) != 0)
  40. {
  41. return 0;
  42. }
  43. if (mbedtls_net_bind(&server->https_context, NULL, "8000", MBEDTLS_NET_PROTO_TCP) != 0)
  44. {
  45. return 0;
  46. }
  47. mbedtls_net_set_nonblock(&server->https_context);
  48. if (mbedtls_ssl_config_defaults(&server->conf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM,
  49. MBEDTLS_SSL_PRESET_DEFAULT) != 0)
  50. {
  51. return 0;
  52. }
  53. mbedtls_ssl_conf_rng(&server->conf, mbedtls_ctr_drbg_random, &server->ctr_drbg);
  54. if (mbedtls_ssl_conf_own_cert(&server->conf, &server->srvcert, &server->pkey) != 0)
  55. {
  56. return 0;
  57. }
  58. int epoll_fd = epoll_create1(0);
  59. if (epoll_fd == -1)
  60. {
  61. return;
  62. }
  63. event.data.fd = server->https_context.fd;
  64. event.events = EPOLLIN;
  65. if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server->https_context.fd, &event) == -1)
  66. {
  67. goto epoll_close;
  68. }
  69. while (1)
  70. {
  71. int nevents = epoll_wait(epoll_fd, events, 128, -1);
  72. if (nevents == -1)
  73. {
  74. goto epoll_close;
  75. }
  76. else if (errno == EINTR)
  77. {
  78. goto epoll_close;
  79. }
  80. for (int i = 0; i < nevents; ++i)
  81. {
  82. if ((events[i].events & (EPOLLERR | EPOLLHUP)))
  83. {
  84. continue;
  85. }
  86. else if (events[i].data.fd == server->https_context.fd)
  87. {
  88. client_data_t client_status = malloc(sizeof(client_data_t));
  89. mbedtls_net_context client_socket;
  90. mbedtls_net_accept(&server->https_context, &client_socket, NULL, 0, NULL);
  91. client_status->https_context = client_socket;
  92. mbedtls_ssl_init(&client_status->ssl);
  93. if (mbedtls_ssl_setup(&client_status->ssl, &server->conf) != 0)
  94. {
  95. return 0;
  96. }
  97. mbedtls_ssl_set_bio(&client_status->ssl, &client_socket, mbedtls_net_send, mbedtls_net_recv, NULL);
  98. int ret;
  99. while (!program_interrupted && ((ret = mbedtls_ssl_handshake(&client_status->ssl)) != 0))
  100. {
  101. if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
  102. {
  103. mbedtls_printf(" failed\n ! mbedtls_ssl_handshake returned %d\n\n", ret);
  104. return 0;
  105. }
  106. }
  107. client_status->request = malloc(REQUEST_SIZE + 1);
  108. event.data.fd = client_socket.fd;
  109. event.data.ptr = client_status;
  110. event.events = EPOLLIN | EPOLLET;
  111. if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_socket.fd, &event) == -1)
  112. {
  113. continue;
  114. }
  115. }
  116. else
  117. {
  118. client_data_t *client_status = events[i].data.ptr;
  119. // Every function call until this was successful, I checked, here nbytes == -69 or nbytes == -76
  120. // mbedtls_ssl_get_bytes_avail(&client_status->ssl) gives 0
  121. ssize_t nbytes = mbedtls_ssl_read(&client_status->ssl, (unsigned char *)(client_status->request), REQUEST_SIZE);
  122. // other stuff
  123. }
  124. }
  125. }
  126. epoll_close:
  127. close(epoll_fd);

字符串
mbedtls调试错误消息如下所示:

  1. ssl_msg.c:5662: => read
  2. ssl_msg.c:4110: => read record
  3. ssl_msg.c:2155: => fetch input
  4. ssl_msg.c:2295: in_left: 0, nb_want: 5
  5. ssl_msg.c:2315: in_left: 0, nb_want: 5
  6. ssl_msg.c:2318: ssl->f_recv(_timeout)() returned -69 (-0x0045)
  7. ssl_msg.c:4782: mbedtls_ssl_fetch_input() returned -69 (-0x0045)
  8. ssl_msg.c:4141: ssl_get_next_record() returned -69 (-0x0045)
  9. ssl_msg.c:5722: mbedtls_ssl_read_record() returned -69 (-0x0045)


编辑:注意,我在从浏览器发送请求后得到EPOLLIN,并且我在边缘触发模式下使用epoll(),所以我使用mbedtls_ssl_read()直到套接字每次都会阻塞,也就是说,直到我得到MBEDTLS_ERR_SSL_WANT_READ
我是这样读这封信的:

  1. while (1)
  2. {
  3. nbytes = mbedtls_ssl_read(&client_status->ssl, (unsigned char *)(request + client_status->request_bytes_read), server_properties->max_request - client_status->request_bytes_read);
  4. if (nbytes < 0)
  5. {
  6. if (errno == EAGAIN || errno == EWOULDBLOCK || nbytes == MBEDTLS_ERR_SSL_WANT_READ)
  7. {
  8. // no application data to read, wait for another epoll_wait() notification
  9. break;
  10. }
  11. else
  12. {
  13. // print error
  14. break;
  15. }
  16. // I also tried to replace the code above with this, but mbedtls_ssl_read() returns -69, which doesn't compare equal to MBEDTLS_ERR_SSL_WANT_READ and so there must be some sort of error
  17. // In case there is control data pending, but no application data, so that epoll_wait() wouldn't hang
  18. /* if (nbytes == MBEDTLS_ERR_SSL_WANT_READ)
  19. {
  20. continue;
  21. }
  22. else if (errno == EAGAIN || errno == EWOULDBLOCK)
  23. {
  24. *blocked = 1;
  25. break;
  26. }
  27. else
  28. {
  29. ERROR_LOG("read() failed");
  30. break;
  31. } */
  32. }
  33. else if (nbytes == 0)
  34. {
  35. break;
  36. }
  37. else
  38. {
  39. client_status->request_bytes_read += nbytes;
  40. client_status->request[client_status->request_bytes_read] = '\0';
  41. char *CRLF = strstr(client_status->request + client_status->request_bytes_read - nbytes, "\r\n\r\n");
  42. if (CRLF)
  43. {
  44. // parse the headers, get Content-Length and move to reading body
  45. // body is read in a similar manner
  46. break;
  47. }
  48. }
  49. }

omhiaaxx

omhiaaxx1#

TLS在TCP之上添加了一个层,它有自己的记录帧,也有没有应用程序有效负载的记录(握手,会话票证)。mbedtls_ssl_read将只在接收到包含应用程序数据的完整记录后返回数据,因为只有这样数据才能被解密。
EPOLL仅在TCP套接字级别工作。这意味着只要套接字上的内容可以读取,它就会发出活动信号,无论这是部分TLS记录还是完整的TLS记录,无论这些是应用程序数据还是只是控制信息。
这意味着你不能确定你可以mbedtls_ssl_read每当EPOLL返回套接字可读.甚至,你可能能够mbedtls_ssl_read即使EPOLL没有说套接字可读,因为最后一个套接字读取了多个TLS记录与应用程序数据.不确定Mbed-TLS,但是使用OpenSSL时,SSL_read只包含来自单个TLS记录的数据,需要使用SSL_pending检查是否有更多数据可用。Mbed-TLS具有类似的功能mbedtls_ssl_check_pending

ergxz8rk

ergxz8rk2#

问题是mbedtls_ssl_set_bio(&client_status->ssl, &client_socket, mbedtls_net_send, mbedtls_net_recv, NULL);中的client_socket超出了作用域,在此之后,read/write函数中的后续BIO回调无法使用该共享上下文。
应该是mbedtls_ssl_set_bio(&client_status->ssl, &client_status->https_context, mbedtls_net_send, mbedtls_net_recv, NULL);

相关问题