This code when compiled allows me to connect to a server using a correct IP address as the program parameter. So far, the connection is fine and data is sent to the server, but when I attempt to use the recv() function to try to collect data in non-blocking mode, I receive a segmentation fault. It always appears after the "receiving data..." line.
As you can see, I reserved 5000 bytes of stack memory for the operation and I'm testing with trying to read only one byte without success. Does recv() not work with stack memory?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
extern errno;
struct sockaddr_in a;
struct timeval tv;
fd_set wready;
long conntoserver(const char* ip,const char* data){
memset((char*)&a,0,sizeof(a));
if (inet_aton(ip,a.sin_addr.s_addr) == 0){printf("Can't set address\n");return -1;}
long s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if (s < 0){printf("Can't make socket\n");return -1;}
tv.tv_sec=1;tv.tv_usec=0;FD_ZERO(&wready);FD_SET(s,&wready);
//start non-blocking mode
if (fcntl(s,F_SETFL,O_NONBLOCK) < 0){printf("Can't make socket non-blocking\n");close(s);return -1;}
a.sin_family=AF_INET;a.sin_port=htons(80);long ret=connect(s,(struct sockaddr*)&a,sizeof(a));
if (ret < 0 && errno != EINPROGRESS){printf("Can't connect to IP. %s\n",strerror(errno));close(s);return -1;}
if (errno == EINPROGRESS){
printf("connection in progress...\n");
ret=select(s+1,NULL,&wready,NULL,&tv);
if (ret < 0){printf("select() error. %s\n",strerror(errno));close(s);return -1;}
if (ret==0){printf("select() timeout. %s\n",strerror(errno));close(s);return -1;}
}
unsigned long sz=strlen(data);
ssize_t ns=send(s,data,sz,0);
if (ns==sz){return s;}else{return -1;}
}
int main(int argc,char* argv[]){
if (argc < 2){printf("Format: %s < IP to connect to >\n",argv[0]);return -1;}
long s=conntoserver(argv[1],"GET / HTTP/1.1\nHost: example.com\n\n");
if (s < 0){printf("Error making server request. %s\n",strerror(errno));close(s);return -1;}
printf("receiving data...\n");
char b[5001];
ssize_t br=recv(s,b,1,0);
printf("br=%s\n",br);
close(s);
return 0;
}
printf("br=%s\n",br);
Should be:
printf("br=%ld\n",br);
BTW -- I had to fix a number of problems to make this code compile. You should always pay attention to compiler warnings.
In addition to the error pointed out by #keithmo, you have an error here:
if (inet_aton(ip,a.sin_addr.s_addr) == 0){printf("Can't set address\n");return -1;}
The second argument to inet_aton() is expected to be a pointer to a struct in_addr but you are passing a value of type uint32_t. In fact, that value is initialized to zero, so the result is likely (but not certain) to be equivalent to passing a NULL pointer. Either way, this produces undefined behavior, and I'd surprised if that didn't manifest as a segfault. Even if the program runs past that, the call as you wrote it cannot produce the (apparently) desired effect of initializing the address in variable a.
What you want instead is
if (inet_aton(ip, &a.sin_addr) == 0) {
printf("Can't set address\n");
return -1;
}
And for goodness's sake, do make free with the spaces and newlines. They greatly improve code legibility. If you're short, I can lend you some.
Related
For a UNIX/C project, I'm supposed to allocate two shared memory segments (which child processes will eventually access with read-only and write permissions, respectively) of two integers each. But any time I try to call shmat(3), it ends up returning -1, setting errno to EACCES, apparently indicating insufficient permissions. I've produced what seems to be the minimum required code fro the error below, with possibly a couple extra includes:
#define _SVID_SOURCE
#include <errno.h>
#include <stdio.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int i, j, shmid, tshmid;
int * clock;
int * shmMsg;
tshmid = shmget(IPC_PRIVATE,sizeof(int)*2, IPC_CREAT | IPC_EXCL | 0777); //0777 permissions are more liberal than I need, but I've tried other various literals and numbers as well as just CREAT/EXCL.
if (tshmid < 1){
printf("Error: In parent process (%d), shmid came up %d \n",getpid(),shmid);
exit(-1);
}
clock = (int *) shmat(shmid,NULL,0); //I've also tried this with the second argument as (void *) 0, and with the third argument as "(SHM_R | SHM_W)" and "0777"
if (clock == (void *) -1){
printf("Error: First shmat couldn't shmattach to shmid #%d. ERRNO %d\n",shmid,errno);
shmdt(clock);
exit(-1);
} //it never even gets this far
shmdt(clock);
}
Each time, this produces an error message like:
Error: First shmat couldn't shmattach to shmid #1033469981. ERRNO 13
The longer version of my program initially returned an identical error, but errno was set to 43 (EIDRM: Segment identified by shm_id was removed). I've recursively chmodded the whole directory to full access, so that's not the issue, and every time my program crashes I have to manually deallocate the shared memory using ipcrm, so the shmids apply to actual segments. Why won't it attach?
I apologize if my code is extensively long, but I'm attempting to make a local server that handles multiple local clients. I even imported ideas from http://www.binarytides.com/multiple-socket-connections-fdset-select-linux/ to try to get it to work with no success.
I run it using 82 for a parameter, and see as expected:
Socket made and ready
Accepting 10 users
I then use CURL to connect to 127.0.0.1:82 and curl stalls. In my program I see as expected:
CLIENT CONNECTION MADE on socket# 0!
But the problem is data isn't being sent from the server to the client.
I tried forcing a break in CURL (via ctrl+c) to see if anything happened on the server and nothing did. I even tried using a web browser to connect and received similar results (a hang-up).
If I force a break on the server (via ctrl+c), then I got what I expect, a disconnection message (like "Empty Reply from server" from CURL).
What I expected to see in my browser is:
Error
This is a hack-ed-server
What could I be doing wrong here? I'm sort-of new to the select() calls so I'm not sure if I configured them correctly.
Here's the code:
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <signal.h>
#include <time.h>
extern errno;
long asock=-1,nsock=-1;
void end_app(int s){
struct sigaction si;
si.sa_handler=SIG_DFL;si.sa_flags=0;
sigaction(SIGCHLD,&si,NULL);
sigaction(SIGTSTP,&si,NULL);
sigaction(SIGTTOU,&si,NULL);
sigaction(SIGTTIN,&si,NULL);
sigaction(SIGSEGV,&si,NULL);
sigaction(SIGTERM,&si,NULL);
sigaction(SIGHUP,&si,NULL);
char v[5000];
sprintf(v,"Abrupt exit detected sig# %d. Closing sockets.\n",s);
write(1,v,strlen(v));
if (asock > -1){close(asock);}
if (nsock > -1){close(nsock);}
}
const long trapsig(){
struct sigaction s,si;
si.sa_handler=SIG_IGN;si.sa_flags=0;
s.sa_handler=end_app;s.sa_flags=0;
sigaction(SIGCHLD,&si,NULL);sigaction(SIGTSTP,&si,NULL);sigaction(SIGTTOU,&si,NULL);sigaction(SIGTTIN,&si,NULL);
if (sigaction(SIGSEGV,&s,NULL)==-1){printf("Cant trap signal!\n");return 1;}
if (sigaction(SIGTERM,&s,NULL)==-1){printf("Cant trap signal!\n");return 1;}
if (sigaction(SIGHUP,&s,NULL)==-1){printf("Cant trap signal!\n");return 1;}
}
//getreq params in: req=external buffer for data
// reqsz=size of external buffer. I set 10000
// nsock=valid socket pointer from accept()
//
//getreq params out: reqsz=actual size of data returned
//
void getreq(char* req,unsigned long *reqsz,long nsock){
//bufsize=how many bytes to read at once. High values like 5000 cause a stall.
//buffer=buffer of data from recv call
const unsigned long ibs=*reqsz,bufsize=5000;
char buffer[ibs],*rp=req;
//spacect=# of spaces in data read
//szct=iterator variable
//mysz=total length of returned data
//bufct=buffer counter to prevent segfault
//recvsz=data size returned from recv or
// forced -2 if buffer hits capacity
// or 2nd space in returned data is found
unsigned long spacect=0,szct=0,mysz=0,bufct=0;
long recvsz=1;char *p=buffer;
//
//Expected data: GET /whatever HTTP/x.x but we
// want /whatever
//
//loop until 2nd space is found or
//ibs bytes of data have been processed
while (recvsz > 0 && bufct < ibs){
recvsz=recv(nsock, p, bufsize, 0);
if (recvsz < 1){break;}
for (szct=1;szct<=recvsz;szct++){
if (*p==' '){spacect++;if (spacect > 2){spacect=2;recvsz=-2;break;}}
if (spacect==1 && *p != ' '){mysz++;if (mysz <= *reqsz){*rp++=*p;}}
p++;bufct++;if (bufct > ibs){recvsz=-2;break;}
}
}
// Process rest of data to try to avoid client errors
while (recvsz == -2){
recvsz=recv(nsock, buffer, bufsize, 0);
}
*reqsz=mysz;
}
int main(int argc,char* argv[]){
if (trapsig() < 0){return 1;}
//set maximum users to 10 and allocate space for each
long maxusers=10;long csock[11];memset(csock,0,11);
//do sanity checks and bind local socket
if (!argv[1]){printf("Port # required\n");return 1;}
if ((asock=socket(AF_INET, SOCK_STREAM, 0)) < 1){printf("Can't make socket! %s\n",strerror(errno));return 1;}
struct sockaddr_in a;
memset(&a,0,sizeof(a));
a.sin_family=AF_INET;
a.sin_addr.s_addr=inet_addr("127.0.0.1");
a.sin_port=htons(strtol(argv[1],NULL,10));
if (bind(asock,(struct sockaddr*)&a, sizeof(a))==-1){printf("Can't bind socket! %s\n",strerror(errno));return 1;}
if (listen(asock,10) < 0){printf("Can't listen! %s\n",strerror(errno));return 1;}
printf("Socket made and ready\nAccepting %d users\n",maxusers);
while(1){
usleep(10); //sleep incase processor is overloaded
fd_set SR;long SMAX=asock,n,canadd=0;
FD_ZERO(&SR);FD_SET(asock,&SR);
for (n=0;n<maxusers;n++){
if (csock[n] > 0){FD_SET(csock[n],&SR);}else{canadd=1;}
if (csock[n] > SMAX){SMAX=csock[n];}
}
long act=select(SMAX+1,&SR,0,0,0);
if (act != EINTR && act < 0){printf("Select error\n");}
if (canadd==1 && FD_ISSET(asock,&SR)){
//incoming connection detected
socklen_t alen=sizeof(a);
if (nsock=accept(asock, (struct sockaddr*)&a, &alen)< 0){printf("Can't accept! %s\n",strerror(errno));close(asock);return -1;}
for (n=0;n<maxusers;n++){if (csock[n]==0){csock[n]=nsock;break;}}
printf("CLIENT CONNECTION MADE on socket# %d!\n",n);
fcntl(nsock, F_SETFD, O_NONBLOCK);
//program reaches here when client first connects
}
for (n=0;n<maxusers;n++){
if (csock[n] > 0 && FD_ISSET(csock[n],&SR)){
//this section never seems to execute
unsigned long reqsz=10000;
char req[reqsz];
printf("Checking incoming data...\n",n);
getreq(req,&reqsz,csock[n]);
if (reqsz > 0){
printf("Received %d bytes\nData: %s\n",reqsz,req);
const char buf[10000]={"HTTP/1.0 200 OK\nConnection: close\nContent-type: text/html\n\n<html><head><title>hacked</title></head><body><H1>Error</h1><p>This is a hack-ed-server</p></body></html>\n\n"};
send(csock[n],buf,strlen(buf),0);
}else{
printf("Received no data\n");
}
printf("Closing.\n");
close(csock[n]);
csock[n]=0;
}
}
}
printf("Closing sockets\n");
close(asock);
return 0;
}
Pay attention to warnings. This
if(nsock=accept(asock, (struct sockaddr*)&a, &alen)< 0)
is not parsed as you seem to expect. In fact it assigns nsock a result of comparison of accept return value vs 0 (which is false aka 0). You want some extra parenthesis:
if ((nsock = accept(asock, (struct sockaddr*)&a, &alen)) < 0)
This
long csock[11];memset(csock,0,11);
does not initialize csock completely -- just the first 11 bytes. You want memset(csock, 0, sizeof(csock));
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
int main()
{
int sock;
struct sockaddr sock_name = {AF_UNIX, "Fred"};
socklen_t len=sizeof(struct sockaddr)+5;
if( (sock=socket(AF_UNIX,SOCK_STREAM,0)) ==-1)
{
printf("error creating socket");
return -1;
}
if( bind(sock,&sock_name,len) != 0 )
{
printf("socket bind error");
return -1;
}
close(sock);
return 0;
}
After the first run, this program keeps reporting binding error. I tried to change the name of the sockaddr. It works again. But after changing it back to "Fred" (in this case), the error continues. Is something being stored in memory I didn't clear? Why does this happen and how could I fix it?
I guess I have found the problem. After the first run, I find a file named "Fred" in the current directory. I removed the file and my program worked again. Why does bind method generate a file in the current directory?
When used with Unix domain sockets, bind(2) will create a special file at the specified path. This file identifies the socket in much the same way a host and port identify a TCP or UDP socket. Just like you can't call bind twice to associate two different sockets with a given host and port*, you can't associate more than one Unix socket
But why doesn't the file disappear when you call close(2)? After all, closing a TCP socket makes the host and port it was bound to available for other sockets.**
That's a good question, and the short answer is, it just doesn't.
So it's customary (at least in example code) to call unlink(2) prior to binding. The Unix domain socket section of Beej's IPC guide has a nice example of this.
*With versions of the Linux kernel >= 3.9, this isn't exactly true.
**After TIME_WAIT or immediately if you use the SO_REUSEADDR socket option.
EDIT
You said this is your teacher's code, but I suggest that you replace your printf calls with perror:
if( bind(sock,&sock_name,len) != 0 )
{
perror("socket bind error");
return -1;
}
...which will print out a human-readable representation of the real problem encountered by bind(2):
$ ./your-example-executable
$ ./your-example-executable
socket bind error: Address already in use
Programming doesn't have to be so inscrutable!
When you successfully open a socket, it stays open until it is closed (even if your program terminates).
It appears that the question code is not closing the socket in the event of an (such as the failure of bind()).
Two processes cannot generally open the same socket.
Each time the code is executed, it is a new process, attempting to open the same socket.
The code needs a better scheme to handle errors.
This is how I would do it:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#define MY_FALSE (0)
#define MY_TRUE (-1)
int main()
{
int rCode=0;
int sock = (-1);
char *socketFile = "Fred");
struct sockaddr sock_name = {AF_UNIX, socketFile};
socklen_t len=sizeof(struct sockaddr)+5;
int bound = MY_FALSE;
if((sock=socket(AF_UNIX,SOCK_STREAM,0)) ==-1)
{
printf("error creating socket");
rCode=(-1);
goto CLEANUP;
}
if( bind(sock,&sock_name,len) != 0 )
{
printf("socket bind error");
rCode=(-1);
goto CLEANUP;
}
bound=MY_TRUE;
This single 'cleanup' area can be used to free allocated memory, close sockets & files, etc.
CLEANUP:
if((-1) != sock)
close(sock);
if(bound)
unlink(socketFile);
return 0;
}
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.
I need the test case for Ethernet in Linux using C code to check eth0. If eth0 is down, we enable the net then check if up and the test is passed.
To check if the link is up, try something like this. It works without root privileges.
#include <stdio.h> // printf
#include <string.h> // strncpy
//#include <sys/socket.h> // AF_INET
#include <sys/ioctl.h> // SIOCGIFFLAGS
#include <errno.h> // errno
#include <netinet/in.h> // IPPROTO_IP
#include <net/if.h> // IFF_*, ifreq
#define ERROR(fmt, ...) do { printf(fmt, __VA_ARGS__); return -1; } while(0)
int CheckLink(char *ifname) {
int state = -1;
int socId = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (socId < 0) ERROR("Socket failed. Errno = %d\n", errno);
struct ifreq if_req;
(void) strncpy(if_req.ifr_name, ifname, sizeof(if_req.ifr_name));
int rv = ioctl(socId, SIOCGIFFLAGS, &if_req);
close(socId);
if ( rv == -1) ERROR("Ioctl failed. Errno = %d\n", errno);
return (if_req.ifr_flags & IFF_UP) && (if_req.ifr_flags & IFF_RUNNING);
}
int main() {
printf("%d\n", CheckLink("eth0"));
}
If IFF_UP is set, it means interface is up (see ifup). If IFF_RUNNING is set then interface is plugged.
I also tried using the ethtool ioctl call, but it failed when the gid was not root. But just for the log:
...
#include <asm/types.h> // __u32
#include <linux/ethtool.h> // ETHTOOL_GLINK
#include <linux/sockios.h> // SIOCETHTOOL
...
int CheckLink(char *ifname) {
...
struct ifreq if_req;
(void) strncpy( if_req.ifr_name, ifname, sizeof(if_req.ifr_name) );
struct ethtool_value edata;
edata.cmd = ETHTOOL_GLINK;
if_req.ifr_data = (char*) &edata;
int rv = ioctl(socId, SIOCETHTOOL, &if_req);
...
return !!edata.data;
}
The network interfaces can be seen in sysfs: /sys/class/net/eth[x]. There you can check the link, interface status, and more.
You may want to take advantage of libudev to get around in /sys:
http://www.signal11.us/oss/udev/
I simply check if an Ip address is assigned to the network card.
You can use something like this to check if lan is up in the given network card (say eth0) :
/sbin/ifconfig eth0| grep 'inet addr:' | wc -l
This should simply return 0 or 1 based on whether or not an ip address is assigned to the nic.
Also you may use Yann Ramin's method to list out all the nic's & perform the check.
I missed out noticing you are looking for a c code. Maybe adding a tag would be good.
Either ways, I think you can look at the same file (ifconfig) manually in c by reading it for an ip.
As this question is somewhat important, I'll add another answer despite its extreme age. You can read the contents of /sys/class/net/eth0/operstate which will simply contain the string "up\n" or "down\n".