Handling errors when refactoring procedural code - c

I was handed some C code that basically consists of a big main() function. I am now trying to unfold the method into smaller functions, to make clearer the code's intent. I am having some trouble, though:
void main(int argc, char *argv[])
{
if(argc != 3)
{
printf("Usage: table-server <port> <n_lists>\n");
return;
}
int port = atoi(argv[1]), n_lists = atoi(argv[2]);
if(port < 1024 || port > 49151 || n_lists < 1)
{
printf("Invalid args.\n");
return;
}
signal(SIGPIPE, SIG_IGN);
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in s_addr;
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(port);
s_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sockfd, (struct sockaddr *)&s_addr, sizeof(s_addr)) < 0)
{
printf("(bind).\n");
return;
}
if(listen(sockfd, SOMAXCONN) < 0)
{
printf("(listen).\n");
return;
}
I can identify 4 main concerns in this code's function:
Verifying the number of args is correct.
Getting from the command line arguments the port.
Calling signal(SIGPIPE, SIG_IGN).
Actually try to make a connection with the socket.
The problem when trying to refactor this into small functions is mainly related with error handling. For instance,r trying to extract the logic of 1. would look like this:
int verify_number_of_args(int argc) {
if (argc != 3) {
printf("...");
return -1;
}
return 0;
}
and calling it would be something like this
if (verify_number_of_args(argc) == -1) return;
which isn't actually that bad. Now, for the socket, that'd be way more troublesome as both sockfd and s_addr need to be returned, plus the status return value:
int sockfd;
struct sockaddr_in* s_addr;
if (create_socket(port, &sockfd, s_addr) == -1)
return;
which kind of defeats the purpose of trying to keep my main method as simple and clear as possible. I could, of course, resort to global variables in the .c file but that doesn't seem that good of an idea.
How do you generally handle this kind of things in C?

Here's the simple approach.
Argument parsing and related error checking are main's concern, so I wouldn't split those out unless main is extremely long.
The actual work, i.e. the networking part of the program, can be split off to a function that is very similar to main, except that it takes properly parsed and validated arguments:
int main(int argc, char *argv[])
{
// handle arguments
return serve(port, n_lists);
}
int serve(int port, int n_lists)
{
// do actual work
}
As for error handling: if this code is not meant to be a library, you can get away with just killing the calling process when something goes wrong in a function, no matter how deep down in the call chain it is; that is in fact recommended practice (Kernighan & Pike, The Practice of Programming). Just make sure you factor out the actual error printing routines in something like
void error(char const *details)
{
extern char const *progname; // preferably, put this in a header
fprintf(stderr, "%s: error (%s): %s\n", progname, details, strerror(errno));
exit(1);
}
to get consistent error messages. (You might want to check out err(3) on Linux and BSD and maybe emulate that interface on other platforms.)
You can also try to factor out those operations that simply can't go wrong or are just calling a few system calls with some fool-proof setup, since those make for easily reusable components.

Leave as is? A bit of setup at the start of main doesn't constitute a problem, IMO. Start refactoring after things are set up.

Isn't that a sign that you are refactoring for the sake of refactoring ?
Anyway, regarding the "let's initialise sockfd and s_addr in one go", you can always
create a structure, and pass a pointer to it :
struct app_ctx {
int init_stage;
int sock_fd;
struct sockaddr_in myaddr;
...
}
Then you pass a pointer to an instance of this structure to all your "do one thing at a time" functions, and return error code.
At cleanup time, you do the same thing and pass the same structure.

Related

How to mock socket in C

I have a function using a socket and I would mock it but I couldn't find how to do it.
Is there a way to mock sockets in C?
Thanks
Most system / library function are weak symbols. That means you can create your own implementation of them that will override the existing versions. You can then use these functions when unit testing.
Suppose you want to test the following function:
src.c:
int get_socket()
{
int s;
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s == -1) {
perror("socket failed");
} else {
printf("socket success\n");
close(s);
}
return s;
}
You would then create the mock function (and any controlling variables) in a separate source file:
mock_socket.c:
int sock_rval;
int socket(int domain, int type, int protocol)
{
printf("calling mock socket\n");
return sock_rval;
}
Then in another file you have your test:
test_socket.c:
extern int sock_rval;
int get_socket();
int main()
{
sock_rval = -1;
int rval = get_socket();
assert(rval == -1);
sock_rval = 3;
int rval = get_socket();
assert(rval == 3);
return 0;
}
You would then compile and link these three modules together. Then get_socket will call the socket function in mock_socket.c instead of the library function.
This technique works not just with socket functions, but with system / library functions in general.
Sockets are managed by the kernel, so there is no userspace-only mechanism for mocking them, but you can set up a genuine socket connected to an endpoint that's part of your test fixture.
In particular, you may be able to use a unix-domain or raw socket for that purpose where the code under test normally has, say, a TCP socket to work with, or you can connect a socket back to the local test fixture via the loopback interface. Or, though I am unaware of any example, in principle you could also find or write a driver that provides sockets for an artificial, for-purpose address family.

Local socket option set vs pointer to socket option set

I wonder... what is the actual difference if you create a function that has a simple socket parameter and you do your basic instructions inside that function like setting different option to that socket (setsockopt()) and after the functions exist it will remain the option? Or should I make that parameter pointer to that socket in order to keep the actual changes that will happen to the socket.
sctp_enable_events( int socket, int ev_mask )
{
struct sctp_event_subscribe ev;
bzero(&ev, sizeof(ev));
if (ev_mask & SCTP_SNDRCV_INFO_EV)
ev.sctp_data_io_event = 1;
/*code */
if (setsockopt(socket,
IPPROTO_SCTP,
SCTP_EVENTS,
SCTP_SET_EVENTS,
(const char*)&ev,
sizeof(ev)) != 0 ) {
fprintf(where,
"sctp_enable_event: could not set sctp events errno %d\n",
errno);
fflush(where);
exit(1);
}
}
Or like this?
sctp_enable_events( int *socket, int ev_mask, struct sctp_event_subscribe *ev )
{
if (ev_mask & SCTP_SNDRCV_INFO_EV)
ev->sctp_data_io_event = 1;
/*code */
if (setsockopt(*socket,
IPPROTO_SCTP,
SCTP_EVENTS,
SCTP_SET_EVENTS,
ev,
sizeof(*ev)) != 0 ) {
fprintf(where,
"sctp_enable_event: could not set sctp events errno %d\n",
errno);
fflush(where);
exit(1);
}
}
I know by passing pointer to struct,int,char etc. you cand modify the values after the function executes and without a pointer the modification will remain local in that function only ,but it will not change it globally.
But how with the setsockopt function?
The setsockopt function can't tell how you come up with it's arguments. Therefore, it cannot act differently. Whether you write:
f(1);
or
int x = 1;
int* xPtr = &x;
f(*xPtr);
is not detectable by f.
This question really has nothing to do with setsockopt. I advise you to strengthen your C knowledge around pointers and values. Without this understanding you are going to make many mistakes in C.
You should generally pass pointers to structs because of the efficiency and because you may want to use those structs (by dereferencing the pointer) in different contexts.
In the case of the socket parameter of the setsockopt(), it is an int, so it is small enough to be passed by value (also the signature of the setsockopt() function requires an int, not a pointer).
As for setsockopt() and other C system API functions, you should lookup its declarations/prototypes and provide the parameters of exactly the same type as the prototype requires (doing the casting if neccessary).
In the case of setsockopt() it would be:
int `setsockopt()` setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);

C, Bind faild: `Address Already in Use`

I am writing a client-server program in C. I am getting an error in Bind function says: Address Already in Use. So I tired to use memset() and I got error says: Invalid Argument. Finally, I looked for similar questions on here, and some of them suggesting the use of setsocketopt() function. I used it and I am getting error says:
/tmp/ccBNsJtU.o: In function main:
socket.c:(.text+0xd0): undefined reference to setsocket
collect2: error: ld returned 1 exit status
I looked at almost-if-not all the similar questions even in different programing languages. Now I am stuck. Do I have to use setsocket() and if I do, is it causing a problem in my code? Or I don't have to use it and the problem is somewhere else? Could the problem be in the client or the server functions where I listen and send messages? This is the part of the code where the bind() and setsocket() functions are:
int main (void) {
int sl, sa, bn, erro, lis;
int status;
//server log socket
struct sockaddr_un server = {AF_UNIX, "log_server"};
sl = socket (AF_UNIX, SOCK_STREAM, 0);
if(sl < 0) {
perror("Faild to create socket - in main");
exit(1);
}
//I added this part
if (setsocket(sl, SOL_SOCKET, SO_REUSEADDR, &server, sizeof(server)) == -1) {
perror("setsocket - in main");
exit(1);
}
bn = bind (sl, (struct sockaddr *)&server, sizeof(server));
if(bn < 0){
perror("Faild to bind - in main");
exit(1);
}
lis = listen (sl, 1); //to be changed to 4
if (lis < 0) {
perror("Faild to listen - in main");
}
"Address already in use" typically means that some other socket is already bound to the given port. This could mean that there's a running program actively listening on that port, or it could mean that a prior instance of your program which is no longer running still has some socket resources open in the OS.
In the latter case, a call to setsockopt (not setsocket) with the SO_REUSEADDR parameter will address this. The fourth parameter should be a pointer to a char whose value is 1, while the fifth parameter should be sizeof(char).
For the users who are facing the problem in bind() function that generate error of the type: Address Already in Use. Here is one tip:
My problem was because the program ran and an address being used by the bind() and then the program generated errors and stopped/terminated. So the unlink() function at the end of the code had no chance to do its job and the address kept in use. So simplest way is at the beginning of the function unlink the processes you are going to bind later in the function.
This seems like so simple and I don't know if it is a good practice but it worked for my purpose.

How do I communicate between a server and a client using sockets? [C]

I am doing a Unix, C assignment. I am creating a Server and a Client which will interact with each other. I am pretty sure I have set up the basic framework but I when I try to send/receive messages, it doesn't work.
Here is the while loop code for the server, I tried to show only the relevant code:
while(1) {
clntAdrLen = sizeof(clntAddr);
clntFd = accept(srvrFd, (struct sockaddr*)&clntAddr, NULL);
if (fork() == 0) {
send(clntFd, "YourMessage", 12, NULL);
close(clntFd);
exit(0);
} else {
close(clntFd);
}
}
And here is the code for client:
do {
result = connect(srvrFd, (struct sockaddr*)&srvrAddr, srvrLen);
if(result==-1) {
sleep(1);
}
recv(srvrFd, buf, sizeof(buf), NULL);
printf("%s", buf); //here I try to print the message sent by server
} while (result==1);
When I run both server and client, It should print "YourMessage". Instead it prints:
N0�,
Am I just doing it wrong? Thanks
I guess your problem is in accept function.
As said in Linux Programmer's Manual:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
The addrlen argument is a value-result argument: the caller must initialize it to contain the size (in bytes) of the structure pointed to by addr; on return it will contain the actual size of the peer address.
Do yourself a favor and buy yourself "UNIX Network Programming" ISBN-10: 0139498761
There is way more to socket programming than meets the eye.
For one, how are you going to know on the receiving end how long the sent string is? Are you going to presume it's always 12, in most practical examples it won't be the same every time.
Are you going to read until you hit an end of string, or are you going to send an integer at the start to tell the reader what the length is?
If you use an integer do you know about endian-ness?
Are you really going to learn anything if we do your homework for you? Presumably you're in college and paying your tuition. Are you there to pass or are you there to learn?

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