我正在处理由第三方编写的代码(我不想根据我的解释进行根本改变),这会打开一个tcp连接来与服务器通话json-rpc。我想将ssl添加到此,因为服务器具有用于相同通信的ssl端口。编辑:我想避免使用外部代理(如stunnel,它工作)。
目前,套接字由socket()
打开,并通过CURLOPT_OPENSOCKETFUNCTION
传递给curl,并告知libcurl在通过CURLOPT_CONNECT_ONLY
连接(不执行数据传输)后停止。
我尝试从curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
开始启用CURL中的SSL选项,但它们没有效果,即连接本身成功,函数connect()
返回true(CURL很高兴)但数据仍然存在未加密地发送。
我的理解是:套接字是在外部创建的(并传递给CURL,被告知不处理数据传输)然后直接写入,而不是通过CURL或OpenSSL,所以没有实际加密传输...尽管启用了SSL在CURL。
我还尝试通过执行sprintf(sctx->curl_url, "https%s", strstr(url, "://"));
将CURL指向https://然后我得到:
SSL: couldn't create a context: error:140A90A1:lib(20):func(169):reason(161)
我还尝试直接在打开的套接字上使用OpenSSL,就像这里https://stackoverflow.com/a/41321247/1676217一样,通过初始化OpenSSL并将其文件描述符设置为sctx->sock
,这是传递给CURL的相同套接字(即调用SSL_set_fd(ssl, sctx->sock);
)但SSL_Connect()
失败。
我无法完全更改代码以删除CURL或删除外部套接字创建,因为两者都在代码的其他部分中使用,但我可以尝试对其进行修改。
基本上,我希望保留thread_recv_line()
和thread_send_line()
的函数声明,并让它们使用SSL。
如果有人精通OpenSSL,CURL和/或套接字可以告诉我如何在这段代码中做到这一点,我将非常感激。
#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';
}
答案 0 :(得分:0)
这不是您要求的,但您应该考虑在代码和远程服务器之间放置一个单独的TCP-to-TLS代理(例如HAProxy)。
您不必弄乱TLS协议细节,也不必修改现有代码。这样的设置可能比你自己写的任何东西都更安全,更快。
答案 1 :(得分:0)
Libcurl与服务器建立SSL连接,即使CURLOPT_CONNECT_ONLY
也是如此。无法在同一TCP连接之上建立另一个SSL连接。继续使用的唯一方法是使用libcurl已经建立的相同SSL连接。
幸运的是,可以使用curl_easy_getinfo
和CURLINFO_TLS_SSL_PTR
从卷曲句柄中检索SSL连接数据。它应该返回SSL*
类型的指针。
thread_send_line
和thread_recv_line
函数不应直接使用套接字,而是在检索到的SSL指针上调用SSL_read
和SSL_write
。
有关详细信息,请参阅CURLINFO_TLS_SSL_PTR
。确保您获得SSL
指针,而不是SSL_CTX
指针。
这样的事情应该有效:
bool thread_send_line(struct ctx *sctx, char *s)
{
bool ret = false;
struct curl_tlssessioninfo* session;
curl_easy_getinfo (sctx->curl, CURLINFO_TLS_SSL_PTR, &session);
SSL* ssl = session->internals;
pthread_mutex_lock(&sctx->sock_lock);
int len = (int) strlen(s);
s[len++] = '\n';
ret = SSL_write(ssl, s, len);
pthread_mutex_unlock(&sctx->sock_lock);
return ret == len;
}