荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: cooler (星光璀璨心依然), 信区: Program
标  题: [转载] [转寄] 原码重读(1)--proxy scan                xh
发信站: BBS 荔园晨风站 (Thu Mar 23 15:52:28 2000), 转信

【 以下文字转载自 cooler 的信箱 】
【 原文由 cooler.bbs@melon.gznet.edu.cn 所发表 】
发信人: xh (强吻), 信区: Socket
标  题: 原码重读(1)--proxy scan
发信站: 华南网木棉站 (Sun Nov  7 14:06:22 1999), 转信

在Netguy的proxy scan大作里,我们可以学到
1) 设计后台运行的程序
2) 操作日志文件
3) 无阻塞socket的connect
4) 用select进行超时限制
5) 异常信号的处理
6) IP的字符串形式和数字形式
7) 用getsockopt检测是否存在错误

下面是Netguy的原程,为了讲解方便,调了一下顺序,对程序作了精简
原码可在socket版精华区Happy的大作里面找到.

命令的参数是: 起始IP地址  结束IP地址   日志文件名

char             serverName[20];

main(int argc,char *argv[])
{
 /*1. 获取搜索的IP范围
   inet_addr()将字符串格式的IP地址转为一个数字形式(255.255.255.255 to FFFFFF)
   ntohl()将以网络字节顺序排列的IP地址转为以本机字节顺序排列
 */

  startIP=ntohl(inet_addr(argv[1]));
  endIP=ntohl(inet_addr(argv[2]));

 /*2.将搜索情况和结果纪录到日志文件
     fprintf直接对文件进行格式化输入,然后用fflush()存盘.
     是书写日志文件的标准用法,在后面的程序里将反复出现。
 */

  FILE *f = fopen(argv[3],"a");           //打开日记文件
  fprintf(f,"%s--->%s\n",argv[1],argv[2]);//纪录搜索的范围
  fflush(f);

  /* 处理SIGTERM信号 */

  signal(SIGTERM,terminate); //用terminate函数处理kill命令的信号

  /*3.以下程序段演示如何书写后台运行的程序*/

  /*第一步,先用fork()复制一个跟父进程完全一样的子进程,然后父进程退出 ,
    用户在shell下将从新得到提示符,可以继续进行别的操作。而子进程(替身)将
    在后台继续运行。*/

  switch(fork())
  {
    case 0:       /*返回值是0,表明是子进程,继续在后台运行*/
     break;
    case -1:      /* 出错       */
     perror("fork( )");
     exit(-1);
    default:     /* 返回值不为0,表明是父进程部分,结束 */
     exit(0);
   }

  /*  余下的代码将由子进程执行  */

  /*第二步,使子进程摆脱外来信号的干扰*/
  /*脱离进程组和对话过程,否则子进程将接收任何作为整体发给该进程组或对
    话过程的信号*/

   setsid();

  /*切断与控制台的联系 ,否则子进程将接收运行该程序的控制台产生的信号*/

  i=open("/dev/tty",O_RDWR);
  ioctl(i,TIOCNOTTY,0);
  close(i);

  /*这个程序不是daemon,否则还要加上umask(设置全限位过滤),changdir(改变
    根目录),以及用文件锁防止程序重复执行等操作*/

  /*4.下面程序正式开始搜索proxy*/
  for(k=startIP;k<=endIP;k++)     /* 循环扫描指定范围内的所有IP  */
  {
    if( (k % 256)==0)             //略过localhost地址
      continue;
    if( (k % 256)==255)           //略过广播地址
      continue;

    /*将数字地址转成字符串形式 */
    struct in_addr   serverIP;
    serverIP.s_addr=k;
    sprintf(serverName,"%s",inet_ntoa(serverIP));  //将搜索的IP纪录到serverName
    findProxy(k);                //对该地址是否存在proxy进行搜索
  }
  close(f);
 }                                //主函数结束,我们再看findproxy()
void findProxy(u_long addr)
{
 int port[N]={ 80,8080,3128,1080};     //欲搜的端口号
 for(i=0;i<4;i++)                      //开始逐个尝试这4个端口
 {
  host.sin_family=AF_INET;
  host.sin_addr.s_addr=htonl(addr);
  host.sin_port=htons(port[i]);
  int sockfd=socket(AF_INET,SOCK_STREAM,0);

   /* 设为非阻塞式socket
     因为阻塞式connect()将会阻塞round-trip所需的时间(由内核根据网络
     距离计算)才会认为超时,返回失败 ,这显然不符合快速搜索proxy的要求。
     因此采用非阻塞的connect(),connect()被调用后将立即返回,如果连接
     已经完成就返回0,否则返回-1,errno设为EINPROGRESS, 而TCP三次握手
     则继续进行,连接的结果将通过select获取。所以配合select,就能限制
     connect() TimeOut的时间。
   */
   fcntl(sockfd,F_SETFL,O_NDELAY)
   status=connect(sockfd,(struct sockaddr *)& host,sizeof(host));
   /*上面的程序段演示了如何限制超时*/
   timeout.tv_sec=2;                // 超时限制为2秒
   timeout.tv_usec=0;

   struct fd_set    mask;
   FD_ZERO( & mask);                //清空集合mask
   FD_SET(sockfd,& mask);           //将sockfd放入集合mask中
   /*select用来判断文件描述符是否就绪
     select的5个参数为需要判断的最大的描述符+1,需要判断是否可读的描述
     符集合,可写的,异常的描述符的集合,select的等待的时间
     第5个参数为0时,将立刻超时,从而有效地轮询集合中的所有的文件描述符
     为NULL时,将会阻塞,直到其中一个描述符就绪。在这里,timeout设为2秒.
     按协议,如果非阻塞的connect()连接成功,sockfd将可写,如果出错,
     则可读可写,如果没连上,select返回0, 所以这里判断sockfd是否可写
   */

   status=select(sockfd+1,NULL,& mask,NULL,& timeout);
   switch(status)
    {
      case -1:
       fprintf(f,"select( ) error at %s\t%d\n",serverName,port[i]);
       fclose(f);
       close(sockfd);
       exit(-1);
      case 0:                     /* 连接超时则继续扫下一个IP地址 */
       close(sockfd);
       break;
      default:                    /* 连上了 */
       if(  FD_ISSET(sockfd,& mask) )
        {
         int errorCode=1;
         errorLen=sizeof(errorCode);
         /*判断是否出错。如果出错,内核将置一个内核变量so_error.
           通过getsockopt(..SO_ERROR)将获得这个关于socket的未判定
           的错误值(然后so_error将清0)
         */

         getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&errorCode,&errorLen);
         if(errorCode==0)  //errorCode=0表明无错,proxy可用
         fprintf(f,"%s\t%d\n",serverName,port[i]);  //纪录IP和port
        }
}
     close(sockfd);
fflush(f);
  }

/*最后我们看一下kill信号发出时的处理函数,就是纪录kill时搜索到的IP,
  以便下次继续搜索*/
void terminate(void)                    /* 处理SIGTERM信号 */
 {
  fprintf(f,"Program killed at %s\n",serverName);
  fclose(f);
   exit(0);
 }


--




                                                   V2 Studio Xh2000




※ 修改:.xh 于 Nov  7 14:21:47 修改本文.[FROM: 202.38.248.62]
※ 来源:.华南网木棉站 bbs.gznet.edu.cn.[FROM: 202.38.248.62]
--
※ 转寄:.华南网木棉站 bbs.gznet.edu.cn.[FROM: 深大荔园晨风转站]
--
※ 转载:·BBS 荔园晨风站 bbs.szu.edu.cn·[FROM: 192.168.28.248]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店