Hi Ashish,

On Wed, 5 Oct 2011, Ashish SHUKLA wrote:

I wasn't aware of both sockaddr_storage, and getnameinfo(). They seem good to
me, and I've updated diff[1] to use them.

References:
[1]  http://people.freebsd.org/~ashish/fossil-ipv6-rev-proxy.diff

A few comments (not all about your code):

You are using the ss_len member of struct sockaddr_storage. 'ss_len' is not required to be there by Posix AFAIK. You can just use the size returned by 'getsockname' or 'getpeername' (third argument).

In my own code I use getaddrinfo(..."localhost", "<port>") to generate one or more sockets to allow connection to localhost only. Works quite well, and you don't need any special-purpose code for generating a local-only socket. This would require restructuring the cgi server code to handle multiple sockets, because on some systems (FreeBSD) you _will_ get multiple sockets. In general it should be possible to write both the client and server code without referring to a specific protocol (IPv4 or IPv6), except if you want to pass a specific protocol to getaddrinfo.

It would simplify things if you pass the port number to 'getnameinfo' as a string in stead of assigning it later in your code. This code could then be removed:

+    if(i->ai_family == AF_INET) {
+      ((struct sockaddr_in*)i->ai_addr)->sin_port = htons(g.urlPort);
+    } else if(i->ai_family == AF_INET6) {
+      ((struct sockaddr_in6*)i->ai_addr)->sin6_port = htons(g.urlPort);
+    }


The existing code (before your patch) has one problem: if a connection disappears between the return of 'select' and the 'accept' call the accept can 'hang' until the next connection gets made. The 60 second timeout will therefor not always work. The way to get around that is set the listening socket to non-blocking, and turn non-blocking back off for the socket you get from 'accept'. If accept produces and error with errno equal to EWOULDBLOCK or ECONNABORTED you just ignore it.

I've attached some sample code that more or less implements this. The code does not fork a new process, but it does the other stuff.

Ge'
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/select.h>
#include <fcntl.h>
#include <unistd.h>

int open_server_sockets(int socklist[], unsigned maxsock, const char *host, 
const char *svc)
{
     int r;
     struct addrinfo hints, *res, *res0;
     const char *fail = NULL, *code = NULL;
     unsigned numsock = 0;
     memset(&hints, 0, sizeof(hints));
     hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
     hints.ai_family = PF_UNSPEC;
     hints.ai_socktype = SOCK_STREAM;
     hints.ai_protocol = IPPROTO_TCP;

     r = getaddrinfo(host, svc, &hints, &res0);
     if(r != 0){
          fprintf(stderr, "can't resolve %s:%s: %s\n", (host ? host : "ANY"), 
(svc ? svc : "ANY"), gai_strerror(r));
          return -1;
     }
     for(res = res0; res != NULL && numsock < maxsock; res = res->ai_next){
          int s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
          int yes = 1, t;
          if(s < 0){
               fail = "socket"; code = strerror(errno);
               continue;
          }
          if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0){
               fail = "setsockopt"; code = strerror(errno);
               continue;
          }
          if(bind(s, res->ai_addr, res->ai_addrlen) < 0){
               fail = "bind"; code = strerror(errno);
               continue;
          }
          t = fcntl(s, F_GETFL, 0);
          if(t < 0 || fcntl(s, F_SETFL, t | O_NONBLOCK) < 0){
               fail = "fcntl"; code = strerror(errno);
               continue;
          }
          if(listen(s, 256) < 0){
               fail = "listen"; code = strerror(errno);
               continue;
          }
          socklist[numsock++] = s;
     }
     freeaddrinfo(res0);
     if(numsock == 0 && fail){
          fprintf(stderr, "%s failed: %s\n", fail, code);
          return -1;
     }else if(fail){
          fprintf(stderr, "warning: %s failed: %s\n", fail, code);
     }
     return (int)numsock;
}

int simple_server(int sock)
{
     char host[NI_MAXHOST], svc[NI_MAXSERV];
     int err, t;
     struct sockaddr_storage sa;
     socklen_t salen = sizeof(sa);
     if(getpeername(sock, (struct sockaddr *)&sa, &salen) < 0){
          fprintf(stderr, "can't get peer address: %s\n", strerror(errno));
          return -1;
     }
     if((err = getnameinfo((struct sockaddr *)&sa, salen, host, sizeof(host), 
svc, sizeof(svc), NI_NUMERICHOST | NI_NUMERICSERV)) < 0){
         fprintf(stderr, "can decode socket address: %s\n", gai_strerror(err));
         return -1;
     }else{
          printf("connection originating from %s:%s\n", host, svc);
     }
     t = fcntl(sock, F_GETFL, 0);
     if(t < 0 || fcntl(sock, F_SETFL, t & ~O_NONBLOCK) < 0){
          fprintf(stderr, "can't turn off O_NONBLOCK: %s\n", strerror(errno));
          return -1;
     }
     return 0;
}

int main ()
{
     enum { N = 20 };
     int socklist[N];
     int n = open_server_sockets(socklist, N, "localhost", "60999");
     if(n <= 0)
          return EXIT_FAILURE;
     while(n > 0){
          int i, maxfd = -1;
          fd_set rd;
          FD_ZERO(&rd);
          for(i = 0; i < n; i++){
               FD_SET(socklist[i], &rd);
               if(socklist[i] > maxfd)
                    maxfd = socklist[i];
          }
          int r = select(maxfd+1, &rd, NULL, NULL, NULL);
          if(r < 0){
               fprintf(stderr, "select: %s\n", strerror(errno));
               return EXIT_FAILURE;
          }
          i = 0;
          while(i < n){
               if(FD_ISSET(socklist[i], &rd)){
                    struct sockaddr_storage sa;
                    socklen_t salen = sizeof(sa);
                    int fd = accept(socklist[i], (struct sockaddr *)&sa, 
&salen);
                    if(fd < 0){
                         switch(errno){
                         case EWOULDBLOCK: case ECONNABORTED:
                              i++;
                              break;
                         default:
                              (void)close(socklist[i]);
                              socklist[i] = socklist[--n];
                         }
                    }else{
                         (void)simple_server(fd);
                         (void)close(fd);
                         i++;
                    }
               }else{
                    i++;
               }
          }
     }

     return EXIT_SUCCESS;
}
_______________________________________________
fossil-users mailing list
fossil-users@lists.fossil-scm.org
http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users

Reply via email to