I'm dealing with code written by a 3rd party (which I don't want to radically change as I'll explain) which opens a tcp connection to talk json-rpc with a server. I want to add ssl to this as the server has an ssl port for the same comms. EDIT: I want to avoid using external proxies (like stunnel, which works).
Currently, the socket is opened by socket()
and passed to curl via CURLOPT_OPENSOCKETFUNCTION
and libcurl is told to stop after connecting (perform no data transfer) via CURLOPT_CONNECT_ONLY
.
I tried enabling the SSL options in CURL starting with curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
but they had no effect, i.e. the connection itself succeeded in that the function connect()
returned true (CURL was happy) but the data is still being sent unencrypted.
My understanding is: the socket is created externally (and passed to CURL which is told to not handle data transfer) and then written to directly, not via CURL or OpenSSL, so nothing actually encrypts the transfer ... despite enabling SSL in CURL.
I also tried to point CURL to https:// by doing sprintf(sctx->curl_url, "https%s", strstr(url, "://"));
and then I get:
SSL: couldn't create a context: error:140A90A1:lib(20):func(169):reason(161)
I've also tried using OpenSSL directly on the opened socket, like here https://stackoverflow.com/a/41321247/1676217, by initializing OpenSSL and setting its file descriptor to sctx->sock
which is the same socket passed to CURL (i.e. calling SSL_set_fd(ssl, sctx->sock);
) but SSL_Connect()
fails.
I can't change the code entirely to remove CURL or to remove the external socket creation as both are used in other parts of the code, though I can attempt to adapt it.
Basically, I'd like to keep the function declarations for thread_recv_line()
and thread_send_line()
and make them use SSL.
If anyone proficient with OpenSSL, CURL and/or sockets could show me how to do it within this code snippet, I'd be very grateful.
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#define socket_blocks() (errno == EAGAIN || errno == EWOULDBLOCK)
#define RBUFSIZE 2048
#define RECVSIZE (RBUFSIZE - 4)
struct ctx {
char *url;
CURL *curl;
char *curl_url;
char curl_err_str[CURL_ERROR_SIZE];
curl_socket_t sock;
size_t sockbuf_size;
char *sockbuf;
pthread_mutex_t sock_lock;
};
static curl_socket_t opensocket_grab_cb(void *clientp, curlsocktype purpose,
struct curl_sockaddr *addr)
{
curl_socket_t *sock = (curl_socket_t*) clientp;
*sock = socket(addr->family, addr->socktype, addr->protocol);
return *sock;
}
bool connect(struct ctx *sctx, const char *url)
{
CURL *curl;
int rc;
pthread_mutex_lock(&sctx->sock_lock);
if (sctx->curl)
curl_easy_cleanup(sctx->curl);
sctx->curl = curl_easy_init();
if (!sctx->curl) {
pthread_mutex_unlock(&sctx->sock_lock);
return false;
}
curl = sctx->curl;
if (!sctx->sockbuf) {
sctx->sockbuf = (char*) calloc(RBUFSIZE, 1);
sctx->sockbuf_size = RBUFSIZE;
}
sctx->sockbuf[0] = '\0';
pthread_mutex_unlock(&sctx->sock_lock);
if (url != sctx->url) {
free(sctx->url);
sctx->url = strdup(url);
}
free(sctx->curl_url);
sctx->curl_url = (char*) malloc(strlen(url));
sprintf(sctx->curl_url, "http%s", strstr(url, "://"));
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
curl_easy_setopt(curl, CURLOPT_URL, sctx->curl_url);
curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, sctx->curl_err_str);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1);
curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1);
curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_keepalive_cb);
curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket_grab_cb);
curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, &sctx->sock);
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1);
rc = curl_easy_perform(curl);
if (rc) {
curl_easy_cleanup(curl);
sctx->curl = NULL;
return false;
}
return true;
}
static bool send_line(curl_socket_t sock, char *s)
{
size_t sent = 0;
int len = (int) strlen(s);
s[len++] = '\n';
while (len > 0) {
struct timeval timeout = {0, 0};
int n;
fd_set wd;
FD_ZERO(&wd);
FD_SET(sock, &wd);
if (select((int) (sock + 1), NULL, &wd, NULL, &timeout) < 1)
return false;
n = send(sock, s + sent, len, 0);
if (n < 0) {
if (!socket_blocks())
return false;
n = 0;
}
sent += n;
len -= n;
}
return true;
}
bool thread_send_line(struct ctx *sctx, char *s)
{
bool ret = false;
pthread_mutex_lock(&sctx->sock_lock);
ret = send_line(sctx->sock, s);
pthread_mutex_unlock(&sctx->sock_lock);
return ret;
}
static bool socket_full(curl_socket_t sock, int timeout)
{
struct timeval tv;
fd_set rd;
FD_ZERO(&rd);
FD_SET(sock, &rd);
tv.tv_sec = timeout;
tv.tv_usec = 0;
if (select((int)(sock + 1), &rd, NULL, NULL, &tv) > 0)
return true;
return false;
}
static void buffer_append(struct ctx *sctx, const char *s)
{
size_t old, n;
old = strlen(sctx->sockbuf);
n = old + strlen(s) + 1;
if (n >= sctx->sockbuf_size) {
sctx->sockbuf_size = n + (RBUFSIZE - (n % RBUFSIZE));
sctx->sockbuf = (char*) realloc(sctx->sockbuf, sctx->sockbuf_size);
}
strcpy(sctx->sockbuf + old, s);
}
char *thread_recv_line(struct ctx *sctx)
{
ssize_t len, buflen;
char *tok, *sret = NULL;
if (!strstr(sctx->sockbuf, "\n")) {
bool ret = true;
time_t rstart;
time(&rstart);
if (!socket_full(sctx->sock, 60))
return sret;
do {
char s[RBUFSIZE];
ssize_t n;
memset(s, 0, RBUFSIZE);
n = recv(sctx->sock, s, RECVSIZE, 0);
if (!n) {
ret = false;
break;
}
if (n < 0) {
if (!socket_blocks() || !socket_full(sctx->sock, 1)) {
ret = false;
break;
}
} else
buffer_append(sctx, s);
} while (time(NULL) - rstart < 60 && !strstr(sctx->sockbuf, "\n"));
if (!ret)
return sret;
}
buflen = (ssize_t) strlen(sctx->sockbuf);
tok = strtok(sctx->sockbuf, "\n");
if (!tok)
return sret;
sret = strdup(tok);
len = (ssize_t) strlen(sret);
if (buflen > len + 1)
memmove(sctx->sockbuf, sctx->sockbuf + len + 1, buflen - len + 1);
else
sctx->sockbuf[0] = '\0';
}