freshclam/mirman.c
376307a0
 /*
  *  Copyright (C) 2007 Tomasz Kojm <tkojm@clamav.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
  */
 
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <time.h>
 
4cd80898
 #ifndef _WIN32
b54eb319
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
44eb77a1
 #endif
b54eb319
 
376307a0
 #include "mirman.h"
 
 #include "libclamav/clamav.h"
 
 #include "shared/output.h"
 
3027857c
 #ifndef HAVE_GETADDRINFO
9424a482
 #ifndef AF_INET6
6df88f29
 #define AF_INET6    0xbeef      /* foo */
9424a482
 #endif
 #endif
 
dd8a6b10
 #define IGNORE_SHORT    (3600)              /* 1 hour */
 #define IGNORE_LONG     (6 * IGNORE_SHORT)  /* 6 hours */
376307a0
 
6df88f29
 void
dd8a6b10
 mirman_free(struct mirdat *mdat)
376307a0
 {
6df88f29
     if (mdat && mdat->num)
     {
         free (mdat->mirtab);
         mdat->num = 0;
376307a0
     }
 }
 
dd8a6b10
 fc_error_t mirman_read(const char *file, struct mirdat *mdat, uint8_t active)
376307a0
 {
6df88f29
     struct mirdat_ip mip;
     int fd, bread;
 
dd8a6b10
     memset (mdat, 0, sizeof(struct mirdat));
6df88f29
 
     if (!(mdat->active = active))
dd8a6b10
         return FC_SUCCESS;
6df88f29
 
     if ((fd = open (file, O_RDONLY | O_BINARY)) == -1)
dd8a6b10
         return FCE_OPEN;
6df88f29
 
dd8a6b10
     while ((bread = read (fd, &mip, sizeof(mip))) == sizeof(mip))
6df88f29
     {
         mdat->mirtab =
             (struct mirdat_ip *) realloc (mdat->mirtab,
dd8a6b10
                                           (mdat->num + 1) * sizeof(mip));
6df88f29
         if (!mdat->mirtab)
         {
dd8a6b10
             logg("!Can't allocate memory for mdat->mirtab\n");
6df88f29
             mirman_free (mdat);
             close (fd);
dd8a6b10
             return FCE_MEM;
6df88f29
         }
dd8a6b10
         memcpy (&mdat->mirtab[mdat->num], &mip, sizeof(mip));
6df88f29
         mdat->num++;
376307a0
     }
 
6df88f29
     close (fd);
376307a0
 
6df88f29
     if (bread)
     {
dd8a6b10
         logg("^Removing broken %s file.\n", file);
6df88f29
         unlink (file);
         mirman_free (mdat);
dd8a6b10
         return FCE_FILE;
376307a0
     }
 
dd8a6b10
     return FC_SUCCESS;
376307a0
 }
 
dd8a6b10
 fc_error_t mirman_check(uint32_t * ip, int af, struct mirdat *mdat,
                         struct mirdat_ip **md, mir_status_t *mirror_status)
376307a0
 {
dd8a6b10
     fc_error_t status = FC_SUCCESS;
     unsigned int i;
     unsigned int flevel = cl_retflevel ();
6df88f29
 
dd8a6b10
     if (NULL == md || NULL == mdat || NULL == ip) {
         logg("!mirman_check: Invalid arguments.\n");
         status = FCE_ARG;
         goto done;
     }
6df88f29
 
dd8a6b10
     *md = NULL;
6df88f29
 
     if (!mdat->active)
dd8a6b10
     {
         *mirror_status = MIRROR_OK;
         goto done;
     }
6df88f29
 
     for (i = 0; i < mdat->num; i++)
     {
dd8a6b10
         if ((af == AF_INET && mdat->mirtab[i].ip4 == *ip) ||
             ((af == AF_INET6) && (!memcmp (mdat->mirtab[i].ip6, ip, 4 * sizeof(uint32_t)))))
6df88f29
         {
dd8a6b10
             /*
              * Mirror found in mirror table.
              */
6df88f29
 
dd8a6b10
             if (mdat->dbflevel && (mdat->dbflevel > flevel) && (mdat->dbflevel - flevel > 3))
6df88f29
             {
dd8a6b10
                 /* Functionality level of database is lower than
                  * level of the database we already have */
                 if (difftime(time(NULL), mdat->mirtab[i].atime) < (mdat->dbflevel - flevel) * 3600)
                 {
                     *mirror_status = MIRROR_IGNORE__OUTDATED_VERSION;
                     goto done;
                 }
6df88f29
             }
 
dd8a6b10
             if ((mdat->mirtab[i].atime > 0) &&
                 (IGNORE_NO != mdat->mirtab[i].ignore))
6df88f29
             {
dd8a6b10
                 /*
                  * Found, but the ignore flag is set.
                  */
                 if (difftime(time(NULL), mdat->mirtab[i].atime) > IGNORE_LONG)
6df88f29
                 {
dd8a6b10
                     /* Long-Ignore timeout expired,
                      * the mirror can be attempted again */
                     mdat->mirtab[i].ignore = IGNORE_NO;
                 }
                 else if ((mdat->mirtab[i].ignore == IGNORE_SHORTTERM) &&
                         (difftime(time(NULL), mdat->mirtab[i].atime) > IGNORE_SHORT))
                 {
                     /* Mirror was only set to Short-Term ignore...
                      * the Short-Ignore timeout expired,
                      * the mirror can be attempted again */
                     mdat->mirtab[i].ignore = IGNORE_NO;
6df88f29
                 }
                 else
                 {
dd8a6b10
                     *mirror_status = MIRROR_IGNORE__PREV_ERRS;
                     goto done;
6df88f29
                 }
             }
 
dd8a6b10
             /* Mirror found, and is ok to try. */
             *md = &mdat->mirtab[i];
             *mirror_status = MIRROR_OK;
             goto done;
6df88f29
         }
376307a0
     }
 
dd8a6b10
     /* Mirror wasn't in mirror table. */
     *mirror_status = MIRROR_OK;
 
 done:
 
     return status;
376307a0
 }
 
dd8a6b10
 fc_error_t mirman_update(uint32_t * ip, int af, struct mirdat *mdat, fc_error_t error)
376307a0
 {
dd8a6b10
     fc_error_t status = FCE_ARG;
     unsigned int i = 0;
     struct mirdat_ip *mirror = NULL;
376307a0
 
dd8a6b10
     if (!mdat->active) {
         /* Disable mirrors.dat management when using a proxy. */
         return FC_SUCCESS;
     }
a7db63d1
 
dd8a6b10
     /*
      * Attempt to find the ip in the mirror table.
      */
6df88f29
     for (i = 0; i < mdat->num; i++)
     {
dd8a6b10
         if (((af == AF_INET) && (mdat->mirtab[i].ip4 == *ip)) ||
             ((af == AF_INET6) && (!memcmp(mdat->mirtab[i].ip6, ip, 4 * sizeof(uint32_t)))))
6df88f29
         {
dd8a6b10
             mirror = &mdat->mirtab[i];
6df88f29
             break;
         }
376307a0
     }
 
dd8a6b10
     if (NULL == mirror)
6df88f29
     {
dd8a6b10
         /*
          * Allocate space in the mirror table for the new mirror IP
          */
6df88f29
         mdat->mirtab =
dd8a6b10
             (struct mirdat_ip *) realloc(mdat->mirtab,
                                         (mdat->num + 1) * sizeof(struct mirdat_ip));
6df88f29
         if (!mdat->mirtab)
         {
dd8a6b10
             logg("!Can't allocate memory for new element in mdat->mirtab\n");
             return FCE_MEM;
6df88f29
         }
dd8a6b10
         memset (&mdat->mirtab[mdat->num], 0, sizeof(struct mirdat_ip));
6df88f29
         if (af == AF_INET)
         {
             mdat->mirtab[mdat->num].ip4 = *ip;
         }
         else
         {
             mdat->mirtab[mdat->num].ip4 = 0;
dd8a6b10
             memcpy (mdat->mirtab[mdat->num].ip6, ip, 4 * sizeof(uint32_t));
6df88f29
         }
         mdat->mirtab[mdat->num].atime = 0;
dd8a6b10
         mdat->mirtab[mdat->num].succ = 0;
         mdat->mirtab[mdat->num].fail = 0;
         mdat->mirtab[mdat->num].ignore = 0;
 
         mirror = &mdat->mirtab[mdat->num];
6df88f29
         mdat->num++;
376307a0
     }
 
dd8a6b10
     mirror->atime = 0;  /* will be updated in mirman_write() */
376307a0
 
dd8a6b10
     if (FC_SUCCESS == error) {
         mirror->succ++;
         mirror->fail = 0;
     }
     else
     {
         mirror->succ = 0;
         mirror->fail++;
     }
09005700
 
dd8a6b10
     if (mirror->fail >= 6)
     {
         mirror->ignore = IGNORE_LONGTERM;
     }
     else if (mirror->fail >= 3)
     {
         mirror->ignore = IGNORE_SHORTTERM;
     }
     else
     {
         mirror->ignore = IGNORE_NO;
     }
 
     return FC_SUCCESS;
09005700
 }
 
dd8a6b10
 void mirman_list(const struct mirdat *mdat)
376307a0
 {
6df88f29
     unsigned int i;
     time_t tm;
     char ip[46];
7c647273
 
6df88f29
     for (i = 0; i < mdat->num; i++)
     {
dd8a6b10
         printf("Mirror #%u\n", i + 1);
3027857c
 #ifdef HAVE_GETADDRINFO
6df88f29
         if (mdat->mirtab[i].ip4)
dd8a6b10
             printf("IP: %s\n",
                 inet_ntop(AF_INET, &mdat->mirtab[i].ip4, ip, sizeof(ip)));
6df88f29
         else
dd8a6b10
             printf("IP: %s\n",
                 inet_ntop(AF_INET6, mdat->mirtab[i].ip6, ip, sizeof(ip)));
9424a482
 #else
6df88f29
         if (mdat->mirtab[i].ip4)
dd8a6b10
             printf("IP: %s\n",
                 inet_ntoa(*(struct in_addr *)&mdat->mirtab[i].ip4));
9424a482
 #endif
dd8a6b10
         printf("Successes: %u\n", mdat->mirtab[i].succ);
         printf("Failures: %u\n", mdat->mirtab[i].fail);
6df88f29
         tm = mdat->mirtab[i].atime;
dd8a6b10
         printf("Last access: %s", ctime((const time_t *) &tm));
         if (mdat->mirtab[i].ignore) {
             time_t ignore_expires = tm + ((mdat->mirtab[i].ignore == IGNORE_LONGTERM) ? IGNORE_LONG
                                                                                       : IGNORE_SHORT);
             double difference = difftime(ignore_expires, time(NULL));
             if (difference > 0) {
                 uint32_t remaining = difference;
                 uint32_t seconds, minutes, hours;
                 seconds = remaining % 60;
                 remaining = remaining / 60;
                 minutes = remaining % 60;
                 remaining = remaining / 60;
                 hours = remaining % 60;
 
                 printf("Ignore: Yes,  %d hours %d minutes %d seconds remaining.\n",
                     hours, minutes, seconds);
             } else {
                 printf("Ignore: No\n");
             }
         } else {
             printf("Ignore: No\n");
         }
6df88f29
         if (i != mdat->num - 1)
dd8a6b10
             printf("-------------------------------------\n");
376307a0
     }
 }
 
dd8a6b10
 void mirman_whitelist(struct mirdat *mdat, unsigned int mode)
a68aa31f
 {
6df88f29
     unsigned int i;
a68aa31f
 
dd8a6b10
     if (NULL == mdat) {
         logg("!mirman_whitelist: Invalid arguments!\n");
         return;
     }
 
     switch (mode)
     {
     case 1:
         logg("*Whitelisting all blacklisted mirrors\n");
         break;
     case 2:
         logg("*Whitelisting short-term blacklisted mirrors\n");
         break;
     default:
         logg("!mirman_whitelist: Unexpected mode argument: %u\n", mode);
         return;
     }
 
6df88f29
     for (i = 0; i < mdat->num; i++)
dd8a6b10
     {
         if (mode == 1)
         {
             mdat->mirtab[i].ignore = IGNORE_NO;
         }
         else if ((mode == 2) && (IGNORE_SHORTTERM == mdat->mirtab[i].ignore))
         {
             mdat->mirtab[i].ignore = IGNORE_NO;
         }
     }
 
     return;
a68aa31f
 }
 
dd8a6b10
 fc_error_t mirman_write(const char *file, const char *dir, struct mirdat *mdat)
376307a0
 {
6df88f29
     int fd;
     unsigned int i;
     char path[512];
376307a0
 
dd8a6b10
     snprintf(path, sizeof(path), "%s/%s", dir, file);
     path[sizeof(path) - 1] = 0;
376307a0
 
6df88f29
     if (!mdat->num)
dd8a6b10
         return FC_SUCCESS;
376307a0
 
6df88f29
     if ((fd =
dd8a6b10
          open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600)) == -1)
6df88f29
     {
dd8a6b10
         logg("!Can't open %s for writing\n", path);
         return FCE_OPEN;
376307a0
     }
 
6df88f29
     for (i = 0; i < mdat->num; i++)
         if (!mdat->mirtab[i].atime)
dd8a6b10
             mdat->mirtab[i].atime = (uint32_t) time(NULL);
04a1171f
 
dd8a6b10
     if (write(fd, mdat->mirtab, mdat->num * sizeof(struct mirdat_ip)) == -1)
6df88f29
     {
dd8a6b10
         logg("!Can't write to %s\n", path);
         close(fd);
         return FCE_FILE;
376307a0
     }
 
dd8a6b10
     close(fd);
     return FC_SUCCESS;
376307a0
 }