庞大资源库的计算机教程网站!
设为首页
加入收藏
总编信箱
投稿或申请专栏请先 [登 陆]
首页 操作系统 程序设计 图形图像 媒体动画 机械电子 WEB开发 数 据 库 办公系列 路由技术 网络原理 网络应用
认证考试 安全技术
首页>安全技术>黑客攻防>黑客入门>正文
资料搜索
Google搜索
Google
返回上级列表

推荐文章

快速保存网页中所有图片的方法
Windows中让光驱巧妙“隐身”技
防范非法用户入侵Win 2000/XP系
两款比较典型的ASP木马防范方法
有关表格边框的css语法整理
Windows XP中可以被禁用的服务
SQL Server导出导入数据方法
Javascript所有对象的属性的获
网页(HTML)中的特殊字符
与篮球共舞,尽显模式本色
QQ病毒的手工清除方法
Photoshop为极品美女打造性感睫
天衣无缝:IIS与PHP水火也相容
SQL Server存储过程编写和优化

如何阅读源代码 二

 作者:本站收集   日期:2005-5-14
字号选择〖 〗/ 双击滚屏 单击停止   
这一段是对于FTP的日志格式,设置搜索列表。
  for (i=0;i MAXHASH;i++)
  {
   sm_htab[i]=sd_htab[i]=NULL; /* initalize hash tables */
   um_htab[i]=NULL;
   rm_htab[i]=NULL;
   am_htab[i]=NULL;
   sr_htab[i]=NULL;
  }
清空哈西表,为下面即将进行的排序工作做好准备。关于哈西表,这是数据结构中常用的一种用来快速排序的结构,如果不清楚,可以参考相关书籍,比如清华的<<数据结构>>教材或者《数据结构的C++实现》等书。
  if (verbose>1)
  {
   uname(&system_info);
   printf("Webalizer V%s-%s (%s %s) %s ",
       version,editlvl,system_info.sysname,
       system_info.release,language);
  }
这一段,是打印有关系统的信息和webalizer程序的信息(可以参考uname的函数说明)。
#ifndef USE_DNS
  if (strstr(argv[0],"webazolver")!=0)
  {
   printf("DNS support not present, aborting... ");
   exit(1);
  }
#endif /* USE_DNS */
这一段,回忆我们在看README文件的时候,曾经提到过可以在编译的时候设置选项开关来设定DNS支持,在源代码中可以看到多次这样的代码段出现,如果不指定DNS支持,这些代码段则会出现(ifdef)或者不出现(ifndef).下面略过这些代码段,不再重复。
  /* open log file */
  if (gz_log)
  {
   gzlog_fp = gzopen(log_fname,"rb");
   if (gzlog_fp==Z_NULL)
   {
     /* Error: Can't open log file ... */
     fprintf(stderr, "%s %s ",msg_log_err,log_fname);
     exit(1);
   }
  }
  else
  {
   if (log_fname)
   {
     log_fp = fopen(log_fname,"r");
     if (log_fp==NULL)
     {
      /* Error: Can't open log file ... */
      fprintf(stderr, "%s %s ",msg_log_err,log_fname);
      exit(1);
     }
   }
  }
这一段,回忆在README文件中曾经读到过,如果log文件是gzip压缩格式,则用gzopen函数打开(可以猜想gz***是一套针对gzip压缩格式的实时解压缩函数),如果不是,则用fopen打开。
  /* switch directories if needed */
  if (out_dir)
  {
   if (chdir(out_dir) != 0)
   {
     /* Error: Can't change directory to ... */
     fprintf(stderr, "%s %s ",msg_dir_err,out_dir);
     exit(1);
   }
  }
同样,回忆在README文件中读到过,如果参数行有-o out_dir, 则将输出结果到该目录,否则,则输出到当前目录。在这一段中,如果输出目录不存在(chdir(out_dir) != 0)则出错。
#ifdef USE_DNS
  if (strstr(argv[0],"webazolver")!=0)
  {
   if (!dns_children) dns_children=5; /* default dns children if needed */
   if (!dns_cache)
   {
     /* No cache file specified, aborting... */
     fprintf(stderr,"%s ",msg_dns_nocf); /* Must have a cache file */
     exit(1);
   }
  }
......
在上面曾经提到过,这是DNS解析的代码部分,可以略过不看,不会影响对整个程序的理解。
  /* prep hostname */
  if (!hname)
  {
   if (uname(&system_info)) hname="localhost";
   else hname=system_info.nodename;
  }
这一段继续处理参数做准备工作。如果在命令行中指定了hostname(机器名)则采用指定的名称,否则调用uname查找机器名,如果没有,则用localhost来作为机器名。(同样在README中说得很详细)
  /* get past history */
  if (ignore_hist) {if (verbose>1) printf("%s ",msg_ign_hist); }
  else get_history();
如果在命令行中指定了忽略历史文件,则不读取历史文件,否则调用get_history()来读取历史数据。在这里,我们可以回想在README文件中同样说过这一细节,在命令行或者配置文件中都能指定这一开关。需要说明的是,我们在这里并不一定需要去看get_history这一函数,因为从函数的名称,README文件和程序注释都能很清楚的得知这一函数的功能,不一定要去看代码。而如果要猜想的话,也可以想到,history是webalizer在上次运行的时候记录下来的一个文件,而这个文件则是去读取它,并将它的数据包括到这次的分析中去。不信,我们可以来看看。
void get_history()
{
  int i,numfields;
  FILE *hist_fp;
  char buffer[BUFSIZE];

  /* first initalize internal array */
  for (i=0;i<12;i++)
  {
   hist_month[i]=hist_year[i]=hist_fday[i]=hist_lday[i]=0;
   hist_hit[i]=hist_files[i]=hist_site[i]=hist_page[i]=hist_visit[i]=0;
   hist_xfer[i]=0.0;
  }

  hist_fp=fopen(hist_fname,"r");

  if (hist_fp)
  {
   if (verbose>1) printf("%s %s ",msg_get_hist,hist_fname);
   while ((fgets(buffer,BUFSIZE,hist_fp)) != NULL)
   {
     i = atoi(buffer) -1;
     if (i>11)
     {
      if (verbose)
        fprintf(stderr,"%s (mth=%d) ",msg_bad_hist,i+1);
      continue;
     }

     /* month# year# requests files sites xfer firstday lastday */
     numfields = sscanf(buffer,"%d %d %lu %lu %lu %lf %d %d %lu %lu",
            &hist_month[i],
            &hist_year[i],
            &hist_hit[i],
            &hist_files[i],
            &hist_site[i],
            &hist_xfer[i],
            &hist_fday[i],
            &hist_lday[i],
            &hist_page[i],
            &hist_visit[i]);

     if (numfields==8) /* kludge for reading 1.20.xx history files */
     {
      hist_page[i] = 0;
      hist_visit[i] = 0;
     }
   }
   fclose(hist_fp);
  }
  else if (verbose>1) printf("%s ",msg_no_hist);
}
/*********************************************/
/* PUT_HISTORY - write out history file */
/*********************************************/

void put_history()
{
  int i;
  FILE *hist_fp;

  hist_fp = fopen(hist_fname,"w");

  if (hist_fp)
  {
   if (verbose>1) printf("%s ",msg_put_hist);
   for (i=0;i<12;i++)
   {
     if ((hist_month[i] != 0) && (hist_hit[i] != 0))
     {
      fprintf(hist_fp,"%d %d %lu %lu %lu %.0f %d %d %lu %lu ",
              hist_month[i],
              hist_year[i],
              hist_hit[i],
              hist_files[i],
              hist_site[i],
              hist_xfer[i],
              hist_fday[i],
              hist_lday[i],
              hist_page[i],
              hist_visit[i]);
     }
   }
   fclose(hist_fp);
  }
  else
   if (verbose)
   fprintf(stderr,"%s %s ",msg_hist_err,hist_fname);
}
在preserve.c中,这两个函数是成对出现的。get_history()读取文件中的数据,并将其记录到hist_开头的一些数组中去。而put_history()则是将一些数据记录到同样的数组中去。我们可以推测得知,hist_数组是全局变量(在函数中没有定义),也可以查找源代码验证。同样,我们可以找一找put_history()出现的地方,来验证刚才的推测是否正确。在webalizer.c的1311行,出现:
     month_update_exit(rec_tstamp); /* calculate exit pages */
     write_month_html(); /* write monthly HTML file */
     write_main_index(); /* write main HTML file */
     put_history(); /* write history */
可以知道,推测是正确的。再往下读代码,
  if (incremental) /* incremental processing? */
  {
   if ((i=restore_state())) /* restore internal data structs */
   {
     /* Error: Unable to restore run data (error num) */
     /* if (verbose) fprintf(stderr,"%s (%d) ",msg_bad_data,i); */
     fprintf(stderr,"%s (%d) ",msg_bad_data,i);
     exit(1);
   }
  ......
  }
同样,这也是处理命令行和做数据准备,而且和get_history(), put_history()有些类似,读者可以自己练习一下。下面,终于进入了程序的主体部分, 在做完了命令行分析,数据准备之后,开始从日志文件中读取数据并做分析了。
  /*********************************************/
  /* MAIN PROCESS LOOP - read through log file */
  /*********************************************/

  while ( (gz_log)?(our_gzgets(gzlog_fp,buffer,BUFSIZE) != Z_NULL):
      (fgets(buffer,BUFSIZE,log_fname?log_fp:stdin) != NULL))
我看到这里的时候,颇有一些不同意作者的这种写法。这一段while中的部分写的比较复杂而且效率不高。因为从程序推断和从他的代码看来,作者是想根据日志文件的类型不同来采用不同的方法读取文件,如果是gzip格式,则用our_gzgets来读取其中一行,如果是普通的文本文件格式,则用fgets()来读取。但是,这段代码是写在while循环中的,每次读取一行就要重复判断一次,明显是多余的而且降低了程序的性能。可以在while循环之前做一次这样的判断,然后就不用重复了。
   total_rec++;
   if (strlen(buffer) == (BUFSIZE-1))
   {
     if (verbose)
     {
      fprintf(stderr,"%s",msg_big_rec);
      if (debug_mode) fprintf(stderr,": %s",buffer);
      else fprintf(stderr," ");
     }

     total_bad++; /* bump bad record counter */

     /* get the rest of the record */
     while ( (gz_log)?(our_gzgets(gzlog_fp,buffer,BUFSIZE)!=Z_NULL):
         (fgets(buffer,BUFSIZE,log_fname?log_fp:stdin)!=NULL))
     {
      if (strlen(buffer) < BUFSIZE-1)
      {
        if (debug_mode && verbose) fprintf(stderr,"%s ",buffer);
        break;
      }
      if (debug_mode && verbose) fprintf(stderr,"%s",buffer);
     }
     continue; /* go get next record if any */
   }
这一段代码,读入一行,如果这一行超过了程序允许的最大字符数(则是错误的日志数据纪录),则跳过本行剩下的数据,忽略掉(continue进行下一次循环)。同时把total_bad增加一个。如果没有超过程序允许的最大字符数(则是正确的日志数据纪录),则
   /* got a record... */
   strcpy(tmp_buf, buffer); /* save buffer in case of error */
   if (parse_record(buffer)) /* parse the record   */
将该数据拷贝到一个缓冲区中,然后调用parse_record()进行处理。我们可以同样的推测一下,get_record()是这个程序的一个主要处理部分,分析了日志数据。在parse_record.c中,有此函数
/*********************************************/
/* PARSE_RECORD - uhhh, you know... */
/*********************************************/

int parse_record(char *buffer)
{
  /* clear out structure */
  memset(&log_rec,0,sizeof(struct log_struct));
/*
  log_rec.hostname[0]=0;
  log_rec.datetime[0]=0;
  log_rec.url[0]=0;
  log_rec.resp_code=0;
  log_rec.xfer_size=0;
  log_rec.refer[0]=0;
  log_rec.agent[0]=0;
  log_rec.srchstr[0]=0;
  log_rec.ident[0]=0;
*/
#ifdef USE_DNS
  memset(&log_rec.addr,0,sizeof(struct in_addr));
#endif

  /* call appropriate handler */
  switch (log_type)
  {
   default:
   case LOG_CLF: return parse_record_web(buffer); break; /* clf */
   case LOG_FTP: return parse_record_ftp(buffer); break; /* ftp */
   case LOG_SQUID: return parse_record_squid(buffer); break; /* squid */
  }
}

可以看到,log_rec是一个全局变量,该函数根据日志文件的类型,分别调用三种不同的分析函数。在webalizer.h中,找到该变量的定义,从结构定义中可以看到,结构定义了一个日志文件所可能包含的所有信息(参考CLF,FTP, SQUID日志文件的格式说明)。
/* log record structure */
struct log_struct { char hostname[MAXHOST]; /* hostname */
            char datetime[29]; /* raw timestamp */
            char url[MAXURL]; /* raw request field */
            int resp_code; /* response code */
            u_long xfer_size; /* xfer size in bytes */
#ifdef USE_DNS
            struct in_addr addr; /* IP address structure */
#endif /* USE_DNS */
            char refer[MAXREF]; /* referrer */
            char agent[MAXAGENT]; /* user agent (browser) */
            char srchstr[MAXSRCH]; /* search string */
            char ident[MAXIDENT]; }; /* ident string (user) */

extern struct log_struct log_rec;

先看一下一个parser.c用的内部函数,然后再来以parse_record_web()为例子看看这个函数是怎么工作的,parse_record_ftp, parse_record_squid留给读者自己分析作为练习。
/*********************************************/
/* FMT_LOGREC - terminate log fields w/zeros */
/*********************************************/

void fmt_logrec(char *buffer)
{
  char *cp=buffer;
  int q=0,b=0,p=0;

  while (*cp != '')
  {
   /* break record up, terminate fields with '' */
   switch (*cp)
   {
    case ' ': if (b || q || p) break; *cp=''; break;
    case '"': q^=1; break;
    case '[': if (q) break; b++; break;
    case ']': if (q) break; if (b>0) b--; break;
    case '(': if (q) break; p++; break;
    case ')': if (q) break; if (p>0) p--; break;
   }
   cp++;
  }
}
上一篇:如何阅读源代码 三    下一篇:如何阅读源代码 一  
[发送给好友]  [关闭窗口]  [返回顶部]   转载请注明来源:www.it00.com   
特别声明: 本站除部分特别声明禁止转载的专稿外的其他文章可以自由转载,但请务必注明出处和原始作者。文章版权归文章原始作者所有。对于被本站转载文章的个人和网站,我们表示深深的谢意。如果本站转载的文章有版权问题请联系编辑人员,我们尽快予以更正。
责任编辑: 原点 投稿作者: 本站收集
信息来源: 网络 录入时间: 2005-5-14
关于我们 - 广告服务 - 版权申明 - 网站地图 - 联系方式 - 总编信箱 - 会员投稿