Clamav杀毒软件源码分析笔记[十]
刺猬@http://blog.csdn.net/littlehedgehog
[客户端处理]
服务端已经把主要的工作都已经处理的差不多了,剩下来也就是服务端等待客户端提出请求,然后根据客户端的请求做相应的工作. 所以客户端所做的事情就只是提交数据,然后坐享其成.服务端是在垂帘听政呢,还是做幕后英雄?话不多说了,下面进入正题.
客户端(clamdscan)的主函数main和clamscan其实用的是同一个main,当然里面的内容同样还是处理命令行,配置文件等相关信息,乏善可陈,经过这系列处理之后,我们来到了client这个客户端的逻辑处理主函数.但是我还是不准备详细来说这个函数,因为它除了因为客户端只是创建socket和使用connect对服务端进行连接而不需要啥绑定,监听,实在和服务端的创建没多少代码上的区别. 只是在连接的时候,我们要分配置文件里是设定的本地连接还是网络连接. 如下所示:
- int dconnect(const struct optstruct *opt)
- {
- struct sockaddr_un server;
- struct sockaddr_in server2;
- struct hostent *he;
- struct cfgstruct *copt, *cpt;
- const char *clamav_conf = getargl(opt, "config-file");
- int sockd;
- if (!clamav_conf)
- clamav_conf = DEFAULT_CFG;
- if ((copt = parsecfg(clamav_conf, 1)) == NULL)
- {
- mprintf("@Can't parse the configuration file./n");
- return -1;
- }
- memset((char *) &server, 0, sizeof(server));
- memset((char *) &server2, 0, sizeof(server2));
-
- server2.sin_addr.s_addr = inet_addr("127.0.0.1");
- if (cfgopt(copt, "TCPSocket") && cfgopt(copt, "LocalSocket"))
- {
- mprintf("@Clamd is not configured properly./n");
- return -1;
- }
- else if ((cpt = cfgopt(copt, "LocalSocket")))
- {
- server.sun_family = AF_UNIX;
- strncpy(server.sun_path, cpt->strarg, sizeof(server.sun_path));
- if ((sockd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
- {
- perror("socket()");
- mprintf("@Can't create the socket./n");
- return -1;
- }
- if (connect(sockd, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0)
- {
- close(sockd);
- perror("connect()");
- mprintf("@Can't connect to clamd./n");
- return -1;
- }
- }
- else if ((cpt = cfgopt(copt, "TCPSocket")))
- {
- if ((sockd = socket(SOCKET_INET, SOCK_STREAM, 0)) < 0)
- {
- perror("socket()");
- mprintf("@Can't create the socket./n");
- return -1;
- }
- server2.sin_family = AF_INET;
- server2.sin_port = htons(cpt->numarg);
- if ((cpt = cfgopt(copt, "TCPAddr")))
- {
- if ((he = gethostbyname(cpt->strarg)) == 0)
- {
- close(sockd);
- perror("gethostbyname()");
- mprintf("@Can't lookup clamd hostname./n");
- return -1;
- }
- server2.sin_addr = *(struct in_addr *) he->h_addr_list[0];
- }
- if (connect(sockd, (struct sockaddr *) &server2, sizeof(struct sockaddr_in)) < 0)
- {
- close(sockd);
- perror("connect()");
- mprintf("@Can't connect to clamd./n");
- return -1;
- }
- }
- else
- {
- mprintf("@Clamd is not configured properly./n");
- return -1;
- }
- return sockd;
- }
建立完连接之后,我们要做得就是提交各种任务给服务端让它做了,不能让它手下一大批线程给闲着.这里我们还是依照书上内容,看看stream扫描:
- else if (!strcmp(opt->filename, "-"))
- {
- if ((sockd = dconnect(opt)) < 0)
- return 2;
- if ((ret = dsstream(sockd, opt)) >= 0)
- *infected += ret;
- else
- errors++;
- close(sockd);
- }
stream的扫描就是在命令行里面加上一个'-', 利用linux强大的重定向功能,我们把文件的内容作为数据流传给服务端.
- int dsstream(int sockd, const struct optstruct *opt)
- {
- int wsockd, loopw = 60, bread, port, infected = 0;
- struct sockaddr_in server;
- struct sockaddr_in peer;
- int peer_size;
- char buff[4096], *pt;
-
- if (write(sockd, "STREAM", 6) <= 0)
- {
- mprintf("@Can't write to the socket./n");
- return 2;
- }
- memset(buff, 0, sizeof(buff));
-
- while (loopw)
- {
- read(sockd, buff, sizeof(buff));
- if ((pt = strstr(buff, "PORT")))
- {
- pt += 5;
- sscanf(pt, "%d", &port);
- break;
- }
- loopw--;
- }
- if (!loopw)
- {
- mprintf("@Daemon not ready for stream scanning./n");
- return -1;
- }
-
- if ((wsockd = socket(SOCKET_INET, SOCK_STREAM, 0)) < 0)
- {
- perror("socket()");
- mprintf("@Can't create the socket./n");
- return -1;
- }
- server.sin_family = AF_INET;
- server.sin_port = htons(port);
-
-
- peer_size = sizeof(peer);
- if (getpeername(sockd, (struct sockaddr *) &peer, &peer_size) < 0)
- {
- perror("getpeername()");
- mprintf("@Can't get socket peer name./n");
- return -1;
- }
-
-
- switch (peer.sin_family)
- {
- case AF_UNIX:
- server.sin_addr.s_addr = inet_addr("127.0.0.1");
- break;
- case AF_INET:
- server.sin_addr.s_addr = peer.sin_addr.s_addr;
- break;
- default:
- mprintf("@Unexpected socket type: %d./n", peer.sin_family);
- return -1;
- }
- if (connect(wsockd, (struct sockaddr *) &server, sizeof(struct sockaddr_in)) < 0)
- {
- close(wsockd);
- perror("connect()");
- mprintf("@Can't connect to clamd [port: %d]./n", port);
- return -1;
- }
-
- while ((bread = read(0, buff, sizeof(buff))) > 0)
- {
- if (write(wsockd, buff, bread) <= 0)
- {
- mprintf("@Can't write to the socket./n");
- close(wsockd);
- return -1;
- }
- }
- close(wsockd);
- memset(buff, 0, sizeof(buff));
-
- while ((bread = read(sockd, buff, sizeof(buff))) > 0)
- {
- mprintf("%s", buff);
- if (strstr(buff, "FOUND/n"))
- {
- infected++;
- logg("%s", buff);
- }
- else if (strstr(buff, "ERROR/n"))
- {
- logg("%s", buff);
- return -1;
- }
- memset(buff, 0, sizeof(buff));
- }
- return infected;
- }
这里有个小问题 在42行有句 if ((wsockd = socket(SOCKET_INET, SOCK_STREAM, 0)) < 0)
我们直接定死了是SOCKET_INET,那如果服务端在本地怎么办呢? 看看67行的处理: server.sin_addr.s_addr = inet_addr("127.0.0.1"); 也就是如果是UNIX域就是用本地IP地址作为sockaddr的地址了.
如果我们扫描到了病毒,这里我们就需要转移病毒文件了,因为这只是个扫毒软件,不能杀毒:
- void move_infected(const char *filename, const struct optstruct *opt)
- {
- char *movedir, *movefilename, *tmp, numext[4 + 1];
- struct stat fstat, mfstat;
- int n, len, movefilename_size;
- struct utimbuf ubuf;
- if (!(movedir = getargl(opt, "move")))
- {
-
- mprintf("@getargc() returned NULL/n", filename);
- notmoved++;
- return;
- }
-
-
- if (access(movedir, W_OK|X_OK) == -1)
- {
- mprintf("@error moving file '%s': cannot write to '%s': %s/n", filename, movedir, strerror(errno));
- notmoved++;
- return;
- }
-
-
- if (stat(filename, &fstat) == -1)
- {
- mprintf("@Can't stat file %s/n", filename);
- mprintf("Try to run clamdscan with clamd privileges/n");
- notmoved++;
- return;
- }
- if (!(tmp = strrchr(filename, '/')))
- tmp = (char *) filename;
-
- movefilename_size = sizeof(char) * (strlen(movedir) + strlen(tmp) + sizeof(numext) + 2);
- if (!(movefilename = mmalloc(movefilename_size)))
- {
- mprintf("@Memory allocation error/n");
- exit(2);
- }
- if (!(strrcpy(movefilename, movedir)))
- {
- mprintf("@strrcpy() returned NULL/n");
- notmoved++;
- free(movefilename);
- return;
- }
-
-
- strcat(movefilename, "/");
-
- if (!(strcat(movefilename, tmp)))
- {
- mprintf("@strcat() returned NULL/n");
- notmoved++;
- free(movefilename);
- return;
- }
-
-
- if (!stat(movefilename, &mfstat))
- {
- if (fstat.st_ino == mfstat.st_ino)
- {
- mprintf("File excluded '%s'/n", filename);
- logg("File excluded '%s'/n", filename);
- notmoved++;
- free(movefilename);
- return;
- }
- else
- {
-
- len = strlen(movefilename);
- n = 0;
- do
- {
-
- movefilename[len] = 0;
-
- sprintf(numext, ".%03d", n++);
- strcat(movefilename, numext);
- }
- while (!stat(movefilename, &mfstat) && (n < 1000));
- }
- }
-
- if (rename(filename, movefilename) == -1)
- {
- if (filecopy(filename, movefilename) == -1)
- {
- mprintf("@cannot move '%s' to '%s': %s/n", filename, movefilename, strerror(errno));
- notmoved++;
- free(movefilename);
- return;
- }
-
- chmod(movefilename, fstat.st_mode);
- chown(movefilename, fstat.st_uid, fstat.st_gid);
- ubuf.actime = fstat.st_atime;
- ubuf.modtime = fstat.st_mtime;
- utime(movefilename, &ubuf);
- if (unlink(filename))
- {
- mprintf("@cannot unlink '%s': %s/n", filename, strerror(errno));
- notremoved++;
- free(movefilename);
- return;
- }
- }
- mprintf("%s: moved to '%s'/n", filename, movefilename);
- logg("%s: moved to '%s'/n", filename, movefilename);
- free(movefilename);
- }
转移主要就是一个copy,再加上同名文件的处理问题,有时候当你电脑成病毒窝的时候,可能病毒很多,统统转移到一个目录下面的时候造成同名冲突的可能性比较打,为了避免覆盖,这里就加上后缀. 这个好像很普遍的处理办法吧....