/*
Lab. Calcolo II - Helper di rete.
File : shelper.c
Author : Francesco Prelz ($Author: prelz $)
e-mail : "francesco.prelz@mi.infn.it"
Revision history :
18-Aug-2000 Original release
Description: Helper routines for opening and connecting,
or listening to and connecting, a TCP socket.
There a certain number of quirks and catches
in the general purpose socket implementation,
so a higher-level approach is proposed.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define OPEN_CONNECT_TIMEOUT 5
int
SHLP_open_and_connect(char *server, unsigned short port)
{
int fd_socket;
static struct hostent *resolved_name=NULL;
struct protoent *prot_descr;
struct sockaddr_in origin;
struct sockaddr_in destination;
fd_set writefs;
struct timeval timeout;
int status,original_status;
struct passwd *thisuser;
int namelen;
int to_be_continued = 0;
char *temp;
int retcod;
int i;
/* Get destination address */
/* Trim trailing whitespace */
temp = (char *)malloc(strlen(server)+1);
if (temp == NULL)
{
perror("Cannot allocate temporary variable");
return(-1);
}
strcpy(temp, server);
for (i=strlen(temp)-1;i>=0;i--)
if (temp[i]==' ' || temp[i]=='\t' || temp[i]=='\r' || temp[i]=='\n')
temp[i]='\000';
else break;
if (resolved_name == NULL) /* Cached info if the same as last time */
resolved_name = gethostbyname(temp);
else if (strstr(resolved_name->h_name,temp)==NULL)
resolved_name = gethostbyname(temp);
free(temp);
if (resolved_name == NULL)
{
fprintf(stderr,"Can't resolve the IP address of %s.\n",server);
return(-1);
}
destination.sin_family = resolved_name->h_addrtype;
destination.sin_port = htons(port);
memcpy(&destination.sin_addr,resolved_name->h_addr_list[0],resolved_name->h_length);
prot_descr = getprotobyname("tcp");
if (prot_descr == NULL)
{
fprintf(stderr,"TCP protocol could not be found in /etc/protocols.\n");
return(-1);
}
if ((fd_socket=socket(PF_INET,SOCK_STREAM,prot_descr->p_proto))==-1)
{
fprintf(stderr,"Cannot create socket: %s\n",strerror(errno));
return(-1);
}
/* Make socket asynchronous in order to enforce connect timeout */
if ((original_status=fcntl(fd_socket,F_GETFL,NULL)) == -1)
{
perror("Cannot fcntl (GETFL) origin socket");
close(fd_socket);
return(-1);
}
status = original_status|FNDELAY;
if ((fcntl(fd_socket,F_SETFL,status)) == -1)
{
perror("Cannot fcntl (SETFL) origin socket");
close(fd_socket);
return(-1);
}
if (connect(fd_socket,(struct sockaddr *)&destination,sizeof(destination))==-1)
{
if (errno!=EINPROGRESS)
{
close(fd_socket);
return(-1);
}
else to_be_continued = 1;
}
/* restore socket (synchronous) status */
if ((fcntl(fd_socket,F_SETFL,original_status)) == -1)
{
perror("Cannot fcntl (SETFL) origin socket");
close(fd_socket);
return(-1);
}
FD_ZERO(&writefs);
FD_SET(fd_socket,&writefs);
timeout.tv_sec=OPEN_CONNECT_TIMEOUT;
timeout.tv_usec=0;
retcod = select(fd_socket+1,(fd_set *) 0,&writefs,(fd_set *) 0,&timeout);
if (retcod <= 0)
{
close(fd_socket);
return(-1);
}
if (to_be_continued != 0)
{
if (connect(fd_socket,(struct sockaddr *)&destination,sizeof(destination))==-1)
{
if (errno!=EISCONN)
{
close(fd_socket);
return(-1);
}
}
}
/* Ok, now the socket is open and connected. Return it. */
return(fd_socket);
}
int
SHLP_listen(unsigned short port)
{
int fd_socket;
struct sockaddr_in recvs;
struct protoent *prot_descr;
recvs.sin_family = AF_INET;
recvs.sin_addr.s_addr = htonl(INADDR_ANY);
recvs.sin_port = htons(port);
prot_descr = getprotobyname("tcp");
if (prot_descr == NULL)
{
fprintf(stderr,"TCP protocol could not be found in /etc/protocols.\n");
return(-1);
}
if ((fd_socket=socket(PF_INET,SOCK_STREAM,prot_descr->p_proto))==-1)
{
fprintf(stderr,"Cannot create socket: %s\n",strerror(errno));
return(-1);
}
if (bind(fd_socket,(struct sockaddr *)&recvs,sizeof(recvs)) == -1)
{
fprintf(stderr,"Cannot bind socket: %s\n",strerror(errno));
return(-1);
}
if (listen(fd_socket,1) == -1)
{
fprintf(stderr,"Cannot listen from socket: %s\n",strerror(errno));
return(-1);
}
/* Good: we are now listening. Return listen socket.*/
return(fd_socket);
}
int
SHLP_connect(int fd_socket, unsigned int in_timeout,
char *caller_id, int caller_id_size)
{
int read_socket;
struct timeval timeout;
struct timeval *req_timeout;
struct sockaddr_in cli_addr;
int addr_size;
struct hostent *resolved_client;
fd_set readfs;
int retcod;
FD_ZERO(&readfs);
FD_SET(fd_socket,&readfs);
if (in_timeout > 0)
{
timeout.tv_sec=in_timeout;
timeout.tv_usec=0;
req_timeout = &timeout;
}
else
{
/* Wait indefinitely. */
req_timeout = NULL;
}
if ((retcod=select(FD_SETSIZE,&readfs,(fd_set *) 0,(fd_set *) 0,req_timeout)) <= 0)
{
if (retcod == 0)
fprintf(stderr,"Done waiting for %d seconds\n",in_timeout);
else
perror("Error receiving data");
close(fd_socket);
return(-1);
}
addr_size = sizeof(cli_addr);
if ((read_socket = accept(fd_socket,(struct sockaddr *)&cli_addr,&addr_size)) == -1)
{
fprintf(stderr,"Cannot accept connection: %s\n",strerror(errno));
return(-1);
}
/* If requested, return the identity of the caller */
if (caller_id != NULL)
{
if ((resolved_client = gethostbyaddr((void *)&(cli_addr.sin_addr.s_addr),
sizeof(cli_addr.sin_addr.s_addr),
AF_INET)) == NULL)
{
strncpy(caller_id,inet_ntoa(cli_addr.sin_addr),caller_id_size);
}
else
{
strncpy(caller_id,resolved_client->h_name,caller_id_size);
}
}
/* Good: we accepted a connection on read_socket. Return it. */
return(read_socket);
}
int
SHLP_listen_and_connect(unsigned short port, unsigned int in_timeout,
char *caller_id, int caller_id_size)
{
int fd_listen;
int fd_connect;
fd_listen = SHLP_listen(port);
if (fd_listen < 0) return(-1);
fd_connect = SHLP_connect(fd_listen, in_timeout, caller_id, caller_id_size);
if (fd_connect < 0)
{
close(fd_listen);
return(-1);
}
/* We don't need to listen anymore */
close(fd_listen);
return(fd_connect);
}