这个 SOCKS 代理请求出了什么问题?
我目前正在尝试在我的 C++ 程序中实现 SOCKS 4/5 功能(即,如果需要,可以通过给定的 SOCKS 代理重定向对任意协议和主机的请求)。我纯粹为 Windows 进行开发,因此使用 Winsock 2。
不过,我的问题比简单的“这是如何工作的”稍微不那么抽象。我已经阅读了 SOCKS 4 的 RFC(我决定首先实现 SOCKS 4,因为它的请求中需要处理的字节较少),但我正在努力创建我需要的 C 字符串 send()。
目前,我定义了一个名为 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
在 C 字符串操作开始之前,我已经测试并验证了 msg_struct 的成员包含正确的数据(并且以正确的字节顺序)。
我尝试使用 memcpy() (例如 memcpy(message_ptr, &msg_struct.port, 2)) 代替赋值,但我只是不明白为什么 Wireshack 总是引用字节发送数据的长度为2(即版本和命令),但没有其他内容。 (我知道我对 C 字符串的了解 - 因此当时的代码 - 有点粗糙,但我无法解释为什么它不起作用)
任何建议将不胜感激!
I'm currently trying to implement SOCKS 4/5 functionality in my C++ program (i.e. requests to arbitrary protocols and hosts can be redirected through a given SOCKS proxy if desired). I'm developing purely for Windows so using Winsock 2.
My problem is slightly less abstract than simply "how does this work" though. I've read the RFC for SOCKS 4 (I decided to implement SOCKS 4 first since it has less bytes in its requests to contend with) but I'm struggling to create the C string I need to send().
At present, I have a struct defined called Socks4Msg which looks like this:
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) { }
};
The function which creates the actual socket and does the work is here (where p and h hold the port and host to test through the proxy -- p is a string to maintain compatibility with HttpProxy which I've already implemented). port and addr are part of the class and are an int and string respectively; they're the details of the proxy server.
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
I have tested and verified that the members of msg_struct contain the correct data (and in the correct byte order) before the C string manipulation starts.
I've tried doing it using memcpy() (e.g. memcpy(message_ptr, &msg_struct.port, 2)) in place of the assignments but I just can't understand why Wireshack always quotes the byte length of the sent data as 2 (i.e. version and command) but nothing else. (I know my knowledge of C strings - and therefore the code at that point - is a bit rough but I can't explain why it doesn't work)
Any advice would be greatly appreciated!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(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 函数更改字节顺序。
First of all
message_ptr
isuint8_t*
and*message_ptr = msg_struct.ip;
is wrong. You should castmessage_ptr
touint_32t*
and then assign data, like* ((uint32_t*)message_ptr) = msg_struct.ip;
otherwise msg_struct.ip will be converted to uint8_t and then assigned. Same problems with other fields.Check this and let me know if it is woring again :)
BTW. I think Wireshark network traffic analyzer could help you a lot in searching such kind of problems.
UPDATE
Probably a better idea is to create a structure which represents the message you want to send and cast
message_ptr
to the pointer on this structure. But do not forget to tell your compiler not to add any paddings.UPDATE 2
Network and host byte order.
Do not forget that you should change bytes order using hton, ntoh, htonl or ntohl functions.