/* * Copyright (C) 2007 Tomasz Kojm * * 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 #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #ifndef _WIN32 #include #include #include #endif #include "mirman.h" #include "libclamav/clamav.h" #include "shared/output.h" #ifndef HAVE_GETADDRINFO #ifndef AF_INET6 #define AF_INET6 0xbeef /* foo */ #endif #endif #define IGNORE_SHORT (3600) /* 1 hour */ #define IGNORE_LONG (6 * IGNORE_SHORT) /* 6 hours */ void mirman_free(struct mirdat *mdat) { if (mdat && mdat->num) { free (mdat->mirtab); mdat->num = 0; } } fc_error_t mirman_read(const char *file, struct mirdat *mdat, uint8_t active) { struct mirdat_ip mip; int fd, bread; memset (mdat, 0, sizeof(struct mirdat)); if (!(mdat->active = active)) return FC_SUCCESS; if ((fd = open (file, O_RDONLY | O_BINARY)) == -1) return FCE_OPEN; while ((bread = read (fd, &mip, sizeof(mip))) == sizeof(mip)) { mdat->mirtab = (struct mirdat_ip *) realloc (mdat->mirtab, (mdat->num + 1) * sizeof(mip)); if (!mdat->mirtab) { logg("!Can't allocate memory for mdat->mirtab\n"); mirman_free (mdat); close (fd); return FCE_MEM; } memcpy (&mdat->mirtab[mdat->num], &mip, sizeof(mip)); mdat->num++; } close (fd); if (bread) { logg("^Removing broken %s file.\n", file); unlink (file); mirman_free (mdat); return FCE_FILE; } return FC_SUCCESS; } fc_error_t mirman_check(uint32_t * ip, int af, struct mirdat *mdat, struct mirdat_ip **md, mir_status_t *mirror_status) { fc_error_t status = FC_SUCCESS; unsigned int i; unsigned int flevel = cl_retflevel (); if (NULL == md || NULL == mdat || NULL == ip) { logg("!mirman_check: Invalid arguments.\n"); status = FCE_ARG; goto done; } *md = NULL; if (!mdat->active) { *mirror_status = MIRROR_OK; goto done; } for (i = 0; i < mdat->num; i++) { if ((af == AF_INET && mdat->mirtab[i].ip4 == *ip) || ((af == AF_INET6) && (!memcmp (mdat->mirtab[i].ip6, ip, 4 * sizeof(uint32_t))))) { /* * Mirror found in mirror table. */ if (mdat->dbflevel && (mdat->dbflevel > flevel) && (mdat->dbflevel - flevel > 3)) { /* 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; } } if ((mdat->mirtab[i].atime > 0) && (IGNORE_NO != mdat->mirtab[i].ignore)) { /* * Found, but the ignore flag is set. */ if (difftime(time(NULL), mdat->mirtab[i].atime) > IGNORE_LONG) { /* 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; } else { *mirror_status = MIRROR_IGNORE__PREV_ERRS; goto done; } } /* Mirror found, and is ok to try. */ *md = &mdat->mirtab[i]; *mirror_status = MIRROR_OK; goto done; } } /* Mirror wasn't in mirror table. */ *mirror_status = MIRROR_OK; done: return status; } fc_error_t mirman_update(uint32_t * ip, int af, struct mirdat *mdat, fc_error_t error) { fc_error_t status = FCE_ARG; unsigned int i = 0; struct mirdat_ip *mirror = NULL; if (!mdat->active) { /* Disable mirrors.dat management when using a proxy. */ return FC_SUCCESS; } /* * Attempt to find the ip in the mirror table. */ for (i = 0; i < mdat->num; i++) { if (((af == AF_INET) && (mdat->mirtab[i].ip4 == *ip)) || ((af == AF_INET6) && (!memcmp(mdat->mirtab[i].ip6, ip, 4 * sizeof(uint32_t))))) { mirror = &mdat->mirtab[i]; break; } } if (NULL == mirror) { /* * Allocate space in the mirror table for the new mirror IP */ mdat->mirtab = (struct mirdat_ip *) realloc(mdat->mirtab, (mdat->num + 1) * sizeof(struct mirdat_ip)); if (!mdat->mirtab) { logg("!Can't allocate memory for new element in mdat->mirtab\n"); return FCE_MEM; } memset (&mdat->mirtab[mdat->num], 0, sizeof(struct mirdat_ip)); if (af == AF_INET) { mdat->mirtab[mdat->num].ip4 = *ip; } else { mdat->mirtab[mdat->num].ip4 = 0; memcpy (mdat->mirtab[mdat->num].ip6, ip, 4 * sizeof(uint32_t)); } mdat->mirtab[mdat->num].atime = 0; mdat->mirtab[mdat->num].succ = 0; mdat->mirtab[mdat->num].fail = 0; mdat->mirtab[mdat->num].ignore = 0; mirror = &mdat->mirtab[mdat->num]; mdat->num++; } mirror->atime = 0; /* will be updated in mirman_write() */ if (FC_SUCCESS == error) { mirror->succ++; mirror->fail = 0; } else { mirror->succ = 0; mirror->fail++; } 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; } void mirman_list(const struct mirdat *mdat) { unsigned int i; time_t tm; char ip[46]; for (i = 0; i < mdat->num; i++) { printf("Mirror #%u\n", i + 1); #ifdef HAVE_GETADDRINFO if (mdat->mirtab[i].ip4) printf("IP: %s\n", inet_ntop(AF_INET, &mdat->mirtab[i].ip4, ip, sizeof(ip))); else printf("IP: %s\n", inet_ntop(AF_INET6, mdat->mirtab[i].ip6, ip, sizeof(ip))); #else if (mdat->mirtab[i].ip4) printf("IP: %s\n", inet_ntoa(*(struct in_addr *)&mdat->mirtab[i].ip4)); #endif printf("Successes: %u\n", mdat->mirtab[i].succ); printf("Failures: %u\n", mdat->mirtab[i].fail); tm = mdat->mirtab[i].atime; 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"); } if (i != mdat->num - 1) printf("-------------------------------------\n"); } } void mirman_whitelist(struct mirdat *mdat, unsigned int mode) { unsigned int i; 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; } for (i = 0; i < mdat->num; i++) { 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; } fc_error_t mirman_write(const char *file, const char *dir, struct mirdat *mdat) { int fd; unsigned int i; char path[512]; snprintf(path, sizeof(path), "%s/%s", dir, file); path[sizeof(path) - 1] = 0; if (!mdat->num) return FC_SUCCESS; if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600)) == -1) { logg("!Can't open %s for writing\n", path); return FCE_OPEN; } for (i = 0; i < mdat->num; i++) if (!mdat->mirtab[i].atime) mdat->mirtab[i].atime = (uint32_t) time(NULL); if (write(fd, mdat->mirtab, mdat->num * sizeof(struct mirdat_ip)) == -1) { logg("!Can't write to %s\n", path); close(fd); return FCE_FILE; } close(fd); return FC_SUCCESS; }