我的代理HTTPS处理来自客户端的CONNECT
HTTP请求,执行以下步骤:
connect()
检查if ((connect_res == -1) && (errno != EINPROGRESS))
; select()
检查服务器套接字是否已准备好发送或接收数据; "HTTP/1.1 200 Connection established\r\n\r\n"
; connect()
调用proxyHTTPS()
;
// if client sent a CONNECT request...
struct sockaddr_in remote_server, local_bind;
int conn_res, select_res;
memset(&remote_server, 0, sizeof(remote_server));
remote_server.sin_family = AF_INET;
remote_server.sin_addr.s_addr = rm.getServerAddr();
remote_server.sin_port = rm.getServerPort();
memset(&local_bind, 0, sizeof(local_bind));
local_bind.sin_family = AF_INET;
local_bind.sin_addr.s_addr = htonl(INADDR_ANY);
local_bind.sin_port = htons(0);
fd_set rdset, wrset;
struct timeval tv;
sockfd_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd_server < 0) {
perror("socket: ");
}
// make socket not blocking
if(!setNonBlocking(sockfd_server))
perror("fcntl");
debug_green("CONNECT set socket to non-blocking mode\n");
bind(sockfd_server, (struct sockaddr*) &local_bind, sizeof(local_bind));
conn_res = connect(sockfd_server, (struct sockaddr *)&remote_server, sizeof(struct sockaddr_in));
// The socket is nonblocking and the connection cannot be completed immediately
// check for EINPROGRESS
if ((conn_res == -1) && (errno != EINPROGRESS)) {
debug_yellow("CONNECT attempting select()\n");
do {
FD_ZERO(&rdset);
FD_SET(sockfd_server, &rdset);
wrset = rdset;
tv.tv_sec = 0;
tv.tv_usec = 0;
select_res = select(sockfd_server+1, &rdset, &wrset, NULL, &tv);
} while ((select_res == -1) && (errno == EINTR));
if ((!FD_ISSET(sockfd_server, &rdset)) && ((!FD_ISSET(sockfd_server, &wrset)))) {
debug_red("SELECT sockfd non risponde\n");
close(sockfd_server);
sockfd_server = -1;
return false;
}
/* repeat not blocking connect() to check if it gives error saying connection already happened (from my teacher's slides) */
conn_res = connect(sockfd_server, (struct sockaddr *)&remote_server, sizeof(struct sockaddr_in));
if (conn_res == -1) {
if(errno == EISCONN)
printf ("connect(): connection already existing, OK\n");
else {
printf("connect(): connection failed\n");
close(sockfd_server);
sockfd_server = -1;
return false;
}
}
printf("connection OK\n");
} else {
debug_green("connected immediately\n");
}
if (!setBlocking(sockfd_server)) {
perror("FCNTL:");
}
debug_green("CONNECT set socket back to blocking mode\n");fflush(stdout);
// #define CONNECT_200_OK HTTP/1.1 200 Connection established\r\n\r\n
std::string connect_200_to_client(CONNECT_200_OK);
int connect_200_to_client_send = send(new_sockfd_client, connect_200_to_client.c_str(), sizeof(connect_200_to_client), 0);
if (connect_200_to_client_send <= 0) {
perror("CONNECT send() 200 OK to client failed");
close(sockfd_server);
sockfd_server = -1;
return false;
}
// finally calling proxyHTTPS
hm.proxyHTTPS(new_sockfd_client, sockfd_server);
现在我应该准备好在从连接的两个方面之一获得数据时从客户端到服务器以及从服务器到客户端转发数据,但它永远不会成功。
这里是应该从连接两端转发数据的代码:
void proxyHTTPS(int new_sockfd_client, int sockfd_server) {
printf("starting proxyHTTPS\n");
fd_set fdset;
int maxp1 = sockfd_server > new_sockfd_client ? sockfd_server+1 : new_sockfd_client+1;
int r;
int read_from_client = 0;
int read_from_server = 0;
int send_to_client = 0;
int send_to_server = 0;
struct timeval timeout;
char https_buf[4096];
int https_buf_size = sizeof(https_buf);
memset(https_buf, 0, https_buf_size);
// tried 0, 5, 10, 20, 30, 60 seconds timeout,
// still got this problem
timeout.tv_sec = 10;
timeout.tv_usec = 0;
while (true) {
FD_ZERO(&fdset);
FD_SET(new_sockfd_client, &fdset);
FD_SET(sockfd_server, &fdset);
timeout.tv_sec = 10;
timeout.tv_usec = 0;
r = select(maxp1, &fdset, NULL, NULL, &timeout);
if (r < 0) {
perror("select()");
break;
}
if (r == 0) { // select timed out
debug_yellow("proxyHTTPS: select() request timeout 408\n");
break;
}
if ((!FD_ISSET(new_sockfd_client, &fdset)) && ((!FD_ISSET(sockfd_server, &fdset)))) {
debug_red("proxyHTTPS: SELECT sockfds not responding\n");
break;
}
else if (FD_ISSET(new_sockfd_client, &fdset)) {
debug_green("proxyHTTPS: reading from client and sending to server\n");
read_from_client = recv(new_sockfd_client, https_buf, https_buf_size, 0);
if (read_from_client < 0) {
debug_red("proxyHTTPS recv() from client " << strerror(errno) << '\n');
break;
}
else if (read_from_client == 0) {
debug_yellow("proxyHTTPS client closed conn\n");
break;
}
else { // read_from_client > 0
send_to_server = send(sockfd_server, https_buf, read_from_client, 0);
if (send_to_server > 0) {
debug_green("proxyHTTPS: sent to server " << std::to_string(send_to_server) << " bytes \n");
}
else if (send_to_server == 0) {
debug_yellow("proxyHTTPS: sent to server 0 bytes...\n");
break;
}
else {
debug_red("proxyHTTPS send() to server " << strerror(errno) << '\n');
break;
}
}
}
else if (FD_ISSET(sockfd_server, &fdset)) {
debug_green("proxyHTTPS: reading from server and sending to client\n");
read_from_server = recv(sockfd_server, https_buf, https_buf_size, 0);
if (read_from_server < 0) {
debug_red("proxyHTTPS recv() from server " << strerror(errno) << '\n');
break;
}
else if (read_from_server == 0) {
debug_yellow("proxyHTTPS server closed conn\n");
break;
}
else { // read_from_server > 0
send_to_client = send(new_sockfd_client, https_buf, read_from_server, 0);
if (send_to_client > 0) {
debug_green("proxyHTTPS: sent to client " << std::to_string(send_to_client) << " bytes\n");
}
else if (send_to_client == 0) {
debug_yellow("proxyHTTPS: sent 0 bytes to client, errno: " << errno << '\n');
break;
}
else {
debug_red("proxyHTTPS send() to client " << strerror(errno) << '\n');
break;
}
}
}
}
debug_yellow("proxyHTTPS: quitting\n");
}
这是代理从客户端获取的CONNECT
请求:
CONNECT www.youtube.com:443 HTTP/1.1
Host: www.youtube.com:443
Proxy-Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36
/r/n
如果客户端发送CONNECT www.netflix.com:443 HTTP/1.1
,我的代理将失败并在终端上打印:
setting non block socket
connected immediately
setting block socket
54.228.227.144:443 OK
CONNECT: send to client HTTP/1.1 200 Connection established
starting proxyHTTPS
proxyHTTPS: reading from server and sending to client
proxyHTTPS read from server: Connection refused
quitting proxyHTTPS
如果客户端发送CONNECT www.youtube.com:443 HTTP/1.1
,我的代理会再次失败并显示:
setting non block socket
connected immediately
setting block socket
216.58.201.238:443 OK
CONNECT: send to client HTTP/1.1 200 Connection established
starting proxyHTTPS
select(): Operation now in progress
proxyHTTPS: select() request timeout 408
quitting proxyHTTPS
我想我已经完成了为了在客户端和服务器之间建立隧道所做的所有步骤,connect()
总是正常的,所以错误必须在我的proxyHTTPS
中。我不知道了!
编辑1 :当perror()
返回0时删除select()
,因为errno
未引发;改进了select()
循环中的错误检查,将返回值>
或==
区分开来。
尽管如此,仍然遇到同样的问题:在某种程度上,无法从客户端或服务器读取任何数据。
编辑2 :更新了有关连接远程服务器的代码。
我开始认为,由于select()
循环似乎正确,问题可能是由其他原因造成的......
上面使用的调试函数定义如下:
#define DEFAULTCOLOR "\033[0m"
#define RED "\033[22;31m"
#define YELLOW "\033[1;33m"
#define GREEN "\033[0;0;32m"
#define debug_red(...) std::cout << RED << __VA_ARGS__ << DEFAULTCOLOR; fflush(stdout);
#define debug_yellow(...) std::cout << YELLOW << __VA_ARGS__ << DEFAULTCOLOR; fflush(stdout);
#define debug_green(...) std::cout << GREEN << __VA_ARGS__ << DEFAULTCOLOR; fflush(stdout);