/* * Save the uuencoded part of the file as it is read in since there's no need * to include it in the parse tree. Saves memory and parse time. * Return < 0 for failure */ int uudecodeFile(message *m, const char *firstline, const char *dir, fmap_t *map, size_t *at) { fileblob *fb; char buffer[RFC2821LENGTH + 1]; char *filename = cli_strtok(firstline, 2, " "); if(filename == NULL) return -1; fb = fileblobCreate(); if(fb == NULL) { free(filename); return -1; } fileblobSetFilename(fb, dir, filename); cli_dbgmsg("uudecode %s\n", filename); free(filename); while(fmap_gets(map, buffer, at, sizeof(buffer) - 1)) { unsigned char data[1024]; const unsigned char *uptr; size_t len; cli_chomp(buffer); if(strcasecmp(buffer, "end") == 0) break; if(buffer[0] == '\0') break; uptr = decodeLine(m, UUENCODE, buffer, data, sizeof(data)); if(uptr == NULL) break; len = (size_t)(uptr - data); if((len > 62) || (len == 0)) break; if(fileblobAddData(fb, data, len) < 0) break; } fileblobDestroy(fb); return 1; }
static void tail(const char *log_file) { FILE *fin; #if defined(TAIL_RESTART) && (TAIL_RESTART > 0) time_t started = time((time_t *)0); #endif char line[512]; sprintf(line, "tail -F %s", log_file); fin = popen(line, "r"); if(fin == NULL) { perror(log_file); syslog(LOG_ERR, "blacklist: can't start tail"); return; } #ifdef NOBODY setuid(NOBODY); #endif while(fgets(line, sizeof(line), fin)) { const char *sendmailID, *reason = NULL; char *ptr = NULL, *iaddr = NULL; int j; struct ip *ip; struct bip *bip; time_t now; /* * Blacklist this sender's IP */ if(strstr(line, "Blocked by SpamAssassin") || strstr(line, "Blocked by blacklist-milter")) { ptr = cli_strtok(line, 3, ":"); if(ptr) { int i; sendmailID = &ptr[1]; for(i = 0, ip = ips; i < NIDS; i++, ip++) if(ip->sendmailID[0]) { #ifdef DEBUG printf("cmp '%s' '%s'\n", ip->sendmailID, sendmailID); #endif if(strcmp(ip->sendmailID, sendmailID) == 0) { iaddr = ip->ip; reason = "sending spam"; break; } } if(iaddr == NULL) syslog(LOG_WARNING, "Couldn't find sendmailID '%s' in the ips table", sendmailID); } else syslog(LOG_WARNING, "Couldn't parse the blocked line '%s'", line); } else if(strstr(line, "Connection rate limit exceeded") || strstr(line, "Too many open connections")) { ptr = cli_strtok(line, 3, "="); if(ptr) { sendmailID = NULL; if((iaddr = strchr(ptr, ',')) != NULL) *iaddr = '\0'; iaddr = ptr; reason = "connecting too often"; } else syslog(LOG_WARNING, "Couldn't parse the connection limit line '%s'", line); } else if(strstr(line, "Spam blocked see: http://spamcop.net/bl.shtml?")) { ptr = cli_strtok(line, 1, "?"); if(ptr) { sendmailID = NULL; if((iaddr = strchr(ptr, '\n')) != NULL) *iaddr = '\0'; iaddr = ptr; reason = "being in SpamCop's database"; } else syslog(LOG_WARNING, "Couldn't parse the spamcop limit line '%s'", line); } if(ptr == NULL) continue; if(iaddr == NULL) { /* * Usually this means that it's from a whitelisted IP, * or we've just started up and this is from a * connection which started before blacklist-milter */ if(sendmailID) { #ifdef DEBUG printf("%s: Couldn't determine the IP address\n", sendmailID); #endif syslog(LOG_WARNING, "%s: Couldn't determine the IP address", sendmailID); } else { /* more serious */ #ifdef DEBUG printf("Couldn't determine the IP address from '%s'\n", line); #endif syslog(LOG_ERR, "Couldn't determine the IP address"); } free(ptr); timeout(); continue; } if(isWhiteList(iaddr)) continue; now = time((time_t *)0); #ifdef DEBUG printf("Decided to block from '%s' because '%s'\n", line, reason); #endif /* * Determine if the IP address is already * blacklisted */ for(j = 0, bip = bips; j < NBLACKLISTED; j++, bip++) if(strcmp(bip->ip, iaddr) == 0) { #ifdef DEBUG printf("%s is already blocked\n", iaddr); #endif bip->time = now; break; } if(j == NBLACKLISTED) /* * Not already blacklisted */ for(j = 0, bip = bips; j < NBLACKLISTED; j++, bip++) if(bip->ip[0] == '\0') { #ifndef NOBODY pid_t pid; int status; switch(pid = fork()) { case -1: perror("fork"); break; case 0: if(execl("/sbin/iptables", "iptables", "-I", "INPUT", "-s", iaddr, "-j", "DROP", NULL) < 0) { perror("/sbin/iptables"); _exit(errno); } /*NOTREACHED*/ } if(sendmailID) { #ifdef DEBUG printf("%s: Will blacklist %s for %d seconds for %s\n", sendmailID, iaddr, TIMEOUT, reason); #endif syslog(LOG_NOTICE, "%s: Will blacklist %s for %d seconds for %s", sendmailID, iaddr, TIMEOUT, reason); } else { #ifdef DEBUG printf("Will blacklist %s for %d seconds for %s\n", iaddr, TIMEOUT, reason); #endif syslog(LOG_NOTICE, "Will blacklist %s for %d seconds for %s", iaddr, TIMEOUT, reason); } strcpy(bip->ip, iaddr); bip->time = now; bip->reason = reason; if(waitpid(pid, &status, 0) < 0) perror("wait"); else if(WEXITSTATUS(status) == 0) syslog(LOG_NOTICE, "Kill host %s", iaddr); else fputs("iptables drop failed\n", stderr); #else /*!NOBODY*/ strcpy(bip->ip, iaddr); bip->time = now; bip->reason = reason; #endif /*NOBODY*/ break; } free(ptr); timeout(); #if defined(TAIL_RESTART) && (TAIL_RESTART > 0) if((now - started) > TAIL_RESTART) break; #endif } #ifdef NOBODY syslog(LOG_ERR, "blacklist: tail has died"); #else syslog(LOG_NOTICE, "blacklist: restarting tail"); #endif pclose(fin); }
/* * TODO: * - strptime() is most likely not portable enough */ int submitstats(const char *clamdcfg, const struct optstruct *opts) { int fd, sd, bread, lread = 0, cnt, ret; char post[SUBMIT_MIN_ENTRIES * 256 + 512]; char query[SUBMIT_MIN_ENTRIES * 256]; char buff[512], statsdat[512], newstatsdat[512], uastr[128]; char logfile[256], fbuff[FILEBUFF]; char *pt, *pt2, *auth = NULL; const char *line, *country = NULL, *user, *proxy = NULL; struct optstruct *clamdopt; const struct optstruct *opt; struct stat sb; struct tm tms; time_t epoch; unsigned int qcnt, entries, submitted = 0, permfail = 0, port = 0; if((opt = optget(opts, "DetectionStatsCountry"))->enabled) { if(strlen(opt->strarg) != 2 || !isalpha(opt->strarg[0]) || !isalpha(opt->strarg[1])) { logg("!SubmitDetectionStats: DetectionStatsCountry requires a two-letter country code\n"); return 56; } country = opt->strarg; } if(!(clamdopt = optparse(clamdcfg, 0, NULL, 1, OPT_CLAMD, 0, NULL))) { logg("!SubmitDetectionStats: Can't open or parse configuration file %s\n", clamdcfg); return 56; } if(!(opt = optget(clamdopt, "LogFile"))->enabled) { logg("!SubmitDetectionStats: LogFile needs to be enabled in %s\n", clamdcfg); optfree(clamdopt); return 56; } strncpy(logfile, opt->strarg, sizeof(logfile)); logfile[sizeof(logfile) - 1] = 0; if(!optget(clamdopt, "LogTime")->enabled) { logg("!SubmitDetectionStats: LogTime needs to be enabled in %s\n", clamdcfg); optfree(clamdopt); return 56; } optfree(clamdopt); if((fd = open("stats.dat", O_RDONLY)) != -1) { if((bread = read(fd, statsdat, sizeof(statsdat) - 1)) == -1) { logg("^SubmitDetectionStats: Can't read stats.dat\n"); bread = 0; } statsdat[bread] = 0; close(fd); } else { *statsdat = 0; } if((fd = open(logfile, O_RDONLY)) == -1) { logg("!SubmitDetectionStats: Can't open %s for reading\n", logfile); return 56; } if(fstat(fd, &sb) == -1) { logg("!SubmitDetectionStats: fstat() failed\n"); close(fd); return 56; } while((line = readbline(fd, fbuff, FILEBUFF, sb.st_size, &lread))) if(strlen(line) >= 32 && !strcmp(&line[strlen(line) - 6], " FOUND")) break; if(!line) { logg("SubmitDetectionStats: No detection records found\n"); close(fd); return 1; } if(*statsdat && !strcmp(line, statsdat)) { logg("SubmitDetectionStats: No new detection records found\n"); close(fd); return 1; } else { strncpy(newstatsdat, line, sizeof(newstatsdat)); } if((opt = optget(opts, "HTTPUserAgent"))->enabled) strncpy(uastr, opt->strarg, sizeof(uastr)); else snprintf(uastr, sizeof(uastr), PACKAGE"/%s (OS: "TARGET_OS_TYPE", ARCH: "TARGET_ARCH_TYPE", CPU: "TARGET_CPU_TYPE")%s%s", get_version(), country ? ":" : "", country ? country : ""); uastr[sizeof(uastr) - 1] = 0; if((opt = optget(opts, "HTTPProxyServer"))->enabled) { proxy = opt->strarg; if(!strncasecmp(proxy, "http://", 7)) proxy += 7; if((opt = optget(opts, "HTTPProxyUsername"))->enabled) { user = opt->strarg; if(!(opt = optget(opts, "HTTPProxyPassword"))->enabled) { logg("!SubmitDetectionStats: HTTPProxyUsername requires HTTPProxyPassword\n"); close(fd); return 56; } auth = proxyauth(user, opt->strarg); if(!auth) { close(fd); return 56; } } if((opt = optget(opts, "HTTPProxyPort"))->enabled) port = opt->numarg; logg("*Connecting via %s\n", proxy); } ret = 0; memset(query, 0, sizeof(query)); qcnt = 0; entries = 0; do { if(strlen(line) < 32 || strcmp(&line[strlen(line) - 6], " FOUND")) continue; if(*statsdat && !strcmp(line, statsdat)) break; strncpy(buff, line, sizeof(buff)); buff[sizeof(buff) - 1] = 0; if(!(pt = strstr(buff, " -> "))) { logg("*SubmitDetectionStats: Skipping detection entry logged without time\b"); continue; } *pt = 0; pt += 4; if(!strptime(buff, "%a %b %d %H:%M:%S %Y", &tms) || (epoch = mktime(&tms)) == -1) { logg("!SubmitDetectionStats: Failed to convert date string\n"); ret = 1; break; } pt2 = &pt[strlen(pt) - 6]; *pt2 = 0; if(!(pt2 = strrchr(pt, ':'))) { logg("!SubmitDetectionStats: Incorrect format of the log file (1)\n"); ret = 1; break; } *pt2 = 0; pt2 += 2; #ifdef C_WINDOWS if((pt = strrchr(pt, '\\'))) #else if((pt = strrchr(pt, '/'))) #endif *pt++ = 0; if(!pt) pt = (char*) "NOFNAME"; qcnt += snprintf(&query[qcnt], sizeof(query) - qcnt, "ts[]=%u&fname[]=%s&virus[]=%s&", (unsigned int) epoch, pt, pt2); entries++; if(entries == SUBMIT_MIN_ENTRIES) { sd = wwwconnect("stats.clamav.net", proxy, port, NULL, optget(opts, "LocalIPAddress")->strarg, optget(opts, "ConnectTimeout")->numarg, NULL, 0, 0); if(sd == -1) { logg("!SubmitDetectionStats: Can't connect to server\n"); ret = 52; break; } query[sizeof(query) - 1] = 0; snprintf(post, sizeof(post), "POST http://stats.clamav.net/submit.php HTTP/1.0\r\n" "Host: stats.clamav.net\r\n%s" "Content-Type: application/x-www-form-urlencoded\r\n" "User-Agent: %s\r\n" "Content-Length: %u\r\n\n" "%s", auth ? auth : "", uastr, (unsigned int) strlen(query), query); if(send(sd, post, strlen(post), 0) < 0) { logg("!SubmitDetectionStats: Can't write to socket\n"); ret = 52; closesocket(sd); break; } pt = post; cnt = sizeof(post) - 1; #ifdef SO_ERROR while((bread = wait_recv(sd, pt, cnt, 0, optget(opts, "ReceiveTimeout")->numarg)) > 0) { #else while((bread = recv(sd, pt, cnt, 0)) > 0) { #endif pt += bread; cnt -= bread; if(cnt <= 0) break; } *pt = 0; closesocket(sd); if(bread < 0) { logg("!SubmitDetectionStats: Can't read from socket\n"); ret = 52; break; } if(strstr(post, "SUBMIT_OK")) { submitted += entries; if(submitted + SUBMIT_MIN_ENTRIES > SUBMIT_MAX_ENTRIES) break; qcnt = 0; entries = 0; memset(query, 0, sizeof(query)); continue; } ret = 52; if((pt = strstr(post, "SUBMIT_PERMANENT_FAILURE"))) { if(!submitted) { permfail = 1; if((pt + 32 <= post + sizeof(post)) && pt[24] == ':') logg("!SubmitDetectionStats: Remote server reported permanent failure: %s\n", &pt[25]); else logg("!SubmitDetectionStats: Remote server reported permanent failure\n"); } } else if((pt = strstr(post, "SUBMIT_TEMPORARY_FAILURE"))) { if(!submitted) { if((pt + 32 <= post + sizeof(post)) && pt[24] == ':') logg("!SubmitDetectionStats: Remote server reported temporary failure: %s\n", &pt[25]); else logg("!SubmitDetectionStats: Remote server reported temporary failure\n"); } } else { if(!submitted) logg("!SubmitDetectionStats: Incorrect answer from server\n"); } break; } } while((line = readbline(fd, fbuff, FILEBUFF, sb.st_size, &lread))); close(fd); if(auth) free(auth); if(submitted || permfail) { if((fd = open("stats.dat", O_WRONLY | O_CREAT | O_TRUNC, 0600)) == -1) { logg("^SubmitDetectionStats: Can't open stats.dat for writing\n"); } else { if((bread = write(fd, newstatsdat, sizeof(newstatsdat))) != sizeof(newstatsdat)) logg("^SubmitDetectionStats: Can't write to stats.dat\n"); close(fd); } } if(ret == 0) { if(!submitted) logg("SubmitDetectionStats: Not enough recent data for submission\n"); else logg("SubmitDetectionStats: Submitted %u records\n", submitted); } return ret; } static int Rfc2822DateTime(char *buf, time_t mtime) { struct tm *gmt; gmt = gmtime(&mtime); return strftime(buf, 36, "%a, %d %b %Y %X GMT", gmt); } static struct cl_cvd *remote_cvdhead(const char *cvdfile, const char *localfile, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int *ims, int ctimeout, int rtimeout, struct mirdat *mdat, int logerr, unsigned int can_whitelist) { char cmd[512], head[513], buffer[FILEBUFF], ipaddr[46], *ch, *tmp; int bread, cnt, sd; unsigned int i, j; char *remotename = NULL, *authorization = NULL; struct cl_cvd *cvd; char last_modified[36], uastr[128]; if(proxy) { remotename = malloc(strlen(hostname) + 8); if(!remotename) { logg("!remote_cvdhead: Can't allocate memory for 'remotename'\n"); return NULL; } sprintf(remotename, "http://%s", hostname); if(user) { authorization = proxyauth(user, pass); if(!authorization) { free(remotename); return NULL; } } } if(!access(localfile, R_OK)) { cvd = cl_cvdhead(localfile); if(!cvd) { logg("!remote_cvdhead: Can't parse file %s\n", localfile); free(remotename); free(authorization); return NULL; } Rfc2822DateTime(last_modified, (time_t) cvd->stime); cl_cvdfree(cvd); } else { time_t mtime = 1104119530; Rfc2822DateTime(last_modified, mtime); logg("*Assuming modification time in the past\n"); } logg("*If-Modified-Since: %s\n", last_modified); logg("Reading CVD header (%s): ", cvdfile); if(uas) strncpy(uastr, uas, sizeof(uastr)); else snprintf(uastr, sizeof(uastr), PACKAGE"/%s (OS: "TARGET_OS_TYPE", ARCH: "TARGET_ARCH_TYPE", CPU: "TARGET_CPU_TYPE")", get_version()); uastr[sizeof(uastr) - 1] = 0; snprintf(cmd, sizeof(cmd), "GET %s/%s HTTP/1.0\r\n" "Host: %s\r\n%s" "User-Agent: %s\r\n" "Connection: close\r\n" "Range: bytes=0-511\r\n" "If-Modified-Since: %s\r\n" "\r\n", (remotename != NULL) ? remotename : "", cvdfile, hostname, (authorization != NULL) ? authorization : "", uastr, last_modified); free(remotename); free(authorization); memset(ipaddr, 0, sizeof(ipaddr)); if(ip[0]) /* use ip to connect */ sd = wwwconnect(ip, proxy, port, ipaddr, localip, ctimeout, mdat, logerr, can_whitelist); else sd = wwwconnect(hostname, proxy, port, ipaddr, localip, ctimeout, mdat, logerr, can_whitelist); if(sd < 0) { return NULL; } else { logg("*Connected to %s (IP: %s).\n", hostname, ipaddr); logg("*Trying to retrieve CVD header of http://%s/%s\n", hostname, cvdfile); } if(!ip[0]) strcpy(ip, ipaddr); if(send(sd, cmd, strlen(cmd), 0) < 0) { logg("%cremote_cvdhead: write failed\n", logerr ? '!' : '^'); closesocket(sd); return NULL; } tmp = buffer; cnt = FILEBUFF; #ifdef SO_ERROR while((bread = wait_recv(sd, tmp, cnt, 0, rtimeout)) > 0) { #else while((bread = recv(sd, tmp, cnt, 0)) > 0) { #endif tmp += bread; cnt -= bread; if(cnt <= 0) break; } closesocket(sd); if(bread == -1) { logg("%cremote_cvdhead: Error while reading CVD header from %s\n", logerr ? '!' : '^', hostname); mirman_update(mdat->currip, mdat->af, mdat, 1); return NULL; } if((strstr(buffer, "HTTP/1.1 404")) != NULL || (strstr(buffer, "HTTP/1.0 404")) != NULL) { logg("%cCVD file not found on remote server\n", logerr ? '!' : '^'); mirman_update(mdat->currip, mdat->af, mdat, 2); return NULL; } /* check whether the resource is up-to-date */ if((strstr(buffer, "HTTP/1.1 304")) != NULL || (strstr(buffer, "HTTP/1.0 304")) != NULL) { *ims = 0; logg("OK (IMS)\n"); mirman_update(mdat->currip, mdat->af, mdat, 0); return NULL; } else { *ims = 1; } if(!strstr(buffer, "HTTP/1.1 200") && !strstr(buffer, "HTTP/1.0 200") && !strstr(buffer, "HTTP/1.1 206") && !strstr(buffer, "HTTP/1.0 206")) { logg("%cUnknown response from remote server\n", logerr ? '!' : '^'); mirman_update(mdat->currip, mdat->af, mdat, 1); return NULL; } i = 3; ch = buffer + i; while(i < sizeof(buffer)) { if (*ch == '\n' && *(ch - 1) == '\r' && *(ch - 2) == '\n' && *(ch - 3) == '\r') { ch++; i++; break; } ch++; i++; } if(sizeof(buffer) - i < 512) { logg("%cremote_cvdhead: Malformed CVD header (too short)\n", logerr ? '!' : '^'); mirman_update(mdat->currip, mdat->af, mdat, 1); return NULL; } memset(head, 0, sizeof(head)); for(j = 0; j < 512; j++) { if(!ch || (ch && !*ch) || (ch && !isprint(ch[j]))) { logg("%cremote_cvdhead: Malformed CVD header (bad chars)\n", logerr ? '!' : '^'); mirman_update(mdat->currip, mdat->af, mdat, 1); return NULL; } head[j] = ch[j]; } if(!(cvd = cl_cvdparse(head))) { logg("%cremote_cvdhead: Malformed CVD header (can't parse)\n", logerr ? '!' : '^'); mirman_update(mdat->currip, mdat->af, mdat, 1); } else { logg("OK\n"); mirman_update(mdat->currip, mdat->af, mdat, 0); } return cvd; } static int getfile(const char *srcfile, const char *destfile, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int ctimeout, int rtimeout, struct mirdat *mdat, int logerr, unsigned int can_whitelist) { char cmd[512], uastr[128], buffer[FILEBUFF], *ch; int bread, fd, totalsize = 0, rot = 0, totaldownloaded = 0, percentage = 0, sd; unsigned int i; char *remotename = NULL, *authorization = NULL, *headerline, ipaddr[46]; const char *rotation = "|/-\\"; #ifdef FILTER_RULES pid_t pid = -1; #endif if(proxy) { remotename = malloc(strlen(hostname) + 8); if(!remotename) { logg("!getfile: Can't allocate memory for 'remotename'\n"); return 75; /* FIXME */ } sprintf(remotename, "http://%s", hostname); if(user) { authorization = proxyauth(user, pass); if(!authorization) { free(remotename); return 75; /* FIXME */ } } } if(uas) strncpy(uastr, uas, sizeof(uastr)); else snprintf(uastr, sizeof(uastr), PACKAGE"/%s (OS: "TARGET_OS_TYPE", ARCH: "TARGET_ARCH_TYPE", CPU: "TARGET_CPU_TYPE")", get_version()); uastr[sizeof(uastr) - 1] = 0; snprintf(cmd, sizeof(cmd), "GET %s/%s HTTP/1.0\r\n" "Host: %s\r\n%s" "User-Agent: %s\r\n" #ifdef FRESHCLAM_NO_CACHE "Cache-Control: no-cache\r\n" #endif "Connection: close\r\n" "\r\n", (remotename != NULL) ? remotename : "", srcfile, hostname, (authorization != NULL) ? authorization : "", uastr); if(remotename) free(remotename); if(authorization) free(authorization); memset(ipaddr, 0, sizeof(ipaddr)); if(ip[0]) /* use ip to connect */ sd = wwwconnect(ip, proxy, port, ipaddr, localip, ctimeout, mdat, logerr, can_whitelist); else sd = wwwconnect(hostname, proxy, port, ipaddr, localip, ctimeout, mdat, logerr, can_whitelist); if(sd < 0) { return 52; } else { logg("*Trying to download http://%s/%s (IP: %s)\n", hostname, srcfile, ipaddr); } if(!ip[0]) strcpy(ip, ipaddr); if(send(sd, cmd, strlen(cmd), 0) < 0) { logg("%cgetfile: Can't write to socket\n", logerr ? '!' : '^'); closesocket(sd); return 52; } /* read http headers */ ch = buffer; i = 0; while(1) { /* recv one byte at a time, until we reach \r\n\r\n */ #ifdef SO_ERROR if((i >= sizeof(buffer) - 1) || wait_recv(sd, buffer + i, 1, 0, rtimeout) == -1) { #else if((i >= sizeof(buffer) - 1) || recv(sd, buffer + i, 1, 0) == -1) { #endif logg("%cgetfile: Error while reading database from %s (IP: %s)\n", logerr ? '!' : '^', hostname, ipaddr); mirman_update(mdat->currip, mdat->af, mdat, 1); closesocket(sd); return 52; } if(i > 2 && *ch == '\n' && *(ch - 1) == '\r' && *(ch - 2) == '\n' && *(ch - 3) == '\r') { i++; break; } ch++; i++; } buffer[i] = 0; /* check whether the resource actually existed or not */ if((strstr(buffer, "HTTP/1.1 404")) != NULL || (strstr(buffer, "HTTP/1.0 404")) != NULL) { logg("^getfile: %s not found on remote server (IP: %s)\n", srcfile, ipaddr); mirman_update(mdat->currip, mdat->af, mdat, 2); closesocket(sd); return 58; } if(!strstr(buffer, "HTTP/1.1 200") && !strstr(buffer, "HTTP/1.0 200") && !strstr(buffer, "HTTP/1.1 206") && !strstr(buffer, "HTTP/1.0 206")) { logg("%cgetfile: Unknown response from remote server (IP: %s)\n", logerr ? '!' : '^', ipaddr); mirman_update(mdat->currip, mdat->af, mdat, 1); closesocket(sd); return 58; } /* get size of resource */ for(i = 0; (headerline = cli_strtok(buffer, i, "\n")); i++){ if(strstr(headerline, "Content-Length:")) { if((ch = cli_strtok(headerline, 1, ": "))) { totalsize = atoi(ch); free(ch); } else { totalsize = 0; } } free(headerline); } #ifdef FILTER_RULES { int pfd[2]; if (pipe(pfd) == -1) { logg("!getfile: Can't create pipe to filter\n"); closesocket(sd); return 57; } pid = fork(); if (pid == -1) { logg("!getfile: Can't filter fork failed\n"); closesocket(sd); return 57; } if (pid == 0) { /* Child */ close(pfd[1]); dup2(pfd[0], 0); close(pfd[0]); execl("/bin/clamfilter", "clamfilter", destfile, NULL); _exit(1); } /* Parent */ close(pfd[0]); fd = pfd[1]; destfile = "clam-filter"; } #else if((fd = open(destfile, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644)) == -1) { char currdir[512]; if(getcwd(currdir, sizeof(currdir))) logg("!getfile: Can't create new file %s in %s\n", destfile, currdir); else logg("!getfile: Can't create new file %s in the current directory\n", destfile); logg("Hint: The database directory must be writable for UID %d or GID %d\n", getuid(), getgid()); closesocket(sd); return 57; } #endif #ifdef SO_ERROR while((bread = wait_recv(sd, buffer, FILEBUFF, 0, rtimeout)) > 0) { #else while((bread = recv(sd, buffer, FILEBUFF, 0)) > 0) { #endif if(write(fd, buffer, bread) != bread) { logg("getfile: Can't write %d bytes to %s\n", bread, destfile); #ifdef FILTER_RULES // if (pid == -1) // unlink(destfile); #else unlink(destfile); #endif close(fd); closesocket(sd); return 57; /* FIXME */ } if(totalsize > 0) { totaldownloaded += bread; percentage = (int) (100 * (float) totaldownloaded / totalsize); } if(!mprintf_quiet) { if(totalsize > 0) { mprintf("Downloading %s [%3i%%]\r", srcfile, percentage); } else { mprintf("Downloading %s [%c]\r", srcfile, rotation[rot]); rot++; rot %= 4; } fflush(stdout); } } closesocket(sd); close(fd); #ifdef FILTER_RULES /*if (pid != -1)*/ { int status; waitpid(pid, &status, 0); if (! WIFEXITED(status) || WEXITSTATUS(status) != 0) { logg("!getfile: Filter failed\n"); return 57; } } #endif if(totalsize > 0) logg("Downloading %s [%i%%]\n", srcfile, percentage); else logg("Downloading %s [*]\n", srcfile); mirman_update(mdat->currip, mdat->af, mdat, 0); return 0; } static int getcvd(const char *cvdfile, const char *newfile, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, unsigned int newver, int ctimeout, int rtimeout, struct mirdat *mdat, int logerr, unsigned int can_whitelist) { struct cl_cvd *cvd; int ret; logg("*Retrieving http://%s/%s\n", hostname, cvdfile); if((ret = getfile(cvdfile, newfile, hostname, ip, localip, proxy, port, user, pass, uas, ctimeout, rtimeout, mdat, logerr, can_whitelist))) { logg("%cCan't download %s from %s\n", logerr ? '!' : '^', cvdfile, hostname); unlink(newfile); return ret; } if((ret = cl_cvdverify(newfile))) { logg("!Verification: %s\n", cl_strerror(ret)); unlink(newfile); return 54; } if(!(cvd = cl_cvdhead(newfile))) { logg("!Can't read CVD header of new %s database.\n", cvdfile); unlink(newfile); return 54; } if(cvd->version < newver) { logg("^Mirror %s is not synchronized.\n", ip); mirman_update(mdat->currip, mdat->af, mdat, 2); cl_cvdfree(cvd); unlink(newfile); return 59; } cl_cvdfree(cvd); return 0; } static int chdir_tmp(const char *dbname, const char *tmpdir) { char cvdfile[32]; if(access(tmpdir, R_OK|W_OK) == -1) { sprintf(cvdfile, "%s.cvd", dbname); if(access(cvdfile, R_OK) == -1) { sprintf(cvdfile, "%s.cld", dbname); if(access(cvdfile, R_OK) == -1) { logg("!chdir_tmp: Can't access local %s database\n", dbname); return -1; } } if(mkdir(tmpdir, 0755) == -1) { logg("!chdir_tmp: Can't create directory %s\n", tmpdir); return -1; } if(cli_cvdunpack(cvdfile, tmpdir) == -1) { logg("!chdir_tmp: Can't unpack %s into %s\n", cvdfile, tmpdir); cli_rmdirs(tmpdir); return -1; } } if(chdir(tmpdir) == -1) { logg("!chdir_tmp: Can't change directory to %s\n", tmpdir); return -1; } return 0; } static int getpatch(const char *dbname, const char *tmpdir, int version, const char *hostname, char *ip, const char *localip, const char *proxy, int port, const char *user, const char *pass, const char *uas, int ctimeout, int rtimeout, struct mirdat *mdat, int logerr, unsigned int can_whitelist) { char *tempname, patch[32], olddir[512]; int ret, fd; if(!getcwd(olddir, sizeof(olddir))) { logg("!getpatch: Can't get path of current working directory\n"); return 50; /* FIXME */ } if(chdir_tmp(dbname, tmpdir) == -1) return 50; tempname = cli_gentemp("."); snprintf(patch, sizeof(patch), "%s-%d.cdiff", dbname, version); logg("*Retrieving http://%s/%s\n", hostname, patch); if((ret = getfile(patch, tempname, hostname, ip, localip, proxy, port, user, pass, uas, ctimeout, rtimeout, mdat, logerr, can_whitelist))) { logg("%cgetpatch: Can't download %s from %s\n", logerr ? '!' : '^', patch, hostname); unlink(tempname); free(tempname); CHDIR_ERR(olddir); return ret; } if((fd = open(tempname, O_RDONLY|O_BINARY)) == -1) { logg("!getpatch: Can't open %s for reading\n", tempname); unlink(tempname); free(tempname); CHDIR_ERR(olddir); return 55; } if(cdiff_apply(fd, 1) == -1) { logg("!getpatch: Can't apply patch\n"); close(fd); unlink(tempname); free(tempname); CHDIR_ERR(olddir); return 70; /* FIXME */ } close(fd); unlink(tempname); free(tempname); if(chdir(olddir) == -1) { logg("!getpatch: Can't chdir to %s\n", olddir); return 50; /* FIXME */ } return 0; }
int downloadmanager(const struct optstruct *opts, const char *hostname, const char *dbdir, int logerr) { time_t currtime; int ret, updated = 0, outdated = 0, signo = 0; unsigned int ttl; char ipaddr[46], *dnsreply = NULL, *pt, *localip = NULL, *newver = NULL; const struct optstruct *opt; struct mirdat mdat; #ifdef HAVE_RESOLV_H const char *dnsdbinfo; #endif time(&currtime); logg("ClamAV update process started at %s", ctime(&currtime)); #ifdef HAVE_GETADDRINFO logg("*Using IPv6 aware code\n"); #endif #ifdef HAVE_RESOLV_H dnsdbinfo = optget(opts, "DNSDatabaseInfo")->strarg; if(optget(opts, "no-dns")->enabled) { dnsreply = NULL; } else { if((dnsreply = txtquery(dnsdbinfo, &ttl))) { logg("*TTL: %d\n", ttl); if((pt = cli_strtok(dnsreply, 3, ":"))) { int rt; time_t ct; rt = atoi(pt); free(pt); time(&ct); if((int) ct - rt > 10800) { logg("^DNS record is older than 3 hours.\n"); free(dnsreply); dnsreply = NULL; } } else { free(dnsreply); dnsreply = NULL; } if(dnsreply) { int vwarning = 1; if((pt = cli_strtok(dnsreply, 4, ":"))) { if(*pt == '0') vwarning = 0; free(pt); } if((newver = cli_strtok(dnsreply, 0, ":"))) { char vstr[32]; logg("*Software version from DNS: %s\n", newver); strncpy(vstr, get_version(), 32); vstr[31] = 0; if((pt = strstr(vstr, "-exp")) || (pt = strstr(vstr,"-broken"))) *pt = 0; if(vwarning && !strstr(vstr, "devel") && !strstr(vstr, "rc")) { if(strcmp(vstr, newver)) { logg("Your ClamAV installation is out of date.\n"); logg("^Local version: %s Recommended version: %s\n", get_version(), newver); logg("A firmware upgrade might resolve this issue..\n"); logg("Also read http://sgkb.securecomputing.com/article.asp?article=11282&p=2\n"); outdated = 1; } } } } } if(!dnsreply) { logg("^Invalid DNS reply. Falling back to HTTP mode.\n"); } } #endif /* HAVE_RESOLV_H */ if((opt = optget(opts, "LocalIPAddress"))->enabled) localip = opt->strarg; if(optget(opts, "HTTPProxyServer")->enabled) mirman_read("mirrors.dat", &mdat, 0); else mirman_read("mirrors.dat", &mdat, 1); memset(ipaddr, 0, sizeof(ipaddr)); if((ret = updatedb("main", hostname, ipaddr, &signo, opts, dnsreply, localip, outdated, &mdat, logerr)) > 50) { if(dnsreply) free(dnsreply); if(newver) free(newver); mirman_write("mirrors.dat", &mdat); return ret; } else if(ret == 0) updated = 1; /* if ipaddr[0] != 0 it will use it to connect to the web host */ if((ret = updatedb("daily", hostname, ipaddr, &signo, opts, dnsreply, localip, outdated, &mdat, logerr)) > 50) { if(dnsreply) free(dnsreply); if(newver) free(newver); mirman_write("mirrors.dat", &mdat); return ret; } else if(ret == 0) updated = 1; /* if ipaddr[0] != 0 it will use it to connect to the web host */ if(!optget(opts, "SafeBrowsing")->enabled) { const char *safedb = NULL; if(!access("safebrowsing.cvd", R_OK)) safedb = "safebrowsing.cvd"; else if(!access("safebrowsing.cld", R_OK)) safedb = "safebrowsing.cld"; if(safedb) { if(unlink(safedb)) logg("^SafeBrowsing is disabled but can't remove old %s\n", safedb); else logg("*%s removed\n", safedb); } } else if((ret = updatedb("safebrowsing", hostname, ipaddr, &signo, opts, dnsreply, localip, outdated, &mdat, logerr)) > 50) { if(dnsreply) free(dnsreply); if(newver) free(newver); mirman_write("mirrors.dat", &mdat); return ret; } else if(ret == 0) updated = 1; if(dnsreply) free(dnsreply); mirman_write("mirrors.dat", &mdat); if(updated) { if(optget(opts, "HTTPProxyServer")->enabled) { logg("Database updated (%d signatures) from %s\n", signo, hostname); } else { logg("Database updated (%d signatures) from %s (IP: %s)\n", signo, hostname, ipaddr); } #ifdef BUILD_CLAMD if((opt = optget(opts, "NotifyClamd"))->active) notify(opt->strarg); #endif if((opt = optget(opts, "OnUpdateExecute"))->enabled) execute("OnUpdateExecute", opt->strarg, opts); } if(outdated) { if((opt = optget(opts, "OnOutdatedExecute"))->enabled) { char *cmd = strdup(opt->strarg); if((pt = newver)) { while(*pt) { if(!strchr("0123456789.", *pt)) { logg("!downloadmanager: OnOutdatedExecute: Incorrect version number string\n"); free(newver); newver = NULL; break; } pt++; } } if(newver && (pt = strstr(cmd, "%v"))) { char *buffer = (char *) malloc(strlen(cmd) + strlen(newver) + 10); if(!buffer) { logg("!downloadmanager: Can't allocate memory for buffer\n"); free(cmd); if(newver) free(newver); return 75; } *pt = 0; pt += 2; strcpy(buffer, cmd); strcat(buffer, newver); strcat(buffer, pt); free(cmd); cmd = strdup(buffer); free(buffer); } if(newver) execute("OnOutdatedExecute", cmd, opts); free(cmd); } } if(newver) free(newver); return updated ? 0 : 1; }
static int updatedb(const char *dbname, const char *hostname, char *ip, int *signo, const struct optstruct *opts, const char *dnsreply, char *localip, int outdated, struct mirdat *mdat, int logerr) { struct cl_cvd *current, *remote; const struct optstruct *opt; unsigned int nodb = 0, currver = 0, newver = 0, port = 0, i, j; int ret, ims = -1; char *pt, cvdfile[32], localname[32], *tmpdir = NULL, *newfile, newdb[32], cwd[512]; const char *proxy = NULL, *user = NULL, *pass = NULL, *uas = NULL; unsigned int flevel = cl_retflevel(), remote_flevel = 0, maxattempts; unsigned int can_whitelist = 0; int ctimeout, rtimeout; snprintf(cvdfile, sizeof(cvdfile), "%s.cvd", dbname); if(!(current = currentdb(dbname, localname))) { nodb = 1; } else { mdat->dbflevel = current->fl; } if(!nodb && dnsreply) { int field = 0; if(!strcmp(dbname, "main")) { field = 1; } else if(!strcmp(dbname, "daily")) { field = 2; } else if(!strcmp(dbname, "safebrowsing")) { field = 6; } else { logg("!updatedb: Unknown database name (%s) passed.\n", dbname); cl_cvdfree(current); return 70; } if(field && (pt = cli_strtok(dnsreply, field, ":"))) { if(!cli_isnumber(pt)) { logg("^Broken database version in TXT record.\n"); } else { newver = atoi(pt); logg("*%s version from DNS: %d\n", cvdfile, newver); } free(pt); } else { logg("^Invalid DNS reply. Falling back to HTTP mode.\n"); } } if(dnsreply) { if((pt = cli_strtok(dnsreply, 5, ":"))) { remote_flevel = atoi(pt); free(pt); if(remote_flevel && (remote_flevel - flevel < 4)) can_whitelist = 1; } } /* Initialize proxy settings */ if((opt = optget(opts, "HTTPProxyServer"))->enabled) { proxy = opt->strarg; if(strncasecmp(proxy, "http://", 7) == 0) proxy += 7; if((opt = optget(opts, "HTTPProxyUsername"))->enabled) { user = opt->strarg; if((opt = optget(opts, "HTTPProxyPassword"))->enabled) { pass = opt->strarg; } else { logg("HTTPProxyUsername requires HTTPProxyPassword\n"); if(current) cl_cvdfree(current); return 56; } } if((opt = optget(opts, "HTTPProxyPort"))->enabled) port = opt->numarg; logg("Connecting via %s\n", proxy); } if((opt = optget(opts, "HTTPUserAgent"))->enabled) uas = opt->strarg; ctimeout = optget(opts, "ConnectTimeout")->numarg; rtimeout = optget(opts, "ReceiveTimeout")->numarg; if(!nodb && !newver) { remote = remote_cvdhead(cvdfile, localname, hostname, ip, localip, proxy, port, user, pass, uas, &ims, ctimeout, rtimeout, mdat, logerr, can_whitelist); if(!nodb && !ims) { logg("%s is up to date (version: %d, sigs: %d, f-level: %d, builder: %s)\n", localname, current->version, current->sigs, current->fl, current->builder); *signo += current->sigs; cl_cvdfree(current); return 1; } if(!remote) { logg("^Can't read %s header from %s (IP: %s)\n", cvdfile, hostname, ip); cl_cvdfree(current); return 58; } newver = remote->version; cl_cvdfree(remote); } if(!nodb && (current->version >= newver)) { logg("%s is up to date (version: %d, sigs: %d, f-level: %d, builder: %s)\n", localname, current->version, current->sigs, current->fl, current->builder); if(!outdated && flevel < current->fl) { /* display warning even for already installed database */ logg("^Current functionality level = %d, recommended = %d\n", flevel, current->fl); logg("Please check if ClamAV tools are linked against the proper version of libclamav\n"); logg("DON'T PANIC! Read http://www.clamav.net/support/faq\n"); } *signo += current->sigs; cl_cvdfree(current); return 1; } if(current) { currver = current->version; cl_cvdfree(current); } /* if(ipaddr[0]) { hostfd = wwwconnect(ipaddr, proxy, port, NULL, localip); } else { hostfd = wwwconnect(hostname, proxy, port, ipaddr, localip); if(!ip[0]) strcpy(ip, ipaddr); } if(hostfd < 0) { if(ipaddr[0]) logg("Connection with %s (IP: %s) failed.\n", hostname, ipaddr); else logg("Connection with %s failed.\n", hostname); return 52; }; */ if(!optget(opts, "ScriptedUpdates")->enabled) nodb = 1; if(!getcwd(cwd, sizeof(cwd))) { logg("!updatedb: Can't get path of current working directory\n"); return 50; /* FIXME */ } newfile = cli_gentemp(cwd); if(nodb) { ret = getcvd(cvdfile, newfile, hostname, ip, localip, proxy, port, user, pass, uas, newver, ctimeout, rtimeout, mdat, logerr, can_whitelist); if(ret) { memset(ip, 0, 16); free(newfile); return ret; } snprintf(newdb, sizeof(newdb), "%s.cvd", dbname); } else { ret = 0; tmpdir = cli_gentemp("."); maxattempts = optget(opts, "MaxAttempts")->numarg; for(i = currver + 1; i <= newver; i++) { for(j = 0; j < maxattempts; j++) { int llogerr = logerr; if(logerr) llogerr = (j == maxattempts - 1); ret = getpatch(dbname, tmpdir, i, hostname, ip, localip, proxy, port, user, pass, uas, ctimeout, rtimeout, mdat, llogerr, can_whitelist); if(ret == 52 || ret == 58) { memset(ip, 0, 16); continue; } else { break; } } if(ret) break; } if(ret) { cli_rmdirs(tmpdir); free(tmpdir); logg("^Incremental update failed, trying to download %s\n", cvdfile); mirman_whitelist(mdat, 2); ret = getcvd(cvdfile, newfile, hostname, ip, localip, proxy, port, user, pass, uas, newver, ctimeout, rtimeout, mdat, logerr, can_whitelist); if(ret) { free(newfile); return ret; } snprintf(newdb, sizeof(newdb), "%s.cvd", dbname); } else { if(buildcld(tmpdir, dbname, newfile, optget(opts, "CompressLocalDatabase")->enabled) == -1) { logg("!Can't create local database\n"); cli_rmdirs(tmpdir); free(tmpdir); free(newfile); return 70; /* FIXME */ } snprintf(newdb, sizeof(newdb), "%s.cld", dbname); cli_rmdirs(tmpdir); free(tmpdir); } } if(!(current = cl_cvdhead(newfile))) { logg("!Can't parse new database %s\n", newfile); unlink(newfile); free(newfile); return 55; /* FIXME */ } if(!nodb && !access(localname, R_OK) && unlink(localname)) { logg("!Can't unlink %s. Please fix it and try again.\n", localname); unlink(newfile); free(newfile); return 53; } #ifdef C_WINDOWS if(!access(newdb, R_OK) && unlink(newdb)) { logg("!Can't unlink %s. Please fix the problem manually and try again.\n", newdb); unlink(newfile); free(newfile); return 53; } #endif if(rename(newfile, newdb) == -1) { logg("!Can't rename %s to %s: %s\n", newfile, newdb, strerror(errno)); unlink(newfile); free(newfile); return 57; } free(newfile); logg("%s updated (version: %d, sigs: %d, f-level: %d, builder: %s)\n", newdb, current->version, current->sigs, current->fl, current->builder); if(flevel < current->fl) { logg("Your ClamAV installation is out of date.\n"); logg("Current functionality level = %d, recommended = %d\n", flevel, current->fl); logg("A firmware upgrade might resolve this issue.\n"); logg("Also read http://sgkb.securecomputing.com/article.asp?article=11282&p=2\n"); } *signo += current->sigs; cl_cvdfree(current); return 0; }
struct cl_cvd *cl_cvdparse(const char *head) { struct cl_cvd *cvd; char *pt; if(strncmp(head, "ClamAV-VDB:", 11)) { cli_errmsg("cli_cvdparse: Not a CVD file\n"); return NULL; } if(!(cvd = (struct cl_cvd *) cli_malloc(sizeof(struct cl_cvd)))) { cli_errmsg("cl_cvdparse: Can't allocate memory for cvd\n"); return NULL; } if(!(cvd->time = cli_strtok(head, 1, ":"))) { cli_errmsg("cli_cvdparse: Can't parse the creation time\n"); free(cvd); return NULL; } if(!(pt = cli_strtok(head, 2, ":"))) { cli_errmsg("cli_cvdparse: Can't parse the version number\n"); free(cvd->time); free(cvd); return NULL; } cvd->version = atoi(pt); free(pt); if(!(pt = cli_strtok(head, 3, ":"))) { cli_errmsg("cli_cvdparse: Can't parse the number of signatures\n"); free(cvd->time); free(cvd); return NULL; } cvd->sigs = atoi(pt); free(pt); if(!(pt = cli_strtok(head, 4, ":"))) { cli_errmsg("cli_cvdparse: Can't parse the functionality level\n"); free(cvd->time); free(cvd); return NULL; } cvd->fl = atoi(pt); free(pt); if(!(cvd->md5 = cli_strtok(head, 5, ":"))) { cli_errmsg("cli_cvdparse: Can't parse the MD5 checksum\n"); free(cvd->time); free(cvd); return NULL; } if(!(cvd->dsig = cli_strtok(head, 6, ":"))) { cli_errmsg("cli_cvdparse: Can't parse the digital signature\n"); free(cvd->time); free(cvd->md5); free(cvd); return NULL; } if(!(cvd->builder = cli_strtok(head, 7, ":"))) { cli_errmsg("cli_cvdparse: Can't parse the builder name\n"); free(cvd->time); free(cvd->md5); free(cvd->dsig); free(cvd); return NULL; } if((pt = cli_strtok(head, 8, ":"))) { cvd->stime = atoi(pt); free(pt); } else { cli_dbgmsg("cli_cvdparse: No creation time in seconds (old file format)\n"); cvd->stime = 0; } return cvd; }
struct cfgstruct *getcfg(const char *cfgfile, int verbose) { char buff[LINE_LENGTH], *name, *arg, *c; FILE *fs; int line = 0, i, found, ctype, calc, val; struct cfgstruct *copt = NULL; struct cfgoption *pt; for(i = 0; ; i++) { pt = &cfg_options[i]; if(!pt->name) break; if(regcfg(&copt, pt->name, pt->strarg ? strdup(pt->strarg) : NULL, pt->numarg, pt->multiple) < 0) { fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n"); freecfg(copt); return NULL; } } if((fs = fopen(cfgfile, "rb")) == NULL) { /* do not print error message here! */ freecfg(copt); return NULL; } while(fgets(buff, LINE_LENGTH, fs)) { line++; if(buff[0] == '#') continue; if(!strncmp("Example", buff, 7)) { if(verbose) fprintf(stderr, "ERROR: Please edit the example config file %s.\n", cfgfile); fclose(fs); freecfg(copt); return NULL; } if((name = cli_strtok(buff, 0, " \r\n"))) { arg = cli_strtok(buff, 1, " \r\n"); found = 0; for(i = 0; ; i++) { pt = &cfg_options[i]; if(pt->name) { if(!strcmp(name, pt->name)) { found = 1; switch(pt->argtype) { case OPT_STR: /* deprecated. Use OPT_QUOTESTR instead since it behaves like this, but supports quotes to allow values to contain whitespace */ if(!arg) { if(verbose) fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires string argument.\n", line, name); fclose(fs); free(name); freecfg(copt); return NULL; } if(regcfg(&copt, name, arg, -1, pt->multiple) < 0) { fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n"); fclose(fs); free(name); free(arg); freecfg(copt); return NULL; } break; case OPT_FULLSTR: /* an ugly hack of the above case */ if(!arg) { if(verbose) fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires string argument.\n", line, name); fclose(fs); free(name); freecfg(copt); return NULL; } free(arg); arg = strstr(buff, " "); arg = strdup(++arg); if((arg) && (c = strpbrk(arg, "\n\r"))) *c = '\0'; if((!arg) || (regcfg(&copt, name, arg, -1, pt->multiple) < 0)) { fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n"); fclose(fs); free(name); free(arg); freecfg(copt); return NULL; } break; case OPT_QUOTESTR: /* an ugly hack of the above case */ if(!arg) { if(verbose) fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires string argument.\n", line, name); fclose(fs); free(name); freecfg(copt); return NULL; } if((*arg == '\'') || (*arg == '"')) { free(arg); c = strstr(buff, " "); arg = strdup(c+2); if(arg) { if((c = strchr(arg, c[1]))) *c = '\0'; else { if(verbose) fprintf(stderr, "ERROR: Parse error at line %d: Option %s missing closing quote.\n", line, name); fclose(fs); free(name); free(arg); freecfg(copt); return NULL; } } } if((!arg) || (regcfg(&copt, name, arg, -1, pt->multiple) < 0)) { fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n"); fclose(fs); free(name); free(arg); freecfg(copt); return NULL; } break; case OPT_NUM: if(!arg || !isnumb(arg)) { if(verbose) fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical argument.\n", line, name); fclose(fs); free(name); free(arg); freecfg(copt); return NULL; } if(regcfg(&copt, name, NULL, atoi(arg), pt->multiple) < 0) { fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n"); fclose(fs); free(name); free(arg); freecfg(copt); return NULL; } free(arg); break; case OPT_COMPSIZE: if(!arg) { if(verbose) fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires argument.\n", line, name); fclose(fs); free(name); freecfg(copt); return NULL; } ctype = tolower(arg[strlen(arg) - 1]); if(ctype == 'm' || ctype == 'k') { char *cpy = (char *) calloc(strlen(arg), 1); strncpy(cpy, arg, strlen(arg) - 1); if(!isnumb(cpy)) { if(verbose) fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical (raw/K/M) argument.\n", line, name); fclose(fs); free(name); free(arg); freecfg(copt); return NULL; } if(ctype == 'm') calc = atoi(cpy) * 1024 * 1024; else calc = atoi(cpy) * 1024; free(cpy); } else { if(!isnumb(arg)) { if(verbose) fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical (raw/K/M) argument.\n", line, name); fclose(fs); free(name); free(arg); freecfg(copt); return NULL; } calc = atoi(arg); } free(arg); if(regcfg(&copt, name, NULL, calc, pt->multiple) < 0) { fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n"); fclose(fs); free(name); free(arg); freecfg(copt); return NULL; } break; case OPT_BOOL: if(!arg) { if(verbose) fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires boolean argument.\n", line, name); fclose(fs); free(name); freecfg(copt); return NULL; } if(!strcasecmp(arg, "yes") || !strcmp(arg, "1") || !strcasecmp(arg, "true")) { val = 1; } else if(!strcasecmp(arg, "no") || !strcmp(arg, "0") || !strcasecmp(arg, "false")) { val = 0; } else { if(verbose) fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires boolean argument.\n", line, name); fclose(fs); free(name); free(arg); freecfg(copt); return NULL; } free(arg); if(regcfg(&copt, name, NULL, val, pt->multiple) < 0) { fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n"); fclose(fs); free(name); free(arg); freecfg(copt); return NULL; } break; default: if(verbose) fprintf(stderr, "ERROR: Parse error at line %d: Option %s is of unknown type %d\n", line, name, pt->argtype); fclose(fs); free(name); free(arg); freecfg(copt); return NULL; } } } else break; } if(!found) { if(verbose) fprintf(stderr, "ERROR: Parse error at line %d: Unknown option %s.\n", line, name); free(name); fclose(fs); freecfg(copt); return NULL; } free(name); } } fclose(fs); return copt; }
int client(const struct optstruct *opt, int *infected) { char cwd[200], *fullpath; int sockd, ret, errors = 0; struct stat sb; *infected = 0; /* parse argument list */ if(opt->filename == NULL || strlen(opt->filename) == 0) { /* scan current directory */ if(!getcwd(cwd, 200)) { mprintf("@Can't get absolute pathname of current working directory.\n"); return 2; } if((sockd = dconnect(opt)) < 0) return 2; if((ret = dsfile(sockd, cwd, opt)) >= 0) *infected += ret; else errors++; close(sockd); #if defined(ENABLE_FD_PASSING) && defined(HAVE_SENDMSG) && (defined(HAVE_ACCRIGHTS_IN_MSGHDR) || defined(HAVE_CONTROL_IN_MSGHDR)) && !defined(C_CYGWIN) } else if(!strcmp(opt->filename, "-")) { /* scan data from stdin */ if((sockd = dconnect(opt)) < 0) return 2; if((ret = dsfd(sockd, 0, opt)) >= 0) *infected += ret; else errors++; close(sockd); #else } else if(!strcmp(opt->filename, "-")) { /* scan data from stdin */ if((sockd = dconnect(opt)) < 0) return 2; if((ret = dsstream(sockd, opt)) >= 0) *infected += ret; else errors++; close(sockd); #endif } else { int x; char *thefilename; for (x = 0; (thefilename = cli_strtok(opt->filename, x, "\t")) != NULL; x++) { fullpath = thefilename; if(stat(fullpath, &sb) == -1) { mprintf("@Can't access file %s\n", fullpath); perror(fullpath); errors++; } else { if(strlen(fullpath) < 2 || (fullpath[0] != '/' && fullpath[0] != '\\' && fullpath[1] != ':')) { fullpath = abpath(thefilename); free(thefilename); if(!fullpath) { mprintf("@Can't determine absolute path.\n"); return 2; } } switch(sb.st_mode & S_IFMT) { case S_IFREG: case S_IFDIR: if((sockd = dconnect(opt)) < 0) return 2; if((ret = dsfile(sockd, fullpath, opt)) >= 0) *infected += ret; else errors++; close(sockd); break; default: mprintf("@Not supported file type (%s)\n", fullpath); errors++; } } free(fullpath); } } return *infected ? 1 : (errors ? 2 : 0); }