/* libsrs - Sender Rewriting Scheme library
*	
*  Author: James Couzens <jcouzens@obscurity.org>
*
*  FILE: srs.c
*  DESC: libsrs API sample program and test app
*
*  License:
*
*  The libspf Software License, Version 1.0
*
*  Copyright (c) 2004 James Couzens.  All rights reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*  1. Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*
*  2. Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in
*     the documentation and/or other materials provided with the
*     distribution.
*
*  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
*  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
*  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*  DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
*  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
*  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
*  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
*  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
*  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
*  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
*  SUCH DAMAGE.
* 
*/

#include <stdio.h>       /* printf */
#include <time.h>        /* time */
#include <sys/types.h>   /* typedefs */
#include <sys/time.h>    /* timeval / timezone / gettimeofday */

#include "srs_cmd.h"

#ifdef size_t
#undef size_t
#define size_t unsigned long 
#endif

int main(int argc, char *argv[])
{
  enum SRS_ACT  ACT         = SRS_DEFAULT;  /* perform default action */  
  SRS_BOOL      QUIET       = FALSE;        /* no output (test mode) */
  u_int8_t      i           = 0;            /* utility */
  
  char          *margv      = NULL;         /* pointer to current argv element */
  
  int32_t       age         = 0;            /* expiry */
  int16_t       hash_len    = 4;            /* base64 chars 4 x 64 bits */
  int16_t       hash_min    = 4;            /* base64 chars */

  char          *secret     = NULL;         /* secret / key */
  char          *addr1      = NULL;         /* email address from */
  char          *addr2      = NULL;         /* email address to  */

  char          *tmp        = NULL;          /* utility pointer */
  
  conf.level = 0;

  if (argc <= 1 || strcmp(argv[1], "--help") == 0 ||
    strcmp(argv[1], "--h") == 0 || strcmp(argv[1], "-h") == 0)
  {
    do_help();
    return(FALSE);
  }

  for (i = 1; i < argc; i++)
  {
    tmp = argv[i];

    if (*tmp && *tmp == '-')
    {
      margv = (tmp + 3);
      
      switch (*(tmp + 1))
      {
        case 'a' :
          age      = atoi(margv);
          xvprintf("AGE: %u\n", age);
          break;
        case 'b' :
          hash_min = atoi(margv);
          xvprintf("HASH MIN: %u\n", hash_min);
          break;
        case 'c' :
          hash_len = atoi(margv);
          xvprintf("HASH LEN: %u\n", hash_len);
          break;
        case 'd' :
          if ((conf.level = atoi(margv)) > 0)
          {
            switch(conf.level)
            {
              case 0:
                break;
              case 1:
                conf.level = FL_A;
                break;
              case 2:
                conf.level = FL_B;
                break;
              case 3:
                conf.level = (FL_A + FL_B);
                break;
              case 4:
                conf.level = FL_D;
                break;
              case 5:
                conf.level = (FL_A + FL_D);
                break;
              case 6:
                conf.level = (FL_B + FL_D);
                break;
              case 7:
                conf.level = (FL_A + FL_B + FL_D);
                break;
            }
          }
          xvprintf("DEBUG level: %u\n", conf.level);
          break;
        case 'f' :
          addr1  = xstrndup(margv, MAX_RWA);
          xvprintf("ADDR: %s\n", addr1);
          ACT = SRS_FORWARD;
          break;
        case 'q' :
          QUIET = atoi(margv);
          break;
        case 'r' :
          addr1  = xstrndup(margv, MAX_HOSTNAME);
          xvprintf("FORWARDER: %s\n", addr1);
          ACT = SRS_REVERSE;
          break;
        case 's' :
          secret = xstrndup(margv, MAX_SECRET);
          xvprintf("SECRET: %s\n", secret);
          break;
        case 't' :
          addr2  = xstrndup(margv, MAX_HOSTNAME);
          xvprintf("FORWARDER: %s\n", addr2);
          break;
        case 'z':
          ACT    = SRS_DEFAULT;
          i      = argc;
          break;
      } /* switch */
    }
  } /* for */

  if (QUIET == FALSE)
  {
    printf("SRS Query v%s - James Couzens <jcouzens@obscurity.org>\n\n", 
      SRS_BVERSION);
  }

  if (ACT == SRS_DEFAULT)
  {
    return(do_default(QUIET));
  }

  if (secret == NULL)
  {
    printf("You need to define a secret!\n\n");
    do_help();
    return(FALSE);
  }
  else if (addr1 == NULL)
  {
    printf("You need to specify a from address\n\n");
    do_help();
    return(FALSE);
  }
  else if (ACT == SRS_FORWARD && addr2 == NULL)
  {
    printf("You need to specify a destination address\n\n");
    do_help();
    return(FALSE);
  }
  
  switch (ACT)
  {
    case SRS_FORWARD:
      return(do_forward(secret, age, hash_len, hash_min, addr1, addr2, QUIET));
    case SRS_REVERSE:
      return(do_reverse(secret, age, hash_len, hash_min, addr1, QUIET));
    case SRS_DEFAULT:
      break;
    default:
      do_help();
      break;
  }

  xfree(addr1);
  xfree(addr2);
  xfree(secret); 
  return(FALSE); 
}

SRS_BOOL do_forward(char *secret, int32_t age, int32_t hash_len, int32_t hash_min, 
  char *addr1, char *addr2, SRS_BOOL QUIET)
{
  size_t len;               /* utility (length of srsaddr) */
  srs_t *srs      = NULL;   /* srs structure */
  char *srsaddr   = NULL;   /* srs address storage */

  if (secret == NULL || addr1 == NULL || addr2 == NULL)
  {
    printf("do_forward :: Passed a NULL char!  Abort!\n");
    return(FALSE);
  }

  if (QUIET == FALSE)
  {
    printf("SRS Forward: from: (%s) to: (%s)\n", addr1, addr2);
  }
  
  gettimeofday(&srs_time[0].start, &srs_time[0].tzp);

  srs = SRS_new(secret, age, hash_len, hash_min);
 
  /* simulate email from HOSTA to HOSTB */
  if ((srsaddr = SRS_forward(srs, addr1, addr2)) == NULL)
  {
    printf("Rewriting of address (%s) failed!  !@$#\n", addr1);
    srs = SRS_del(srs);
    return(FALSE);
  }

  len = strlen(srsaddr);

  if (QUIET == FALSE)
  {
    printf("SRS Forward complete: envelope-from: (%s) : %lu\n",
      srsaddr, (u_long)len);
  
    gettimeofday(&srs_time[0].finish, &srs_time[0].tzp);
    printf("Rewrite executed in %lu.%u seconds\n",
      (srs_time[0].finish.tv_sec    - srs_time[0].start.tv_sec),
      (u_int8_t)(srs_time[0].finish.tv_usec - srs_time[0].start.tv_usec));
  }
 
  xfree(secret);
  xfree(addr1);
  xfree(addr2);
  xfree(srsaddr);
  srs = SRS_del(srs);

  return(TRUE);
}


SRS_BOOL do_reverse(char *secret, int32_t age, int32_t hash_len, int32_t hash_min, 
  char *addr, SRS_BOOL QUIET)
{
  size_t len;                  /* utility (length of srsaddr) */
  srs_t *srs        = NULL;    /* srs structure */
  char  *srsaddr    = NULL;    /* srs address storage */

  if (secret == NULL || addr == NULL)                             
  {
    printf("do_reverse :: Passed a NULL char!  Abort!\n");
    return(FALSE);
  } 

  if (QUIET == FALSE)
  {
    printf("SRS Reverse: envelope-from: (%s)\n", addr);
  }
  
  gettimeofday(&srs_time[0].start, &srs_time[0].tzp);

  srs = SRS_new(secret, age, hash_len, hash_min);
  len = strlen(srsaddr);

  /* simulate HOSTD reversing address */
  if ((srsaddr = SRS_reverse(srs, addr)) == NULL)
  {
    printf("Rewriting of address (%s) failed!  !@$#\n", addr);
    srs = SRS_del(srs);
    return(FALSE);
  }
  else
  {
    printf("SRS Reverse complete: HMAC verify succeeded, bouncing email : "
      "enevelope-from: (%s) : %lu\n", srsaddr, (u_long)len);
    return(TRUE);
  }

  if (QUIET == FALSE)
  {
    printf("SRS Reverse complete: HMAC verify failed, dropping email : "
      "enevelope-from: (%s) : %lu\n", srsaddr, (u_long)len);
  
    gettimeofday(&srs_time[0].finish, &srs_time[0].tzp);
    printf("Rewrite executed in %lu.%u seconds\n",
      (srs_time[0].finish.tv_sec    - srs_time[0].start.tv_sec),
      (u_int8_t)(srs_time[0].finish.tv_usec - srs_time[0].start.tv_usec));
  }
 
  xfree(secret);
  xfree(addr);
  xfree(srsaddr);
  srs = SRS_del(srs);
  
  return(FALSE);
}

SRS_BOOL do_default(SRS_BOOL QUIET)
{
  srs_t         *srs        = NULL;     /* srs structure */

  char          *srsaddr    = NULL;     /* srs address structure */
  char          *addr1      = NULL;     /* email address from */
  char          *addr2      = NULL;     /* email address to  */
  char          *addr3      = NULL;     /* email address forwarded to */
  char          *addr4      = NULL;     /* email address forwarded to again */

  gettimeofday(&srs_time[0].start, &srs_time[0].tzp);

  srs   = SRS_new((char *)"asshat", (int32_t)THREE_DAYS, 4, 4);
  addr1 = strdup((char *)"james@hosta.org");
  addr2 = strdup((char *)"james@hostb.org");
  addr3 = strdup((char *)"james@hostc.org");
  addr4 = strdup((char *)"james@hostd.org");

  /* simulate email from HOSTA to HOSTB */
  if ((srsaddr = SRS_forward(srs, addr1, addr2)) == NULL)
  {
    printf("HOSTB: Rewriting of address failed!  !@$#\n");
    srs = SRS_del(srs);
    return(FALSE);
  }

  if (QUIET == FALSE)
  {
    printf("From: (%s) to: (%s) which forwards to: (%s)\n", addr1, addr2, addr3);
    printf("HOSTA Send: envelope-from: (%s) : %lu\n", addr1, (u_long)strlen(addr1));  
    printf("HOSTB Rewrite: envelope-from: (%s) : %lu\n", srsaddr, (u_long)strlen(srsaddr));
  }
  
  xfree(addr1);
  addr1 = NULL;
  xfree(addr2);
  addr2 = strdup(srsaddr);
  xfree(srsaddr);
  srsaddr = NULL;
  
  if (addr3 != NULL)
  {  
    /* simulate email from HOSTA to HOSTB forwarded to HOSTC */
    if ((srsaddr = SRS_forward(srs, addr2, addr3)) == NULL)
    {
      printf("HOSTC: Rewriting of address failed!  !@$#\n");
      srs = SRS_del(srs);
      return(FALSE);
    }

    if (QUIET == FALSE)
    {
      printf("HOSTC Rewrite: envelope-from: (%s) : %lu\n", srsaddr, (u_long)strlen(srsaddr));
    }

    xfree(addr2);
    addr2 = NULL;
    xfree(addr3);
    addr3 = strdup(srsaddr);
    xfree(srsaddr);
    srsaddr = NULL;
  }
  else 
  {
    srsaddr = addr2;
  }
  
  if (addr4 != NULL)
  { 

    /* simulate email from HOSTA to HOSTB forwarded to HOSTC forwarded to HOSTD */
    if ((srsaddr = SRS_forward(srs, addr3, addr4)) == NULL)
    {
      printf("HOSTB: Rewriting of address failed!  !@$#\n");
      srs = SRS_del(srs);
      return(FALSE);
    }

    if (QUIET == FALSE)
    {
      printf("HOSTD Rewrite: envelope-from: (%s) : %lu\n", srsaddr, (u_long)strlen(srsaddr));
    }
    
    xfree(addr3);
    xfree(addr4);
  }
  else if (addr3 != NULL)
  {
    srsaddr = addr3;
  }
  else
  {
    srsaddr = addr2;
  }
  
  /* simulate HOSTD reversing address */
  if ((addr3 = SRS_reverse(srs, srsaddr)) == NULL)
  {
    printf("HOSTD Rewriting of address failed!  !@$#\n");
    srs = SRS_del(srs);
    return(FALSE);
  }

  if (QUIET == FALSE)
  {
    printf("HOSTB Bounce: envelope-from: (%s) : %lu\n", addr3, (u_long)strlen(addr3));
  }
  
  xfree(srsaddr);

  /* simulate HOSTB receiving the bounce, and verifying the hash */
  if ((addr2 = SRS_reverse(srs, addr3)) == NULL)
  {
    printf("Rewriting of address failed!  !@$#\n");
    srs = SRS_del(srs);
    return(FALSE);
  }
  else
  {
    if (QUIET == FALSE)
    {
      printf("HOSTB HMAC VERIFIED : enevelope-from: (%s) : %lu\n", 
        addr2, (u_long)strlen(addr2));
    }
  }
  
  if (QUIET == FALSE)
  {
    gettimeofday(&srs_time[0].finish, &srs_time[0].tzp);
    printf("Rewrite executed in %lu.%u seconds\n",
      (srs_time[0].finish.tv_sec - srs_time[0].start.tv_sec),
      (u_int8_t)(srs_time[0].finish.tv_usec - srs_time[0].start.tv_usec));
  }
  
  xfree(addr3);
  xfree(addr2);
  srs = SRS_del(srs);
  
  return(TRUE);
}

void do_help(void)
{
  printf("Useage: srs [options]:\nOptions:\n"
    "  -a\t\tValid age of hash (in seconds) EG: 1 day = 86400\n"
    "  -b\t\tShortest hash to auth against (default 4)\n"
    "  -c\t\tNbytes of Base64 chars to use (default 4)\n"
    "  -d\t\tDebug: 0 = all debugging off (default)\n"
    "\t\t- 1 = regular (general debugging output from SRS_ functions)\n"
    "\t\t- 2 = verbose (general debugging from UTIL functions)\n"
    "\t\t- 3 = both (combined regular and verbose debugging)\n");
  printf("\t\t- 4 = profiling (timers on HMAC and base64 enc/decoding)\n"
    "\t\t- 5 = regular + profile (regular debugging with profile)\n"
    "\t\t- 6 = verbose + profile (verbose debugging with profile)\n"
    "\t\t- 7 = regular, verbose, and profile (Super Mega Debugging!!! !@#$)\n"
    "  -f f@d.tld\tPerform an SRS forward using f@d.tld as the from address\n");
  printf(
    "  -q\tQuiet : For testing purposes, quiets all output persiod.\n"
    "  -r RWA\tPerform an SRS reverse using RWA (eg: SRS0=HiSf=FT=dom=user@fw.tld)\n"
    "  -s\t\tSecret to use when crypting\n"
    "  -t t@d.tld\tTo address.  Required for SRS Forward\n"
    "  -z\t\tTest mode, fairly complete API test.  Use -d 1 or -d 2 for.\n\n");
    
#ifndef DEBUG
  printf("To enable regular and verbose debugging build with WITH_SRS_DEBUG=yes\n");
#endif
  
  return;
}

/* end srsquery.c */


syntax highlighted by Code2HTML, v. 0.9.1