荔园在线
荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀
[回到开始]
[上一篇][下一篇]
发信人: 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软件 网络书店