I am trying to connect to a tor server through https. I use a non-blocking socket, with the SSL object not linked with the socket.
The reading works likes this: all the encrypted data is read from the socket, written to the rbio(BIO_write) and finally read from the rbio(SSL_read), such that it is decrypted. The writing works likes this: to encrypt the unencrypted data(destined for the socket) it is first written to wbio(SSL_write) and then read from wbio(BIO_read) to write it to the socket.
The handshake succeeds and the client can write to the server normally, but when something is read from socket and written to the rbio it fails at SSL_read. SSL_read gives an error and the weird thing is if BIO_read is called instead of SSL_read, it reads all. But I want the decrypted data instead of the encrypted data given by BIO_read.
SSL_read gives the SSL_ERROR_WANT_READ error, and I know it gives this error when a non-blocking socket is used and there is nothing to read. But there is something to read and when I do the same thing with a blocking socket, it just blocks the whole time.
I tried a lot of things to figure out what's wrong. I also used Wireshark to see if there is something wrong with the handshake, but there isn't.
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <malloc/malloc.h>
#include <unistd.h>
#include <poll.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
struct ssl_information {
int connected;
int sock;
BIO *bio_read;
BIO *bio_write;
SSL *ssl;
SSL_CTX *ctx;
};
void ssl_connection_init()
{
SSL_load_error_strings();
SSL_library_init();
OpenSSL_add_all_algorithms();
}
void ssl_disconnect(struct ssl_information *ssl)
{
if(!ssl->connected) return;
if(ssl->ctx) SSL_CTX_free(ssl->ctx);
if(ssl->ssl) {
SSL_shutdown(ssl->ssl);
SSL_free(ssl->ssl);
}
close(ssl->sock);
ssl->connected = 0;
}
int waiting_socket_reading(int sock, unsigned long milli_seconds)
{
struct pollfd file_descriptor;
file_descriptor.fd = sock;
file_descriptor.events = POLLIN;
return poll(&file_descriptor, 1, milli_seconds);
}
int tcp_connection(char *ip, int port)
{
int flags, rc, sock;
struct sockaddr_in address;
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == -1) return -1;
flags = fcntl(sock, F_GETFL);
rc = fcntl(sock, F_SETFL, flags | O_NONBLOCK);
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr(ip);
address.sin_port = htons(port);
connect(sock, (struct sockaddr *)&address, sizeof(address));
return sock;
}
int ssl_socket_read(struct ssl_information *ssl)
{
int rc;
char buffer[4096];
if(waiting_socket_reading(ssl->sock, 200)) {
rc = read(ssl->sock, buffer, sizeof(buffer));
if(rc <= 0) return rc;
rc = BIO_write(ssl->bio_read, buffer, rc);
if(rc <= 0) return rc;
} else return 0;
return rc;
}
int ssl_socket_write(struct ssl_information *ssl)
{
int rc;
char buffer[4096];
if(BIO_pending(ssl->bio_write)) {
rc = BIO_read(ssl->bio_write, buffer, sizeof(buffer));
if(rc <= 0) return rc;
rc = write(ssl->sock, buffer, rc);
if(rc <= 0) return rc;
} else return 0;
return rc;
}
struct ssl_information ssl_connection(char *ip, int port)
{
int rc;
static struct ssl_information ssl;
ssl.sock = tcp_connection(ip, port);
if(ssl.sock == -1) {
ssl.connected = 0;
return ssl;
}
ssl_connection_init();
ssl.ctx = SSL_CTX_new(TLS_method());
ssl.ssl = SSL_new(ssl.ctx);
ssl.bio_read = BIO_new(BIO_s_mem());
BIO_set_mem_eof_return(ssl.bio_read, -1);
ssl.bio_write = BIO_new(BIO_s_mem());
BIO_set_mem_eof_return(ssl.bio_write, -1);
SSL_set_connect_state(ssl.ssl);
SSL_set_bio(ssl.ssl, ssl.bio_read, ssl.bio_write);
while(1) {
rc = SSL_connect(ssl.ssl);
switch(SSL_get_error(ssl.ssl, rc)) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
rc = ssl_socket_read(&ssl);
rc = ssl_socket_write(&ssl);
continue;
default:
ssl_disconnect(&ssl);
return ssl;
case SSL_ERROR_NONE:
ssl.connected = 1;
}
break;
}
return ssl;
}
int main()
{
int rc, request_size;
char *request;
struct ssl_information ssl;
char response[4096];
ssl = ssl_connection("199.249.230.174", 443);
if(!ssl.connected) {
printf("DISCONNECTED\n");
ssl_disconnect(&ssl);
return 0;
}
printf("CONNECTED\n");
rc = SSL_write(ssl.ssl, request, request_size); //request and request_size are set of course
printf("Written %d unencrypted bytes to the bio.\n", rc);
rc = ssl_socket_write(&ssl);
printf("Written %d encrypted bytes to the socket.\n", rc);
rc = ssl_socket_read(&ssl);
printf("Read %d encrypted bytes from the socket.\n", rc);
memset(response, 0, sizeof(response));
rc = SSL_read(ssl.ssl, response, sizeof(response)); //it goes well untill this line
printf("Read %d unencrypted bytes from the bio.\n", rc); //rc is set to -1
printf("Error %d is given\n", SSL_get_error(ssl.ssl, rc)); //prints 2 i.e. SSL_ERROR_WANT_READ
ssl_disconnect(&ssl);
return 1;
}