How to connect two computers over internet using socket programming in C? - c

This is simple Client-Server chat program.This code is working fine on computers connected on Network, how to modify it such that it can connect between computers over the Internet. Using Public IP of the server in gethostbyname() isn't working.
//Client.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main(void)
{
int clientSocket; /* Socket Decriptor for Client */
struct sockaddr_in server_addr;
struct hostent *ptrh;
char message[100];
char received[100];
int n = 0;
clientSocket=socket(AF_INET, SOCK_STREAM, 0);
memset((char*)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(10000);
/* bind(clientSocket, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)); */
ptrh=gethostbyname("110.172.156.2");
memcpy(&server_addr.sin_addr,ptrh->h_addr,ptrh->h_length);
if( -1 == (connect(clientSocket, (struct sockaddr*)&server_addr, sizeof(server_addr))))
{ printf("\nServer Not Ready !!\n"); exit(1); }
while(1)
{
printf("\nUser:-");
// memset(message, '\0', 10);
gets(message);
n = write(clientSocket, message, strlen(message)+1);
if( (strcmp(message,"q") == 0 ) || (strcmp(message,"Q") == 0 ))
{
printf("Wrong place...Socket Closed\n");
close(clientSocket);
break;
}
//printf("Write:<%u>\n", n);
read(clientSocket, received, sizeof(received));
if( (strcmp(received,"q") == 0 ) || (strcmp(received,"Q") == 0 ))
{
printf("Wrong place...Socket Closed\n");
close(clientSocket);
break;
}
else
printf("Server:- %s\n", received);
}
return 0;
}
//Server.c
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<netdb.h>
int main(void)
{
int serverSocket,client_connected,len;
struct sockaddr_in client_addr,server_addr;
struct hostent *ptrh;
int n=0;
char message[100],received[100];
serverSocket=socket(AF_INET, SOCK_STREAM, 0);
memset((char*)&server_addr,0,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(10000);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(serverSocket,
(struct sockaddr*)&server_addr,sizeof(server_addr)) == -1)
printf("Bind Failure\n");
else
printf("Bind Success:<%u>\n", serverSocket);
while(1)
{
listen(serverSocket,5);
len=sizeof(struct sockaddr_in);
client_connected=accept(serverSocket,
(struct sockaddr*)&client_addr,&len);
if (-1 != client_connected)
printf("Connection accepted:<%u>\n", client_connected);
while(1)
{
n = read(client_connected, received, sizeof(received));
if( (strcmp(received,"q") == 0 ) || (strcmp(received,"Q") == 0 ))
{
printf("Wrong place...Socket Closed of Client\n");
close(client_connected);
break;
}
else{
printf("\nUser:-%s", received);}
printf("\nServer:-");
// memset(message, '\0', 10);
gets(message);
write(client_connected, message, sizeof(message));
if( (strcmp(message,"q") == 0 ) || (strcmp(message,"Q") == 0 ))
{
printf("Wrong place...Socket Closed of Client\n");
close(client_connected);
break;
}
}
}
close(serverSocket); printf("\nServer Socket Closed !!\n");
return 0;
}

Based on the information you've given, I don't think it's possible to provide a solution to the question you're asking. You've stated that your code works when the two computers are on the same local network, so clearly the code (which, yes, has issues) works at least well enough to connect from client to server.
If (as is established,) the code works, then it shouldn't matter whether the client and server are on the same network or separate networks, so long as there's a route, a path, a connection between the two networks. Therefore, if the client can't connect to the server, the conclusion is that this path is missing. The path being missing, however, is not a problem we can troubleshoot for you: It might be "my 'Windows Firewall' is blocking this app", it might be "my ISP (or the other guy's ISP) is blocking this port", it might be "The other guy's terms of service with his ISP includes a "no servers" clause which they enforce by blocking all ports", it might even be "my ISP is on the outs with the other guy's ISP and won't route packets to them anymore".
However, since you've gone to all the trouble of posting this code, and I've gone to the trouble of (a) reading it, and (b) writing a response, I've decided to include some commentary on issues I see in your code. Note that this is guaranteed not to be an exhaustive list.
In Client.c:
You're calling memset(), and casting the first argument to char *. The memset() function is defined to take a void * as the first argument. Since you've #included <string.h> you have a correct prototype in scope, so whatever you pass to it will be converted to a void * automatically. The cast is therefore both incorrect and pointless.
You're calling gethostbyname(), and the string you're passing is an IPv4 address.
The gethostbyname() and gethostbyaddr() functions were deprecated in POSIX.1-2004 and were excluded from POSIX.1-2008. They are replaced by getaddrinfo() (and getnameinfo()), and I refer you to System Calls/getaddrinfo() section of Beej's Guide to Network Programming for further information.
According to POSIX.1-2004, "the behavior of gethostbyname() when passed a numeric address string is unspecified". The gethostbyname() function expects to be passed an actual hostname, for IP addresses there's gethostbyaddr(). (Or getaddrinfo() now, of course.)
You're using the gets() function. The gets() function was deprecated in C99, marked as Obsolete in POSIX.1-2008, and excluded from C11, because it is fundamentally unsafe due to not having any way of limiting input size. The generally recommended alternative is fgets(), note that unlike gets(), the fgets() function doesn't discard the \n character.
In Server.c:
You're still casting the first argument to memset() to char *, which is still unnecessary and wrong,
You're still using the gets() function, which is still inherently problematic,
You're doing write(client_connected, message, sizeof(message));. Every response from the server will be the full 100 bytes long, with garbage bytes being written after the response string. Use strlen(message)+1 instead.
In both:
Your message is a string that was input by the user, but when the client sends the message, it doesn't include the terminal null byte. The recipient can only read what the sender writes, so it isn't receiving a terminal null byte... which is only a problem because the receiving code assumes that what it received is a valid string. Make sure your messages include the null at the end of the string, by having the specified message size be one more than strlen(message).

Well after my research I came up with this answer. If you want to connect Devices over Internet you need to have a Server having a unique IP Address Eg you could buy one online. When you try to create a device in your home network as Server you need to provide the Global IP Address and since the ISP provides you with a single Public IP shared by a lot of devices over the Network using a router, you cant create a ServerSocket over a Home network shared by many Devices

You can't connect to a device only by the global IP but if you open a port, port forward, only for the server then the socket can be made.

Related

bind() function is not executing at all, no output when testing it

I'm learning socket programming in C. I have gotten my server to create a socket that was successful, but when I try to bind my socket to a port nothing happens. No error occurs and it is not successful. It's as if the bind() function is not even executing at all.
I've checked out the documentation on the bind() function here but there's no mention of why it won't execute at all. I've also tried searching through this site with no avail.
I also tried following this tutorial from start to finish but the error (or lack thereof) still occurs.
Here is my full code leading up to the problem:
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "include.h"
int main() {
// Descriptors. Used to check the status of functions such as socket, listen, bind etc.
// If a descriptor is equal to 0, then everything is okay. Else, if they are equal to -1, something went wrong.
int socketDescriptor, newSocketDescriptor = 1;
// The process ID of a child process (the client) when a new one is spawned (the client connects).
pid_t childPID;
// A string to hold the commands being sent a received.
char* commandBuffer = calloc(BUFFER_SIZE, sizeof(char));
// A structure to hold information on the server address.
struct sockaddr_in serverAddress;
memset(&serverAddress, '\0', sizeof(serverAddress));
// Fill in the server address information.
// Set the address family to AF_INET, which specifies we will be using IPv4.
// htons() takes the given int and converts it to the appropriate format. Used for port numbers.
// inet_addr() takes the given string and converts it to the appropriate format. Used for IP addresses.
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(PORT);
serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1");
// A structure to hold information a client when a new one connects to this server.
struct sockaddr_in clientAddress;
memset(&clientAddress, '\0', sizeof(clientAddress));
// socklen_t defines the length of a socket structure. Need this for the accept() function.
socklen_t addressSize;
// Creating the socket.
// AF_NET specifies that we will be using IPv4 addressing.
// SOCK_STREAM specifies that we will be using TCP to communicate.
socketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
if (socketDescriptor < 0) {
perror("ERROR CREATING SOCKET");
exit(1);
}
else
printf("Socket created successfully.\n");
// Binding to the specified port. 0 if everything is fine, -1 if there was an error.
if (bind(socketDescriptor, (struct sockaddr*) & serverAddress, sizeof(struct sockaddr_in)) < 0) {
perror("ERROR BINDNING");
exit(1);
}
else
printf("Socket bound to %s:%s.\n", serverAddress.sin_addr.s_addr, serverAddress.sin_port);
The last if statement at the bottom is where the code fails. It should either print and error or print "Socket bound to 127.0.0.1:80" but neither happens. See an example here.
I'm lost for what to do.
A server socket won't show up in a netstat listing unless you call listen after binding the socket.
Also, you're using the %s format specifier in your printf after the bind call on serverAddress.sin_addr.s_addr and serverAddress.sin_port. These are not strings but integers. Using the wrong format specifier invokes undefined behavior and is likely causing your program to crash. Using the correct format specifier such as %d or %x will fix this.
if (bind(socketDescriptor, (struct sockaddr*)&serverAddress, sizeof(struct sockaddr_in)) < 0) {
perror("ERROR BINDNING");
exit(1);
}
else
// use %x to print instead
printf("Socket bound to %x:%x.\n", serverAddress.sin_addr.s_addr, serverAddress.sin_port);
if (listen(socketDescriptor, 3) < 0) {
perror("listen failed");
} else {
printf("socket is listening\n");
}

Connecting to local virtual machine

I suspect this has an easy solution I'm overlooking, probably to do with the client or how this is set up.
Anyways, I'm trying to set up a simple Echo server/client to understand the basics of socket programming. I have a virtual machine running Linux Mint, and the host is running Windows 10. The virtual machine I am setting to run the server c code, and the Windows will be running the client.
I started off making the server code
//Echo Server for UNIX: Using socket programming in C, a client sends a string
//to this server, and the server responds with the same string sent back to the client
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
int main()
{
char stringBuffer[50]; //string buffer for reading incoming and resending
int listener, communicator, c; //store values returned by socket system call
if((listener = socket(AF_INET, SOCK_STREAM, 0)) == -1) //creates a new socket
puts("Could not create socket");
puts("Socket Created");
struct sockaddr_in servAddr, client; //structure from <netinet/in.h> for address of server
servAddr.sin_family = AF_INET; //addressing scheme set to IP
servAddr.sin_port = htons(8888); //server listens to port 5000
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //symbolic constant of server IP address
//binds the socket to the address of the current host and port# the server will run on
if (bind(listener, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0){
puts("Bind failed");
return 1;
}
puts("Bind Successful");
listen(listener, 5); //listens for up to 5 connections at a time
c = sizeof(struct sockaddr_in);
if ((communicator = accept(listener, (struct sockaddr*)&client, (socklen_t*)&c ))<0)
puts("accept failed");
puts("Connection Accepted");
//wait until someone wants to connect, then whatever is sent can be read from communicator, which can then be sent back
while(1){
bzero(stringBuffer, 50); //sets buffer to 0
read(communicator, stringBuffer, 50); //reads from communicator into buffer
write(communicator, stringBuffer, strlen(stringBuffer)+1); //returns back
}
return 0;
}
after that I tested it out by opening another terminal in the guest machine and typed "telnet localhost 8888" and input whatever strings I wanted.
This test worked so now, onto my Windows machine to create the client side of the socket programming:
#include <winsock.h>
#include <stdio.h>
#include <string.h>
#pragma comment(lib,"ws2_32.lib") //Winsock Library
int main(int argc, char *argv[])
{
WSADATA wsadata; //variable for using sockets in windows
SOCKET sock; //socket variable for network commands
char sendString[50], recieveString[50]; //variables for sending and recieving messages to/from server
//check if WSA initialises correctly
if (WSAStartup(MAKEWORD(2,2), &wsadata) != 0)
printf("Error Code: %d", WSAGetLastError());
//creates new socket and saves into sock
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
printf("Could not create socket: %d", WSAGetLastError());
printf("Socket created\n");
struct sockaddr_in servAddr;
servAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //sets the IP address to the same machine as the server
servAddr.sin_family = AF_INET; //addressing scheme set to TCP/IP
servAddr.sin_port = htons(8888); //server address is on port 8888
//connects to device with specifications from servAddr
if (connect(sock, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0) {
printf("Connection Error %d\n", WSAGetLastError());
return 1;
}
printf("Connection Accepted\n");
while(1){
fgets(sendString, 50, stdin); //uses stdin to get input to put into sendString
//sends sendString to server using sock's properties
if (send(sock, sendString, strlen(sendString) + 1, 0) < 0); {
printf("Send Failed");
return 0;
}
//reads from server into recieveString
if ((recv(sock, recieveString, 50, 0)) == SOCKET_ERROR)
printf("Recieve Failed");
printf("%s", recieveString); //prints out recieveString
}
}
Now, with the server still running, when I try out the client-side, I get the response "Connection Error" (from line 35). Having looked at both Unix and WinSock examples, I'm unsure as to why I would be failing the connection. I suspect it might have something to do with a windows to linux VM but I'm not sure.
---UPDATE---
Having updated the accidental semicolon and added the WSAGetLastError, it's showing an error code of 10061; This translates to
"Connection refused.
No connection could be made because the target computer actively refused it. This usually results from trying to connect to a service that is inactive on the foreign host—that is, one with no server application running."
[after the 3rd edit:]
Sry, just re-read your question. The important thing is here:
The virtual machine I am setting to run the server c code, and the Windows will be running the client.
127.0.0.1 is an address always only local to an IP enabled box. So you your server is listening on the interface 127.0.0.1 local to the Linux VM and the client tries to connect to 127.0.0.0 local to the Windows box. Those two interfaces are not the same. The result is the obvious, namely the client does not find anything to connect to.
127.0.0.1 (the so called "IPv4 local loopback interface") can only be used for connections local to exactly one box.
if (connect(sock, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0); {
printf("Connection Error");
return 1;
}
This is just a trivial syntax mistake. You are entering the block unconditionally. Remove the first semicolon.
However there is a much more important point to be made. When you get an error from a system call such as connect(), you must print the error. Not just some message of your own devising. Otherwise you don't know whether you simply have a bug, or a temporary problem, or a long-lasting problem, or a permanent problem.
Change the printf() to:
printf("Connect error %s\n", WSAGetLastError());
and then don't continue as though the error didn't happen.
Note that this applies to all system calls, specifically including socket(), bind(), listen(), connect(), accept(), recv(), send(), and friends.

TCP Server - Recover from "Too many open files"

I'm writing a TCP server in C and find something unusual happens once the listening fd get "Too many open files" error. The accept call doesn't block anymore and returns -1 all the time.
I also tried closing the listening fd and re-opening, re-binding it, but didn't seem to work.
My questions are why accept keeps returning -1 in this situation, what am I supposed to do to stop it and make the server be able to accept new connections after any old clients closed? (the socket is of course able to accept correctly again when some connections closed)
====== UPDATE: clarification ======
The problem occurs just because the number of active clients is more than the limit of open fds, so I don't close any of the accepted fds in the sample code, just to make it reproduce more quickly.
I add the timestamp each time accept returns to the output and slow down connect frequency to once in 2 seconds, then I find that in fact the "Too many open files" error occurs immediately after the lastest success accept. So I think that is because when the maxium fds is reached, each call to accept will return immediately, and the return value is -1. (What I thought is that accept would still block, but returns -1 at the next incoming connect. The behavior of accept in this situation is my own theory, not from the man page. If it's wrong, please let me know).
So to my second question, to make it stop, I think it's a solution that stop to call accept before any connection is closed.
Also update the sample codes. Thanks for your help.
====== Sample codes ======
Here is how I test it. First set ulimit -n to a low value (like 16) and run the server program compiled from the following C source; then use the Python script to create several connections
/* TCP server; bind :5555 */
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUFSIZE 1024
#define PORT 5555
void error(char const* msg)
{
perror(msg);
exit(1);
}
int listen_port(int port)
{
int parentfd; /* parent socket */
struct sockaddr_in serveraddr; /* server's addr */
int optval; /* flag value for setsockopt */
parentfd = socket(AF_INET, SOCK_STREAM, 0);
if (parentfd < 0) {
error("ERROR opening socket");
}
optval = 1;
setsockopt(parentfd, SOL_SOCKET, SO_REUSEADDR,
(const void *)&optval , sizeof(int));
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)port);
if (bind(parentfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) {
error("ERROR on binding");
}
if (listen(parentfd, 5) < 0) {
error("ERROR on listen");
}
printf("Listen :%d\n", port);
return parentfd;
}
int main(int argc, char **argv)
{
int parentfd; /* parent socket */
int childfd; /* child socket */
int clientlen; /* byte size of client's address */
struct sockaddr_in clientaddr; /* client addr */
int accept_count; /* times of accept called */
accept_count = 0;
parentfd = listen_port(PORT);
clientlen = sizeof(clientaddr);
while (1) {
childfd = accept(parentfd, (struct sockaddr *) &clientaddr, (socklen_t*) &clientlen);
printf("accept returns ; count=%d ; time=%u ; fd=%d\n", accept_count++, (unsigned) time(NULL), childfd);
if (childfd < 0) {
perror("error on accept");
/* the following 2 lines try to close the listening fd and re-open it */
// close(parentfd);
// parentfd = listen_port(PORT);
// the following line let the program exit at the first error
error("--- error on accept");
}
}
}
The Python program to create connections
import time
import socket
def connect(host, port):
s = socket.socket()
s.connect((host, port))
return s
if __name__ == '__main__':
socks = []
try:
try:
for i in xrange(100):
socks.append(connect('127.0.0.1', 5555))
print ('connect count: ' + str(i))
time.sleep(2)
except IOError as e:
print ('error: ' + str(e))
print ('stop')
while True:
time.sleep(10)
except KeyboardInterrupt:
for s in socks:
s.close()
why accept keeps returning -1 in this situation
Because you've run out of file descriptors, just like the error message says.
what am I supposed to do to stop it and make the server be able to accept new connections after any old clients closed?
Close the clients. The problem is not accept() returning -1, it is that you aren't closing accepted sockets once you're finished with them.
Closing the listening socket isn't a solution. It's just another problem.
EDIT By 'finished with them' I mean one of several things:
They have finished with you, which is shown by recv() returning zero.
You have finished with them, e.g. after sending a final response.
When you've had an error sending or receiving to/from them other than EAGAIN/EWOULDBLOCK.
When you've had some other internal fatal error that prevents you dealing further with that client, for example receiving an unparseable request, or some other fatal application error that invalidates the connection or the session, or the entire client for that matter.
In all these cases you should close the accepted socket.
The answer of EJP is correct, but it does not tell you how to deal with the situation. What you have to do is actually do something with the sockets that you get as accept returns. Simple calling close on them you won't receive anything of course but it would deal with the resource depletion problem. What you have to do to have a correct implementation is start receiving on the accepted sockets and keep receiving until you receive 0 bytes. If you receive 0 bytes, that is an indication that the peer is done using his side of the socket. That is your trigger to call close on the socket as well and deal with the resource problem.
You don't have to stop listening. That would stop your server from being able to process new requests and that is not the problem here.
The solution I implemented here was to review the value of the new (accepted) fd and if that value was equal or higher then the allowed server capacity, then a "busy" message is sent and the new connection is closed.
This solution is quite effective and allows you to inform your clients about the server's status.

Plaintext POP3 client using C in Linux - error reading after sending command to server

I've been trying to write a POP3 client for yahoo, but whenever I send a valid command, I cannot read from socket . I know it's not safe to create applications that deal in plaintext over the web, but I'm just trying to learn more about POP3 for now.
I'm creating a graphic interface using QT , but I believe this is irrelevant for now.
First of all, I made sure that yahoo supports plaintext communication by running simple commands in the terminal:
telnet pop.mail.yahoo.com 110
+OK hello from popgate-0.8.0.504347 pop011.mail.ir2.yahoo.com
user validuser
+OK password required.
pass validpassword
+OK maildrop ready, 100 messages (134513 octets) (13531)
This is how I handle opening the connection:
int openConnection(char ip[], int port)
{
//descriptor of socket
int sd;
struct sockaddr_in server;
//fill in server structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr (ip);
server.sin_port = htons (port);
//create communication socket
if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{
//if cannot create, return error
return -2;
}
if (connect (sd, (struct sockaddr *) &server,sizeof (struct sockaddr)) == -1)
{
return -1;
}
return sd;
//values to be returned
//-2 - could not create socket
//-1 - could not connect to server
//socket descriptor in case of success
}
I get no problem with creating the socket and connecting when I call
int sd = openConnection ("188.125.68.106", 110)
(188.125.68.106 = ip of pop.mail.yahoo.com)
I can even read the welcoming message without using select or epoll.
char message[80];
read (sd, message, 80);
Message:
+OK hello from popgate-0.8.0.504347 pop011.mail.ir2.yahoo.com
I can also send all sorts of invalid commands, and read the error returned
int sd = openConnection ("188.125.68.106", 110)
write (sd,"this command is invalid",80);
char messageFromS [80];
bzero (messageFromS, 80);
read (sd,messageFromS,80);
And I get something between the lines of
-ERR invalid command
But whenever I send a valid command, my client just freezes up after calling read and I have to force quit
write (sd,"user validuser",80);
char messageFromS [80];
bzero (messageFromS, 80);
read (sd,messageFromS,80);
Afterwards, I decided to use select() because I know it's a good practice, and maybe it could give good results
//function returns true (1) if we can read from the socket
bool isClosed(int sock)
{
fd_set rfd;
FD_ZERO(&rfd);
FD_SET(sock, &rfd);
timeval tv;
//timeout value is set to 5 seconds
tv.tv_sec = 5;
select(sock+1, &rfd, 0, 0, &tv);
if (!FD_ISSET(sock, &rfd))
return false;
int n = 0;
ioctl(sock, FIONREAD, &n);
return n == 0;
}
But it seems that whenever I call
isClosed(sd)
It always returns True.
This is the point where I cannot think of any other options. Is there anything that Telnet has that my miniature client doesn't ?
EDIT: Thank you for your answers.
I played around with reading all the messages received byte by byte, until it found a termination character (apparently it wasn't NULL), and, after re-reading the RFC for pop3 (https://www.rfc-editor.org/rfc/rfc1081) closely I realized that I wasn't ending my commands properly. I should have appended '\r' and '\n' to the end of all commands sent.
I also checked the select () command, which returned -1 and set the tv.tv_usec = 0 which pretty much solved it.
Thank you for your answers.
I played around with reading all the messages received byte by byte, until it found a termination character (apparently it wasn't NULL), and, after re-reading the RFC for pop3 (https://www.rfc-editor.org/rfc/rfc1081) closely I realized that I wasn't ending my commands properly. I should have appended '\r' and '\n' to the end of all commands sent.
I also checked the select () command, which returned -1 and set the tv.tv_usec = 0 which pretty much solved it.
– Shoshinsha purogurama

How to create a simple Proxy to access web servers in C

I’m trying to create an small Web Proxy in C. First, I’m trying to get a webpage, sending a GET frame to the server.
I don’t know what I have missed, but I am not receiving any response. I would really appreciate if you can help me to find what is missing in this code.
int main (int argc, char** argv) {
int cache_size, //size of the cache in KiB
port,
port_google = 80,
dir,
mySocket,
socket_google;
char google[] = "www.google.es", ip[16];
struct sockaddr_in socketAddr;
char buffer[10000000];
if (GetParameters(argc,argv,&cache_size,&port) != 0)
return -1;
GetIP (google, ip);
printf("ip2 = %s\n",ip);
dir = inet_addr (ip);
printf("ip3 = %i\n",dir);
/* Creation of a socket with Google */
socket_google = conectClient (port_google, dir, &socketAddr);
if (socket_google < 0) return -1;
else printf("Socket created\n");
sprintf(buffer,"GET /index.html HTTP/1.1\r\n\r\n");
if (write(socket_google, (void*)buffer, MESSAGE_LENGTH+1) < 0 )
return 1;
else printf("GET frame sent\n");
strcpy(buffer,"\n");
read(socket_google, buffer, sizeof(buffer));
// strcpy(message,buffer);
printf("%s\n", buffer);
return 0;
}
And this is the code I use to create the socket. I think this part is OK, but I copy it just in case.
int conectClient (int puerto, int direccion, struct sockaddr_in *socketAddr) {
int mySocket;
char error[1000];
if ( (mySocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("Error when creating the socket\n");
return -2;
}
socketAddr->sin_family = AF_INET;
socketAddr->sin_addr.s_addr = direccion;
socketAddr->sin_port = htons(puerto);
if (connect (mySocket, (struct sockaddr *)socketAddr,sizeof (*socketAddr)) == -1) {
snprintf(error, sizeof(error), "Error in %s:%d\n", __FILE__, __LINE__);
perror(error);
printf("%s\n",error);
printf ("-- Error when stablishing a connection\n");
return -1;
}
return mySocket;
}
Thanks!
First, you're not checking how many bytes the write(2) call actually wrote to the socket. The return value of the call tells you that. Same for the read(2). TCP socket is a bi-directional stream, so as a rule always do both in a loop until expected number of bytes is transferred, EOF is read (zero return from read(2)), or an error occurred (which you are not checking for when reading either).
Then HTTP is rather complex protocol. Make yourself familiar with RFC 2616, especially application level connection management and transfer encodings.
Edit 0:
Hmm, there's no such thing as "simple" proxy. You need to manage multiple connections (at least client-to-proxy and proxy-to-server), so it's probably best to look into select(2)/poll(2)/epoll(4)/kqueue(2) family of system call, which allow you to multiplex I/O. This is usually combined with non-blocking sockets. Look into helper libraries like libevent. Look at how this is done in good web-servers/proxies like nginx. Sound like it's a lot for you to discover, but don't worry, it's fun :)
Since you didn't post the GetIP routine, I am not certain that your hostname lookup is correct, as from the looks of it, I am not sure that you are using inet_addr function correctly.
Nikolai has pointed out some very good points (and I fully agree). In fact you GET request is actually broken, and while I was testing it on my own local Apache web server on my system, it didn't work.
sprintf(buffer,"GET /index.html HTTP/1.1\r\n\r\n");
if (write(socket_google, (void*)buffer, LONGITUD_MSJ+1) < 0 )
return 1;
else printf("GET frame sent\n");
...
strcpy(buffer,"\n");
read(socket_google, buffer, sizeof(buffer));
should be replaced with
snprintf(buffer, sizeof(buffer),
"GET / HTTP/1.1\r\nHost: %s\r\nUser-Agent: TEST 0.1\r\n\r\n",
google);
if (write(socket_google, buffer, strlen(buffer)+1) < 0 ) {
close(socket_google);
return 1;
} else
printf("GET frame sent\n");
...
buffer[0] = '\0';
/* Read message from socket */
bytes_recv = read(socket_google, buffer, sizeof(buffer));
if (bytes_recv < 0) {
fprintf(stderr, "socket read error: %s\n", strerror(errno));
close(socket_google);
exit(10);
}
buffer[bytes_recv] = '\0'; /* NUL character */
/* strcpy(message,buffer); */
printf("%s\n", buffer);
...
You should also close the socket before exiting the program. Enable standard C89/90 or C99 mode of your compiler (e.g. -std=c99 for gcc) and enable warnings (e.g. -Wall for gcc), and read them. And #include the necessary header files (assuming Linux in my case) for function prototypes:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h> /* for gethostbyname() */
There is some casting of pointers and structs in regards to the hostname / IP address resolving, which can be confusing and easy place to make a mistake, so verify that is working as you expect it is.
in_addr_t ip;
...
GetIP(google, &ip); /* I changed the parameters */
printf("IP address = %x (%s)\n",
ip,
inet_ntoa(*((struct in_addr*)&ip)));
Actually, I've been implementing a small web proxy using my library called rzsocket link to it.
One of the most difficult things I've found when implementing the web proxy, perhaps this might also be your problem, was that, in order to make the proxy work properly, I had to set keep-alive settings false. One way of doing this in FireFox, is accessing about:config address, and setting the value of network.http.proxy.keep-alive to false.

Resources