轉自INTERNET
第七章
端口掃瞄
一個端口就是一個潛在的通信通道,也就是一個入侵通道。對目標計算機進行端口掃瞄,能得到許多有用的信息。進行掃瞄的方法很多,可以是手工進行掃瞄,也可以用端口掃瞄軟件進行。
在手工進行掃瞄時,需要熟悉各種命令。對命令執行後的輸出進行分析。用掃瞄軟件進行掃瞄時,許多掃瞄器軟件都有分析數據的功能。
通過端口掃瞄,可以得到許多有用的信息,從而發現系統的安全漏洞。
下面首先介紹幾個常用網絡命令,對端口掃瞄原理進行介紹,然後提供一個簡單的掃瞄程序。
第一節 幾個常用網絡相關命令
Ping命令經常用來對TCP/IP網絡進行診斷。通過目標計算機發送一個數據包,讓它將這個數據包反送回來,如果返回的數據包和發送的數據包一致,那就是說你的PING命令成功了。通過這樣對返回的數據進行分析,就能判斷計算機是否開著,或者這個數據包從發送到返回需要多少時間。
Ping命令的基本格式:
ping hostname
其中hostname是目標計算機的地址。Ping還有許多高級使用,下面就是一個例子。
C:> ping -f hostname
這條命令給目標機器發送大量的數據,從而使目標計算機忙於回應。在Windows 95的計算機上,使用下面的方法:
c:-l 65510 saddam_hussein's.computer.mil
這樣做了之後,目標計算機有可能會掛起來,或從新啟動。由於 -l 65510 產生一個巨大的數據包。由於要求返回一個同樣的數據包,會使目標計算機反應不過來。
在Linux計算機上,可以編寫一個程序來實現上述方法。
#include < stdio.h>
#include < sys/types.h>
#include < sys/socket.h>
#include < netdb.h>
#include < netinet/in.h>
#include < netinet/in_systm.h>
#include < netinet/ip.h>
#include < netinet/ip_icmp.h>
/*
* If your kernel doesn't muck with raw packets, #define REALLY_RAW.
* This is probably only Linux.
*/
#ifdef REALLY_RAW
#define FIX(x) htons(x)
#else
#define FIX(x) (x)
#endif
int
main(int argc, char **argv)
{
int s;
char buf[1500];
struct ip *ip = (struct ip *)buf;
struct icmp *icmp = (struct icmp *)(ip + 1);
struct hostent *hp;
struct sockaddr_in dst;
int offset;
int on = 1;
bzero(buf, sizeof buf);
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_IP)) < 0) {
perror("socket");
exit(1);
}
if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
perror("IP_HDRINCL");
exit(1);
}
if (argc != 2) {
fprintf(stderr, "usage: %s hostname", argv[0]);
exit(1);
}
if ((hp = gethostbyname(argv[1])) == NULL) {
if ((ip->ip_dst.s_addr = inet_addr(argv[1])) == -1) {
fprintf(stderr, "%s: unknown host", argv[1]);
}
} else {
bcopy(hp->h_addr_list[0], &ip->ip_dst.s_addr, hp->h_length);
}
printf("Sending to %s", inet_ntoa(ip->ip_dst));
ip->ip_v = 4;
ip->ip_hl = sizeof *ip >> 2;
ip->ip_tos = 0;
ip->ip_len = FIX(sizeof buf);
ip->ip_id = htons(4321);
ip->ip_off = FIX(0);
ip->ip_ttl = 255;
ip->ip_p = 1;
ip->ip_sum = 0; /* kernel fills in */
ip->ip_src.s_addr = 0; /* kernel fills in */
dst.sin_addr = ip->ip_dst;
dst.sin_family = AF_INET;
icmp->icmp_type = ICMP_ECHO;
icmp->icmp_code = 0;
icmp->icmp_cksum = htons(~(ICMP_ECHO < < 8));
/* the checksum of all 0's is easy to compute */
for (offset = 0; offset < 65536; offset += (sizeof buf - sizeof *ip)) {
ip->ip_off = FIX(offset >> 3);
if (offset < 65120)
ip->ip_off |= FIX(IP_MF);
else
ip->ip_len = FIX(418); /* make total 65538 */
if (sendto(s, buf, sizeof buf, 0, (struct sockaddr *)&dst,
sizeof dst) < 0) {
fprintf(stderr, "offset %d: ", offset);
perror("sendto");
}
if (offset == 0) {
icmp->icmp_type = 0;
icmp->icmp_code = 0;
icmp->icmp_cksum = 0;
}
}
}
Tracert命令用來跟蹤一個消息從一台計算機到另一台計算機所走的路徑,比方說從你的計算機走到浙江信息超市。在DOS窗口下,命令如下:
C:>tracert 202.96.102.4
Tracing route to 202.96.102.4 over a maximum of 30 hops
1 84 ms 82 ms 95 ms 202.96.101.57 2 100 ms 100 ms 95 ms
0fa1.1-rtr1-a-hz1.zj.CN.NET [202.96.101.33] 3 95 ms 90 ms 100 ms 202.101.165.1 4
90 ms 90 ms 90 ms 202.107.197.98 5 95 ms 90 ms 99 ms 202.96.102.4 6 90 ms 95 ms
100 ms 202.96.102.4
Trace complete.
上面的這些輸出代表什麼意思?左邊的數字是該路由通過的計算機數目。"150
ms"是指向那台計算機發送消息的往返時間,單位是微秒。由於每條消息每次的來回的時間不一樣,tracert將顯示來回時間三次。"*"表示來回時間太長,tracert將這個時間「忘掉了」。在時間信息到來後,計算機的名字信息也到了。開始是一種便於人們閱讀的格式,
接著是數字格式。
C:>tracert 152.163.199.56
Tracing route to dns-aol.ANS.NET [198.83.210.28]over a maximum of 30 hops:
1 124 ms 106 ms 105 ms 202.96.101.57 2 95 ms 95 ms 90 ms
0fa1.1-rtr1-a-hz1.zj.CN.NET [202.96.101.33] 3 100 ms 90 ms 100 ms 202.101.165.1
4 90 ms 95 ms 95 ms 202.97.18.241 5 105 ms 105 ms 100 ms 202.97.18.93 6 100 ms
99 ms 100 ms 202.97.10.37 7 135 ms 98 ms 100 ms 202.97.9.78 8 760 ms 725 ms 768
ms gip-ftworth-4-serial8-3.gip.net [204.59.178.53] 9 730 ms 750 ms 715 ms
gip-ftworth-4-serial8-3.gip.net [204.59.178.53] 10 750 ms 785 ms 772 ms
144.232.11.9 11 740 ms 800 ms 735 ms sl-bb11-pen-2-0.sprintlink.NET
[144.232.8.158] 12 790 ms 800 ms 735 ms sl-nap2-pen-4-0-0.sprintlink.net
[144.232.5.66] 13 770 ms 800 ms 800 ms p219.t3.ans.net [192.157.69.13] 14 775 ms
820 ms 780 ms h14-1.t60-6.Reston.t3.ANS.NET [140.223.17.18] 15 780 ms 800 ms 800
ms h11-1.t60-2.Reston.t3.ANS.NET [140.223.25.34] 16 790 ms 795 ms 800 ms
h14-1.t104-0.Atlanta.t3.ANS.NET [140.223.65.18] 17 *
h14-1.t104-0.Atlanta.t3.ANS.NET [140.223.65.18] reports: Destination host
unreachable.
Trace complete.
rusers和finger
這兩個都是Unix命令。通過這兩個命令,你能收集到目標計算機上的有關用戶的消息。
使用rusers命令,產生的結果如下示意:
gajake snark.wizard.com:ttyp1 Nov 13 15:42 7:30 (remote)
root snark.wizard.com:ttyp2 Nov 13 14:57 7:21 (remote)
robo snark.wizard.com:ttyp3 Nov 15 01:04 01 (remote)
angel111 snark.wizard.com:ttyp4 Nov14 23:09 (remote)
pippen snark.wizard.com:ttyp6 Nov 14 15:05 (remote)
root snark.wizard.com:ttyp5 Nov 13 16:03 7:52 (remote)
gajake snark.wizard.com:ttyp7 Nov 14 20:20 2:59 (remote)
dafr snark.wizard.com:ttyp15Nov 3 20:09 4:55 (remote)
dafr snark.wizard.com:ttyp1 Nov 14 06:12 19:12 (remote)
dafr snark.wizard.com:ttyp19Nov 14 06:12 19:02 (remote)
最左邊的是通過遠程登錄的用戶名。還包括上次登錄時間,使用的SHELL類型等等信息。
使用finger可以產生類似下面的結果:
user S00 PPP ppp-122-pm1.wiza Thu Nov 14 21:29:30 - still logged in
user S15 PPP ppp-119-pm1.wiza Thu Nov 14 22:16:35 - still logged in
user S04 PPP ppp-121-pm1.wiza Fri Nov 15 00:03:22 - still logged in
user S03 PPP ppp-112-pm1.wiza Thu Nov 14 22:20:23 - still logged in
user S26 PPP ppp-124-pm1.wiza Fri Nov 15 01:26:49 - still logged in
user S25 PPP ppp-102-pm1.wiza Thu Nov 14 23:18:00 - still logged in
user S17 PPP ppp-115-pm1.wiza Thu Nov 14 07:45:00 - still logged in
user S-1 0.0.0.0 Sat Aug 10 15:50:03 - still logged in
user S23 PPP ppp-103-pm1.wiza Fri Nov 15 00:13:53 - still logged in
user S12 PPP ppp-111-pm1.wiza Wed Nov 13 16:58:12 - still logged in
這個命令能顯示用戶的狀態。該命令是建立在客戶/服務模型之上的。用戶通過客戶端軟件向服務器請求信息,然後解釋這些信息,提供給用戶。在服務器上一般運行一個叫做fingerd的程序,根據服務器的機器的配置,能向客戶提供某些信息。如果考慮到保護這些個人信息的話,有可能許多服務器不提供這個服務,或者只提供一些無關的信息。
host命令
host是一個Unix命令,它的功能和標準的nslookup查詢一樣。唯一的區別是host命令比較容易理解。host命令的危險性相當大,下面舉個使用實例,演示一次對bu.edu的host查詢。
host -l -v -t any bu.edu
這個命令的執行結果所得到的信息十分多,包括操作系統,機器和網絡的很多數據。先看一下基本信息: Found 1 addresses for BU.EDU
Found 1 addresses for RS0.INTERNIC.NET
Found 1 addresses for SOFTWARE.BU.EDU
Found 5 addresses for RS.INTERNIC.NET
Found 1 addresses for NSEGC.BU.EDU
Trying 128.197.27.7
bu.edu 86400 IN SOA BU.EDU HOSTMASTER.BU.EDU(
961112121 ;serial (version)
900 ;refresh period
900 ;retry refresh this often
604800 ;expiration period
86400 ;minimum TTL
)
bu.edu 86400 IN NS SOFTWARE.BU.EDU
bu.edu 86400 IN NS RS.INTERNIC.NET
bu.edu 86400 IN NS NSEGC.BU.EDU
bu.edu 86400 IN A 128.197.27.7
這些本身並沒有危險,只是一些機器和它們的DNS服務器。這些信息可以用WHOIS或在註冊域名的站點中檢索到。但看看下面幾行信息:
bu.edu 86400 IN HINFO SUN-SPARCSTATION-10/41 UNIX
PPP-77-25.bu.edu 86400 IN A 128.197.7.237
PPP-77-25.bu.edu 86400 IN HINFO PPP-HOST PPP-SW
PPP-77-26.bu.edu 86400 IN A 128.197.7.238
PPP-77-26.bu.edu 86400 IN HINFO PPP-HOST PPP-SW
ODIE.bu.edu 86400 IN A 128.197.10.52
ODIE.bu.edu 86400 IN MX 10 CS.BU.EDU
ODIE.bu.edu 86400 IN HINFO DEC-ALPHA-3000/300LX OSF1
從這裡,我們馬上就發現一台EDC Alpha運行的是OSF1操作系統。在看看:
STRAUSS.bu.edu 86400 IN HINFO PC-PENTIUM DOS/WINDOWS
BURULLUS.bu.edu 86400 IN HINFO SUN-3/50 UNIX (Ouch)
GEORGETOWN.bu.edu 86400 IN HINFO MACINTOSH MAC-OS
CHEEZWIZ.bu.edu 86400 IN HINFO SGI-INDIGO-2 UNIX
POLLUX.bu.edu 86400 IN HINFO SUN-4/20-SPARCSTATION-SLC UNIX
SFA109-PC201.bu.edu 86400 IN HINFO PC MS-DOS/WINDOWS
UH-PC002-CT.bu.edu 86400 IN HINFO PC-CLONE MS-DOS
SOFTWARE.bu.edu 86400 IN HINFO SUN-SPARCSTATION-10/30 UNIX
CABMAC.bu.edu 86400 IN HINFO MACINTOSH MAC-OS
VIDUAL.bu.edu 86400 IN HINFO SGI-INDY IRIX
KIOSK-GB.bu.edu 86400 IN HINFO GATORBOX GATORWARE
CLARINET.bu.edu 86400 IN HINFO VISUAL-X-19-TURBO X-SERVER
DUNCAN.bu.edu 86400 IN HINFO DEC-ALPHA-3000/400 OSF1
MILHOUSE.bu.edu 86400 IN HINFO VAXSTATION-II/GPX UNIX
PSY81-PC150.bu.edu 86400 IN HINFO PC WINDOWS-95
BUPHYC.bu.edu 86400 IN HINFO VAX-4000/300 OpenVMS
可見,任何人都能通過在命令行裡鍵入一個命令,就能收集到一個域裡的所有計算機的重要信息。而且只化了3秒時間。
我們利用上述有用的網絡命令,可以收集到許多有用的信息,比方一個域裡的名字服務器的地址,一台計算機上的用戶名,一台服務器上正在運行什麼服務,這個服務是哪個軟件提供的,計算機上運行的是什麼操作系統。
如果你知道目標計算機上運行的操作系統和服務應用程序後,就能利用已經發現的他們的漏洞來進行攻擊。如果目標計算機的網絡管理員沒有對這些漏洞及時修補的話,入侵者能輕而易舉的闖入該系統,獲得管理員權限,並留下後門。
如果入侵者得到目標計算機上的用戶名後,能使用口令破解軟件,多次試圖登錄目標計算機。經過嘗試後,就有可能進入目標計算機。得到了用戶名,就等於得到了一半的進入權限,剩下的只是使用軟件進行攻擊而已。
第二節 端口掃瞄器源程序
什麼是掃瞄器
掃瞄器是一種自動檢測遠程或本地主機安全性弱點的程序,通過使用掃瞄器你可一不留痕跡的發現遠程服務器的各種TCP端口的分配及提供的服務和它們的軟件版本!這就能讓我們間接的或直觀的瞭解到遠程主機所存在的安全問題。
工作原理
掃瞄器通過選用遠程TCP/IP不同的端口的服務,並記錄目標給予的回答,通過這種方法,可以搜集到很多關於目標主機的各種有用的信息(比如:是否能用匿名登陸!是否有可寫的FTP目錄,是否能用TELNET,HTTPD是用ROOT還是nobady在跑!)
掃瞄器能幹什麼?
掃瞄器並不是一個直接的攻擊網絡漏洞的程序,它僅僅能幫助我們發現目標機的某些內在的弱點。一個好的掃瞄器能對它得到的數據進行分析,幫助我們查找目標主機的漏洞。但它不會提供進入一個系統的詳細步驟。
掃瞄器應該有三項功能:發現一個主機或網絡的能力;一旦發現一台主機,有發現什麼服務正運行在這台主機上的能力;通過測試這些服務,發現漏洞的能力。 編寫掃瞄器程序必須要很多TCP/IP程序編寫和C,
Perl和或SHELL語言的知識。需要一些Socket編程的背景,一種在開發客戶/服務應用程序的方法。開發一個掃瞄器是一個雄心勃勃的項目,通常能使程序員感到很滿意。
下面對常用的端口掃瞄技術做一個介紹。
TCP connect() 掃瞄
這是最基本的TCP掃瞄。操作系統提供的connect()系統調用,用來與每一個感興趣的目標計算機的端口進行連接。如果端口處於偵聽狀態,那麼connect()就能成功。否則,這個端口是不能用的,即沒有提供服務。這個技術的一個最大的優點是,你不需要任何權限。系統中的任何用戶都有權利使用這個調用。另一個好處就是速度。如果對每個目標端口以線性的方式,使用單獨的connect()調用,那麼將會花費相當長的時間,你可以通過同時打開多個套接字,從而加速掃瞄。使用非阻塞I/O允許你設置一個低的時間用盡週期,同時觀察多個套接字。但這種方法的缺點是很容易被發覺,並且被過濾掉。目標計算機的logs文件會顯示一連串的連接和連接是出錯的服務消息,並且能很快的使它關閉。
TCP SYN掃瞄
這種技術通常認為是「半開放」掃瞄,這是因為掃瞄程序不必要打開一個完全的TCP連接。掃瞄程序發送的是一個SYN數據包,好像準備打開一個實際的連接並等待反應一樣(參考TCP的三次握手建立一個TCP連接的過程)。一個SYN|ACK的返回信息表示端口處於偵聽狀態。一個RST返回,表示端口沒有處於偵聽態。如果收到一個SYN|ACK,則掃瞄程序必須再發送一個RST信號,來關閉這個連接過程。這種掃瞄技術的優點在於一般不會在目標計算機上留下記錄。但這種方法的一個缺點是,必須要有root權限才能建立自己的SYN數據包。
TCP FIN 掃瞄
有的時候有可能SYN掃瞄都不夠秘密。一些防火牆和包過濾器會對一些指定的端口進行監視,有的程序能檢測到這些掃瞄。相反,FIN數據包可能會沒有任何麻煩的通過。這種掃瞄方法的思想是關閉的端口會用適當的RST來回復FIN數據包。另一方面,打開的端口會忽略對FIN數據包的回復。這種方法和系統的實現有一定的關係。有的系統不管端口是否打開,都回復RST,這樣,這種掃瞄方法就不適用了。並且這種方法在區分Unix和NT時,是十分有用的。
IP段掃瞄
這種不能算是新方法,只是其它技術的變化。它並不是直接發送TCP探測數據包,是將數據包分成兩個較小的IP段。這樣就將一個TCP頭分成好幾個數據包,從而過濾器就很難探測到。但必須小心。一些程序在處理這些小數據包時會有些麻煩。
TCP 反向 ident掃瞄
ident
協議允許(rfc1413)看到通過TCP連接的任何進程的擁有者的用戶名,即使這個連接不是由這個進程開始的。因此你能,舉個例子,連接到http端口,然後用identd來發現服務器是否正在以root權限運行。這種方法只能在和目標端口建立了一個完整的TCP連接後才能看到。
FTP 返回攻擊
FTP協議的一個有趣的特點是它支持代理(proxy)FTP連接。即入侵者可以從自己的計算機a.com和目標主機target.com的FTP
server-PI(協議解釋器)連接,建立一個控制通信連接。然後,請求這個server-PI激活一個有效的server-DTP(數據傳輸進程)來給Internet上任何地方發送文件。對於一個User-DTP,這是個推測,儘管RFC明確地定義請求一個服務器發送文件到另一個服務器是可以的。但現在這個方法好像不行了。這個協議的缺點是「能用來發送不能跟蹤的郵件和新聞,給許多服務器造成打擊,用盡磁盤,企圖越過防火牆」。
我們利用這個的目的是從一個代理的FTP服務器來掃瞄TCP端口。這樣,你能在一個防火牆後面連接到一個FTP服務器,然後掃瞄端口(這些原來有可能被阻塞)。如果FTP服務器允許從一個目錄讀寫數據,你就能發送任意的數據到發現的打開的端口。
對於端口掃瞄,這個技術是使用PORT命令來表示被動的User
DTP正在目標計算機上的某個端口偵聽。然後入侵者試圖用LIST命令列出當前目錄,結果通過Server-DTP發送出去。如果目標主機正在某個端口偵聽,傳輸就會成功(產生一個150或226的回應)。否則,會出現"425
Can't build data connection: Connection
refused."。然後,使用另一個PORT命令,嘗試目標計算機上的下一個端口。這種方法的優點很明顯,難以跟蹤,能穿過防火牆。主要缺點是速度很慢,有的FTP服務器最終能得到一些線索,關閉代理功能。
這種方法能成功的情景:
220 xxxxxxx.com FTP server (Version wu-2.4(3) Wed Dec 14 ...) ready.
220 xxx.xxx.xxx.edu FTP server ready.
220 xx.Telcom.xxxx.EDU FTP server (Version wu-2.4(3) Tue Jun 11 ...) ready.
220 lem FTP server (SunOS 4.1) ready.
220 xxx.xxx.es FTP server (Version wu-2.4(11) Sat Apr 27 ...) ready.
220 elios FTP server (SunOS 4.1) ready
這種方法不能成功的情景:
220 wcarchive.cdrom.com FTP server (Version DG-2.0.39 Sun May 4 ...) ready.
220 xxx.xx.xxxxx.EDU Version wu-2.4.2-academ[BETA-12](1) Fri Feb 7
220 ftp Microsoft FTP Service (Version 3.0).
220 xxx FTP server (Version wu-2.4.2-academ[BETA-11](1) Tue Sep 3 ...) ready.
220 xxx.unc.edu FTP server (Version wu-2.4.2-academ[BETA-13](6) ...) ready.
UDP ICMP端口不能到達掃瞄
這種方法與上面幾種方法的不同之處在於使用的是UDP協議。由於這個協議很簡單,所以掃瞄變得相對比較困難。這是由於打開的端口對掃瞄探測並不發送一個確認,關閉的端口也並不需要發送一個錯誤數據包。幸運的是,許多主機在你向一個未打開的UDP端口發送一個數據包時,會返回一個ICMP_PORT_UNREACH錯誤。這樣你就能發現哪個端口是關閉的。UDP和ICMP錯誤都不保證能到達,因此這種掃瞄器必須還實現在一個包看上去是丟失的時候能重新傳輸。這種掃瞄方法是很慢的,因為RFC對ICMP錯誤消息的產生速率做了規定。同樣,這種掃瞄方法需要具有root權限。
UDP recvfrom()和write() 掃瞄
當非root用戶不能直接讀到端口不能到達錯誤時,Linux能間接地在它們到達時通知用戶。比如,對一個關閉的端口的第二個write()調用將失敗。在非阻塞的UDP套接字上調用recvfrom()時,如果ICMP出錯還沒有到達時回返回EAGAIN-重試。如果ICMP到達時,返回ECONNREFUSED-連接被拒絕。這就是用來查看端口是否打開的技術。
ICMP echo掃瞄
這並不是真正意義上的掃瞄。但有時通過ping,在判斷在一個網絡上主機是否開機時非常有用。
前面幾章基礎知識介紹已經為這裡的編程作了準備了。
下面是一個端口掃瞄器的源程序,功能相當的簡單,一個典型的TCP connect()掃瞄。沒有對返回的數據進行分析。
#include < stdio.h>
#include < sys/socket.h>
#include < netinet/in.h>
#include < errno.h>
#include < netdb.h>
#include < signal.h>
int main(int argc, char **argv)
{
int probeport = 0;
struct hostent *host;
int err, i, net;
struct sockaddr_in sa;
if (argc != 2) {
printf("用法: %s hostname", argv[0]);
exit(1);
}
for (i = 1; i < 1024; i++) { //這裡有點不是很好,可以將主機地址放在循環外
strncpy((char *)&sa, "", sizeof sa);
sa.sin_family = AF_INET;
if (isdigit(*argv[1]))
sa.sin_addr.s_addr = inet_addr(argv[1]);
else if ((host = gethostbyname(argv[1])) != 0)
strncpy((char *)&sa.sin_addr, (char *)host->h_addr, sizeof sa.sin_addr);
else {
herror(argv[1]);
exit(2);
}
sa.sin_port = htons(i);
net = socket(AF_INET, SOCK_STREAM, 0);
if (net < 0) {
perror("");
exit(2);
}
err = connect(net, (struct sockaddr *) &sa, sizeof sa);
if (err < 0) {
printf("%s %-5d %s", argv[1], i, strerror(errno));
fflush(stdout);
} else {
printf("%s %-5d accepted. ", argv[1], i);
if (shutdown(net, 2) < 0) {
perror("");
exit(2);
}
}
close(net);
}
printf(" ");
fflush(stdout);
return (0);
}
下面這個又是一個端口器:
#include < stdio.h>
#include < sys/types.h>
#include < sys/socket.h>
#include "netdb.h"
struct hostent *gethostbyaddr();
void bad_addr();
main(argc, argv)
int argc;
char *argv[];
{
char addr[4];
int i, j,
a0, a1, a2, a3,
c,
classB, classC, single, hex;
char *fmt = "%d.%d.%d";
char **ptr;
struct hostent *host;
extern char *optarg;
classB = classC = single = hex = 0;
while((c = getopt(argc,argv,"bcsx")) != EOF) {
switch(c) {
case 'b':
classB++;
break;
case 'c':
classC++;
break;
case 's':
single++;
break;
case 'x':
hex++;
break;
}
}
if(classB == 0 && classC == 0 && single == 0) {
fprintf(stderr, "usage: %s [-b||-c||-s] [-x] xxx.xxx[.xxx[.xxx]]", argv[0]);
exit(1);
}
if(classB)
if(hex) {
fmt = "%x.%x";
sscanf(argv[3], fmt, &a0, &a1);
} else {
fmt = "%d.%d";
sscanf(argv[2], fmt, &a0, &a1);
}
else if(classC)
if(hex) {
fmt = "%x.%x.%x";
sscanf(argv[3], fmt, &a0, &a1, &a2);
} else {
fmt = "%d.%d.%d";
sscanf(argv[2], fmt, &a0, &a1, &a2);
}
else if(single)
if(hex) {
fmt = "%x.%x.%x.%x";
sscanf(argv[3], fmt, &a0, &a1, &a2, &a3);
} else {
fmt = "%d.%d.%d.%d";
sscanf(argv[2], fmt, &a0, &a1, &a2, &a3);
}
sscanf(argv[1], fmt, &a0, &a1, &a2);
addr[0] = (unsigned char)a0;
addr[1] = (unsigned char)a1;
if(a0>255||a0< 0)
bad_addr(a0);
if(a1>255||a1< 0)
bad_addr(a1);
if(classB) {
if(hex)
printf("Converting address from hex. (%x.%x)", a0, a1);
printf("Scanning Class B network %d.%d...", a0, a1);
while(j!=256) {
a2=j;
addr[2] = (unsigned char)a2;
jmpC:
if(classC)
if(hex)
printf("Converting address from hex. (%x.%x.%x)", a0, a1, a2);
printf("Scanning Class C network %d.%d.%d...", a0, a1, a2);
while(i!=256) {
a3=i;
addr[3] = (unsigned char)a3;
jmpS:
if ((host = gethostbyaddr(addr, 4, AF_INET)) != NULL) {
printf("%d.%d.%d.%d => %s", a0, a1, a2, a3, host->h_name);
ptr = host->h_aliases;
while (*ptr != NULL) {
printf("%d.%d.%d.%d => %s (alias)", a0, a1, a2, a3, *ptr);
ptr++;
}
}
if(single)
exit(0);
i++;
}
if(classC)
exit(0);
j++;
}
} else if(classC) {
addr[2] = (unsigned char)a2;
if(a2>255||a2< 0)
bad_addr(a2);
goto jmpC;
} else if(single) {
addr[2] = (unsigned char)a2;
addr[3] = (unsigned char)a3;
if(a2>255||a2< 0)
bad_addr(a2);
if(a3>255||a3< 0)
bad_addr(a3);
goto jmpS;
}
exit(0);
}
void
bad_addr(addr)
int *addr;
{
printf("Value %d is not valid.", addr);
exit(0);
}