#include <errno.h>       /* obligatory includes */
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>

#include <time.h>

#include <sys/stat.h>
#include <unistd.h>

/* Log level constants */
#define LOG_DEBUG 0
#define LOG_MSG 1
#define LOG_REQ 2
#define LOG_ERR 3

#define VERSION "Meredydd Luff's Surfboard/1.1.6 (UNIX/beta)"

#define CGI_ENV_LEN 64
/*      ^^^^^^^^^^^ = max. number of environment variables for CGI script*/

char hostname[1024]="";

/* This can be overridden from the command line */
char conffile[1024] = "/etc/surfboard/surfboard.conf";

/* All of these can be overridden from the config file */
char docroot[1024] = "/pub";
char logfile[1024] = "/var/log/surfboard/httpd.log";
char mimefile[1024] = "/etc/surfboard/mime.conf";
char mime_default[512] = "text/plain";
char dirindex[1024] = "index.html";

int myport=80;
int uid=99; /* "nobody" on my machine */
int gid=99; /* "nogroup" for me */

int log_threshold=LOG_MSG; /* By default, log everything bar debugs */
int header_max=1024;

void process(int, char *, int);
void readconf(void);
void log_msg(int level, char * msg);

char * getextension(char *);
void cgi_setenv(char **, char *, char *);
void getmime(char *, char *, char *);
void dumperror(int, int, char *);
void mk_index(int, char *, char *, int);
void fillout_header(char *);
void add2header(char*, char *);
char readstr(int, char *, int);
void writestr(int, char *);
void chomp(char *);
int stripqmark(char *);
int setupsock(void);
void fireman(void);

main(int argc, char * argv[])
{
  int s, a; 

  if(argc>1) { strcpy(conffile, argv[1]); }

  readconf();

  log_msg(LOG_MSG, "Surfboard started");

  if((s=setupsock())<0)
  {
    log_msg(LOG_ERR, "Could not open socket");
    perror("Could not open socket");
    return 1;
  }

  signal(SIGCHLD, fireman);

  if(fork()) { exit(0); }

  while(1)
  {
    int ns;
    while((ns=accept(s, NULL, NULL))<0);
    if(!fork()) {
      close(ns);
    } else { 
      if(uid) { setuid(uid); }
      if(gid) { setgid(gid); }
      process(ns, docroot, s);
      exit(0);
    }
  }
}
/************************************************************************/
void process(int s, char * docroot, int orgs)
{
  char * http_header;
  char http_version[10]="NONE";
  int do_header=0;
  char rawreq[1024];
  char content_type[512];
  char handler[1024]="dump";
  char req[2048];
  char method[10];
  char buf[2048];

  int methodok=0;

  struct stat file_inf;

  log_msg(LOG_DEBUG, "Request received");


  http_header=(char *)malloc(header_max * sizeof(char));
  http_header[0]='\0';

  readstr(s, method, 10);
  
  if(!strcmp(method, "GET"))
  {
    methodok=1;
  }

  if(!methodok)
  {
    sprintf(buf, "Unknown method \"%s\", bailing out with a 400", method);
    log_msg(LOG_ERR, buf);
    add2header(http_header, "HTTP/1.1 400 Bad Request\r\n");
    fillout_header(http_header);
    add2header(http_header, "Content-type: text/html\r\n\r\n");
    if(do_header) { writestr(s, http_header); }
    dumperror(s, 400, "");
    exit(0);
  }

  if(readstr(s, rawreq, 1024)==' ')
  {
    char c=' ', oldc='\n';

    readstr(s, http_version, 10);
    do_header=1;
    
    /* Flush the browser options - perhaps I'll take some notice of them
       sometime ;-)
    */
    while(1)
    {
      while(read(s, &c, 1)<1);
      if(c=='\r') { continue; }
      if(c=='\n' && oldc=='\n') { break; }
      oldc=c;
    }
  } else {
    do_header=0;
  }

  sprintf(buf, "Asked for %s", rawreq);
  log_msg(LOG_DEBUG, buf);

  if(strstr(rawreq, "..") || strstr(rawreq, "`"))
  {
    log_msg(LOG_ERR, "Relative path and/or shell escape - ATTACK ATTEMPT");
    add2header(http_header, "HTTP/1.1 400 Bad Request\r\n");
    fillout_header(http_header);
    add2header(http_header, "Content-type: text/html\r\n\r\n");
    if(do_header) { writestr(s, http_header); }
    dumperror(s, 400, "");
    exit(0);
  }

  strcpy(req, docroot);
  strcat(req, rawreq);
  if(strstr(req, "?"))
  {
    char * p;
    p=(char *)strstr(req, "?");
    *p='\0';
  }

  errno=0;

  stat(req, &file_inf);

  if(errno==ENOENT)
  {
    add2header(http_header, "HTTP/1.1 404 Not Found\r\n");
    fillout_header(http_header);
    add2header(http_header, "Content-type: text/html\r\n\r\n");
    if(do_header) { writestr(s, http_header); }
    sprintf(buf, "%s %s %s 404", method, rawreq, http_version);
    log_msg(LOG_REQ, buf);
    dumperror(s, 404, rawreq);
    exit(0);
  }
  if(S_ISDIR(file_inf.st_mode))
  {
    log_msg(LOG_DEBUG, "Indexing directory");

    mk_index(s, req, rawreq, do_header);
    /* also handles redirect to put slashes on dir names*/

    sprintf(buf, "Finished indexing, now requesting %s", req);
    log_msg(LOG_DEBUG, buf);
  }

  log_msg(LOG_DEBUG, "Determining MIME type...");

  getmime(req, content_type, handler);

  sprintf(buf, "Determined MIME type: %s (action:%s)", content_type, handler);
  log_msg(LOG_DEBUG, buf);

  if(!strcmp(handler, "dump"))
  {
    int rdhandle;

    log_msg(LOG_DEBUG, "Dumping file");
    errno=0;

    rdhandle=open(req, O_RDONLY);
    switch(errno)
    {
      case(0) : { break; }
      default : {
        add2header(http_header, "HTTP/1.1 499 Unknown Error\r\n");
        fillout_header(http_header);
        add2header(http_header, "Content-type: text/html\r\n\r\n");
        if(do_header) { writestr(s, http_header); }
        sprintf(buf, "%s %s %s 499", method, rawreq, http_version);
        log_msg(LOG_REQ, buf);
        dumperror(s, 499, rawreq);
        exit(0);
                }
    }

    http_header[0]='\0';
    add2header(http_header, "HTTP/1.1 200 OK\r\n");
    fillout_header(http_header);
    add2header(http_header, "Content-type: ");
    add2header(http_header, content_type);
    add2header(http_header, "\r\n\r\n");
    if(do_header) { writestr(s, http_header); }
    sprintf(buf, "%s %s %s 200", method, rawreq, http_version);
    log_msg(LOG_REQ, buf);
    stat(req, &file_inf);
    while(file_inf.st_size>0)
    {
      int dlen;
      dlen=read(rdhandle, buf, 1024);
      file_inf.st_size-=dlen;
      write(s, buf, dlen);
    }
    close(rdhandle);
    log_msg(LOG_DEBUG, "File closed, going to the big exit statement in the sky...");
  }

  if(handler[0]=='/' || !strcmp(handler, "cgi") || !strcmp(handler, "cgi-nph"))
  {
    int qpos=0;
    char * cgienv[CGI_ENV_LEN];
    char path[1024];

    strcpy(path, req);
    for(qpos=strlen(path)-1; qpos>0; qpos--)
    {
       if(path[qpos]=='/') { break; }
       path[qpos]='\0';
    }
    chdir(path);

    log_msg(LOG_DEBUG, "Identified as CGI script/handler");

    if(handler[0]=='/') { stat(handler, &file_inf); }

    if(!(file_inf.st_mode&S_IXOTH))
    {
      http_header[0]='\0';
      if(do_header)
      {
        add2header(http_header, "HTTP/1.1 403 Forbidden\r\n");
        fillout_header(http_header);
        add2header(http_header, "Content-type: text/html\r\n\r\n");
        writestr(s, http_header);
      }
      sprintf(buf, "%s %s %s 403", method, rawreq, http_version);
      log_msg(LOG_REQ, buf);
      dumperror(s, 403, rawreq);
      exit(0);
    }

    if(!strcmp(handler, "cgi"))
    {
      log_msg(LOG_DEBUG, "Not NPH, building header");
      http_header[0]='\0';
      add2header(http_header, "HTTP/1.1 200 OK\r\n");
      fillout_header(http_header);
      writestr(s, http_header); /* Broken if HTTP/x.x not specified! */
    }
    close(1); /* stdout */
    log_msg(LOG_DEBUG, "stdout closed");

    if(dup(s)!=1)
    {
      log_msg(LOG_ERR, "CGI script stdout redirection failed");
      http_header[0]='\0';
      add2header(http_header, "HTTP/1.1 500 Internal Server Error\r\n");
      fillout_header(http_header);
      if(do_header) { writestr(s, http_header); }
      dumperror(s, 500, "");
    }

    log_msg(LOG_DEBUG, "Setting up environment variables");

    cgienv[0]=NULL;

    /* Got this list from the CGI 1.1 specification
	(http://hoohoo.ncsa.uiuc.edu/cgi/)
    */
    cgi_setenv(cgienv, "SERVER_SOFTWARE", VERSION);
    cgi_setenv(cgienv, "SERVER_NAME", hostname);
    cgi_setenv(cgienv, "GATEWAY_INTERFACE", "CGI/1.1");
    cgi_setenv(cgienv, "SERVER_PROTOCOL", http_version);
    sprintf(buf, "%d", myport);
    cgi_setenv(cgienv, "SERVER_PORT", buf);
    cgi_setenv(cgienv, "REQUEST_METHOD", method);
    /*
	Neither PATH_INFO nor PATH_TRANSLATED are set, as I haven't a clue
	what they are! Any help to m_luff@wincoll.ac.uk
    */
    log_msg(LOG_DEBUG, "Stripping arguments...");
    qpos=stripqmark(rawreq);
    cgi_setenv(cgienv, "SCRIPT_NAME", rawreq);
    log_msg(LOG_DEBUG, "Stripped arguments OK");
    /* cgi_setenv(cgienv, "REMOTE_ADDR", ); */		/* STORE IT */
    /* cgi_setenv(cgienv, "REMOTE_HOST", );  - Leaving unset */
    /* AUTH_TYPE is not set */
    /* REMOTE_USER is not set */
    /* REMOTE_IDENT is not set */
    /* CONTENT_TYPE is not set */
    /* CONTENT_LENGTH is not set */

    /* One day I'll put in HTTP_xxx from the client */

    if(handler[0]=='/')
    {
      log_msg(LOG_DEBUG, "Launching CGI handler");
      cgi_setenv(cgienv, "QUERY_STRING", rawreq+qpos);
      execle(handler, handler, req, NULL, cgienv);
    } else if(strstr(rawreq+qpos, "=") || *(rawreq+qpos)=='\0') {
      log_msg(LOG_DEBUG, "Query string");
      cgi_setenv(cgienv, "QUERY_STRING", rawreq+qpos);
      execle(req, req, NULL, cgienv);
    } else {
      log_msg(LOG_DEBUG, "Command-line option");
      execle(req, req, rawreq+qpos, NULL, cgienv);
    }
    log_msg(LOG_ERR, "execle() call returned!");
    perror("execle()");
    dup(2); /* stderr is copied onto the first free descriptor - stdout,
		in case something went wrong */
  }

  free(http_header);
  close(s);
}
/************************************************************************/
void dumperror(int s, int num, char * req)
{
  char buf[1024];

  switch(num)
  {
    case(400) : {
      writestr(s, "<HTML>\n<HEAD><TITLE>400 Bad Request</TITLE></HEAD>\n");
      writestr(s, "<BODY>\n<H1 ALIGN=CENTER>400 Bad Request</H1>\n");
      writestr(s, "<P ALIGN=CENTER><I>Your client sent an invalid request, ");
      writestr(s, "or one which the server cannot deal with.</I></P>\n");
      writestr(s, "<P ALIGN=RIGHT>" VERSION "</P>\n</BODY>\n</HTML>\n");
      close(s);
      break;
                }

    case(403) : {
      writestr(s, "<HTML>\n<HEAD><TITLE>403 Permission Denied</TITLE></HEAD>\n");
      writestr(s, "<BODY>\n<H1 ALIGN=CENTER>403 Permission Denied</H1>\n");
      writestr(s, "<P ALIGN=CENTER><I>You do not have permission to ");
      sprintf(buf, "access %s on this server</I></P>\n<P ALIGN=RIGHT>" VERSION
	"</P>\n</BODY>\n</HTML>\n", req);
      writestr(s, buf);
      close(s);
      break;
                }

    case(404) : {
      writestr(s, "<HTML>\n<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
      writestr(s, "<BODY>\n<H1 ALIGN=CENTER>404 Not Found</H1>\n");
      writestr(s, "<P ALIGN=CENTER><I>The specified file was not ");
      writestr(s, "found</I></P>\n<P ALIGN=RIGHT>" VERSION
	"</P>\n</BODY>\n</HTML>\n");
      close(s);
      break;
                }

    case(499) : {
      writestr(s, "<HTML>\n<HEAD><TITLE>499 Unknown Error</TITLE></HEAD>\n");
      writestr(s, "<BODY>\n<H1 ALIGN=CENTER>499 Unknown Error</H1>\n");
      writestr(s, "<P ALIGN=CENTER><I>An error occurred while opening ");
      writestr(s, "this file. What it is, I do not know! Contact ");
      writestr(s, "the webmaster.</I></P>\n<P ALIGN=RIGHT>" VERSION
	"</P>\n</BODY>\n</HTML>\n");
      close(s);
      break;
                }

    case(500) : {
      writestr(s, "<HTML>\n<HEAD><TITLE>500 Internal Server Error</TITLE></HEAD>\n");
      writestr(s, "<BODY>\n<H1 ALIGN=CENTER>500 Internal Server Error</H1>\n");
      writestr(s, "<P ALIGN=CENTER><I>Something went wrong inside"
	" the server. Perhaps a CGI script went awry, or something "
	"in the bowels of the httpd is broken. Get your admin to check the "
	"logfile for error messages.</I></P>\n<P ALIGN=RIGHT>" VERSION
	"</P>\n</BODY>\n</HTML>\n");
      close(s);
      break;
                }

    default   : {
      sprintf(buf, "<HTML>\n<HEAD><TITLE>%d - Unhandled HTTP Error"
	"</TITLE></HEAD>\n<BODY><H1 ALIGN=CENTER>%d - Unhandled HTTP "
	"Error</H1>\n<P ALIGN=CENTER><I>This is a bug in the web server. "
	"The server has trapped an error and knows what went wrong, but " 
	"the author has forgotten to write an explanation of this "
	"error code. Mail Meredydd Luff (at <A "
	"HREF=mailto:m_luff@wincoll.ac.uk>m_luff@wincoll.ac.uk</A>) "
	"and tell him what happened.</I></P>\n<P ALIGN=RIGHT>" VERSION
	"</P>\n</BODY>\n</HTML>\n", num, num);
      writestr(s, buf);
      close(s);
      break;
                }
  }
}
/************************************************************************/
void cgi_setenv(char ** env, char * name, char * value)
{
  int a=0;
  while(env[a]!=NULL) { a++; }
  if(a==CGI_ENV_LEN-1)
  {
    char buf[1024];
    sprintf(buf, "CGI environment full - cannot add %s=%s", name, value);
    log_msg(LOG_ERR, buf);
    return;
  }
  env[a+1]=NULL;
  env[a]=(char *)malloc((strlen(name)+strlen(value)+64)*sizeof(char));
  strcpy(env[a], name);
  strcat(env[a], "=");
  strcat(env[a], value);
}
/************************************************************************/
void redirect(int s, char * from, char * to, int do_header)
{
  char buf[4096];
  char * http_header;
  http_header=(char *)malloc(header_max*sizeof(char));

  http_header[0]='\0';
  add2header(http_header, "HTTP/1.1 301 Moved Permanently\r\n");
  fillout_header(http_header);
  add2header(http_header, "Location: ");
  add2header(http_header, to);
  add2header(http_header, "\r\n\r\n");
  if(do_header) { writestr(s, http_header); }
  sprintf(buf, "<HTML>\n<HEAD><TITLE>301 Moved Permanently</TITLE>"
	"</HEAD>\n<BODY><H1 ALIGN=CENTER>301 Moved Permanently</H1>\n"
	"<P ALIGN=CENTER><I>The URL %s has moved to <A HREF=\"%s\">%s</A>"
	"</I></P>\n<P ALIGN=RIGHT>" VERSION "</P>\n</BODY>\n</HTML>\n",
	from, to, to);
  writestr(s, buf);
  free(http_header);
}
/************************************************************************/
void mk_index(int s, char * req, char * rawreq, int do_header)
{
  char buf[4096];
  int pos=0;
  int p=0, do404=0;

  struct stat file_inf;

  if(req[strlen(req)-1]!='/')
  {
    char http_header[1024]="";
    char corrected[1024];

    strcpy(corrected, rawreq);
    strcat(corrected, "/");
    redirect(s, rawreq, corrected, do_header);
    exit(0);
  }

  errno=0;

  log_msg(LOG_DEBUG, "Trying:");

  do
  {
    strcpy(buf, req);

    p=pos;
    while(!(dirindex[p]=='\0' || isspace(dirindex[p]))) { p++; }

    if(dirindex[p]=='\0') { do404=1; }
    dirindex[p]='\0';
    if(dirindex[pos]=='/')
    { strcpy(buf, dirindex+pos); } else { strcat(buf, dirindex+pos); }

    log_msg(LOG_DEBUG, buf);

    errno=0;
    stat(buf, &file_inf);

    if(S_ISDIR(file_inf.st_mode)) { errno=ENOENT; }

    pos=p+1;
  } while(errno==ENOENT && !do404);

  if(do404 && errno==ENOENT)
  {
    char * http_header;
    http_header=(char *)malloc(header_max*sizeof(char));
    http_header[0]='\0';
    add2header(http_header, "HTTP/1.1 404 Not Found\r\n");
    fillout_header(http_header);
    add2header(http_header, "Content-type: text/html\r\n\r\n");
    if(do_header) { writestr(s, http_header); }
    dumperror(s, 404, rawreq);
    free(http_header);
    exit(0);
  } else {
    strcpy(req, buf);
  }

}
/************************************************************************/
void fillout_header(char * header)
{
  add2header(header, "Server: " VERSION "\r\n");
  add2header(header, "Connection: close\r\n");
}
/************************************************************************/
void getmime(char * req, char * content_type, char * handler)
{
  FILE * mime;
  char buf[4096];

  strcpy(content_type, mime_default);
  strcpy(handler, "dump");

  if((mime=fopen(mimefile, "r"))==NULL)
  {
    sprintf(buf, "Could not open MIME config file \"%s\", using default "
	"content type %s\n", mimefile, mime_default);
    log_msg(LOG_ERR, buf);
    strcpy(content_type, mime_default);
  } else {
    char thisext[512];
    char thistype[512];
    char thishandler[1024];
    char myext[512];

    strcpy(myext, getextension(req));
    while(!feof(mime))
    {
      fscanf(mime, " %s", thisext);
      if(thisext[0]=='#') { while(getc(mime)!='\n'); continue; }
      fscanf(mime, " %s ", thistype);
      fgets(thishandler, 1024, mime);
      chomp(thishandler);
      if(!strcmp(myext, thisext))
      { strcpy(content_type, thistype); strcpy(handler, thishandler); break; }
    }
    fclose(mime);
  }
}
/************************************************************************/
void add2header(char * header, char * line)
{
  if(strlen(header)+strlen(line)+1>header_max)
  {
    log_msg(LOG_ERR, "Header exceeded max length, line ignored");
  } else {
    strcat(header, line);
  }
}
/************************************************************************/
void readconf(void)
{
  FILE * conf;
  char cmd[512];
  char buf[1600];

  sprintf(buf, "Reading config from file \"%s\"", conffile);
  log_msg(LOG_DEBUG, buf);

  if((conf=fopen(conffile, "r"))==NULL)
  {
    sprintf(buf, "Could not open config file \"%s\"", conffile);
    log_msg(LOG_ERR, buf);
    fprintf(stderr, "%s\n", buf);
    exit(1);
  }

  while(1)
  {
    fscanf(conf, " %s ", cmd);
    if(feof(conf)) { break; }
    if(cmd[0]=='#') { while(getc(conf)!='\n'); continue; }

    if(!strcmp(cmd, "Port"))
    {
      fscanf(conf, " %d", &myport);
      continue;
    }

    if(!strcmp(cmd, "DocRoot"))
    {
      fscanf(conf, " %s", docroot);
      continue;
    }

    if(!strcmp(cmd, "LogFile"))
    {
      fscanf(conf, " %s", logfile);
      continue;
    }

    if(!strcmp(cmd, "LogThreshold"))
    {
      fscanf(conf, " %d", &log_threshold);
      continue;
    }

    if(!strcmp(cmd, "MimeFile"))
    {
      fscanf(conf, " %s", mimefile);
      continue;
    }

    if(!strcmp(cmd, "DefaultMime"))
    {
      fscanf(conf, " %s", mime_default);
      continue;
    }

    if(!strcmp(cmd, "SetUID"))
    {
      fscanf(conf, " %d", &uid);
      continue;
    }

    if(!strcmp(cmd, "SetGID"))
    {
      fscanf(conf, " %d", &gid);
      continue;
    }

    if(!strcmp(cmd, "DirIndex"))
    {
      fgets(dirindex, 1024, conf);
      chomp(dirindex);
      continue;
    }

    fprintf(stderr, "Surfboard - Invalid config option \"%s\"\n", cmd);
    fclose(conf);
    exit(1);
  }
  fclose(conf);
  log_msg(LOG_DEBUG, "Finished reading config file");
}
/************************************************************************/
void log_msg(int level, char * str)
{
  if(level>=log_threshold)
  {
    if(!strcmp(logfile, "-"))
    {
      fprintf(stderr, "Surfboard - Log level %d: %s\n", level, str);
    } else {
      FILE * logfileptr;
      int a;
      time_t now;

      time(&now);

      for(a=0; a<5; a++)
      {
        logfileptr=fopen(logfile, "a");
        if(logfileptr==NULL) { continue; }
        fprintf(logfileptr, "%d %s%s\n", level, ctime(&now), str);
        fclose(logfileptr);
        return;
      }
      printf("Surfboard - Cannot open logfile!\a\n");
      perror("Surfboard - Cannot open logfile!\a");
      printf("Surfboard - Log level %d: %s\n", level, str);
    }
  }
}
/************************************************************************/
char * getextension(char * s)
{
  char scopy[1024];
  char * retval;
  
  strcpy(scopy, s);
  retval=strstr(scopy, "?");
  if(retval==NULL)
  { retval=scopy+strlen(s); } else { *retval='\0'; }
  
  while(1)
  {
    retval--;
    if(*retval=='.')
    {     log_msg(LOG_DEBUG, retval+1);
 return (retval+1); }
    if(retval==scopy) { return s; }
  }
}
/************************************************************************/
void writestr(int s, char * buf)
{
  write(s, buf, strlen(buf));
}
/************************************************************************/
void chomp(char * s)
{
  if(s[0]=='\0') { return; }
  if(s[strlen(s)-1]=='\n') { s[strlen(s)-1]='\0'; }
}
/************************************************************************/
int stripqmark(char * s)
{
  int pos=0;

  do
  {
    if(s[pos]=='?') { s[pos]='\0'; return pos+1; }
    pos++;
  } while(s[pos]!='\0');
  return pos;
}
/************************************************************************/
char readstr(int s, char * buf, int maxlen)
{
  char c;
  int pos=0;
  
  while(1)
  {
    while(read(s, &c, 1)<1);
    if(c=='\r') { continue; } /* There's a \n coming... */
    if(isspace(c) || pos==maxlen-1) { buf[pos]='\0'; return c; }
    buf[pos]=c;
    pos++;
  }
}
/************************************************************************/
int setupsock(void)
{
  int s;
  struct sockaddr_in myopen;
  struct hostent *hp;

  memset(&myopen, 0, sizeof(struct sockaddr_in)); /* clear my address */
  gethostname(hostname, 1024);                  /* who are we? (also
						   store for later) */
  hp= gethostbyname(hostname);                  /* get our address info */
  if (hp == NULL)                             /* we don't exist !? */
    return(-1);
  myopen.sin_family= hp->h_addrtype;              /* this is our host address */
  myopen.sin_port= htons(myport);                 /* this is our port number */
  if ((s= socket(AF_INET, SOCK_STREAM, 0)) < 0) /* create socket */
    return(-1);
  if (bind(s,(struct sockaddr *)&myopen,sizeof(struct sockaddr_in)) < 0) {
    close(s);
    return(-1);                               /* bind address to socket */
  }
  listen(s, 3);                               /* max # of queued connects */
  return(s);
}
/*************************************************************************/
/* Fireman - anti-zombie code. Greek to me, but it works ;-) */
void fireman(void)
{
  while (waitpid(-1, NULL, WNOHANG) > 0);
}
