/* 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 <stdio.h> #include <string.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #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); }