[exim-cvs] cvs commit: exim/exim-test/src cf.c checkaccess.c…

トップ ページ
このメッセージを削除
このメッセージに返信
著者: Philip Hazel
日付:  
To: exim-cvs
題目: [exim-cvs] cvs commit: exim/exim-test/src cf.c checkaccess.c client.c fakens.c fd.c iefbr14.c loaded.c mtpscript.c server.c showids.c
ph10 2006/02/06 16:24:05 GMT

  Added files:
    exim-test/src        cf.c checkaccess.c client.c fakens.c fd.c 
                         iefbr14.c loaded.c mtpscript.c server.c 
                         showids.c 
  Log:
  CVSing the test suite.


  Revision  Changes    Path
  1.1       +715 -0    exim/exim-test/src/cf.c (new)
  1.1       +41 -0     exim/exim-test/src/checkaccess.c (new)
  1.1       +898 -0    exim/exim-test/src/client.c (new)
  1.1       +588 -0    exim/exim-test/src/fakens.c (new)
  1.1       +104 -0    exim/exim-test/src/fd.c (new)
  1.1       +11 -0     exim/exim-test/src/iefbr14.c (new)
  1.1       +42 -0     exim/exim-test/src/loaded.c (new)
  1.1       +169 -0    exim/exim-test/src/mtpscript.c (new)
  1.1       +612 -0    exim/exim-test/src/server.c (new)
  1.1       +26 -0     exim/exim-test/src/showids.c (new)


Index: cf.c
====================================================================
/* $Cambridge: exim/exim-test/src/cf.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */

  /************************************************
  *                  PH-Compare                   *
  ************************************************/


/* A program to compare two files line by line.

History:

It was originally written in C, but the C under
Panos is still a shambles (1986). Translated therefore
to BCPL -- this explains some of the odd style.

Modified to run on Archimedes, August 1987.
Modified to run under MSDOS, March 1989.
Modified to run under CINTERP interpreter, July 1989.
Modified to run under Unix, October 1989.

Translated back into C, March 1990! */

/* Copyright (c) 1986, 1987, 1989, 1990, 1994, 2001 by Philip Hazel */

/* Previously modified: October 1994*/
/* Last modified: September 2001 - a long-lived bug fixed! */


#include <stdio.h>
#include <errno.h>

#ifdef __STDC__
#include <string.h>
#include <stdlib.h>
#endif

/* ----- parameters ----- */

  #define version            8
  #define defaultstore  100000     /* default recovery buffer size */
  #define minstore         500     /* minimum recovery buffer size */


/* ----- misc defines ----- */

#define FALSE 0
#define TRUE 1

  #ifdef __STDC__
  #define pvoid     void
  #else
  #define pvoid
  #endif


#define EqString(s, t) (strcmp(s, t) == 0)

/* ----- line structure ----- */

  typedef struct line {
    struct line *next;
    int  number;
    char text[999999];
  } line;



/* ----- global variables ----- */

  FILE *f_one;           /* files */
  FILE *f_two;
  FILE *f_out;


  int lines_one = 0;            /* line counts */
  int lines_two = 0;
  int return_code = 0;
  int eof_one = FALSE;          /* eof flags */
  int eof_two = FALSE;
  int exact = FALSE;            /* TRUE => no strip spaces */
  int echo = TRUE;              /* TRUE => show mismatched lines */
  int sync_count = 3;           /* resync count */
  int storesize = defaultstore; /* size of each buffer */


  char *name_one = NULL;        /* file names */
  char *name_two = NULL;
  char *to_name = NULL;


  char *bufbase_one;            /* start buffer */
  char *bufbase_two;
  char *bufnext_one;            /* next free byte */
  char *bufnext_two;
  char *buftop_one;             /* end buffer */
  char *buftop_two;


  line *rootline_one;           /* mis-match point */
  line *rootline_two;
  line *lastline_one;           /* last in store */
  line *lastline_two;
  line *pline_one;              /* working line */
  line *pline_two;



  /*************************************************
  *             Help Information                   *
  *************************************************/


  void givehelp(pvoid)
  {
  printf("PH's CMP v%d\n", version);
  printf("Keywords:\n");
  printf("       <file>    ) files to compare\n");
  printf("       <file>    ) no keywords used\n");
  printf("-to    <file>      output destination\n");
  printf("-exact             include trailing spaces & match tabs\n");
  printf("-noecho            don't echo differences (just give line numbers)\n");
  printf("-s, -sync  <n>     set re-sync count, default 3\n");
  printf("-buffer <n>        buffer size (for each file) default 100000\n");
  printf("-id                give program version id\n");
  printf("-h, -help          give this help\n");
  printf("\nExamples:\n");
  printf("cmp old.f77 new.f77\n");
  printf("cmp first second -noecho -sync 1\n");
  printf("cmp large1 large2 -buffer 200000 -noecho -to diffs\n");
  }




  /************************************************
  *               Errors -- all serious           *
  ************************************************/


  void moan(code, text)
  int code;
  char *text;
  {
  fprintf(stderr, "\n** ");
  switch (code)
    {
    case 1:
    fprintf(stderr, "Unable to open file \"%s\"", text);
    if (errno)
      {
      fprintf(stderr, " - ");
      perror(NULL);
      }
    else fprintf(stderr, "\n");
    break;


    case 2:
    fprintf(stderr, "Buffer overflow for file \"%s\"\n", text);
    break;


    case 3:
    fprintf(stderr, "Two file names must be given\n");
    break;


    default:
    fprintf(stderr, "Unknown error %d\n", code);
    break;
    }


fprintf(stderr, "** CMP abandoned\n");
exit(99);
}



  /*************************************************
  *         Write line identification              *
  *************************************************/


  void write_id(n1, n2, c, name, p1, p2)
  int n1, n2, c;
  char *name, *p1, *p2;
  {
  if (n2 < 0) n2 = -n2;
  n2 -= 1;
  fprintf(f_out, "%cine", c);
  if (n1 == n2) fprintf(f_out, " %d of \"%s\"%s", n1, name, p1);
    else fprintf(f_out, "s %d-%d of \"%s\"%s", n1, n2, name, p2);
  }



  /*************************************************
  *           Write sequence of lines              *
  *************************************************/


  void write_lines(s, t)
  line *s, *t;
  {
  while (s != t)
    {
    char *p = s->text;
    while (*p != '\n') fputc(*p++, f_out);
    fputc('\n', f_out);
    s = s->next;
    }
  }




  /*************************************************
  *           Write separator rule                 *
  *************************************************/


void rule(s, l)
int s, l;
{
while (l-- > 0) fprintf(f_out, "%c", s);
fprintf(f_out, "\n");
}



  /*************************************************
  *          Write message on re-sync or eof       *
  *************************************************/


void write_message(tline_one, tline_two)
line *tline_one, *tline_two;
{
int s1 = rootline_one->number;
int t1 = tline_one->number;
int s2 = rootline_two->number;
int t2 = tline_two->number;
if (echo) rule('=', 15);

  if (s1 == t1)
    {
    write_id(s2, t2, 'L', name_two, " occurs ", " occur ");
    if (s1 < 0) fprintf(f_out, "at the end");
      else fprintf(f_out, "before line %d", s1);
    fprintf(f_out, " of \"%s\".\n", name_one);
    if (echo)
      {
      rule('-', 10);
      write_lines(rootline_two, tline_two);
      }
    }


  else if (s2 == t2)
    {
    write_id(s1, t1, 'L', name_one, " occurs ", " occur ");
    if (s2 < 0) fprintf(f_out, "at the end");
      else fprintf(f_out, "before line %d", s2);
    fprintf(f_out, " of \"%s\".\n", name_two);
    if (echo)
      {
      rule('-', 10);
      write_lines(rootline_one, tline_one);
      }
    }


  else if (t1 < 0 && t2 < 0)
    {
    fprintf(f_out, "From line %d of \"%s\" and line %d of \"%s\" ",
      rootline_one->number, name_one, rootline_two->number, name_two);
    fprintf(f_out, "the files are different.\n");
    if (echo)
      {
      rule('-', 10);
      if (-t1-s1 < 21) write_lines(rootline_one, tline_one);
        else fprintf(f_out, "... <more than 20 lines> ...\n");
      rule('-', 10);
      if (-t2-s2 < 21) write_lines(rootline_two, tline_two);
        else fprintf(f_out, "... <more than 20 lines> ...\n");
      }
    }


  else
    {
    write_id(s1, t1, 'L', name_one, " does ", " do ");
    fprintf(f_out, "not match ");
    write_id(s2, t2, 'l', name_two, ".\n", ".\n");
    if (echo)
      {
      rule('-', 10);
      write_lines(rootline_one, tline_one);
      rule('-', 10);
      write_lines(rootline_two, tline_two);
      }
    }
  }





  /*************************************************
  *           Advance to next line in store        *
  *************************************************/


/* A separate procedure exists for each file, for
simplicity and efficiency. */

int nextline_one(pvoid)
{
if (pline_one == NULL || pline_one->next == NULL) return FALSE;
pline_one = pline_one->next;
return TRUE;
}

int nextline_two(pvoid)
{
if (pline_two == NULL || pline_two->next == NULL) return FALSE;
pline_two = pline_two->next;
return TRUE;
}


  /*************************************************
  *             Read a line into store             *
  *************************************************/


/* A separate procedure exists for each file, for
simplicity and efficiency. */

void readline_one(pvoid)
{
int count = 0;
int c = fgetc(f_one);
line *nextline = (line *)bufnext_one;

bufnext_one = nextline->text;
if (bufnext_one >= buftop_one) moan(2, name_one);

nextline->next = NULL;

  lines_one ++;
  if (c == EOF)
    {
    eof_one = TRUE;
    nextline->number = -lines_one;
    }
  else
    {
    nextline->number = lines_one;
    for (;;)
      {
      if (c == EOF) c = '\n';
      if (c == '\n')
        {
        if (!exact)
          while (bufnext_one > nextline->text)
            { if (bufnext_one[-1] == ' ') bufnext_one--; else break; }
        *(bufnext_one++) = '\n';
        if (bufnext_one >= buftop_one) moan(2, name_one);
        break;
        }
      if (c == '\t' && !exact)
        do { *(bufnext_one++) = ' '; count++; } while ((count & 7) != 0);
      else { *(bufnext_one++) = c; count++; }
      if (bufnext_one >= buftop_one) moan(2, name_one);
      c = fgetc(f_one);
      }
    }


if (lastline_one != NULL) lastline_one->next = nextline;
lastline_one = nextline;
pline_one = nextline;

bufnext_one = (char *) (((int)bufnext_one+3) & (-4));
}



void readline_two(pvoid)
{
int count = 0;
int c = fgetc(f_two);
line *nextline = (line *)bufnext_two;

bufnext_two = nextline->text;
if (bufnext_two >= buftop_two) moan(2, name_two);

nextline->next = NULL;

  lines_two ++;
  if (c == EOF)
    {
    eof_two = TRUE;
    nextline->number = -lines_two;
    }
  else
    {
    nextline->number = lines_two;
    for (;;)
      {
      if (c == EOF) c = '\n';
      if (c == '\n')
        {
        if (!exact)
          while (bufnext_two > nextline->text)
            { if (bufnext_two[-1] == ' ') bufnext_two--; else break; }
        *(bufnext_two++) = '\n';
        if (bufnext_two >= buftop_two) moan(2, name_two);
        break;
        }
      if (c == '\t' && !exact)
        do { *(bufnext_two++) = ' '; count++; } while ((count & 7) != 0);
      else { *(bufnext_two++) = c; count++; }
      if (bufnext_two >= buftop_two) moan(2, name_two);
      c = fgetc(f_two);
      }
    }


if (lastline_two != NULL) lastline_two->next = nextline;
lastline_two = nextline;
pline_two = nextline;

bufnext_two = (char *) (((int)bufnext_two+3) & (-4));
}



  /**************************************************
  *              Compare two lines                  *
  **************************************************/


int compare_lines(a, b)
line *a, *b;
{
int n1 = a->number;
int n2 = b->number;
char *s = a->text;
char *t = b->text;

if (n1 < 0 && n2 < 0) return TRUE;
if (n1 < 0 || n2 < 0) return FALSE;

  while (*s == *t)
    {
    if (*s == '\n') return TRUE;
    s++; t++;
    }


return FALSE;
}


  /*************************************************
  *             Re-synchronizing code              *
  *************************************************/


int resync(pvoid)
{
int i;
int matched = TRUE;
line *tline_one = pline_one;
line *tline_two = pline_two;

  if (eof_one || eof_two) matched = FALSE; else
    {
    for (i = 1; i < sync_count; i++)
      {
      if (!nextline_one()) readline_one();
      if (!nextline_two()) readline_two();
      if (!compare_lines(pline_one, pline_two)) { matched = FALSE; break; }
      if (eof_one || eof_two) { matched = FALSE; break; }
      }
    }


  if (matched) write_message(tline_one, tline_two); else
    {
    pline_one = tline_one;
    pline_two = tline_two;
    }


return matched;
}



  /*************************************************
  *                 Main compare code              *
  *************************************************/


void compare(pvoid)
{
int matched = TRUE;

/* Big main loop - exit by return or unmatched at eof */

  while (matched)
    {
    /* First minor loop, while in step */


    while (matched && !eof_one && !eof_two)
      {
      /* Advance or read next lines */


      if (!nextline_one())
        {
        bufnext_one = bufbase_one;
        lastline_one = NULL;
        readline_one();
        }


      if (!nextline_two())
        {
        bufnext_two = bufbase_two;
        lastline_two = NULL;
        readline_two();
        }


      /* Compare and check for end of file */


      matched = compare_lines(pline_one, pline_two);


      } /* End first minor loop */


    if (matched) return;    /* successful end of file */


    /* There has been a mis-match */


    return_code++;
    rootline_one = pline_one;   /* Fail point */
    rootline_two = pline_two;


    /* Second minor loop, trying to regain sync */


    while (!eof_one || !eof_two)
      {
      /* Advance one and scan all of two */


      if (!eof_one)
        {
        line *zline = pline_two;
        if (!nextline_one()) readline_one();
        pline_two = rootline_two;
        for (;;)
          {
          if (compare_lines(pline_one, pline_two))
            {
            matched = resync();
            if (matched) break;
            }
          if (pline_two == zline) break;
          pline_two = pline_two->next;
          }
        if (matched) break;
        }


      /* Advance two and scan all of one */


      if (!eof_two)
        {
        line *zline = pline_one;
        if (!nextline_two()) readline_two();
        pline_one = rootline_one;
        for (;;)
          {
          if (compare_lines(pline_one, pline_two))
            {
            matched = resync();
            if (matched) break;
            }
          if (pline_one == zline) break;
          pline_one = pline_one->next;
          }
        if (matched) break;
        }


      } /* End second minor loop */


    } /* End of major loop */


write_message(lastline_one, lastline_two);
}




  /*************************************************
  *                   Entry Point                  *
  *************************************************/


int main(argc, argv)
int argc;
char **argv;
{
int argp = 1;
int arg_id = FALSE;
int arg_help = FALSE;

f_out = stdout;

/* Scan argument strings */

  while (argp < argc)
    {
    char  *arg = argv[argp];
    char **lv_name = (name_one == NULL)? &name_one:&name_two;  /* default for positional */
    int   *lv_value = NULL;
    int    value = TRUE;


    if (arg[0] == '-')
      {                            /* keyed argument */
      if (EqString(arg,"-help") || EqString(arg, "-h"))
        { arg_help = TRUE; value = FALSE; }
      else if (EqString(arg, "-id"))
        { arg_id = TRUE; value = FALSE; }
      else if (EqString(arg, "-exact"))
        { exact = TRUE; value = FALSE; }
      else if (EqString(arg, "-noecho"))
        { echo = FALSE; value = FALSE; }
      else if (EqString(arg, "-to")) lv_name = &to_name;
      else if (EqString(arg, "-sync") || EqString(arg, "-s"))
         lv_value = &sync_count;
      else if (EqString(arg, "-buffer")) lv_value = &storesize;
      else { printf("Unknown keyword %s\n", arg); exit(99); }


      if (++argp >= argc && value)
        { printf("Value for keyword %s missing\n", arg); exit(99); }
      }


    /* Deal with keys that take values */


    if (value)
      {
      if (lv_value == &sync_count || lv_value == &storesize)
        {
        int ch;
        int i = 0;
        char *argval = argv[argp++];
        *lv_value = 0;
        while ((ch = argval[i++]) != 0)
          {
          if ('0' <= ch && ch <= '9') *lv_value = 10*(*lv_value) + ch - '0'; else
            {
            printf("Number expected after \"%s\" but \"%s\" read\n",
              arg, argval);
            exit(99);
            }
          }
        }


      else if (*lv_name != NULL)
        {
        printf("Keyword expected but \"%s\" read", arg);
        printf(" - use \"cmp -h\" for help\n");
        exit(99);
        }
      else *lv_name = argv[argp++];
      }
    }


/* Deal with help and id */

  if (arg_id && !arg_help)
    {
    printf("PH's CMP v%d\n", version);
    exit(0);
    }


  if (arg_help)
    {
    givehelp();
    exit(0);
    }


/* Deal with file names */

if (name_one == NULL || name_two == NULL) moan(3, "");

  if (to_name != NULL)
    {
    f_out = fopen(to_name, "w");
    if (f_out == NULL) moan(1, to_name);
    }


/* Further general initialization */

if (storesize < minstore) storesize = defaultstore;
f_one = fopen(name_one, "r");
if (f_one == NULL) moan(1, name_one);
f_two = fopen(name_two, "r");
if (f_two == NULL) moan(1, name_two);

bufbase_one = (char *)malloc(storesize);
buftop_one = bufbase_one + storesize;
bufbase_two = (char *)malloc(storesize);
buftop_two = bufbase_two + storesize;

/* Do the job */

compare();

/* Final messages */

  if (return_code == 0)
    fprintf(f_out, "\"%s\" and \"%s\" are identical.\n", name_one, name_two);
  else
    {
    if (echo) rule('=', 15);
    fprintf(f_out, "%d difference", return_code);
    if (return_code != 1) fprintf(f_out, "s");
    fprintf(f_out, " found.\n");


    lines_one -= 1;
    fprintf(f_out, "\"%s\" contains %d line", name_one, lines_one);
    if (lines_one != 1) fprintf(f_out, "s");


    lines_two -= 1;
    fprintf(f_out, "; \"%s\" contains %d line", name_two, lines_two);
    if (lines_two != 1) fprintf(f_out, "s");
    fprintf(f_out, ".\n");
    }


free(bufbase_one);
free(bufbase_two);

fclose(f_one);
fclose(f_two);
if (f_out != stdout) fclose(f_out);

return return_code;
}

/* End of PH-Compare. */

Index: checkaccess.c
====================================================================
/* $Cambridge: exim/exim-test/src/checkaccess.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */

/* This is a baby program that is run as root from the runtest script. It is
passed the Exim uid and gid as arguments, and the name of a file in the
test-suite directory. It gives up all supplementary groups, changes to the
given uid/gid, and then tries to read the file. The yield is 0 if that is
successful, and non-zero otherwise (use different values to aid debugging). See
comments in the exim.c source file about the use of setgroups() for getting rid
of extraneous groups. */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>

#include <stdio.h>


int main(int argc, char **argv)
{
int fd;
gid_t group_list[10];
struct passwd *pw = getpwnam(argv[2]);
struct group *gr = getgrnam(argv[3]);

if (pw == NULL) return 1;
if (gr == NULL) return 2;
if (setgroups(0, NULL) != 0 && setgroups(1, group_list) != 0) return 4;
if (setgid(gr->gr_gid) != 0) return 5;
if (setuid(pw->pw_uid) != 0) return 6;

fd = open(argv[1], O_RDONLY);
if (fd < 0) return 7;

close(fd);
return 0;
}

/* End */

Index: client.c
====================================================================
/* $Cambridge: exim/exim-test/src/client.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */

/* A little hacked up program that makes a TCP/IP call and reads a script to
drive it, for testing Exim server code running as a daemon. It's got a bit
messy with the addition of support for either OpenSSL or GnuTLS. The code for
those was hacked out of Exim itself. */

/* ANSI C standard includes */

#include <ctype.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* Unix includes */

#include <errno.h>
#include <dirent.h>
#include <sys/types.h>

#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>

#include <netdb.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <utime.h>

#ifdef AF_INET6
#define HAVE_IPV6 1
#endif

#ifndef S_ADDR_TYPE
#define S_ADDR_TYPE u_long
#endif

typedef unsigned char uschar;

#define CS (char *)
#define US (unsigned char *)

  #define FALSE         0
  #define TRUE          1




static int sigalrm_seen = 0;


/* TLS support can be optionally included, either for OpenSSL or GnuTLS. The
latter needs a whole pile of tables. */

#ifdef HAVE_OPENSSL
#define HAVE_TLS
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#endif


#ifdef HAVE_GNUTLS
#define HAVE_TLS
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>

  #define DH_BITS      768
  #define RSA_BITS     512


/* Local static variables for GNUTLS */

static gnutls_rsa_params rsa_params = NULL;
static gnutls_dh_params dh_params = NULL;

static gnutls_certificate_credentials_t x509_cred = NULL;
static gnutls_session tls_session = NULL;

static int ssl_session_timeout = 200;

/* Priorities for TLS algorithms to use. */

static const int protocol_priority[16] = { GNUTLS_TLS1, GNUTLS_SSL3, 0 };

  static const int kx_priority[16] = {
    GNUTLS_KX_RSA,
    GNUTLS_KX_DHE_DSS,
    GNUTLS_KX_DHE_RSA,
    GNUTLS_KX_RSA_EXPORT,
    0 };


  static int default_cipher_priority[16] = {
    GNUTLS_CIPHER_AES_256_CBC,
    GNUTLS_CIPHER_AES_128_CBC,
    GNUTLS_CIPHER_3DES_CBC,
    GNUTLS_CIPHER_ARCFOUR_128,
    0 };


  static const int mac_priority[16] = {
    GNUTLS_MAC_SHA,
    GNUTLS_MAC_MD5,
    0 };


static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 };
static const int cert_type_priority[16] = { GNUTLS_CRT_X509, 0 };

#endif




  /*************************************************
  *            SIGALRM handler - crash out         *
  *************************************************/


  static void
  sigalrm_handler_crash(int sig)
  {
  sig = sig;    /* Keep picky compilers happy */
  printf("\nClient timed out\n");
  exit(99);
  }



  /*************************************************
  *            SIGALRM handler - set flag          *
  *************************************************/


  static void
  sigalrm_handler_flag(int sig)
  {
  sig = sig;    /* Keep picky compilers happy */
  sigalrm_seen = 1;
  }




/****************************************************************************/
/****************************************************************************/

  #ifdef HAVE_OPENSSL
  /*************************************************
  *         Start an OpenSSL TLS session           *
  *************************************************/


int tls_start(int sock, SSL **ssl, SSL_CTX *ctx)
{
int rc;
static const char *sid_ctx = "exim";

RAND_load_file("client.c", -1); /* Not *very* random! */

*ssl = SSL_new (ctx);
SSL_set_session_id_context(*ssl, sid_ctx, strlen(sid_ctx));
SSL_set_fd (*ssl, sock);
SSL_set_connect_state(*ssl);

signal(SIGALRM, sigalrm_handler_flag);
sigalrm_seen = 0;
alarm(5);
rc = SSL_connect (*ssl);
alarm(0);

  if (sigalrm_seen)
    {
    printf("SSL_connect timed out\n");
    return 0;
    }


  if (rc <= 0)
    {
    ERR_print_errors_fp(stdout);
    return 0;
    }


printf("SSL connection using %s\n", SSL_get_cipher (*ssl));
return 1;
}


  /*************************************************
  *           SSL Information callback             *
  *************************************************/


static void
info_callback(SSL *s, int where, int ret)
{
where = where;
ret = ret;
printf("SSL info: %s\n", SSL_state_string_long(s));
}
#endif


/****************************************************************************/
/****************************************************************************/


  #ifdef HAVE_GNUTLS
  /*************************************************
  *            Handle GnuTLS error                 *
  *************************************************/


/* Called from lots of places when errors occur before actually starting to do
the TLS handshake, that is, while the session is still in clear.

  Argument:
    prefix    prefix text
    err       a GnuTLS error number, or 0 if local error


  Returns:    doesn't - it dies
  */


static void
gnutls_error(uschar *prefix, int err)
{
fprintf(stderr, "GnuTLS connection error:%s", prefix);
if (err != 0) fprintf(stderr, " %s", gnutls_strerror(err));
fprintf(stderr, "\n");
exit(1);
}



  /*************************************************
  *          Setup up RSA and DH parameters        *
  *************************************************/


/* For the test suite, the parameters should always be available in the spool
directory. */

static void
init_rsa_dh(void)
{
int fd;
int ret;
gnutls_datum m;
uschar filename[200];
struct stat statbuf;

/* Initialize the data structures for holding the parameters */

ret = gnutls_rsa_params_init(&rsa_params);
if (ret < 0) gnutls_error(US"init rsa_params", ret);

ret = gnutls_dh_params_init(&dh_params);
if (ret < 0) gnutls_error(US"init dh_params", ret);

/* Open the cache file for reading and if successful, read it and set up the
parameters. If we can't set up the RSA parameters, assume that we are dealing
with an old-style cache file that is in another format, and fall through to
compute new values. However, if we correctly get RSA parameters, a failure to
set up D-H parameters is treated as an error. */

  fd = open("aux-fixed/gnutls-params", O_RDONLY, 0);
  if (fd < 0)
    {
    fprintf(stderr, "Failed to open spool/gnutls-params: %s\n", strerror(errno));
    exit(1);
    }


  if (fstat(fd, &statbuf) < 0)
    {
    (void)close(fd);
    return gnutls_error(US"TLS cache stat failed", 0);
    }


  m.size = statbuf.st_size;
  m.data = malloc(m.size);
  if (m.data == NULL)
    return gnutls_error(US"memory allocation failed", 0);
  if (read(fd, m.data, m.size) != m.size)
    return gnutls_error(US"TLS cache read failed", 0);
  (void)close(fd);


ret = gnutls_rsa_params_import_pkcs1(rsa_params, &m, GNUTLS_X509_FMT_PEM);
if (ret < 0) return gnutls_error(US"RSA params import", ret);
ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM);
if (ret < 0) return gnutls_error(US"DH params import", ret);
free(m.data);
}




  /*************************************************
  *            Initialize for GnuTLS               *
  *************************************************/


  /*
  Arguments:
    certificate     certificate file
    privatekey      private key file
  */


static void
tls_init(uschar *certificate, uschar *privatekey)
{
int rc;

rc = gnutls_global_init();
if (rc < 0) gnutls_error(US"gnutls_global_init", rc);

/* Read RSA and D-H parameters from the cache file. */

init_rsa_dh();

/* Create the credentials structure */

rc = gnutls_certificate_allocate_credentials(&x509_cred);
if (rc < 0) gnutls_error(US"certificate_allocate_credentials", rc);

/* Set the certificate and private keys */

  if (certificate != NULL)
    {
    rc = gnutls_certificate_set_x509_key_file(x509_cred, CS certificate,
      CS privatekey, GNUTLS_X509_FMT_PEM);
    if (rc < 0) gnutls_error("gnutls_certificate", rc);
    }


/* Associate the parameters with the x509 credentials structure. */

gnutls_certificate_set_dh_params(x509_cred, dh_params);
gnutls_certificate_set_rsa_export_params(x509_cred, rsa_params);
}



  /*************************************************
  *        Initialize a single GNUTLS session      *
  *************************************************/


static gnutls_session
tls_session_init(void)
{
gnutls_session session;

gnutls_init(&session, GNUTLS_CLIENT);

gnutls_cipher_set_priority(session, default_cipher_priority);
gnutls_compression_set_priority(session, comp_priority);
gnutls_kx_set_priority(session, kx_priority);
gnutls_protocol_set_priority(session, protocol_priority);
gnutls_mac_set_priority(session, mac_priority);

gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);

gnutls_dh_set_prime_bits(session, DH_BITS);
gnutls_db_set_cache_expiration(session, ssl_session_timeout);

return session;
}
#endif


/****************************************************************************/
/****************************************************************************/




  /*************************************************
  *                 Main Program                   *
  *************************************************/


  /* Usage: client
            <IP address>
            <port>
            [<outgoing interface>]
            [<cert file>]
            [<key file>]
  */


int main(int argc, char **argv)
{
struct sockaddr *s_ptr;
struct sockaddr_in s_in4;
char *interface = NULL;
char *address = NULL;
char *certfile = NULL;
char *keyfile = NULL;
int argi = 1;
int host_af, port, s_len, rc, sock, save_errno;
int timeout = 1;
int tls_active = 0;
int sent_starttls = 0;
int tls_on_connect = 0;

#if HAVE_IPV6
struct sockaddr_in6 s_in6;
#endif

  #ifdef HAVE_OPENSSL
  SSL_CTX* ctx;
  SSL*     ssl;
  #endif


unsigned char outbuffer[10240];
unsigned char inbuffer[10240];
unsigned char *inptr = inbuffer;

*inptr = 0; /* Buffer empty */

/* Options */

  while (argc >= argi + 1 && argv[argi][0] == '-')
    {
    if (strcmp(argv[argi], "-tls-on-connect") == 0)
      {
      tls_on_connect = 1;
      argi++;
      }
    else if (argv[argi][1] == 't' && isdigit(argv[argi][2]))
      {
      timeout = atoi(argv[argi]+1);
      argi++;
      }
    else
      {
      printf("Unrecognized option %s\n", argv[argi]);
      exit(1);
      }
    }


/* Mandatory 1st arg is IP address */

  if (argc < argi+1)
    {
    printf("No IP address given\n");
    exit(1);
    }


address = argv[argi++];
host_af = (strchr(address, ':') != NULL)? AF_INET6 : AF_INET;

/* Mandatory 2nd arg is port */

  if (argc < argi+1)
    {
    printf("No port number given\n");
    exit(1);
    }


port = atoi(argv[argi++]);

/* Optional next arg is interface */

  if (argc > argi &&
    (isdigit((unsigned char)argv[argi][0]) || argv[argi][0] == ':'))
      interface = argv[argi++];


/* Any more arguments are the name of a certificate file and key file */

if (argc > argi) certfile = argv[argi++];
if (argc > argi) keyfile = argv[argi++];


#if HAVE_IPV6
/* For an IPv6 address, use an IPv6 sockaddr structure. */

  if (host_af == AF_INET6)
    {
    s_ptr = (struct sockaddr *)&s_in6;
    s_len = sizeof(s_in6);
    }
  else
  #endif


/* For an IPv4 address, use an IPv4 sockaddr structure,
even on an IPv6 system. */

    {
    s_ptr = (struct sockaddr *)&s_in4;
    s_len = sizeof(s_in4);
    }


printf("Connecting to %s port %d ... ", address, port);

  sock = socket(host_af, SOCK_STREAM, 0);
  if (sock < 0)
    {
    printf("socket creation failed: %s\n", strerror(errno));
    exit(1);
    }


/* Bind to a specific interface if requested. On an IPv6 system, this has
to be of the same family as the address we are calling. On an IPv4 system the
test is redundant, but it keeps the code tidier. */

  if (interface != NULL)
    {
    int interface_af = (strchr(interface, ':') != NULL)? AF_INET6 : AF_INET;


    if (interface_af == host_af)
      {
      #if HAVE_IPV6


      /* Set up for IPv6 binding */


      if (host_af == AF_INET6)
        {
        memset(&s_in6, 0, sizeof(s_in6));
        s_in6.sin6_family = AF_INET6;
        s_in6.sin6_port = 0;
        if (inet_pton(AF_INET6, interface, &s_in6.sin6_addr) != 1)
          {
          printf("Unable to parse \"%s\"", interface);
          exit(1);
          }
        }
      else
      #endif


      /* Set up for IPv4 binding */


        {
        memset(&s_in4, 0, sizeof(s_in4));
        s_in4.sin_family = AF_INET;
        s_in4.sin_port = 0;
        s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(interface);
        }


      /* Bind */


      if (bind(sock, s_ptr, s_len) < 0)
        {
        printf("Unable to bind outgoing SMTP call to %s: %s",
          interface, strerror(errno));
        exit(1);
        }
      }
    }


/* Set up a remote IPv6 address */

  #if HAVE_IPV6
  if (host_af == AF_INET6)
    {
    memset(&s_in6, 0, sizeof(s_in6));
    s_in6.sin6_family = AF_INET6;
    s_in6.sin6_port = htons(port);
    if (inet_pton(host_af, address, &s_in6.sin6_addr) != 1)
      {
      printf("Unable to parse \"%s\"", address);
      exit(1);
      }
    }
  else
  #endif


/* Set up a remote IPv4 address */

    {
    memset(&s_in4, 0, sizeof(s_in4));
    s_in4.sin_family = AF_INET;
    s_in4.sin_port = htons(port);
    s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(address);
    }


/* SIGALRM handler crashes out */

signal(SIGALRM, sigalrm_handler_crash);
alarm(timeout);
rc = connect(sock, s_ptr, s_len);
save_errno = errno;
alarm(0);

/* A failure whose error code is "Interrupted system call" is in fact
an externally applied timeout if the signal handler has been run. */

  if (rc < 0)
    {
    close(sock);
    printf("failed: %s\n", strerror(save_errno));
    exit(1);
    }


printf("connected\n");


/* --------------- Set up for OpenSSL --------------- */

#ifdef HAVE_OPENSSL
SSL_library_init();
SSL_load_error_strings();

  ctx = SSL_CTX_new(SSLv23_method());
  if (ctx == NULL)
    {
    printf ("SSL_CTX_new failed\n");
    exit(1);
    }


  if (certfile != NULL)
    {
    if (!SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM))
      {
      printf("SSL_CTX_use_certificate_file failed\n");
      exit(1);
      }
    printf("Certificate file = %s\n", certfile);
    }


  if (keyfile != NULL)
    {
    if (!SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM))
      {
      printf("SSL_CTX_use_PrivateKey_file failed\n");
      exit(1);
      }
    printf("Key file = %s\n", keyfile);
    }


SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
SSL_CTX_set_timeout(ctx, 200);
SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
#endif


/* --------------- Set up for GnuTLS --------------- */

#ifdef HAVE_GNUTLS
if (certfile != NULL) printf("Certificate file = %s\n", certfile);
if (keyfile != NULL) printf("Key file = %s\n", keyfile);
tls_init(certfile, keyfile);
tls_session = tls_session_init();
gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)sock);

/* When the server asks for a certificate and the client does not have one,
there is a SIGPIPE error in the gnutls_handshake() function for some reason
that is not understood. As luck would have it, this has never hit Exim itself
because it ignores SIGPIPE errors. Doing the same here allows it all to work as
one wants. */

signal(SIGPIPE, SIG_IGN);
#endif

/* ---------------------------------------------- */


/* Start TLS session if configured to do so without STARTTLS */

  #ifdef HAVE_TLS
  if (tls_on_connect)
    {
    printf("Attempting to start TLS\n");


    #ifdef HAVE_OPENSSL
    tls_active = tls_start(sock, &ssl, ctx);
    #endif


    #ifdef HAVE_GNUTLS
    sigalrm_seen = FALSE;
    alarm(timeout);
    tls_active = gnutls_handshake(tls_session) >= 0;
    alarm(0);
    #endif


    if (!tls_active)
      printf("Failed to start TLS\n");
    else
      printf("Succeeded in starting TLS\n");
    }
  #endif


  while (fgets(outbuffer, sizeof(outbuffer), stdin) != NULL)
    {
    int n = (int)strlen(outbuffer);
    while (n > 0 && isspace(outbuffer[n-1])) n--;
    outbuffer[n] = 0;


    /* Expect incoming */


    if (strncmp(outbuffer, "??? ", 4) == 0)
      {
      unsigned char *lineptr;
      printf("%s\n", outbuffer);


      if (*inptr == 0)   /* Refill input buffer */
        {
        if (tls_active)
          {
          #ifdef HAVE_OPENSSL
          rc = SSL_read (ssl, inbuffer, sizeof(inbuffer) - 1);
          #endif
          #ifdef HAVE_GNUTLS
          rc = gnutls_record_recv(tls_session, CS inbuffer, sizeof(inbuffer) - 1);
          #endif
          }
        else
          {
          alarm(timeout);
          rc = read(sock, inbuffer, sizeof(inbuffer));
          alarm(0);
          }


        if (rc < 0)
          {
          printf("Read error %s\n", strerror(errno));
          exit(1)  ;
          }
        else if (rc == 0)
          {
          printf("Unexpected EOF read\n");
          close(sock);
          exit(1);
          }
        else
          {
          inbuffer[rc] = 0;
          inptr = inbuffer;
          }
        }


      lineptr = inptr;
      while (*inptr != 0 && *inptr != '\r' && *inptr != '\n') inptr++;
      if (*inptr != 0)
        {
        *inptr++ = 0;
        if (*inptr == '\n') inptr++;
        }


      printf("<<< %s\n", lineptr);
      if (strncmp(lineptr, outbuffer + 4, (int)strlen(outbuffer) - 4) != 0)
        {
        printf("\n******** Input mismatch ********\n");
        exit(1);
        }


      #ifdef HAVE_TLS
      if (sent_starttls)
        {
        if (lineptr[0] == '2')
          {
          printf("Attempting to start TLS\n");
          fflush(stdout);


          #ifdef HAVE_OPENSSL
          tls_active = tls_start(sock, &ssl, ctx);
          #endif


          #ifdef HAVE_GNUTLS
          sigalrm_seen = FALSE;
          alarm(timeout);
          tls_active = gnutls_handshake(tls_session) >= 0;
          alarm(0);
          #endif


          if (!tls_active)
            {
            printf("Failed to start TLS\n");
            fflush(stdout);
            }
          else
            printf("Succeeded in starting TLS\n");
          }
        else printf("Abandoning TLS start attempt\n");
        }
      sent_starttls = 0;
      #endif
      }


    /* Wait for a bit before proceeding */


    else if (strncmp(outbuffer, "+++ ", 4) == 0)
      {
      printf("%s\n", outbuffer);
      sleep(atoi(outbuffer + 4));
      }


    /* Send outgoing, but barf if unconsumed incoming */


    else
      {
      unsigned char *escape;


      if (*inptr != 0)
        {
        printf("Unconsumed input: %s", inptr);
        printf("   About to send: %s\n", outbuffer);
        exit(1);
        }


      #ifdef HAVE_TLS


      /* Shutdown TLS */


      if (strcmp(outbuffer, "stoptls") == 0 ||
          strcmp(outbuffer, "STOPTLS") == 0)
        {
        if (!tls_active)
          {
          printf("STOPTLS read when TLS not active\n");
          exit(1);
          }
        printf("Shutting down TLS encryption\n");


        #ifdef HAVE_OPENSSL
        SSL_shutdown(ssl);
        SSL_free(ssl);
        #endif


        #ifdef HAVE_GNUTLS
        gnutls_bye(tls_session, GNUTLS_SHUT_WR);
        gnutls_deinit(tls_session);
        tls_session = NULL;
        gnutls_global_deinit();
        #endif


        tls_active = 0;
        continue;
        }


      /* Remember that we sent STARTTLS */


      sent_starttls = (strcmp(outbuffer, "starttls") == 0 ||
                       strcmp(outbuffer, "STARTTLS") == 0);


      /* Fudge: if the command is "starttls_wait", we send the starttls bit,
      but we haven't set the flag, so that there is no negotiation. This is for
      testing the server's timeout. */


      if (strcmp(outbuffer, "starttls_wait") == 0)
        {
        outbuffer[8] = 0;
        n = 8;
        }
      #endif


      printf(">>> %s\n", outbuffer);
      strcpy(outbuffer + n, "\r\n");


      /* Turn "\n" and "\r" into the relevant characters. This is a hack. */


      while ((escape = strstr(outbuffer, "\\r")) != NULL)
        {
        *escape = '\r';
        memmove(escape + 1, escape + 2,  (n + 2) - (escape - outbuffer) - 2);
        n--;
        }


      while ((escape = strstr(outbuffer, "\\n")) != NULL)
        {
        *escape = '\n';
        memmove(escape + 1, escape + 2,  (n + 2) - (escape - outbuffer) - 2);
        n--;
        }


      /* OK, do it */


      alarm(timeout);
      if (tls_active)
        {
        #ifdef HAVE_OPENSSL
          rc = SSL_write (ssl, outbuffer, n + 2);
        #endif
        #ifdef HAVE_GNUTLS
          rc = gnutls_record_send(tls_session, CS outbuffer, n + 2);
          if (rc < 0)
            {
            printf("GnuTLS write error: %s\n", gnutls_strerror(rc));
            exit(1);
            }
        #endif
        }
      else
        {
        rc = write(sock, outbuffer, n + 2);
        }
      alarm(0);


      if (rc < 0)
        {
        printf("Write error: %s\n", strerror(errno));
        exit(1);
        }
      }
    }


printf("End of script\n");
close(sock);

exit(0);
}

/* End of client.c */

Index: fakens.c
====================================================================
/* $Cambridge: exim/exim-test/src/fakens.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */

  /*************************************************
  *       fakens - A Fake Nameserver Program       *
  *************************************************/


/* This program exists to support the testing of DNS handling code in Exim. It
avoids the need to install special zones in a real nameserver. When Exim is
running in its (new) test harness, DNS lookups are first passed to this program
instead of to the real resolver. (With a few exceptions - see the discussion in
the test suite's README file.) The program is also passed the name of the Exim
spool directory; it expects to find its "zone files" in ../dnszones relative to
that directory. Note that there is little checking in this program. The fake
zone files are assumed to be syntactically valid.

The zones that are handled are found by scanning the dnszones directory. A file
whose name is of the form db.ip4.x is a zone file for .x.in-addr.arpa; a file
whose name is of the form db.ip6.x is a zone file for .x.ip6.arpa; a file of
the form db.anything.else is a zone file for .anything.else. A file of the form
qualify.x.y specifies the domain that is used to qualify single-component
names, except for the name "dontqualify".

The arguments to the program are:

    the name of the Exim spool directory
    the domain name that is being sought
    the DNS record type that is being sought


The output from the program is written to stdout. It is supposed to be in
exactly the same format as a traditional namserver response (see RFC 1035) so
that Exim can process it as normal. At present, no compression is used.
Error messages are written to stderr.

The return codes from the program are zero for success, and otherwise the
values that are set in h_errno after a failing call to the normal resolver:

    1 HOST_NOT_FOUND     host not found (authoritative)
    2 TRY_AGAIN          server failure
    3 NO_RECOVERY        non-recoverable error
    4 NO_DATA            valid name, no data of requested type


In a real nameserver, TRY_AGAIN is also used for a non-authoritative not found,
but it is not used for that here. There is also one extra return code:

    5 PASS_ON            requests Exim to call res_search()


This is used for zones that fakens does not recognize. It is also used if a
line in the zone file contains exactly this:

    PASS ON NOT FOUND


and the domain is not found. It converts the the result to PASS_ON instead of
HOST_NOT_FOUND. */

#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <arpa/nameser.h>
#include <sys/types.h>
#include <dirent.h>

  #define FALSE         0
  #define TRUE          1
  #define PASS_ON       5


typedef int BOOL;
typedef unsigned char uschar;

#define CS (char *)
#define CCS (const char *)
#define US (unsigned char *)

  #define Ustrcat(s,t)       strcat(CS(s),CCS(t))
  #define Ustrchr(s,n)       US strchr(CCS(s),n)
  #define Ustrcmp(s,t)       strcmp(CCS(s),CCS(t))
  #define Ustrcpy(s,t)       strcpy(CS(s),CCS(t))
  #define Ustrlen(s)         (int)strlen(CCS(s))
  #define Ustrncmp(s,t,n)    strncmp(CCS(s),CCS(t),n)
  #define Ustrncpy(s,t,n)    strncpy(CS(s),CCS(t),n)



  typedef struct adomainstr {
    struct adomainstr *next;
    uschar name[1];
  } adomainstr;


  typedef struct zoneitem {
    uschar *zone;
    uschar *zonefile;
  } zoneitem;


  typedef struct tlist {
    uschar *name;
    int value;
  } tlist;


/* On some (older?) operating systems, the standard ns_t_xxx definitions are
not available, and only the older T_xxx ones exist in nameser.h. If ns_t_a is
not defined, assume we are in this state. A really old system might not even
know about AAAA and SRV at all. */

  #ifndef ns_t_a
  #define ns_t_a      T_A
  #define ns_t_ns     T_NS
  #define ns_t_cname  T_CNAME
  #define ns_t_soa    T_SOA
  #define ns_t_ptr    T_PTR
  #define ns_t_mx     T_MX
  #define ns_t_txt    T_TXT
  #define ns_t_aaaa   T_AAAA
  #define ns_t_srv    T_SRV
  #ifndef T_AAAA
  #define T_AAAA      28
  #endif
  #ifndef T_SRV
  #define T_SRV       33
  #endif
  #endif


  static tlist type_list[] = {
    { US"A",       ns_t_a },
    { US"NS",      ns_t_ns },
    { US"CNAME",   ns_t_cname },
  /*  { US"SOA",     ns_t_soa },  Not currently in use */
    { US"PTR",     ns_t_ptr },
    { US"MX",      ns_t_mx },
    { US"TXT",     ns_t_txt },
    { US"AAAA",    ns_t_aaaa },
    { US"SRV",     ns_t_srv },
    { NULL,        0 }
  };




  /*************************************************
  *           Get memory and sprintf into it       *
  *************************************************/


/* This is used when building a table of zones and their files.

  Arguments:
    format       a format string
    ...          arguments


  Returns:       pointer to formatted string
  */


static uschar *
fcopystring(uschar *format, ...)
{
uschar *yield;
char buffer[256];
va_list ap;
va_start(ap, format);
vsprintf(buffer, format, ap);
va_end(ap);
yield = (uschar *)malloc(Ustrlen(buffer) + 1);
Ustrcpy(yield, buffer);
return yield;
}


  /*************************************************
  *             Pack name into memory              *
  *************************************************/


/* This function packs a domain name into memory according to DNS rules. At
present, it doesn't do any compression.

  Arguments:
    name         the name
    pk           where to put it


  Returns:       the updated value of pk
  */


  static uschar *
  packname(uschar *name, uschar *pk)
  {
  while (*name != 0)
    {
    uschar *p = name;
    while (*p != 0 && *p != '.') p++;
    *pk++ = (p - name);
    memmove(pk, name, p - name);
    pk += p - name;
    name = (*p == 0)? p : p + 1;
    }
  *pk++ = 0;
  return pk;
  }




  /*************************************************
  *              Scan file for RRs                 *
  *************************************************/


/* This function scans an open "zone file" for appropriate records, and adds
any that are found to the output buffer.

  Arguments:
    f           the input FILE
    zone        the current zone name
    domain      the domain we are looking for
    qtype       the type of RR we want
    qtypelen    the length of qtype
    pkptr       points to the output buffer pointer; this is updated
    countptr    points to the record count; this is updated
    adomainptr  points to where to hang additional domains


  Returns:      0 on success, else HOST_NOT_FOUND or NO_DATA or NO_RECOVERY or
                PASS_ON - the latter if a "PASS ON NOT FOUND" line is seen
  */


  static int
  find_records(FILE *f, uschar *zone, uschar *domain, uschar *qtype,
    int qtypelen, uschar **pkptr, int *countptr, adomainstr **adomainptr)
  {
  int yield = HOST_NOT_FOUND;
  int zonelen = Ustrlen(zone);
  int domainlen = Ustrlen(domain);
  BOOL pass_on_not_found = FALSE;
  tlist *typeptr;
  uschar *pk = *pkptr;
  uschar buffer[256];
  uschar rrdomain[256];


/* Decode the required type */

  for (typeptr = type_list; typeptr->name != NULL; typeptr++)
    { if (Ustrcmp(typeptr->name, qtype) == 0) break; }
  if (typeptr->name == NULL)
    {
    fprintf(stderr, "fakens: unknown record type %s\n", qtype);
    return NO_RECOVERY;
    }


  rrdomain[0] = 0;                 /* No previous domain */
  (void)fseek(f, 0, SEEK_SET);     /* Start again at the beginning */


/* Scan for RRs */

  while (fgets(CS buffer, sizeof(buffer), f) != NULL)
    {
    uschar *rdlptr;
    uschar *p, *ep, *pp;
    BOOL found_cname = FALSE;
    int i, plen, value;
    int tvalue = typeptr->value;
    int qtlen = qtypelen;


    p = buffer;
    while (isspace(*p)) p++;
    if (*p == 0 || *p == ';') continue;


    if (Ustrncmp(p, "PASS ON NOT FOUND", 17) == 0)
      {
      pass_on_not_found = TRUE;
      continue;
      }


    ep = buffer + Ustrlen(buffer);
    while (isspace(ep[-1])) ep--;
    *ep = 0;


    p = buffer;
    if (!isspace(*p))
      {
      uschar *pp = rrdomain;
      while (!isspace(*p)) *pp++ = tolower(*p++);
      if (pp[-1] != '.') Ustrcpy(pp, zone); else pp[-1] = 0;
      }


    /* Compare domain names; first check for a wildcard */


    if (rrdomain[0] == '*')
      {
      int restlen = Ustrlen(rrdomain) - 1;
      if (domainlen > restlen &&
          Ustrcmp(domain + domainlen - restlen, rrdomain + 1) != 0) continue;
      }


    /* Not a wildcard RR */


    else if (Ustrcmp(domain, rrdomain) != 0) continue;


    /* The domain matches */


    if (yield == HOST_NOT_FOUND) yield = NO_DATA;


    /* Compare RR types; a CNAME record is always returned */


    while (isspace(*p)) p++;


    if (Ustrncmp(p, "CNAME", 5) == 0)
      {
      tvalue = ns_t_cname;
      qtlen = 5;
      found_cname = TRUE;
      }
    else if (Ustrncmp(p, qtype, qtypelen) != 0 || !isspace(p[qtypelen])) continue;


    /* Found a relevant record */


    yield = 0;
    *countptr = *countptr + 1;


    p += qtlen;
    while (isspace(*p)) p++;


    pk = packname(domain, pk);            /* Not rrdomain because of wildcard */
    *pk++ = (tvalue >> 8) & 255;
    *pk++ = (tvalue) & 255;
    *pk++ = 0;
    *pk++ = 1;     /* class = IN */


    pk += 4;       /* TTL field; don't care */


    rdlptr = pk;   /* remember rdlength field */
    pk += 2;


    /* The rest of the data depends on the type */


    switch (tvalue)
      {
      case ns_t_soa:  /* Not currently used */
      break;


      case ns_t_a:
      for (i = 0; i < 4; i++)
        {
        value = 0;
        while (isdigit(*p)) value = value*10 + *p++ - '0';
        *pk++ = value;
        p++;
        }
      break;


      /* The only occurrence of a double colon is for ::1 */
      case ns_t_aaaa:
      if (Ustrcmp(p, "::1") == 0)
        {
        memset(pk, 0, 15);
        pk += 15;
        *pk++ = 1;
        }
      else for (i = 0; i < 8; i++)
        {
        value = 0;
        while (isxdigit(*p))
          {
          value = value * 16 + toupper(*p) - (isdigit(*p)? '0' : '7');
          p++;
          }
        *pk++ = (value >> 8) & 255;
        *pk++ = value & 255;
        p++;
        }
      break;


      case ns_t_mx:
      value = 0;
      while (isdigit(*p)) value = value*10 + *p++ - '0';
      while (isspace(*p)) p++;
      *pk++ = (value >> 8) & 255;
      *pk++ = value & 255;
      goto PACKNAME;


      case ns_t_txt:
      pp = pk++;
      if (*p == '"') p++;   /* Should always be the case */
      while (*p != 0 && *p != '"') *pk++ = *p++;
      *pp = pk - pp - 1;
      break;


      case ns_t_srv:
      for (i = 0; i < 3; i++)
        {
        value = 0;
        while (isdigit(*p)) value = value*10 + *p++ - '0';
        while (isspace(*p)) p++;
        *pk++ = (value >> 8) & 255;
        *pk++ = value & 255;
        }


      /* Fall through */


      case ns_t_cname:
      case ns_t_ns:
      case ns_t_ptr:
      PACKNAME:
      if (ep[-1] != '.') sprintf(ep, "%s.", zone);
      pk = packname(p, pk);
      plen = Ustrlen(p);
      if (adomainptr != NULL && plen > zonelen + 2 &&
          Ustrncmp(p + plen - zonelen - 1, zone, zonelen) == 0)
        {
        adomainstr *adomain = (adomainstr *)malloc(sizeof(adomainstr) + plen);
        *adomainptr = adomain;
        adomainptr = &(adomain->next);
        adomain->next = NULL;
        Ustrncpy(adomain->name, p, plen - 1);
        adomain->name[plen-1] = 0;
        }
      break;
      }


    /* Fill in the length, and we are done with this RR */


    rdlptr[0] = ((pk - rdlptr - 2) >> 8) & 255;
    rdlptr[1] = (pk -rdlptr - 2) & 255;


    /* If we have just yielded a CNAME, we must change the domain name to the
    new domain, and re-start the scan from the beginning. */


    if (found_cname)
      {
      domain = fcopystring("%s", p);
      domainlen = Ustrlen(domain);
      domain[domainlen - 1] = 0;       /* Removed trailing dot */
      rrdomain[0] = 0;                 /* No previous domain */
      (void)fseek(f, 0, SEEK_SET);     /* Start again at the beginning */
      }
    }


*pkptr = pk;
return (yield == HOST_NOT_FOUND && pass_on_not_found)? PASS_ON : yield;
}



  /*************************************************
  *           Entry point and main program         *
  *************************************************/


int
main(int argc, char **argv)
{
FILE *f;
DIR *d;
int dirlen, domlen, qtypelen;
int yield, count;
int i;
int zonecount = 0;
tlist *typeptr;
struct dirent *de;
adomainstr *adomain = NULL;
zoneitem zones[32];
uschar *qualify = NULL;
uschar *p, *zone;
uschar *zonefile = NULL;
uschar domain[256];
uschar buffer[256];
uschar qtype[12];
uschar packet[512];
uschar *pk = packet;

  if (argc != 4)
    {
    fprintf(stderr, "fakens: expected 3 arguments, received %d\n", argc-1);
    return NO_RECOVERY;
    }


/* Find the zones */

(void)sprintf(buffer, "%s/../dnszones", argv[1]);

  d = opendir(CCS buffer);
  if (d == NULL)
    {
    fprintf(stderr, "fakens: failed to opendir %s: %s\n", buffer,
      strerror(errno));
    return NO_RECOVERY;
    }


  while ((de = readdir(d)) != NULL)
    {
    uschar *name = de->d_name;
    if (Ustrncmp(name, "qualify.", 8) == 0)
      {
      qualify = fcopystring("%s", name + 7);
      continue;
      }
    if (Ustrncmp(name, "db.", 3) != 0) continue;
    if (Ustrncmp(name + 3, "ip4.", 4) == 0)
      zones[zonecount].zone = fcopystring("%s.in-addr.arpa", name + 6);
    else if (Ustrncmp(name + 3, "ip6.", 4) == 0)
      zones[zonecount].zone = fcopystring("%s.ip6.arpa", name + 6);
    else
      zones[zonecount].zone = fcopystring("%s", name + 2);
    zones[zonecount++].zonefile = fcopystring("%s", name);
    }
  (void)closedir(d);


/* Get the RR type and upper case it, and check that we recognize it. */

Ustrncpy(qtype, argv[3], sizeof(qtype));
qtypelen = Ustrlen(qtype);
for (p = qtype; *p != 0; p++) *p = toupper(*p);

/* Find the domain, lower case it, check that it is in a zone that we handle,
and set up the zone file name. The zone names in the table all start with a
dot. */

domlen = Ustrlen(argv[2]);
if (argv[2][domlen-1] == '.') domlen--;
Ustrncpy(domain, argv[2], domlen);
domain[domlen] = 0;
for (i = 0; i < domlen; i++) domain[i] = tolower(domain[i]);

  if (Ustrchr(domain, '.') == NULL && qualify != NULL &&
      Ustrcmp(domain, "dontqualify") != 0)
    {
    Ustrcat(domain, qualify);
    domlen += Ustrlen(qualify);
    }


  for (i = 0; i < zonecount; i++)
    {
    int zlen;
    zone = zones[i].zone;
    zlen = Ustrlen(zone);
    if (Ustrcmp(domain, zone+1) == 0 || (domlen >= zlen &&
        Ustrcmp(domain + domlen - zlen, zone) == 0))
      {
      zonefile = zones[i].zonefile;
      break;
      }
    }


  if (zonefile == NULL)
    {
    fprintf(stderr, "fakens: query not in faked zone: domain is: %s\n", domain);
    return PASS_ON;
    }


(void)sprintf(buffer, "%s/../dnszones/%s", argv[1], zonefile);

/* Initialize the start of the response packet. We don't have to fake up
everything, because we know that Exim will look only at the answer and
additional section parts. */

memset(packet, 0, 12);
pk += 12;

/* Open the zone file. */

  f = fopen(buffer, "r");
  if (f == NULL)
    {
    fprintf(stderr, "fakens: failed to open %s: %s\n", buffer, strerror(errno));
    return NO_RECOVERY;
    }


/* Find the records we want, and add them to the result. */

count = 0;
yield = find_records(f, zone, domain, qtype, qtypelen, &pk, &count, &adomain);
if (yield == NO_RECOVERY) goto END_OFF;

packet[6] = (count >> 8) & 255;
packet[7] = count & 255;

/* Search for additional records and add them to the result. */

  count = 0;
  for (; adomain != NULL; adomain = adomain->next)
    {
    (void)find_records(f, zone, adomain->name, US"AAAA", 4, &pk, &count, NULL);
    (void)find_records(f, zone, adomain->name, US"A", 1, &pk, &count, NULL);
    }


packet[10] = (count >> 8) & 255;
packet[11] = count & 255;

/* Close the zone file, write the result, and return. */

END_OFF:
(void)fclose(f);
(void)fwrite(packet, 1, pk - packet, stdout);
return yield;
}

/* End of fakens.c */

Index: fd.c
====================================================================
/* $Cambridge: exim/exim-test/src/fd.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */

/* A program to check on open file descriptors. There are some weird options
for running it in Exim testing. If -q is given, make output suitable for
queryprogram. If -f is given, copy the input as for a transport filter. If -s
is given, add extra output from stat(). */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <limits.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>


/* The way of finding out the maximum file descriptor various between OS.
Most have sysconf(), but a few don't. */

  #ifdef _SC_OPEN_MAX
    #define mac_maxfd (sysconf(_SC_OPEN_MAX) - 1)
  #elif defined OPEN_MAX
    #define mac_maxfd (OPEN_MAX - 1)
  #elif defined NOFILE
    #define mac_maxfd (NOFILE - 1)
  #else
    #define mac_maxfd 255;    /* just in case */
  #endif



int main(int argc, char **argv)
{
int fd;
int qpgm = 0;
int filter = 0;
int use_stat = 0;
struct stat statbuf;
char buffer[8192];
char *p = buffer;

  while (argc > 1)
    {
    char *arg = argv[--argc];
    if (strcmp(arg, "-q") == 0) qpgm = 1;
    if (strcmp(arg, "-f") == 0) filter = 1;
    if (strcmp(arg, "-s") == 0) use_stat = 1;
    }


  if (filter)
    {
    int len;
    while ((len = read(0, buffer, sizeof(buffer))) > 0)
      write(1, buffer, len);
    }


p += sprintf(p, "max fd = %d\n", (int)mac_maxfd);

  for (fd = 0; fd <= mac_maxfd; fd++)
    {
    int options = fcntl(fd, F_GETFD);
    if (options >= 0)
      {
      int status = fcntl(fd, F_GETFL);
      p += sprintf(p, "%3d opt=%d status=%X ", fd, options, status);
      switch(status & 3)
        {
        case 0: p += sprintf(p, "RDONLY");
        break;
        case 1: p += sprintf(p, "WRONLY");
        break;
        case 2: p += sprintf(p, "RDWR");
        break;
        }
      if (isatty(fd)) p += sprintf(p, " TTY");
      if ((status & 8) != 0) p += sprintf(p, " APPEND");


      if (use_stat && fstat(fd, &statbuf) >= 0)
        {
        p += sprintf(p, " mode=%o uid=%d size=%d", (int)statbuf.st_mode,
          (int)statbuf.st_uid, (int)statbuf.st_size);
        }


      p += sprintf(p, "\n");
      }
    else if (errno != EBADF)
      {
      p += sprintf(p, "%3d errno=%d %s\n", fd, errno, strerror(errno));
      }
    }


  if (qpgm)
    {
    for (p = buffer; *p != 0; p++)
      if (*p == '\n') *p = ' ';
    printf("ACCEPT DATA=\"%s\"\n", buffer);
    }
  else printf("%s", buffer);


exit(0);
}

/* End */

Index: iefbr14.c
====================================================================
/* $Cambridge: exim/exim-test/src/iefbr14.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */

/* This is a program that does nothing, and returns zero. It is exactly the
same as the Unix "true" utility. Why do we need it? Well, not all systems have
"true" in the same place, and sometimes it's a shell built-in. Given that we
have little utilities anyway, it's just easier to do this. As for its name,
those with IBM mainframe memories will know where that comes from. */

int main(void) { return 0; }

/* End */

Index: loaded.c
====================================================================
/* $Cambridge: exim/exim-test/src/loaded.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */

/* This is a test function for dynamic loading in Exim expansions. It uses the
number of arguments to control the result. */

/* These lines are taken from local_scan.h in the Exim source: */

/* ========================================================================== */
/* Return codes from the support functions lss_match_xxx(). These are also the
codes that dynamically-loaded ${dlfunc functions must return. */

  #define  OK            0          /* Successful match */
  #define  DEFER         1          /* Defer - some problem */
  #define  FAIL          2          /* Matching failed */
  #define  ERROR         3          /* Internal or config error */


/* Extra return code for ${dlfunc functions */

  #define  FAIL_FORCED   4          /* "Forced" failure */
  /* ========================================================================== */



  int dltest(unsigned char **yield, int argc, unsigned char *argv[])
  {
  switch (argc)
    {
    case 0:
    return ERROR;


    case 1:
    *yield = argv[0];
    return OK;


    case 2:
    *yield = (unsigned char *)"yield FAIL_FORCED";
    return FAIL_FORCED;


    default:
    *yield = (unsigned char *)"yield FAIL";
    return FAIL;
    }
  }


Index: mtpscript.c
====================================================================
/* $Cambridge: exim/exim-test/src/mtpscript.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */

/* A little hacked up program that allows a script to play the part of a remote
SMTP/LMTP server on stdin/stdout for testing purposes. Hacked from the more
complicated version that does it over a socket. */


/* ANSI C standard includes */

#include <ctype.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* Unix includes */

#include <errno.h>
#include <unistd.h>


static FILE *log;


  /*************************************************
  *            SIGALRM handler - crash out         *
  *************************************************/


  static void
  sigalrm_handler(int sig)
  {
  sig = sig;    /* Keep picky compilers happy */
  fprintf(log, "Server timed out\n");
  exit(99);
  }




  /*************************************************
  *                 Main Program                   *
  *************************************************/


int main(int argc, char **argv)
{
char *logfile;
char *logmode = "w";
FILE *script;
unsigned char sbuffer[1024];
unsigned char ibuffer[1024];

  if (argc < 3)
    {
    fprintf(stdout, "500 Script and log file required\n");
    exit(1);
    }


/* Get the script and log open */

  script = fopen(argv[1], "r");
  if (script == NULL)
    {
    fprintf(stdout, "500 Failed to open script %s: %s\r\n", argv[1],
      strerror(errno));
    exit(1);
    }


  logfile = argv[2];
  if (logfile[0] == '+')
    {
    logfile++;
    logmode = "a";
    }


  log = fopen(logfile, logmode);
  if (log == NULL)
    {
    fprintf(stdout, "500 Failed to open log %s: %s\r\n", logfile,
      strerror(errno));
    exit(1);
    }


/* SIGALRM handler crashes out */

signal(SIGALRM, sigalrm_handler);

/* Read the script, and do what it says. */

  while (fgets(sbuffer, sizeof(sbuffer), script) != NULL)
    {
    int n = (int)strlen(sbuffer);
    while (n > 0 && isspace(sbuffer[n-1])) n--;
    sbuffer[n] = 0;


    /* If the script line starts with a digit, it is a response line which
    we are to send. */


    if (isdigit(sbuffer[0]))
      {
      fprintf(log, "%s\n", sbuffer);
      fflush(log);
      fprintf(stdout, "%s\r\n", sbuffer);
      fflush(stdout);
      }


    /* If the script line starts with "*sleep" we just sleep for a while
    before continuing. Do not write this to the log, as it may not get
    written at the right place in a log that's being shared. */


    else if (strncmp(sbuffer, "*sleep ", 7) == 0)
      {
      sleep(atoi(sbuffer+7));
      }


    /* Otherwise the script line is the start of an input line we are expecting
    from the client, or "*eof" indicating we expect the client to close the
    connection. Read command line or data lines; the latter are indicated
    by the expected line being just ".". */


    else
      {
      int data = strcmp(sbuffer, ".") == 0;


      fprintf(log, "%s\n", sbuffer);
      fflush(log);


      /* Loop for multiple data lines */


      for (;;)
        {
        int n;
        alarm(5);
        if (fgets(ibuffer, sizeof(ibuffer), stdin) == NULL)
          {
          fprintf(log, "%sxpected EOF read from client\n",
            (strncmp(sbuffer, "*eof", 4) == 0)? "E" : "Une");
          goto END_OFF;
          }
        alarm(0);
        n = (int)strlen(ibuffer);
        while (n > 0 && isspace(ibuffer[n-1])) n--;
        ibuffer[n] = 0;
        fprintf(log, "<<< %s\n", ibuffer);
        if (!data || strcmp(ibuffer, ".") == 0) break;
        }


      /* Check received what was expected */


      if (strncmp(sbuffer, ibuffer, (int)strlen(sbuffer)) != 0)
        {
        fprintf(log, "Comparison failed - bailing out\n");
        goto END_OFF;
        }
      }
    }


/* This could appear in the wrong place in a shared log, so forgo it. */
/* fprintf(log, "End of script\n"); */

END_OFF:
fclose(script);
fclose(log);

exit(0);
}

/* End of mtpscript.c */

Index: server.c
====================================================================
/* $Cambridge: exim/exim-test/src/server.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */

/* A little hacked up program that listens on a given port and allows a script
to play the part of a remote MTA for testing purposes. This scripted version is
hacked from my original interactive version. A further hack allows it to listen
on a Unix domain socket as an alternative to a TCP/IP port.

In an IPv6 world, listening happens on both an IPv6 and an IPv4 socket, always
on all interfaces, unless the option -noipv6 is given. */

/* ANSI C standard includes */

#include <ctype.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* Unix includes */

#include <errno.h>
#include <dirent.h>
#include <sys/types.h>

#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>

#ifdef HAVE_NETINET_IP_VAR_H
#include <netinet/ip_var.h>
#endif

#include <netdb.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <utime.h>

#ifdef AF_INET6
#define HAVE_IPV6 1
#endif

#ifndef S_ADDR_TYPE
#define S_ADDR_TYPE u_long
#endif


  typedef struct line {
    struct line *next;
    char line[1];
  } line;



  /*************************************************
  *            SIGALRM handler - crash out         *
  *************************************************/


  static void
  sigalrm_handler(int sig)
  {
  sig = sig;    /* Keep picky compilers happy */
  printf("\nServer timed out\n");
  exit(99);
  }



  /*************************************************
  *          Get textual IP address                *
  *************************************************/


/* This function is copied from Exim */

char *
host_ntoa(const void *arg, char *buffer)
{
char *yield;

/* The new world. It is annoying that we have to fish out the address from
different places in the block, depending on what kind of address it is. It
is also a pain that inet_ntop() returns a const char *, whereas the IPv4
function inet_ntoa() returns just char *, and some picky compilers insist
on warning if one assigns a const char * to a char *. Hence the casts. */

  #if HAVE_IPV6
  char addr_buffer[46];
  int family = ((struct sockaddr *)arg)->sa_family;
  if (family == AF_INET6)
    {
    struct sockaddr_in6 *sk = (struct sockaddr_in6 *)arg;
    yield = (char *)inet_ntop(family, &(sk->sin6_addr), addr_buffer,
      sizeof(addr_buffer));
    }
  else
    {
    struct sockaddr_in *sk = (struct sockaddr_in *)arg;
    yield = (char *)inet_ntop(family, &(sk->sin_addr), addr_buffer,
      sizeof(addr_buffer));
    }


/* If the result is a mapped IPv4 address, show it in V4 format. */

if (strncmp(yield, "::ffff:", 7) == 0) yield += 7;

#else /* HAVE_IPV6 */

/* The old world */

yield = inet_ntoa(((struct sockaddr_in *)arg)->sin_addr);
#endif

strcpy(buffer, yield);
return buffer;
}


  /*************************************************
  *                 Main Program                   *
  *************************************************/


  #define v6n 0    /* IPv6 socket number */
  #define v4n 1    /* IPv4 socket number */
  #define udn 2    /* Unix domain socket number */
  #define skn 2    /* Potential number of sockets */


int main(int argc, char **argv)
{
int i;
int port = 0;
int listen_socket[3] = { -1, -1, -1 };
int accept_socket;
int dup_accept_socket;
int connection_count = 1;
int count;
int on = 1;
int timeout = 5;
int use_ipv4 = 1;
int use_ipv6 = 1;
int debug = 0;
int na = 1;
line *script = NULL;
line *last = NULL;
line *s;
FILE *in, *out;

char *sockname = NULL;
unsigned char buffer[10240];

  struct sockaddr_un sockun;            /* don't use "sun" */
  struct sockaddr_un sockun_accepted;
  int sockun_len = sizeof(sockun_accepted);


#if HAVE_IPV6
struct sockaddr_in6 sin6;
struct sockaddr_in6 accepted;
struct in6_addr anyaddr6 = IN6ADDR_ANY_INIT ;
#else
struct sockaddr_in accepted;
#endif

/* Always need an IPv4 structure */

struct sockaddr_in sin4;

int len = sizeof(accepted);


/* Sort out the arguments */

  while (na < argc && argv[na][0] == '-')
    {
    if (strcmp(argv[na], "-d") == 0) debug = 1;
    else if (strcmp(argv[na], "-t") == 0) timeout = atoi(argv[++na]);
    else if (strcmp(argv[na], "-noipv4") == 0) use_ipv4 = 0;
    else if (strcmp(argv[na], "-noipv6") == 0) use_ipv6 = 0;
    else
      {
      printf("server: unknown option %s\n", argv[na]);
      exit(1);
      }
    na++;
    }


  if (!use_ipv4 && !use_ipv6)
    {
    printf("server: -noipv4 and -noipv6 cannot both be given\n");
    exit(1);
    }


  if (na >= argc)
    {
    printf("server: no port number or socket name given\n");
    exit(1);
    }


  if (argv[na][0] == '/')
    {
    sockname = argv[na];
    unlink(sockname);       /* in case left lying around */
    }
  else port = atoi(argv[na]);
  na++;


if (na < argc) connection_count = atoi(argv[na]);


/* Create sockets */

  if (port == 0)  /* Unix domain */
    {
    if (debug) printf("Creating Unix domain socket\n");
    listen_socket[udn] = socket(PF_UNIX, SOCK_STREAM, 0);
    if (listen_socket[udn] < 0)
      {
      printf("Unix domain socket creation failed: %s\n", strerror(errno));
      exit(1);
      }
    }
  else
    {
    #if HAVE_IPV6
    if (use_ipv6)
      {
      if (debug) printf("Creating IPv6 socket\n");
      listen_socket[v6n] = socket(AF_INET6, SOCK_STREAM, 0);
      if (listen_socket[v6n] < 0)
        {
        printf("IPv6 socket creation failed: %s\n", strerror(errno));
        exit(1);
        }


      /* If this is an IPv6 wildcard socket, set IPV6_V6ONLY if that option is
      available. */


      #ifdef IPV6_V6ONLY
      if (setsockopt(listen_socket[v6n], IPPROTO_IPV6, IPV6_V6ONLY, (char *)(&on),
            sizeof(on)) < 0)
        printf("Setting IPV6_V6ONLY on IPv6 wildcard "
          "socket failed (%s): carrying on without it\n", strerror(errno));
      #endif  /* IPV6_V6ONLY */
      }
    #endif  /* HAVE_IPV6 */


    /* Create an IPv4 socket if required */


    if (use_ipv4)
      {
      if (debug) printf("Creating IPv4 socket\n");
      listen_socket[v4n] = socket(AF_INET, SOCK_STREAM, 0);
      if (listen_socket[v4n] < 0)
        {
        printf("IPv4 socket creation failed: %s\n", strerror(errno));
        exit(1);
        }
      }
    }



/* Set SO_REUSEADDR on the IP sockets so that the program can be restarted
while a connection is being handled - this can happen as old connections lie
around for a bit while crashed processes are tidied away. Without this, a
connection will prevent reuse of the smtp port for listening. */

  for (i = v6n; i <= v4n; i++)
    {
    if (listen_socket[i] >= 0 &&
        setsockopt(listen_socket[i], SOL_SOCKET, SO_REUSEADDR, (char *)(&on),
          sizeof(on)) < 0)
      {
      printf("setting SO_REUSEADDR on socket failed: %s\n", strerror(errno));
      exit(1);
      }
    }



/* Now bind the sockets to the required port or path. If a path, ensure
anyone can write to it. */

  if (port == 0)
    {
    struct stat statbuf;
    sockun.sun_family = AF_UNIX;
    if (debug) printf("Binding Unix domain socket\n");
    sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1), sockname);
    if (bind(listen_socket[udn], (struct sockaddr *)&sockun, sizeof(sockun)) < 0)
      {
      printf("Unix domain socket bind() failed: %s\n", strerror(errno));
      exit(1);
      }
    (void)stat(sockname, &statbuf);
    if (debug) printf("Setting Unix domain socket mode: %0x\n",
      statbuf.st_mode | 0777);
    if (chmod(sockname, statbuf.st_mode | 0777) < 0)
      {
      printf("Unix domain socket chmod() failed: %s\n", strerror(errno));
      exit(1);
      }
    }


  else
    {
    for (i = 0; i < skn; i++)
      {
      if (listen_socket[i] < 0) continue;


      /* For an IPv6 listen, use an IPv6 socket */


      #if HAVE_IPV6
      if (i == v6n)
        {
        memset(&sin6, 0, sizeof(sin6));
        sin6.sin6_family = AF_INET6;
        sin6.sin6_port = htons(port);
        sin6.sin6_addr = anyaddr6;
        if (bind(listen_socket[i], (struct sockaddr *)&sin6, sizeof(sin6)) < 0)
          {
          printf("IPv6 socket bind() failed: %s\n", strerror(errno));
          exit(1);
          }
        }
      else
      #endif


      /* For an IPv4 bind, use an IPv4 socket, even in an IPv6 world. If an IPv4
      bind fails EADDRINUSE after IPv6 success, carry on, because it means the
      IPv6 socket will handle IPv4 connections. */


        {
        memset(&sin4, 0, sizeof(sin4));
        sin4.sin_family = AF_INET;
        sin4.sin_addr.s_addr = (S_ADDR_TYPE)INADDR_ANY;
        sin4.sin_port = htons(port);
        if (bind(listen_socket[i], (struct sockaddr *)&sin4, sizeof(sin4)) < 0)
          {
          if (listen_socket[v6n] < 0 || errno != EADDRINUSE)
            {
            printf("IPv4 socket bind() failed: %s\n", strerror(errno));
            exit(1);
            }
          else
            {
            close(listen_socket[i]);
            listen_socket[i] = -1;
            }
          }
        }
      }
    }



/* Start listening. If IPv4 fails EADDRINUSE after IPv6 succeeds, ignore the
error because it means that the IPv6 socket will handle IPv4 connections. Don't
output anything, because it will mess up the test output, which will be
different for systems that do this and those that don't. */

  for (i = 0; i <= skn; i++)
    {
    if (listen_socket[i] >= 0 && listen(listen_socket[i], 5) < 0)
      {
      if (i != v4n || listen_socket[v6n] < 0 || errno != EADDRINUSE)
        {
        printf("listen() failed: %s\n", strerror(errno));
        exit(1);
        }
      }
    }



/* This program handles only a fixed number of connections, in sequence. Before
waiting for the first connection, read the standard input, which contains the
script of things to do. A line containing "++++" is treated as end of file.
This is so that the Perl driving script doesn't have to close the pipe -
because that would cause it to wait for this process, which it doesn't yet want
to do. The driving script adds the "++++" automatically - it doesn't actually
appear in the test script. */

  while (fgets(buffer, sizeof(buffer), stdin) != NULL)
    {
    line *next;
    int n = (int)strlen(buffer);
    while (n > 0 && isspace(buffer[n-1])) n--;
    buffer[n] = 0;
    if (strcmp(buffer, "++++") == 0) break;
    next = malloc(sizeof(line) + n);
    next->next = NULL;
    strcpy(next->line, buffer);
    if (last == NULL) script = last = next;
      else last->next = next;
    last = next;
    }


fclose(stdin);

/* SIGALRM handler crashes out */

signal(SIGALRM, sigalrm_handler);

/* s points to the current place in the script */

s = script;

  for (count = 0; count < connection_count; count++)
    {
    alarm(timeout);
    if (port <= 0)
      {
      printf("Listening on %s ... ", sockname);
      fflush(stdout);
      accept_socket = accept(listen_socket[udn],
        (struct sockaddr *)&sockun_accepted, &sockun_len);
      }


    else
      {
      int lcount;
      int max_socket = 0;
      fd_set select_listen;


      printf("Listening on port %d ... ", port);
      fflush(stdout);


      FD_ZERO(&select_listen);
      for (i = 0; i < skn; i++)
        {
        if (listen_socket[i] >= 0) FD_SET(listen_socket[i], &select_listen);
        if (listen_socket[i] > max_socket) max_socket = listen_socket[i];
        }


      lcount = select(max_socket + 1, &select_listen, NULL, NULL, NULL);
      if (lcount < 0)
        {
        printf("Select failed\n");
        fflush(stdout);
        continue;
        }


      accept_socket = -1;
      for (i = 0; i < skn; i++)
        {
        if (listen_socket[i] > 0 && FD_ISSET(listen_socket[i], &select_listen))
          {
          accept_socket = accept(listen_socket[i],
            (struct sockaddr *)&accepted, &len);
          FD_CLR(listen_socket[i], &select_listen);
          break;
          }
        }
      }
    alarm(0);


    if (accept_socket < 0)
      {
      printf("accept() failed: %s\n", strerror(errno));
      exit(1);
      }


    out = fdopen(accept_socket, "w");


    dup_accept_socket = dup(accept_socket);


    if (port > 0)
      printf("\nConnection request from [%s]\n", host_ntoa(&accepted, buffer));
    else
      {
      printf("\nConnection request\n");


      /* Linux supports a feature for acquiring the peer's credentials, but it
      appears to be Linux-specific. This code is untested and unused, just
      saved here for reference. */


      /**********--------------------
      struct ucred cr;
      int cl=sizeof(cr);


      if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl)==0) {
        printf("Peer's pid=%d, uid=%d, gid=%d\n",
                cr.pid, cr.uid, cr.gid);
      --------------*****************/
      }


    if (dup_accept_socket < 0)
      {
      printf("Couldn't dup socket descriptor\n");
      printf("421 Connection refused: %s\n", strerror(errno));
      fprintf(out, "421 Connection refused: %s\r\n", strerror(errno));
      fclose(out);
      exit(2);
      }


    in = fdopen(dup_accept_socket, "r");


    /* Loop for handling the conversation(s). For use in SMTP sessions, there are
    default rules for determining input and output lines: the latter start with
    digits. This means that the input looks like SMTP dialog. However, this
    doesn't work for other tests (e.g. ident tests) so we have explicit '<' and
    '>' flags for input and output as well as the defaults. */


    for (; s != NULL; s = s->next)
      {
      char *ss = s->line;


      /* Output lines either start with '>' or a digit. In the '>' case we can
      fudge the sending of \r\n as required. Default is \r\n, ">>" send nothing,
      ">CR>" sends \r only, and ">LF>" sends \n only. We can also force a
      connection closedown by ">*eof". */


      if (ss[0] == '>')
        {
        char *end = "\r\n";
        printf("%s\n", ss++);


        if (strncmp(ss, "*eof", 4) == 0)
          {
          s = s->next;
          goto END_OFF;
          }


        if (*ss == '>')
          { end = ""; ss++; }
        else if (strncmp(ss, "CR>", 3) == 0)
          { end = "\r"; ss += 3; }
        else if (strncmp(ss, "LF>", 3) == 0)
          { end = "\n"; ss += 3; }


        fprintf(out, "%s%s", ss, end);
        }


      else if (isdigit((unsigned char)ss[0]))
        {
        printf("%s\n", ss);
        fprintf(out, "%s\r\n", ss);
        }


      /* If the script line starts with "*sleep" we just sleep for a while
      before continuing. */


      else if (strncmp(ss, "*sleep ", 7) == 0)
        {
        int sleepfor = atoi(ss+7);
        printf("%s\n", ss);
        fflush(out);
        sleep(sleepfor);
        }


      /* Otherwise the script line is the start of an input line we are expecting
      from the client, or "*eof" indicating we expect the client to close the
      connection. Read command line or data lines; the latter are indicated
      by the expected line being just ".". If the line starts with '<', that
      doesn't form part of the expected input. (This allows for incoming data
      starting with a digit.) */


      else
        {
        int offset;
        int data = strcmp(ss, ".") == 0;


        if (ss[0] == '<')
          {
          buffer[0] = '<';
          offset = 1;
          }
        else offset = 0;


        fflush(out);


        for (;;)
          {
          int n;
          alarm(timeout);
          if (fgets(buffer+offset, sizeof(buffer)-offset, in) == NULL)
            {
            printf("%sxpected EOF read from client\n",
              (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
            s = s->next;
            goto END_OFF;
            }
          alarm(0);
          n = (int)strlen(buffer);
          while (n > 0 && isspace(buffer[n-1])) n--;
          buffer[n] = 0;
          printf("%s\n", buffer);
          if (!data || strcmp(buffer, ".") == 0) break;
          }


        if (strncmp(ss, buffer, (int)strlen(ss)) != 0)
          {
          printf("Comparison failed - bailing out\n");
          printf("Expected: %s\n", ss);
          break;
          }
        }
      }


    END_OFF:
    fclose(in);
    fclose(out);
    }


if (s == NULL) printf("End of script\n");

if (sockname != NULL) unlink(sockname);
exit(0);
}

/* End of server.c */

Index: showids.c
====================================================================
/* $Cambridge: exim/exim-test/src/showids.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(void)
{
int count, i;
gid_t grouplist[100];

  printf("uid=%d gid=%d euid=%d egid=%d\n",
    getuid(), getgid(), geteuid(), getegid());


/* Can no longer use this because on different systems, the supplemental
groups will be different. */

#ifdef NEVER
printf("supplemental groups: ");
count = getgroups(100, grouplist);
for (i = 0; i < count; i++) printf("%d ", grouplist[i]);
printf("\n");
#endif

return 0;
}