/*-
 * Copyright (c) 2002  Granch Ltd. 	All rigts reserved.
 *
 * All or some portions of this file are derived from material licensed
 * to the Grahcn Ltd. and are reproduced herein with the permission of
 * Granch Ltd.
 *
 * 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.
 * 3. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE GRANCH LTD. AND THEIR CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS 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 REGENTS OR 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.
 *
 * Author : Rashid N. Achilov		E-Mail shelton@granch.ru
 *
 *	@(#)findroach.c			0.91.3 (Granch Ltd.) 	23/01/03
 */
/*------------------------------------------------------------------------

        Kaspersky Anti-Virus mail filter, based on libmilter API

 	UNIX FreeBSD 4.6 version

        Find a roach in a mail message.

--------------------------------------------------------------------------*/
#include "kavmilter.h"
#include "externals.h"
#include "functions.h"

// Find roaches in mail message (and probaply, find nothing :-) )

short int _KAV_milter_find_roach(char *fname,char *reply,short int *pretlen)
{
  int kavfd;   				// KAV socket descriptor
  char *localid = "(KAV check)";	// Local identity
  time_t now;				// Current time to send to KAV
  char *timebuf;			// Ctime_r work buffer
  char *querybuf;			// Query buffer for KAV query
  char *resstr;				// Result buffer for KAV response
  long uintbuf = 0;			// Response code buffer
  int exitcode,ret;				// KAV exit code
  struct sockaddr_un KAV_address;	// Socket to connect to KAV

// Create socket to connect to KAV data channel

  bzero((char *) &KAV_address,sizeof(KAV_address));
  KAV_address.sun_family = AF_UNIX;
  strncpy(KAV_address.sun_path,_KAV_milter_config.KAV_pipe,sizeof(KAV_address.sun_path) - 1);

  if ((kavfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
    {
      syslog(LOG_ERR,"create socket in %s error: %m",localid);
      return ERR;
    }

// Connect to KAV data channel

  if (connect(kavfd,(struct sockaddr *)(&KAV_address),
  	sizeof(KAV_address.sun_family) + strlen(KAV_address.sun_path) + 1) != 0)
    {
      syslog(LOG_ERR,"connect to KAV socket in %s error: %m",localid);
      return ERR;
    }

// Allocate memory to query buffer, prepare and send query to KAV daemon

  if ((querybuf = (char *) malloc(PATH_MAX + TEMPLENGTH + 26)) == NULL)
    {
      syslog(LOG_ERR,"%s KAV query buffer %s, failed",cannot,localid);
      return ERR;
    }
   else
     timebuf = querybuf + PATH_MAX + TEMPLENGTH;

  now = time(NULL);
  sprintf(querybuf,"<0>%.15s:%s",ctime_r(&now,timebuf) + 4,fname);

// Write prepared buffer

  if (_KAV_milter_write_socket(kavfd, querybuf, strlen(querybuf) + 1) == ERR)
    {
      syslog(LOG_ERR,"cannot send message to KAV daemon: %m");
      close(kavfd);
      return ERR;
    }

// Read KAV answer (first stage)

  if ((_KAV_milter_read_socket(kavfd,(char *) &uintbuf, 2)) == ERR)
    {
      syslog(LOG_ERR,"cannot receive message from KAV daemon: %m");
      close(kavfd);
      return ERR;
    }

// Transform first stage answer and think, what can do at next stage

  exitcode = uintbuf & (0xff - 0x30);

  if ((uintbuf & 0x00ff) == 0x3f) 			//0x3f '?'
    {
      syslog(LOG_ERR,"misconfigured KAV daemon, answer is 0x%x",exitcode);
      close(kavfd);
      return ERR;
    }

  if (_KAV_milter_config.debug_level > 50)
    syslog(LOG_ERR,"KAV test result: 0x%x, flags: 0x%x",
       			(int) uintbuf & 0x00ff,(int) uintbuf & 0xff00);

// If we have a special conditions after receiving answer

  if ((uintbuf & 0xff00) != 0)
    {

// When special condition 2, we should read size of shared memory object
// with disinfected file. We assumed, that KAV don't setup to automatic
// disinfect, and can safely ignore this special condition

      if ((uintbuf & 0x200) != 0)
        {
          syslog(LOG_ERR,"misconfigured KAV daemon (shm), answer is 0x%x",exitcode);
          close(kavfd);
          return ERR;
        }

// When special condition 1, we should read size of report, follwed they.
// We assumed, that KAV dont' setup to show Packers, OK, and other important
// but not very high :-) information

      if ((uintbuf & 0x100) != 0)
        {

// Read length itself

          if ((ret = _KAV_milter_read_socket(kavfd,
          			(char *) &uintbuf, sizeof(long))) == ERR)
            {
              syslog(LOG_ERR,"cannot receive message from KAV daemon: %m");
              close(kavfd);
              return ERR;
            }

          if (_KAV_milter_config.debug_level > 50)
            syslog(LOG_ERR,"KAV reply string length: %d",(int) uintbuf);

// Store reply length in external parameter to call out

          *pretlen = uintbuf;
          reply[0]=0;

// Read main KAV response, until we won't read zero bytes

          for(resstr = reply;uintbuf > 0;uintbuf -= ret,resstr += ret)
           {
             ret = _KAV_milter_read_socket(kavfd, resstr, uintbuf);
             resstr[ret] = 0;
           }

// Print raw KAV reply text (debug information)

          if (*pretlen > 0)
            if (_KAV_milter_config.debug_level > 50)
              syslog(LOG_ERR,"read %d bytes,KAV reply (raw): %s",
            					(resstr - reply),reply);
        }
    }

// When we have an extended return code, we are complaining about problems
// in KAV itself

  switch (exitcode & 0xf0)
   {
     case 8:
       syslog(LOG_ERR,"KAV internal error: Integrity failed");
       exitcode = ERR;
       break;

     case 4:
       syslog(LOG_ERR,"KAV internal error: Bases not found");
       exitcode = ERR;
       break;

     default: ;
   }

// Close KAV data channel and go back to the future

  close(kavfd);
  return exitcode;
}


syntax highlighted by Code2HTML, v. 0.9.1