Я пишу неблокирующий клиент Websocket и использую OpenSSL для уровня TLS. Я могу подключиться к удаленному серверу, выполнить квитирование TLS, отправить запрос на обновление, получить ответ, подтверждающий обновление, и получить реальный ответ через веб-сокет, прежде чем уровень TLS отключится с помощью SSL_ERROR_ZERO_RETURN
,
SSL_get_error(...)
возвращает: 6 // SSL_ERROR_ZERO_RETURN
ERR_error_string(ERR_get_error(), nullptr)
возвращает: error:00000000:lib(0):func(0):reason(0)
Из моего понимания, ERR_get_error()
должен появиться и вернуть первую ошибку в очередь ошибок, и SSL_get_error()
возвращает последнюю ошибку SSL_*
функция. я не понимаю почему SSL_get_error()
вернет значение ошибки, но ERR_get_error()
не. Согласно этому предыдущему Вопрос переполнения стека, SSL_get_error()
НЕ звонит ERR_get_error()
,
Следующий код вызывается повторно (так как это неблокирующий сокет):
ERR_clear_error();
int ret = SSL_read(...);
if (ret > 0) {
// read bytes from socket
} else {
int err_code = SSL_get_error(ssl_session_, ret);
if (err_code == SSL_ERROR_ZERO_RETURN || err_code == SSL_ERROR_SYSCALL || err_code == SSL_ERROR_SSL) {
sprintf("Disconnected: %d %s", err_code, ERR_error_string(ERR_get_error(), nullptr));
// Disconnect Code
}
}
У меня есть два вопроса:
Почему я не получаю значение ошибки для ERR_get_error ()?
Почему я так быстро отключаюсь после установления сеанса TLS и Websocket?
РЕДАКТИРОВАТЬ 1
Я использовал wireshark для захвата пакетов между клиентом и сервером. Я подтвердил, что квитирование TLS, обновление websocket и первоначальный ответ сервера были успешными. Я заметил, что после первоначального ответа сервера мой клиент получает Encrypted Alert 21
с сервера, который, по моему мнению, является фатальной ошибкой и объясняет, почему сеанс TLS завершается немедленно, а моя очередь ошибок SSL пуста (хотя это, вероятно, проблема на стороне клиента, я не думаю, что это результат недавнего действия), и вид объясняет SSL_ERROR_ZERO_RETURN
значение, которое я получаю после SSL_Read
,
Я не уверен, что Encrypted Alert 21
влечет за собой. Это может быть сертификат, который я использую (самоподписанный). Нужно расследовать дальше.
Хорошо, коренная причина проблемы была определена, но было прыгнуто много обручей, и время было потрачено, чтобы добраться туда. Я смог расшифровать трафик SSL, взяв главный ключ, используя метод OpenSSL, SSL_SESSION_get_master_key (), и случайное значение Hello клиента с помощью wireshark.
Соответствующий код для вывода мастер-ключа после SSL_Connect:
ERR_clear_error();
int ret = SSL_connect(ssl_ptr);
if (ret > 0) {
SSL_SESSION * ssl_session = SSL_get_session(ssl_ptr);
if(ssl_session != NULL) {
unsigned char master_key_buf[256];
size_t outlen = sizeof(master_key_buf);
size_t buf_size = SSL_SESSION_get_master_key(ssl_session, master_key_buf, outlen);
if(outlen > 0) {
char hex_encoded_master_buf[513];
// hex encode the master key
for(size_t i = 0; i < buf_size; ++i) {
sprintf(&hex_encoded_master_buf[2*i], "%02x", master_key_buf[i]);
}
hex_encoded_master_buf[(2*buf_size)] = '\0';
// log out the hex-encoded master key in master buf here
}
}
}
С использованием Журнал ключей NSS CLIENT_RANDOM Формат в Wireshark для расшифровки захваченного трафика SSL, я смог изучить вышеупомянутые Encrypted Alert 21
который в итоге оказался просто WebSocket FIN и close_notify.
Оказывается, основной причиной было то, что во время рукопожатия мое сообщение с запросом на обновление WSS действительно содержало правильные заголовки, но я фактически отправлял полезную нагрузку вместе с ним. Это был случай установки размера с помощью sizeof
буфера сообщений вместо strlen
при отправке сообщения. Сервер отлично бы проанализировал сообщение Upgrade и успешно завершил рукопожатие, но в следующий раз, когда он проверял свой сокет, он считывал мусор, когда ожидал сообщение WSS. Это вызвало внезапное закрытие соединения websocket.
В заключение, чтобы ответить на мои оригинальные два вопроса:
Соединение прерывается на стороне сервера, а очередь ошибок будет содержать ошибки на стороне клиента, которых нет, по крайней мере на уровне SSL / TLS.
Мой начальный запрос на обновление содержал действительный запрос на обновление Websocket, за которым следовали данные мусора. Сервер проанализировал запрос на обновление Websocket, подтвердил обновление и начал отправлять данные обратно. В следующий раз, когда сервер проверил свой сокет, у него все еще были значения мусора, которые были отправлены с оригинальным Запросом на обновление Websocket. Поскольку сервер не распознал его как допустимое сообщение Websocket или что-либо еще в этом отношении, он решил разорвать соединение с close_notify
,
Других решений пока нет …