Linux socket プログラミング/利用可能ポート検査プログラム目次†概要†いわゆるポートスキャンと言われるプログラムです。他人のマシンのポートをスキャンすると、場合によってはハッキングだと思われ て管理者から怒られかねませんので、ここでは自分のマシン(Linux)のポートを調査するプログラムを紹介します。 プログラムのイメージ†このプログラムで使われる関数のイメージを以下に示します。
プログラム†1 /* 2 * Scan local tcp ports 3 * selfck.c 4 * cc selfck.c -lnsl -o selfck 5 */ 6 7 #include <stdio.h> 8 #include <sys/socket.h> 9 #include <netinet/in.h> 10 #include <errno.h> 11 #include <netdb.h> 12 #include <signal.h> 13 #include <string.h> 14 #include <sys/utsname.h> 15 #include <stdlib.h> 16 17 #define MAXPORT 1024 18 #define MAXSVCNAME 15 19 20 int main() 21 { 22 int net, portnum; 23 struct hostent *host; 24 struct servent *svc_ent; 25 struct sockaddr_in sa; 26 struct utsname h_name[1]; 27 char svc_name[MAXSVCNAME]; 28 29 memset(&sa, 0, sizeof(struct sockaddr_in)); 30 if ( (uname(h_name)) < 0 ){ 31 perror("uname"); 32 exit(1); 33 } 34 if ((host = gethostbyname(h_name->nodename)) == NULL){ 35 perror("gethostbyname()"); 36 exit(1); 37 } 38 memcpy(&sa.sin_addr, host->h_addr, sizeof(struct in_addr)); 39 sa.sin_family = AF_INET; 40 41 for (portnum = 1; portnum <= MAXPORT ; portnum++) { 42 sa.sin_port = htons(portnum); 43 if((net = socket(AF_INET, SOCK_STREAM, 0)) < 0){ 44 perror("socket"); 45 exit(1); 46 } 47 if (connect(net, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0){ 48 printf("%s %-5d %s\r", h_name->nodename, portnum, strerror(errno)); 49 fflush(stdout); 50 } else { 51 if ((svc_ent = getservbyport( htons(portnum) ,"tcp")) == NULL) { 52 sprintf(svc_name,"Unknown"); 53 } else { 54 snprintf(svc_name, MAXSVCNAME, "%s", svc_ent->s_name); 55 } 56 printf("%s %-5d < %-15s> accepted. \n",h_name->nodename,portnum, svc_name); 57 if (shutdown(net, 2) < 0) { 58 perror("shutdown()"); 59 exit(1); 60 } 61 } 62 close(net); 63 } 64 printf(" \r"); 65 exit(0); 66 } ソースファイル selfck.c 実行例†プログラムの実行例を以下に示します。 % ./selfck myhost 22 < ssh > accepted. myhost 53 < domain > accepted. % エラーにならなかったポートがあると、ホスト名、ポート番号、サービス名 の順に出力します。 このサービス名の出力に、前述のサービス名照会プログラムが生きています(笑) 解説†このプログラムの流れは 最初、自分のホスト名、IP アドレス を調べ、connect()システムコールを使って自ホストにコネクション要求を出し、connect() が エラーを返した ら、エラーを出力し、正常であればポート番号を報告するとものです。以下、順を追って説明して行きます。 26 struct utsname h_name[1]; ここで utsname という構造体が出てきます。utsname の中身は以下のとおりです。 struct utsname{ char sysname[_UTSNAME_SYSNAME_LENGTH]; char nodename[_UTSNAME_NODENAME_LENGTH]; char release[_UTSNAME_RELEASE_LENGTH]; char version[_UTSNAME_VERSION_LENGTH]; char machine[_UTSNAME_MACHINE_LENGTH]; char domainname[_UTSNAME_DOMAIN_LENGTH]; }; この utsname 構造体は uname() を使って 自分の node 名( = ホスト名)を参照する時に利用します。 uname()についての説明はしませんが、指定した utsname 構造体へ自ホストの情報を格納してくれる関数です。 29 memset(&sa, 0, sizeof(struct sockaddr_in)); 25 行目で宣言した sockaddr_in 構造体「sa」を 29 行目で初期化しています。この初期化をしないと おかしな値が入る事があるそうですが、ためしてないのでわかりません。 34 if ((host = gethostbyname(h_name->nodename)) == NULL){ 35 perror("gethostbyname()"); 36 exit(1); 37 } 38 memcpy(&sa.sin_addr, host->h_addr, sizeof(struct in_addr)); 34 行目で、上の uname で得られた ホスト名を引数にして gethostbyname() を呼び出しています。
以下に hostent 構造体を示します。 struct hostent { char *h_name; /* official name of host */ char **h_aliases; /* alias list */ int h_addrtype; /* host address type */ int h_length; /* length of address */ char **h_addr_list; /* list of addresses */ } 33 行目で gethostbyname()で得られた hostent 構造体 host のメンバーである「h_addr」(ホストアドレス)を sockaddr_in 構造体 sa のメンバーである sin_addr にコピーしています。 41 for (portnum = 1; portnum <= MAXPORT ; portnum++) { ここで ポート番号を1番から MAXPORT(1024) まで順に指定していきます。 あっ、説明いらないですか? 43 if((net = socket(AF_INET, SOCK_STREAM, 0)) < 0){ 44 perror("socket"); 45 exit(1); 46 } 43 行目で socket() を呼び出しています。
socket()の戻り値は socket 記述子です。これはファイル記述子と同等のもので socket()はファイルの読み込み/書き込み時に使う open() システムコールに相当する関数といえます。 47 if (connect(net, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0){ 48 printf("%s %-5d %s\r", h_name->nodename, portnum, strerror(errno)); 49 fflush(stdout); 50 } else { 51 if ((svc_ent = getservbyport( htons(portnum) ,"tcp")) == NULL) { 52 sprintf(svc_name,"Unknown"); 53 } else { 54 snprintf(svc_name, MAXSVCNAME, "%s", svc_ent->s_name); 55 } 56 printf("%s %-5d < %-15s> accepted. \n",h_name->nodename,portnum, svc_name); 57 if (shutdown(net, 2) < 0) { 58 perror("shutdown()"); 59 exit(1); 60 } 61 }
ここで 先ほど設定したアドレス構造体「sa」を使います。 57 if (shutdown(net, 2) < 0) { 58 perror("shutdown()"); 59 exit(1); 60 } 61 } 62 close(net); 63 }
最後に shutdown() で確立したコネクションを切断しましょう。(57 行目) |