我正在处理由第三方编写的代码(我不想从根本上改变,因为我将解释),它打开一个tcp连接来与服务器对话json-rpc。我想将ssl添加到此,因为服务器有一个用于相同通信的ssl端口。编辑:我想避免使用外部代理(比如stunnel,它是有效的)。
目前,套接字由socket()
打开,并通过CURLOPT_OPENSOCKETFUNCTION
传递给curl,通过CURLOPT_CONNECT_ONLY
连接(不进行数据传输)后,libcurl被告知停止。
我尝试从curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
开始启用CURL中的SSL选项,但它们没有效果,即连接本身成功,因为函数connect()
返回true (CURL很高兴),但数据仍在未加密的情况下发送。
我的理解是:套接字是在外部创建的(并传递给CURL,CURL被告知不处理数据传输),然后直接写入,而不是通过CURL或OpenSSL,所以实际上没有任何东西加密传输……尽管在CURL中启用了SSL。
我还尝试通过执行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';
}
发布于 2018-03-14 15:57:18
这不是您所要求的,但是您应该考虑在您的代码和远程服务器之间放置一个单独的TCP-to-TLS代理(比如HAProxy)。
你既不需要修改TLS协议的细节,也不需要修改现有的代码。这样的设置可能比您自己编写的任何东西都更安全、更快。
发布于 2018-03-14 19:44:50
Libcurl与服务器建立SSL连接,即使使用CURLOPT_CONNECT_ONLY
也是如此。不能在同一TCP连接之上建立另一个SSL连接。继续的惟一方法是使用libcurl已经建立的SSL连接。
幸运的是,可以使用curl_easy_getinfo
和CURLINFO_TLS_SSL_PTR
从curl句柄检索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;
}
https://stackoverflow.com/questions/49249357
复制相似问题