我目前正在尝试在我的C ++程序中实现SOCKS 4/5功能(例如,如果需要,可以通过给定的SOCKS代理重定向对任意协议和主机的请求)。我正在开发纯粹的Windows,所以使用Winsock 2。
我的问题略微抽象,而不仅仅是“这是如何工作的”。我已经阅读了SOCKS 4的RFC(我决定首先实现SOCKS 4,因为它的请求中的字节数较少)但是我很难创建我需要发送()的C字符串
目前,我有一个名为 Socks4Msg 的结构,如下所示:
struct Socks4Msg {
const static uint8_t version = 0x04; //SOCKS version 4 (obviously)
const static uint8_t command = 0x01; //1 is TCP CONNECT command
const static uint8_t nullbyte = 0x00; //null byte sent at message end
uint16_t port; //16 bit/2 byte port (network order)
uint32_t ip; //32 bit/4 byte IP address (network order)
Socks4Msg(uint16_t p, uint32_t i) : port(p), ip(i) { }
};
创建实际套接字并完成工作的函数在这里(其中p和h保持端口和主机测试通过代理--p是一个字符串,以保持与HttpProxy的兼容性我已经实施了)。 port和addr是类的一部分,分别是int和string;它们是代理服务器的细节。
int Socks4Proxy::test(std::string p, std::string h) const {
uint16_t network_port = htons(str_to_numt<uint16_t>(p));
uint32_t network_ip = hostname_to_ip(h);
Socks4Msg msg_struct(network_port,network_ip);
SOCKET s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
int last_error;
if(s == INVALID_SOCKET) {
last_error = WSAGetLastError();
std::cerr << "Failed to initialise socket! Error code: " << last_error << std::endl;
return 2;
}
sockaddr_in st_addr;
st_addr.sin_family = AF_INET;
st_addr.sin_port = htons(port);
ipaddr_t ip = inet_addr(addr.c_str());
st_addr.sin_addr.S_un.S_addr = ip;
if(connect(s,(sockaddr*)&st_addr,sizeof(st_addr))!=0) {
last_error = WSAGetLastError();
std::cerr << "Socket failed to connect. Error code: " << last_error << std::endl;
return 2;
}
uint8_t message[13];
uint8_t* message_ptr;
memset(message, 0, 13);
message_ptr = message;
*message_ptr = msg_struct.version;
message_ptr++;
*message_ptr = msg_struct.command;
message_ptr++;
*message_ptr = msg_struct.port;
message_ptr += 2;
*message_ptr = msg_struct.ip;
message_ptr += 4;
*message_ptr = 'b'; message_ptr++; *message_ptr = 'o'; message_ptr++; *message_ptr = 'b'; message_ptr++;
*message_ptr = msg_struct.nullbyte;
message_ptr++;
*message_ptr = 0x00;
char smessage[13];
memcpy(smessage, message, 13);
int return_val;
while(return_val = send(s, smessage, strlen(smessage), 0)) {
if(return_val == SOCKET_ERROR) {
last_error = WSAGetLastError();
std::cerr << "Writing data failed. Error code: " << last_error << std::endl;
return 2;
}
//implement return_val < strlen(message) here
else break;
}
//remainder of function
我已经测试并验证了msg_struct的成员在C字符串操作开始之前包含正确的数据(并且以正确的字节顺序)。
我尝试使用 memcpy()(例如memcpy(message_ptr,&amp; msg_struct.port,2))代替分配,但我无法理解为什么Wireshack总是引用数据的字节长度为2(即版本和命令),但没有别的。 (我知道我对C字符串的了解 - 因此那时的代码 - 有点粗糙,但我无法解释为什么它不起作用)
非常感谢任何建议!
答案 0 :(得分:1)
首先message_ptr
是uint8_t*
而*message_ptr = msg_struct.ip;
是错误的。您应该将message_ptr
转换为uint_32t*
,然后分配数据,如* ((uint32_t*)message_ptr) = msg_struct.ip;
,否则msg_struct.ip将转换为uint8_t然后分配。与其他领域相同的问题。
检查一下,让我知道它是否再次出现问题:)
顺便说一句。我认为Wireshark网络流量分析器可以帮助您解决这类问题。
<强>更新强>
可能更好的想法是创建一个结构,表示您要发送的消息并将message_ptr
强制转换为此结构上的指针。但是别忘了告诉编译器不要添加任何填充。
更新2
网络和主机字节顺序。
不要忘记你应该使用hton,ntoh,htonl或ntohl函数更改字节顺序。