My Windows Socket Program Cannot Open Some Urls That Browsers Such As FireFox, IE etc Can - winapi

I have made the following win32 socket program to browse web pages. I am using wingw to avoid dependency on any runtime. To get ipaddresses I ping urls such as www.google.com, www.yahoo.com through command prompt and use those ip addreses in my program. Port is ofcourse 80.
I am able to get default pages of google, yahoo etc by using "GET /\r\n". I am also able to get non-default pages, even those inside directories, such as http://yasini.com/newsite/index.aspx by using "GET /newsite/index.aspx". The output of the program is in the form of html received from webserver, saved on hard disk. This file is later opened in firefox to see how did the communication go.
I have made a test webpage, http://a.domaindlx.com/trysite/hello.asp, which I can open in firefox. Then I ping the domain, a.domaindlx.com and get this ipaddress, 66.36.238.30. I try to access the said page by using "GET /trysite/hello.asp" but get this in response, "No web site is configured at this address. No web site is configured at this address."
I know that the said response is sent by the webserver, so I was able to connect to the webserver. The problem is that the webserver is not recognizing the url I am trying to access. I have used different webpages, both htm and asp and none is accessible.
When trying to open website using ipaddress directly in browser, I get the same error, "No website is configured...".
The basic puzzle is, why are these pages accessible through a browser such as firefox, but not through my code, when my code is essentially a browser, mean open connection with webserver at port 80.
#include windows.h
#include stdio.h
WSADATA ws;
int d;
char aa[1000];
struct sockaddr_in a;
SOCKET s;
int li;
void abc(char *p)
{
FILE *fp = fopen("c:\\data.htm", "a+");
fprintf(fp, "%s\n", p);
fclose(fp);
}
_stdcall WinMain (HINSTANCE i, HINSTANCE j, char * k, int l)
{
d = WSAStartup(0x101, &ws);
sprintf(aa, "WSASTARTUP = %d", d);
abc(aa);
s = socket(AF_INET, SOCK_STREAM, 0);
sprintf(aa, "SOCKET = %d", s);
abc(aa);
a.sin_family = AF_INET;
a.sin_port = htons(80);
//a.sin_addr.s_addr = inet_addr("74.125.236.145");
a.sin_addr.s_addr = inet_addr("66.36.238.30"); //a.domaindlx.com
//a.sin_addr.s_addr = inet_addr("206.225.85.18"); //www.domaindlx.com
//a.sin_addr.s_addr = inet_addr("87.248.122.122"); //www.yahoo.com
//a.sin_addr.s_addr = inet_addr("72.167.153.9"); //www.yasini.com
d = connect(s, (struct sockaddr *) &a, sizeof(a));
strcpy(aa, "GET /trysite/hello.asp\r\n");
strcat(aa, "HTTP 1.0 \r\n\r\n");
send(s, aa, sizeof(aa), 0);
li = 1;
while(li != 0)
{
li = recv(s, aa, 1000, 0);
abc(aa);
}
}
Note: Please enclose the header file names in the include line in angle brackets for the code to work. I had to remove that to property format the html.

The troublesome URL is running on a subdomain. The successful URLs are not. Many webservers host multiple accounts on the same physical IP(s), so they need to know which particular domain/subdomain is being requested in order to access the correct account. You need to include a Host header in your request.
Also note that when you call send() to send the request, you are sending the entire 1000 bytes of the aa buffer, which is wrong. You need to send only what you actualy filled in.
Lastly, you are not really managing the socket very well in general. You need better error handling.
Try this:
#include <windows.h>
#include <stdio.h>
void abc(char *p, int l = -1)
{
FILE *fp = fopen("c:\\data.htm", "a+");
if (fp)
{
if (l == -1) l = strlen(p);
fwrite(p, 1, l, fp);
fclose(fp);
}
}
int WINAPI WinMain (HINSTANCE i, HINSTANCE j, char * k, int l)
{
char aa[1000];
WSADATA ws;
int d = WSAStartup(0x101, &ws);
sprintf(aa, "WSASTARTUP = %d\n", d);
abc(aa);
if (d == 0)
{
SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
sprintf(aa, "SOCKET = %d\n", s);
abc(aa);
if (s != INVALID_SOCKET)
{
char *host = "a.domaindlx.com";
char *file = "/trysite/hello.asp";
struct sockaddr_in a;
memset(&a, 0, sizeof(a));
a.sin_family = AF_INET;
a.sin_port = htons(80);
struct hostent *h = gethostbyname(host);
if (!h)
{
sprintf(aa, "gethostbyname(\"%s\") FAILED\n", host);
abc(aa);
}
else
{
sprintf(aa, "gethostbyname(\"%s\") TYPE = %d\n", host, h->h_addrtype);
abc(aa);
if (h->h_addrtype == AF_INET)
{
a.sin_addr = * (struct in_addr*) h->h_addr;
sprintf(aa, "gethostbyname(\"%s\") IP = %s\n", host, inet_ntoa(a.sin_addr));
abc(aa);
d = connect(s, (struct sockaddr *) &a, sizeof(a));
sprintf(aa, "CONNECT = %d\n", d);
abc(aa);
if (d == 0)
{
sprintf(aa,
"GET %s HTTP/1.0\r\n"
"Host: %s\r\n"
"Connection: close\r\n"
"\r\n",
file, host);
char *p = aa;
int t = strlen(aa);
int li;
do
{
li = send(s, p, t, 0);
if (li < 1)
break;
p += li;
t -= li;
}
while (t > 0);
if (t != 0)
{
abc("SEND FAILED\n");
}
else
{
abc("SEND OK\n");
do
{
li = recv(s, aa, sizeof(aa), 0);
if (li < 1)
break;
abc(aa, li);
}
while (true);
}
}
}
}
closesocket(s);
}
WSACleanup();
}
return 0;
}
I strongly suggest you get a packet sniffer, such as Wireshark. Then you can see EXACTLY what webbrowsers (or any other socket app) is actually sending and receiving. Then you can match that in your code as needed.

There are two problems with your code. The first one is that there should be a space not \r\n before HTTP 1.0. Without this your are sending HTTP 0.9.
The second problem is that some IP addresses are used to host multiple sites and require sending a Host header.
The site that tells you "No web site is configured at this address" may work better if you add the Host: header. Your request to that site should look like this:
"GET /trysite/hello.asp HTTP 1.0\r\nHost: a.domaindlx.com\r\n\r\n"

You're not following the protocol correctly. You want GET /trysite/hello.asp HTTP/1.0\r\n\r\n See here for the full spec.

Related

sendmsg() with Unix domain socket blocks forever on Mac with specific sizes

I'm sending messages on Unix domain sockets on Mac with sendmsg(). However, it sometimes hangs forever.
I've called getsockopt(socket, SOL_SOCKET, SO_SNDBUF, ...) to get the size of the send buffer. (The default is 2048).
If I try sending a message larger than 2048 bytes, I correctly get
EMSGSIZE and know I need to send a smaller message.
If I try sending a message less than 2036 bytes, the message is sent fine.
If I try sending a message between 2036 and 2048 bytes, the
sendmsg call...hangs forever.
What's going on here? What's the correct way to deal with this? Is it safe to just subtract 13 bytes from the maximum size I try sending, or could I run into issues if e.g. there's other messages in the buffer already?
Here's the (simplified) code I'm using:
// Get the maximum message size
int MaxMessageSize(int socket) {
int sndbuf = 0;
socklen_t optlen = sizeof(sndbuf);
if (getsockopt(socket, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0) {
return -1;
}
return sndbuf;
}
// Send a message
static int send_chunk(int socket, const char *data, size_t size) {
struct msghdr msg = {0};
char buf[CMSG_SPACE(0)];
memset(buf, '\0', sizeof(buf));
int iov_len = size;
if (iov_len > 512) {
int stat = send_size(socket, iov_len);
if (stat < 0) return stat;
}
char iov_buf[iov_len];
memcpy(iov_buf, data, size);
struct iovec io = {.iov_base = (void *)iov_buf, .iov_len = iov_len};
msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(0);
msg.msg_controllen = CMSG_SPACE(0);
std::cerr << "Attempting to send message of size " << iov_len << std::endl;
ssize_t ret = sendmsg(socket, &msg, 0);
std::cerr << "sendmsg returned: " << ret << std::endl;
return ret;
}

Can't get curl response after updating port number and checksum in TC with ebpf

I am trying to reroute packets with destination port 80 to 5432 using ebpf. I am locally running nginx on port 80 and postgresql on port 5432. What I would like is whenever I execute curl localhost:80, my program will change the port number to 5432 and I will get the results of executing curl localhost:5432. Both nginx and postgresql are installed and deployed locally without using any containers. All I am changing is the port number. No ip address is changed.
I have written an ebpf program to be attached to the TC hook. Since the three way handshake is established in the lo interface on my device, This program is attached on iface=lo and attempts to rewrite any packet with destination port=80. I used two methods to recalculate the checksum after the port rewrite, but both methods produce the same error:
static __always_inline __u16 csum_fold_helper(__u32 csum) {
__u32 sum;
sum = (csum>>16) + (csum & 0xffff);
sum += (sum>>16);
return ~sum;
}
SEC("tc_cls")
int tc_egress(struct __sk_buff *skb) {
void *data_end = (void *)(long)skb->data_end;
void *data = (void *)(long)skb->data;
struct ethhdr *eth;
struct iphdr *iph;
struct tcphdr *tcph;
eth = data;
if ((void *)eth + sizeof(*eth) > data_end) {
return TC_ACT_OK;
}
if (eth->h_proto != __bpf_htons(0x0800)) {
return TC_ACT_OK;
}
iph = data + sizeof(*eth);
if ((void *)iph + sizeof(*iph) > data_end) {
return TC_ACT_OK;
}
if (iph->protocol != IPPROTO_TCP) {
return TC_ACT_OK;
}
tcph = data + sizeof(*eth) + sizeof(*iph);
if ((void *)tcph + sizeof(*tcph) > data_end) {
return TC_ACT_OK;
}
if (tcph->dest == __bpf_htons(80)) {
// update checksum method #1
// __u64 from = tcph->dest;
// __u16 new_port = __bpf_htons(5432);
// tcph->dest = new_port;
// __u64 res = bpf_skb_store_bytes(skb, ETH_HLEN + sizeof(struct iphdr) + 2, &(new_port), 2, BPF_F_RECOMPUTE_CSUM);
// update checksum method #2
__u16 new_port = __bpf_htons(5432);
__u16 old_csum = tcph->check;
__u32 sum = bpf_csum_diff(&tcph->dest, 2, &new_port, 2, 0);
__u16 csum = csum_fold_helper(sum);
tcph->dest = new_port;
bpf_l4_csum_replace(skb, ETH_HLEN + sizeof(struct iphdr) + 16, old_csum, csum, BPF_F_PSEUDO_HDR);
}
return TC_ACT_OK;
}
I have seen some posts regarding this issue (most of them involve changing ip addresses) and I have tried to follow the advice, but none of it worked on my program. Tcpdump is used to verify activity on port 5432, and I can see that packets are arriving. All packets with the S flag show incorrect checksum, and R show correct checksum. However, when I detach the ebpf program and run a simple curl on localhost:5432, all packets seem to show an incorrect checksum even if I do get a curl response. Hence, I would like some help checking whether the checksum is correctly updated and whether I am missing other steps to complete the program.
Any advice is greatly appreciated!

ZeroMQ's EPGM not working in weather PUB-SUB demo

I have compiled libzmq with openpgm with no changes under windows. Code here is taken from ZeroMQ Guide ("weather publisher" server/client). But if i change "tcp" to "epgm" it doesn't work any more (data is not received, but connection is established).
void test_serv()
{
// Prepare our context and publisher
void *context = zmq_ctx_new();
void *publisher = zmq_socket(context, ZMQ_PUB);
int rc = zmq_bind(publisher, "epgm://127.0.0.1:5556");
assert(rc == 0);
// Initialize random number generator
srandom((unsigned)time(NULL));
while (!stop_server)
{
// Get values that will fool the boss
int zipcode, temperature, relhumidity;
zipcode = randof(1000) + 600;
temperature = randof(215) - 80;
relhumidity = randof(50) + 10;
// Send message to all subscribers
char update[20];
sprintf(update, "%d %d %d", zipcode, temperature, relhumidity);
s_send(publisher, update);
}
LOG("END Server shutdown");
Sleep(500);
zmq_close(publisher);
zmq_ctx_destroy(context);
}
void test_sock()
{
// Socket to talk to server
LOG("Collecting updates from weather server...");
void *context = zmq_ctx_new();
void *subscriber = zmq_socket(context, ZMQ_SUB);
int rc = zmq_connect(subscriber, "epgm://127.0.0.1:5556");
assert(rc == 0);
// Subscribe to zipcode, default is NYC, 10001
char *filter = "1001 ";
rc = zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE,
filter, strlen(filter));
assert(rc == 0);
// Process 100 updates
int update_nbr;
long total_temp = 0;
for (update_nbr = 0; update_nbr < 10; update_nbr++) {
char *string = s_recv(subscriber);
int zipcode, temperature, relhumidity;
sscanf(string, "%d %d %d",
&zipcode, &temperature, &relhumidity);
total_temp += temperature;
LOG(">> " << string);
free(string);
}
LOG("Average temperature for zipcode "<< filter << "was " << (int)(total_temp / update_nbr) << 'F');
zmq_close(subscriber);
zmq_ctx_destroy(context);
}
I run two functions in different threads, with tcp anything works as expected.
I have tried doing "route print 0.0.0.0" with cmd.exe and using interface IP (192.168.137.64) as prefix instead of "eth0" like shown in RFC: epgm://192.168.137.64;127.0.0.1:5556 on connect and/or bind, but this brokes my socket and raises error.
Also "PGM" requires administrator rights and i cannot test it now.
The error IS NOT "protocol not supported" errno is set to B (11) and i don't understand what does it mean (no docs on it).
EPGM is a bit finicky. According to this list post, if you're using EPGM your publisher and subscriber must be on separate hosts. More details here, it looks like this was a deliberate choice by the ZMQ team.
So, try it by spinning up your PUB and SUB on separate machines (changing the network addresses accordingly, of course).
The reason might be that windows does not support loopback capture interface. I tried weather example with protocol changed to epgm on linux and it works fine (well, shows some warnings about loopback, but the messages are transfered correctly)

SendMessage() to win32 application vc++

I have a win32 application project. but when the program get to a place like
let's say new_socket = accept(socket, (sockaddr *)&client, &c);
it get stuck. in these kind of scripts it makes me unable to use any other button, file menu and etc. is anybody that can tell me what is wrong and how am going to fix it.
this is the function where it get stuck:
void server(){
WSADATA wsa;
SOCKET server_socket, client_socket;
struct sockaddr_in server, client;
int c, yes=1;
int sent_length = 1;
if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) printf("Failed. Error Code : %d",WSAGetLastError());
if((server_socket = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET){
printf("Could not create socket : %d" , WSAGetLastError());
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = 0;
server.sin_port = htons(8080);
memset(&(server.sin_zero), '\0', 8);
bind(server_socket ,(struct sockaddr *)&server , sizeof(server));
listen(server_socket, 3);
c = sizeof(struct sockaddr_in);
while(1){
client_socket = accept(server_socket,(struct sockaddr *)&client, &c);
send(client_socket, "Hello, World", 13, 0);
}
WSACleanup();
}
accept is syncronous call and not return, until client connect. this mast not be used in GUI thread. need or do this call in another thread or (the best) use only asynchronous api (AcceptEx)

recv() only reads 1 byte (implementing an FTP with winsock)

I'm trying to implement a simple FTP client using winsock. I'm having problems trying to download a file. Here's the code I'm using at the moment:
bool FTPHandler::downloadFile(const char * remoteFilePath, const char * filePath) {
if (!isConnected()) {
setErrorMsg("Not connected, imposible to upload file...");
return false;
}
if (usePasiveMode) {
this->pasivePort = makeConectionPasive();
if (this->pasivePort == -1) {
//error msg will be setted by makeConectionPasive()
return false;
}
} else {
setErrorMsg("Unable to upload file not in pasive mode :S");
return false;
}
char * fileName = new char[500];
getFileName(remoteFilePath,fileName);
// Default name and path := current directory and same name as remote.
if (filePath == NULL) {
filePath = fileName;
}
if (!setDirectory(remoteFilePath)) {
return false;
}
char msg[OTHER_BUF_SIZE];
char serverMsg[SERVER_BUF_SIZE];
sprintf(msg,"%s%s\n",RETR_MSG,fileName);
send(sock, msg, strlen(msg), 0);
SOCKET passSocket;
SOCKADDR_IN passServer;
passSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (passSocket == INVALID_SOCKET) {
WSACleanup();
sprintf(errorMsg,"Error trying to create socket (WSA error code: %d)",WSAGetLastError());
return false;
}
passServer.sin_family = PF_INET;
passServer.sin_port = htons(this->pasivePort);
passServer.sin_addr = *((struct in_addr *)gethostbyname(this->host)->h_addr);
memset(server.sin_zero,0,8);
int errorCode = connect(passSocket, (LPSOCKADDR) &passServer, sizeof(struct sockaddr));
int tries = 0;
while (errorCode == SOCKET_ERROR) {
tries++;
if (tries >= MAX_TRIES) {
closesocket(passSocket);
sprintf(errorMsg,"Error trying to create socket");
WSACleanup();
return false;
}
}
char * buffer = (char *) malloc(CHUNK_SIZE);
ofstream f(filePath);
Sleep(WAIT_TIME);
while (int readBytes = ***recv(passSocket, buffer, CHUNK_SIZE, 0)***>0) {
buffer[readBytes] = '\0';
f.write(buffer,readBytes);
}
f.close();
Sleep(WAIT_TIME);
recv(sock, serverMsg, OTHER_BUF_SIZE, 0);
if (!startWith(serverMsg, FILE_STATUS_OKEY_CODE)) {
sprintf(errorMsg,"Bad response: %s",serverMsg);
return false;
}
return true;
}
That last recv() returns 1 byte several times, and then the method ends and the file that should be around 1Kb is just 23 bytes.
Why isn't recv reading the hole file?
There are all kinds of logic holes and incorrect/missing error handling in this code. You really need to clean up this code in general.
You are passing the wrong sizeof() value to connect(), and not handling an error correctly if connect() fails (your retry loop is useless). You need to use sizeof(sockaddr_in) or sizeof(passServer) instead of sizeof(sockaddr). You are also not initializing passServer correctly.
You are not checking recv() for errors. And in the off-chance that recv() actually read CHUCK_SIZE number of bytes then you have a buffer overflow that will corrupt memory when you write the null byte into the buffer (which you do not need to do) because you are writing it past the boundaries of the buffer.
If connect() fails, or recv() fails with any error other than a server-side initiated disconnect, you are not telling the server to abort the transfer.
Once you tell the server to go into Passive mode, you need to connect to the IP/Port (not just the Port) that the server tells you, before you then send your RETR command.
Don't forget to send the server a TYPE command so it knows what format to send the file bytes in, such as TYPE A for ASCII text and TYPE I for binary data. If you try to transfer a file in the wrong format, you can corrupt the data. FTP's default TYPE is ASCII, not Binary.
And lastly, since you clearly do not seem to know how to program sockets effectively, I suggest you use the FTP portions of the WinInet library instead of WinSock directly, such as the FtpGetFile() function. Let WinInet handle the details of transferring FTP files for you.

Resources