From: drtr@ast.cam.ac.uk (David Robinson)
Subject: Allow Apache to bind to multiple ports
Affects: httpd.h, http_conf_globals.h, http_config.c, htp_core.c, http_main.c,
         util.c, conf.h
ChangeLog: Add Listen directive, causing Apache to bind to the address and
           port specified. Add a host_port element to the server structure,
           detailing which port this server matches (0 for any port). This
           is set from the port in the <VirtualHost addr:port>, unlike the
           'port' structure element, which is set by the Port command.
           Allow <VirtualHost *>, <VirtualHost *:port>, VirtualHost addr:*>
           syntaxes.
Comments: Probably conflicts with the NeXT setsockopt patch.

*** httpd.h.orig	Wed Jan 31 02:54:07 1996
--- httpd.h	Mon Feb 12 14:53:12 1996
***************
*** 237,242 ****
--- 237,243 ----
  typedef struct conn_rec conn_rec;
  typedef struct server_rec server_rec;
  typedef struct request_rec request_rec;
+ typedef struct listen_rec listen_rec;
  
  struct request_rec {
  
***************
*** 367,372 ****
--- 368,374 ----
    
    char *server_admin;
    char *server_hostname;
+   short port;                    /* for redirects, etc. */
    
    /* Log files --- note that transfer log is now in the modules... */
    
***************
*** 375,380 ****
--- 377,383 ----
    
    /* Module-specific configuration for server, and defaults... */
  
+   int is_virtual;               /* true if this is the virtual server */
    void *module_config;		/* Config vector containing pointers to
  				 * modules' per-server config structures.
  				 */
***************
*** 385,388 ****
  
!   short port;
!   struct in_addr host_addr;	/* Specific address, if "virtual" server */
    int timeout;			/* Timeout, in seconds, before we give up */
--- 388,391 ----
  
!   struct in_addr host_addr;	/* The bound address, for this server */
!   short host_port;              /* The bound port, for this server */
    int timeout;			/* Timeout, in seconds, before we give up */
***************
*** 395,397 ****
--- 395,404 ----
+ /* These are more like real hosts than virtual hosts */
+ struct listen_rec {
+     listen_rec *next;
+     struct sockaddr_in local_addr; /* local IP address and port */
+ /* more stuff here, like which protocol is bound to the port */
+ };
+ 
  /* Prototypes for utilities... util.c.
   */
  
***************
*** 439,445 ****
       
  char *get_local_host(pool *);
  struct in_addr get_local_addr (int sd);
! unsigned long get_virthost_addr (char *hostname, int wild_allowed);
  void get_remote_host(conn_rec *conn);
  int get_portnum(int sd);
       
--- 449,455 ----
       
  char *get_local_host(pool *);
  struct in_addr get_local_addr (int sd);
! unsigned long get_virthost_addr (char *hostname, short int *port);
  void get_remote_host(conn_rec *conn);
  int get_portnum(int sd);
       
*** http_main.c.orig	Tue Jan 30 02:19:19 1996
--- http_main.c	Mon Feb 12 15:32:05 1996
***************
*** 101,106 ****
--- 101,107 ----
  char *pid_fname;
  char *server_argv0;
  struct in_addr bind_address;
+ listen_rec *listeners;
  int daemons_to_start;
  int daemons_min_free;
  int daemons_max_free;
***************
*** 115,120 ****
--- 116,123 ----
  JMP_BUF jmpbuffer;
  JMP_BUF restart_buffer;
  int sd;
+ static fd_set listenfds;
+ static int listenmaxfd;
  pid_t pgrp;
  
  /* one_process --- debugging mode variable; can be set from the command line
***************
*** 653,664 ****
  pool *pconf;			/* Pool for config stuff */
  pool *ptrans;			/* Pool for per-transaction stuff */
  
! server_rec *find_virtual_server (struct in_addr server_ip, server_rec *server)
  {
      server_rec *virt;
  
      for (virt = server->next; virt; virt = virt->next)
! 	if (virt->host_addr.s_addr == server_ip.s_addr)
  	    return virt;
  
      return server;
--- 656,670 ----
  pool *pconf;			/* Pool for config stuff */
  pool *ptrans;			/* Pool for per-transaction stuff */
  
! server_rec *find_virtual_server (struct in_addr server_ip, int port,
! 				 server_rec *server)
  {
      server_rec *virt;
  
      for (virt = server->next; virt; virt = virt->next)
! 	if ((virt->host_addr.s_addr == htonl(INADDR_ANY) ||
! 	     virt->host_addr.s_addr == server_ip.s_addr) &&
! 	    (virt->host_port == 0 || virt->host_port == port))
  	    return virt;
  
      return server;
***************
*** 666,685 ****
  
  void default_server_hostnames(server_rec *s)
  {
      /* Main host first */
      
      if (!s->server_hostname)
  	s->server_hostname = get_local_host(pconf);
  
      /* Then virtual hosts */
      
      for (s = s->next; s; s = s->next)
  	if (!s->server_hostname) {
! 	    struct hostent *h = gethostbyaddr ((char *)&(s->host_addr),
! 					       sizeof (struct in_addr),
! 					       AF_INET);
! 	    if (h != NULL) {
! 		s->server_hostname = pstrdup (pconf, (char *)h->h_name);
  	    }
  	}
  }
--- 672,698 ----
  
  void default_server_hostnames(server_rec *s)
  {
+     struct hostent *h;
+     char *def_hostname;
      /* Main host first */
      
      if (!s->server_hostname)
  	s->server_hostname = get_local_host(pconf);
  
+     def_hostname = s->server_hostname;
+ 
      /* Then virtual hosts */
      
      for (s = s->next; s; s = s->next)
  	if (!s->server_hostname) {
! 	    if (s->host_addr.s_addr == htonl(INADDR_ANY))
! 		s->server_hostname = def_hostname;
! 	    else
! 	    {
! 		h = gethostbyaddr ((char *)&(s->host_addr),
! 				   sizeof (struct in_addr), AF_INET);
! 		if (h != NULL)
! 		    s->server_hostname = pstrdup (pconf, (char *)h->h_name);
  	    }
  	}
  }
***************
*** 704,710 ****
      conn = (conn_rec *)pcalloc(p, sizeof(conn_rec));
      
      conn->pool = p;
!     conn->server = find_virtual_server (saddr->sin_addr, server);
      conn->client = out;
      conn->request_in = in;
      
--- 717,724 ----
      conn = (conn_rec *)pcalloc(p, sizeof(conn_rec));
      
      conn->pool = p;
!     conn->server = find_virtual_server(saddr->sin_addr, ntohs(saddr->sin_port),
! 				       server);
      conn->client = out;
      conn->request_in = in;
      
***************
*** 780,785 ****
  	update_child_status (child_num, SERVER_READY);
  	
  	accept_mutex_on();  /* Lock around "accept", if necessary */
! 	
! 	while ((csd=accept(sd, &sa_client, &clen)) == -1) 
!            if (errno != EINTR) 
--- 794,815 ----
  	update_child_status (child_num, SERVER_READY);
  	
  	accept_mutex_on();  /* Lock around "accept", if necessary */
! 
! 	if (listeners != NULL)
! 	{
! 	    fd_set fds;
! 
! 	    for (;;) {
! 		memcpy(&fds, &listenfds, sizeof(fd_set));
! 		csd = select(listenmaxfd+1, &fds, NULL, NULL, NULL);
! 		if (csd == -1 && errno != EINTR)
! 		    log_error("select error", server_conf);
! 		if (csd <= 0) continue;
! 		for (sd=listenmaxfd; sd >= 0; sd--)
! 		    if (FD_ISSET(sd, &fds)) break;
! 		if (sd < 0) continue;
! 
! 		clen=sizeof(sa_client);
! 		do csd=accept(sd, &sa_client, &clen);
! 		while (csd == -1 && errno == EINTR);
! 		if (csd != -1) break;
***************
*** 816,818 ****
--- 816,823 ----
+ 	    }
+ 	} else
+ 	    while ((csd=accept(sd, &sa_client, &clen)) == -1) 
+ 		if (errno != EINTR) 
+ 		    log_error("socket error: accept failed", server_conf);

	accept_mutex_off(); /* unlock after "accept" */

***************
*** 848,859 ****
      }
  }
  
  /*****************************************************************
   * Executive routines.
   */
  
- static int keepalive_value = 1;  
- static int one = 1;
  static int num_children = 0;
  
  void standalone_main(int argc, char **argv)
--- 883,936 ----
      }
  }
  
+ static int
+ make_sock(pool *pconf, const struct sockaddr_in *server)
+ {
+     int s;
+     const int one = 1;
+     const int keepalive_value = 1;  
+ 
+     if ((s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == -1) {
+         perror("socket");
+         fprintf(stderr,"httpd: could not get socket\n");
+         exit(1);
+     }
+ 
+     note_cleanups_for_fd (pconf, s); /* arrange to close on exec or restart */
+     
+     if((setsockopt(s, SOL_SOCKET,SO_REUSEADDR,(const char *)&one,sizeof(one)))
+        == -1) {
+         perror("setsockopt");
+         fprintf(stderr,"httpd: could not set socket option\n");
+         exit(1);
+     }
+     if((setsockopt(s, SOL_SOCKET,SO_KEEPALIVE,(const char *)&keepalive_value,
+         sizeof(keepalive_value))) == -1) {
+         perror("setsockopt"); 
+         fprintf(stderr,"httpd: could not set socket option SO_KEEPALIVE\n"); 
+         exit(1); 
+     }
+ 
+     if(bind(s, (struct sockaddr *)server,sizeof(struct sockaddr_in)) == -1)
+     {
+         perror("bind");
+ 	if (server->sin_addr.s_addr != htonl(INADDR_ANY))
+ 	    fprintf(stderr,"httpd: could not bind to address %s port %d\n",
+ 		    inet_ntoa(server->sin_addr), ntohs(server->sin_port));
+ 	else
+ 	    fprintf(stderr,"httpd: could not bind to port %d\n",
+ 		    ntohs(server->sin_port));
+         exit(1);
+     }
+     listen(s, 512);
+     return s;
+ }
+ 
+ 
  /*****************************************************************
   * Executive routines.
   */
  
  static int num_children = 0;
  
  void standalone_main(int argc, char **argv)
***************
*** 896,937 ****
      
      default_server_hostnames (server_conf);
  
!     if ((sd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == -1) {
!         fprintf(stderr,"httpd: could not get socket\n");
!         perror("socket");
!         exit(1);
!     }
  
!     note_cleanups_for_fd (pconf, sd); /* arrange to close on exec or restart */
!     
!     if((setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,(const char *)&one,sizeof(one)))
!        == -1) {
!         fprintf(stderr,"httpd: could not set socket option\n");
!         perror("setsockopt");
!         exit(1);
!     }
!     if((setsockopt(sd,SOL_SOCKET,SO_KEEPALIVE,(const void *)&keepalive_value,
!         sizeof(keepalive_value))) == -1) {
!         fprintf(stderr,"httpd: could not set socket option SO_KEEPALIVE\n"); 
!         perror("setsockopt"); 
!         exit(1); 
!     }
  
!     memset((char *) &sa_server, 0, sizeof(sa_server));
!     sa_server.sin_family=AF_INET;
!     sa_server.sin_addr=bind_address;
!     sa_server.sin_port=htons(server_conf->port);
!     if(bind(sd,(struct sockaddr *) &sa_server,sizeof(sa_server)) == -1) {
! 	if (bind_address.s_addr != htonl(INADDR_ANY))
! 	    fprintf(stderr,"httpd: could not bind to address %s port %d\n",
! 		    inet_ntoa(bind_address), server_conf->port);
! 	else
! 	    fprintf(stderr,"httpd: could not bind to port %d\n",
! 		    server_conf->port);
!         perror("bind");
!         exit(1);
      }
-     listen(sd, 512);
  
      set_signals();
      log_pid(pconf, pid_fname);
--- 973,1001 ----
      
      default_server_hostnames (server_conf);
  
!     if (listeners == NULL)
!     {
! 	memset((char *) &sa_server, 0, sizeof(sa_server));
! 	sa_server.sin_family=AF_INET;
! 	sa_server.sin_addr=bind_address;
! 	sa_server.sin_port=htons(server_conf->port);
  
! 	sd = make_sock(pconf, &sa_server);
!     } else
!     {
! 	listen_rec *lr;
! 	int fd;
  
! 	listenmaxfd = -1;
! 	FD_ZERO(&listenfds);
! 	for (lr=listeners; lr != NULL; lr=lr->next)
! 	{
! 	    fd = make_sock(pconf, &lr->local_addr);
! 	    FD_SET(fd, &listenfds);
! 	    if (fd > listenmaxfd) listenmaxfd = fd;
! 	}
! 	sd = -1;
      }
  
      set_signals();
      log_pid(pconf, pid_fname);
*** http_conf_globals.h.orig	Sun Dec 17 14:32:28 1995
--- http_conf_globals.h	Mon Feb 12 13:55:00 1996
***************
*** 62,67 ****
--- 62,68 ----
  extern gid_t group_id;
  extern int max_requests_per_child;
  extern struct in_addr bind_address;
+ extern listen_rec *listeners;
  extern int daemons_to_start;
  extern int daemons_min_free;
  extern int daemons_max_free;
*** http_core.c.orig	Sun Dec 17 14:32:29 1995
--- http_core.c	Mon Feb 12 14:51:59 1996
***************
*** 120,126 ****
  {
      core_server_config *conf =
        (core_server_config *)pcalloc(a, sizeof(core_server_config));
!     int is_virtual = is_virtual_server (s);
    
      conf->access_name = is_virtual ? NULL : DEFAULT_ACCESS_FNAME;
      conf->document_root = is_virtual ? NULL : DOCUMENT_LOCATION;
--- 120,126 ----
  {
      core_server_config *conf =
        (core_server_config *)pcalloc(a, sizeof(core_server_config));
!     int is_virtual = s->is_virtual;
    
      conf->access_name = is_virtual ? NULL : DEFAULT_ACCESS_FNAME;
      conf->document_root = is_virtual ? NULL : DOCUMENT_LOCATION;
***************
*** 446,452 ****
  
      if (endp) *endp = '\0';
      
!     if (is_virtual_server (main_server))
  	return "<VirtualHost> doesn't nest!";
      
      s = init_virtual_host (p, arg);
--- 446,452 ----
  
      if (endp) *endp = '\0';
      
!     if (main_server->is_virtual)
  	return "<VirtualHost> doesn't nest!";
      
      s = init_virtual_host (p, arg);
***************
*** 550,559 ****
  }
  
  char *set_bind_address (cmd_parms *cmd, void *dummy, char *arg) {
!     bind_address.s_addr = get_virthost_addr (arg, 1);
      return NULL;
  }
  
  /* Note --- change the mask below, and ErrorDocument will work from
   * .htaccess files.  The question is, what AllowOverride should webmasters
   * have to turn it off?
--- 550,587 ----
  }
  
  char *set_bind_address (cmd_parms *cmd, void *dummy, char *arg) {
!     bind_address.s_addr = get_virthost_addr (arg, NULL);
      return NULL;
  }
  
+ char *set_listener(cmd_parms *cmd, void *dummy, char *ips)
+ {
+     listen_rec *new;
+     char *ports;
+ 
+     if (cmd->server->is_virtual) return "Listen not allowed in <VirtualHost>";
+     ports=strchr(ips, ':');
+     if (ports != NULL)
+     {
+ 	if (ports == ips) return "Missing IP address";
+ 	else if (ports[0] == '\0')
+ 	    return "Address must end in :<port-number>";
+ 	*(ports++) = '\0';
+     } else
+ 	ports = ips;
+ 
+     new=palloc(cmd->pool, sizeof(listen_rec));
+     new->local_addr.sin_family = AF_INET;
+     if (ports == ips) /* no address */
+ 	new->local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+     else
+ 	new->local_addr.sin_addr.s_addr = get_virthost_addr(ips, NULL);
+     new->local_addr.sin_port = htons(atoi(ports));
+     new->next = listeners;
+     listeners = new;
+     return NULL;
+ }
+ 
  /* Note --- change the mask below, and ErrorDocument will work from
   * .htaccess files.  The question is, what AllowOverride should webmasters
   * have to turn it off?
***************
*** 620,625 ****
--- 648,655 ----
  { "MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, TAKE1, NULL },
  { "BindAddress", set_bind_address, NULL, RSRC_CONF, TAKE1,
    "'*', a numeric IP address, or the name of a host with a unique IP address"},
+ { "Listen", set_listener, NULL, RSRC_CONF, TAKE1,
+       "a port number or a numeric IP address and a port number"},
  { "<VirtualHost", virtualhost_section, NULL, RSRC_CONF, RAW_ARGS, NULL },
  { "</VirtualHost>", end_virtualhost_section, NULL, RSRC_CONF, NO_ARGS, NULL },
  { NULL },
*** http_config.c.orig	Sun Dec 17 14:32:29 1995
--- http_config.c	Mon Feb 12 14:54:37 1996
***************
*** 626,629 ****
      }
  #endif
  
-     s->port = 0;
--- 626,628 ----
      }
  #endif
 
***************
*** 634,640 ****
      s->access_confname = NULL;
      s->timeout = 0;
      s->do_rfc931 = 0;
!     s->host_addr.s_addr = get_virthost_addr (hostname, 0);
      s->next = NULL;
  
      s->module_config = create_empty_config (p);
--- 633,640 ----
      s->access_confname = NULL;
      s->timeout = 0;
      s->do_rfc931 = 0;
!     s->host_addr.s_addr = get_virthost_addr (hostname, &s->host_port);
!     s->port = s->host_port;  /* set them the same, by default */
      s->next = NULL;
  
      s->module_config = create_empty_config (p);
***************
*** 645,651 ****
  
  int is_virtual_server (server_rec *s)
  {
!     return s->host_addr.s_addr != htonl (INADDR_ANY);
  }
  
  void fixup_virtual_hosts (pool *p, server_rec *main_server)
--- 645,651 ----
  
  int is_virtual_server (server_rec *s)
  {
!     return s->is_virtual;
  }
  
  void fixup_virtual_hosts (pool *p, server_rec *main_server)
***************
*** 700,705 ****
--- 700,706 ----
      pid_fname = DEFAULT_PIDLOG;
      max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
      bind_address.s_addr = htonl(INADDR_ANY);
+     listeners = NULL;
  }
  
  server_rec *init_server_config(pool *p)
***************
*** 719,724 ****
--- 720,726 ----
  					       * don't match any real network
  					       * interface.
  					       */
+     s->host_port = 0; /* matches any port */
  
      s->module_config = create_server_config (p, s);
      s->lookup_defaults = create_default_per_dir_config (p);
*** conf.h.orig	Tue Jan 30 02:19:20 1996
--- conf.h	Mon Feb 12 14:58:25 1996
***************
*** 181,186 ****
--- 181,187 ----
  #undef NEED_STRDUP
  #define JMP_BUF sigjmp_buf
  #define FCNTL_SERIALIZED_ACCEPT
+ #include <sys/time.h>     
  
  #elif defined(SCO)
  #undef HAS_GMTOFF
*** util.c.orig	Sun Dec 17 14:32:29 1995
--- util.c	Mon Feb 12 14:53:20 1996
***************
*** 823,838 ****
  }
  #endif
  
! unsigned long get_virthost_addr (char *w, int wild_allowed) {
      struct hostent *hep;
      unsigned long my_addr;
!     
!     if (wild_allowed && !strcmp(w, "*")) 
  	return htonl(INADDR_ANY);
  	
      my_addr = inet_addr(w);
      if (my_addr != ((unsigned long) 0xffffffff))
  	return my_addr;
  
      hep = gethostbyname(w);
  	    
--- 823,857 ----
  }
  #endif
  
! /*
!  * Parses a host of the form <address>[:port]
!  * :port is permitted if 'port' is not NULL
!  */
! unsigned long get_virthost_addr (char *w, short int *ports) {
      struct hostent *hep;
      unsigned long my_addr;
!     char *p;
! 
!     p = strchr(w, ':');
!     if (ports != NULL)
!     {
! 	*ports = 0;
! 	if (p != NULL && strcmp(p+1, "*") != 0) *ports = atoi(p+1);
!     }
! 
!     if (p != NULL) *p = '\0';
!     if (strcmp(w, "*") == 0)
!     {
! 	if (p != NULL) *p = ':';
  	return htonl(INADDR_ANY);
+     }
  	
      my_addr = inet_addr(w);
      if (my_addr != ((unsigned long) 0xffffffff))
+     {
+ 	if (p != NULL) *p = ':';
  	return my_addr;
+     }
  
      hep = gethostbyname(w);
  	    
***************
*** 848,853 ****
--- 867,874 ----
  	exit(1);
      }
  	    
+     if (p != NULL) *p = ':';
+ 
      return ((struct in_addr *)(hep->h_addr))->s_addr;
  }
  
