Chinaunix首页 | 论坛 | 博客
  • 博客访问: 444620
  • 博文数量: 403
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: -70
  • 用 户 组: 普通用户
  • 注册时间: 2016-09-05 12:45
文章分类

全部博文(403)

文章存档

2014年(3)

2013年(1)

2012年(3)

2011年(21)

2010年(13)

2009年(64)

2008年(9)

2007年(36)

2006年(253)

分类:

2006-09-15 16:55:18

谢翰
 
 
 
 

一、开发目的:

以尽量快的速度,发现internet上存在ftp服务的主机地址。

 

二、实现方法:

不同于以往的调用connect,而是自己实现了TCPIP协议的一些内容,向目标主机发送TCP SYN连接信号的IP包,一边监听发往本机IP包,从中找出那些对本机发出的SYN连接信号的响应,包括SYNRST,分别为接收连接和拒绝连接;还要找出ICMP不可到达包,表示目标地址不可达。对于地址范围内的IP地址,如果对SYN信号没有响应,应该再发送几次,因为IP包是可能在网络中丢失的。

 

三、数据组织

模块内部保存了用一棵红黑树保存了那些已经有响应的IP地址和端口号,下一轮中就无需再向其发送SYN连接信号,而且可以保证同一个IP地址和端口不会被重复返回。扫描完全国地址(1个端口)这棵红黑树可能要占用100M左右的内存空间。

 

四、模块接口使用说明

#ifndef _TCP_SCAN_H_

#define _TCP_SCAN_H_

 

#include

 

#define TS_ACCEPTED         1

#define TS_REFUSED          2

#define TS_UNREACHABLE      3

 

struct addrseg

{

    int as_family;

    void *as_address;

    int as_bits;

};

 

typedef int (*scan_info_t)(const struct sockaddr *, socklen_t, int, void *);

 

#ifdef __cplusplus

extern "C"

{

#endif

 

int tcp_scan(const struct addrseg *addrscope, const unsigned short *ports,

             unsigned int ifindex, const char *ifname, int resetuid,

             scan_info_t info, void *arg);

 

#ifdef __cplusplus

}

#endif

 

#endif

 

以上是tcp_scan.h文件。可以看到,这个模块只有一个函数tcp_scan。现在解释一下函数的接口。

int tcp_scan(const struct addrseg *addrscope, const unsigned short *ports,

          unsigned int ifindex, const char *ifname, int resetuid,

          scan_info_t info, void *arg);

 

const struct addrseg *addrscope:

IP地址范围,包括多个地址段,每个地址段是一个struct addrseg结构。struct addrseg的定义如下:

struct addrseg

{

    int as_family;

    void *as_address;

    int as_bits;

};

成员as_family表示地址类型,可以是AF_INETAF_INET6,分别表示IPV4地址和IPV6地址。

成员as_address指向一个IPV4地址(struct in_addr)或IPV6地址(struct in6_addr)。

成员as_bits表示地址的有效位数,例如扫描61.*.*.*,则有效位数为8

tcp_scan的第一个参数addrscope实际上就是struct addrseg型的数组,数组中的最后一addrsegas_family域必须为AF_UNSPEC,表示数组的结束。

 

const unsigned short *ports:

要扫描的端口集合,也就是unsigned short型的数组,数组最后个一元素必须为0

 

unsigned int ifindex:

由于我不想自己去访问路由表(偷懒了^_^,所以要求用户指定一个网卡作为通信设备。在单网卡的机器上关系不大,但多网卡时就只能使用一块网卡。

ifindex表示网络设备号,如果你不清楚则这个参数置为0就可以了。

 

const char *ifname:

    网络设备名。这个参数仅当ifindex0时有效,表示用于通信的网络设备名。如果你的机器只有一块网卡,其设备名通常是”eth0”

 

int resetuid:

tcp­_scan运行时需要root权限,resetuid表示是否在无需root权限时把进程的有效权限置为运行者的权限。如果你不知道我说什么,请置为1

scan_info_t info:

scan_info_t的定义如是下:

typedef int (*scan_info_t)(const struct sockaddr *, socklen_t, int, void *);

这是一个回调函数,当tcp_scan发现一个IP地址的一个端口号可连接时,通过这个回调函数返回。如果你不知道什么是回调函数,你就需要好好补一补C语言了。我比较喜欢用回调,所以以后我写的模块里可能经常看到。

scan_info_t的第一个参数const struct sockaddr *一个有响应的IP地址和端口号,封装在一个struct sockaddr结构里;第二个参数socklen_t,前面那个结构的长度,应该很好理解;第三个参数为状态,可能为TS_ACCEPTEDTS_REFUSEDTS_UNREACHABLE,在3个宏在tcp_scan.h里已定义,分别表示地址接受连接,拒绝连接和地址不可达;最后个一参数void *,回调函数的惯例,用户自定义。

 

void *arg:

原封不动作为回调函数info的最后一个参数。

 

tcp_scan返回负值表示失败,失败原因查看errno;非负值表示成功。另外,回调函数info返回负值的话可让tcp_scan立即以失败返回。

 

五、例示程序

int info(const struct sockaddr *sockaddr, socklen_t addrlen,

         int state, void *arg)

{

    struct sockaddr_in *sin = (struct sockaddr_in *)sockaddr;

 

    printf("%s:%d ", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));

    switch (state)

    {

    case TS_ACCEPTED:

        printf("Connection accepted\n");

        break;

    case TS_REFUSED:

        printf("Connection refused\n");

        break;

    case TS_UNREACHABLE:

        printf("Destination unreachable\n");

        break;

    }

 

    return 0;

}

 

int main(void)

{

    struct addrseg addrseg[10];

unsigned short port[2] = { 21, 0 };

 

addrseg[0].as_family = AF_INET;

    addrseg[0].as_address = malloc(sizeof (struct in_addr));

    inet_pton(AF_INET, "162.105.0.0", addrseg[0].as_address);

    addrseg[0].as_bits = 16;

 

    addrseg[1].as_family = AF_INET;

    addrseg[1].as_address = malloc(sizeof (struct in_addr));

    inet_pton(AF_INET, "166.111.0.0", addrseg[1].as_address);

    addrseg[1].as_bits = 16;

 

    addrseg[2].as_family = AF_UNSPEC;

 

    tcp_scan(addrseg, port, 0, "eth0", 1, info, NULL);

}

 

以上是扫描北大清华所有IP地址的ftp端口的示使,由于是示例,没有注意代码风格,见谅。

 

六、测试结果

扫描了61.*.*.*202.*.*.*210.*.*.*212.*.*.*218.*.*.*162.105.*.*166.111.*.*。一次扫描用时约70分钟,由于目前把扫描次数定为3,所以tcp_scan正常返回大约需70*3分钟。测试环境为P4 1.4G256M内存。以上IP地址范围大约有只有一半是国内IP地址。

 

七、说明

目前本模块还有少量功能未完成,IPV6地址扫描还差一个函数,因此目前还不能扫描IPV6地址。此外还必须加入MSL,也就是说扫描完成后还必须再等一段时间,可能还会收到响应。根椐RFC的建议MSL2分钟,BSD系统一般定为30秒。那么,那怕只扫描一个IP地址也必须等待2*MSL=1分钟-4分钟。但这对我们的关系不大,对于大范围的扫描14分钟是完全可以忽略的。MSL功能也很快可以加上。此外,虽然本模块提供了多端口扫描功能,但目前tcp_scan的空间复杂性是随端口数线性增长的(可以进行优代),所以最好不要在大IP地址范围内(如全国)扫描多个端口,除非你的内存吃得消;代替方法是调用多次tcp_scan,每次扫描一个端口。

本模块中设置了一些隐藏接口(为了好玩),如果你熟读源代码还可以发现更灵活的使用tcp_scan

本模块中红-黑树的实现取自Linux内核源代码,此外还有一个in_cksum函数,取自unp的示例代码。除此之外不存在其实非自编代码。

 

 

注意:进行tcp_scan必须有root权限!

阅读(2139) | 评论(0) | 转发(0) |
0

上一篇:BT协议集合

下一篇:TCP端口扫描模块

给主人留下些什么吧!~~