我已经成功创建了一个 HTTP 服务器,其中主进程接受连接并将它们交给先前生成的进程池来处理请求。它使用带有 msghdr 和 cmsghdr 的 sendmsg() 来发送打开的套接字。
我还使用
但是我找不到如何将两者放在一起,我已经搜索了很长时间。我特别不想为每个请求创建一个新进程,而是使用预先存在的进程池。显然,主服务器进程必须以特权(root)运行才能访问私钥,但我真的不希望池进程以 root 身份运行。
显然主进程必须使用 SSL_new 创建 SSL* 并执行 SSL_accept() 操作。 但我找不到任何方法将 SSL* 与套接字一起发送到池进程。我知道这样的事情是可能的,因为我可以看到 Apache 正在这样做。
我在 freeBSD 下使用 C 或 C++ 进行编程,并且无法更改它。
我包含了现有代码的三个大纲,显示了我已经工作的所有重要内容。前两个是针对非安全服务器的主进程和池进程,第三个是针对单进程安全服务器。
**Non-secure main process**
union cmsghdr_with_int
{ struct cmsghdr hdr;
unsigned char buffer[CMSG_SPACE(sizeof(int))]; };
main()
----
listensock = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, (char *) & r, sizeof(r));
struct sockaddr_in addr; ...... fill it up ......
bind(listensock, (struct sockaddr *) & addr, sizeof(addr));
listen(listensock, 5);
socketpair(PF_LOCAL, SOCK_STREAM, 0, sockpair);
for each child
pid_t p = fork();
if (p == 0)
close(sockpair[0]);
child_run();
close(sockpair[1]);
loop:
... select(listensock + 1, & fds, NULL, NULL, & tv);
acceptsock = accept(listensock, NULL, NULL);
... set up cmsghdr_with_int msgctrlbuffer
... and msghdr msghd with iov length 1
... and char socketbuffer[...]
... and struct cmsghdr * msgctrl with SOL_SOCKET, SCM_RIGHTS, CMSG_LEN
* (int *) CMSG_DATA(msgctrl) = acceptsock;
sendmsg(sockpair[0], & msghd, 0);
r = close(acceptsock);
close(listensock);
**Non-secure pool processes**
main()
----
loop:
... set up msghdr msghd with iovlen 1, CMSG_SPACE
recvmsg(fromserver, & msghd, 0);
msgctrl = CMSG_FIRSTHDR(& msghd);
while (msgctrl != NULL)
if ... CMSG_LEN, SOL_SOCKET, SCM_RIGHTS ...
acceptsock = * (int *) CMSG_DATA(msgctrl);
read headers
send response
close acceptsock
**Secure single-process server**
main()
----
const SSL_METHOD * meth = TLS_server_method();
sslctxt = SSL_CTX_new(meth);
SSL_CTX_use_certificate_file(sslctxt, "...apache...xxxxx.crt", SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(sslctxt, "...apache...xxxxx.key", SSL_FILETYPE_PEM);
... the usual socket-bind-listen
while (... still serving ...)
int sock = accept(mainsock, ... usual
SSL * sslio = SSL_new(sslctxt);
SSL_set_fd(sslio, sock);
SSL_accept(sslio);
read headers:
SSL_read(sslio, buffer, sizeof(buffer) - 1);
send response:
SSL_write(sslio, s.c_str(), s.length());
SSL_shutdown(sslio);
SSL_free(sslio);
close(sock);
close(mainsock);
SSL_CTX_free(sslctxt);
与 TCP 套接字关联的数据结构位于操作系统内核中,并在进程之间发送套接字时共享。与 SSL 相关的数据结构位于进程的用户空间内,并且在进程之间发送套接字时不共享。鉴于这些数据结构包括指向正在运行的进程的指针,也没有受支持的方法以某种方式将它们传输到具有不同内存布局的另一个进程。
有两种方法可以处理此问题:在主进程内处理所有 SSL 或在工作进程内处理所有 SSL - 但切勿在主进程中处理某些 SSL,而在工作进程中处理某些 SSL。
在第一种情况下(主进程中的所有 SSL),主进程将像某些反向代理一样,终止 SSL 连接并将解密的流量发送给工作线程。由于这些操作都会阻碍您当前的主流程设计,因此该方法不能很好地工作。
在第二种情况下(工作进程中的所有 SSL),主进程可以在分叉工作进程之前使用证书和密钥创建 SSL 上下文,但主进程永远不会进行 SSL 握手。相反,它只会接受 TCP 套接字并将其传输给工作线程,然后工作线程将继续进行 SSL 握手 (SSL_accept) 以及所有读取和写入操作(SSL_read、SSL_write)。这样,当前 SSL 会话的状态仅包含在工作进程中。