% cc -o ftp-client ftp-client.cでコンパイルします。SunOS ではネットワーク関係のライブラリが libc に含まれていないので、
% cc -o ftp-client ftp-client.c -lresolv -lsocket -lnslとします。
「ftp://ホスト名/パス」のファイルを取得したい場合は、
% ./ftp-client ユーザ名 パスワード ホスト名 パスとします。ftp サーバへのログイン時には、引数で指定したユーザ名とパスワードが使われます。 anonymous ftp サーバからファイルを取得したい場合は
% ./ftp-client anonymous 68user@X68000.startshop.co.jp ftp.jp.FreeBSD.org /pub/FreeBSD/README.TXTのような感じですね。
% ./ftp-client -d anonymous 68user@X68000.startshop.co.jp ftp.jp.FreeBSD.org /pub/FreeBSD/README.TXTと -d オプションを付けるとデバッグモードになり、送受信した FTP プロトコルを表示します。
解説はナシです。
なお、このプログラムは Active mode にしか対応していません。 そのため NAT 環境で使用すると
--> PORT 192,168,0,7,9,58 <-- 500 Illegal PORT range rejected.などとプライベート IP アドレスを送信してしまいます。 しかもエラーチェックを行っていないため、 デッドロックしてしまいますのでご注意を。
1: /* $Id: ftp-client.c,v 1.4 2004/05/29 05:36:31 68user Exp $ */ 2: 3: #include <stdio.h> 4: #include <string.h> 5: #include <stdlib.h> 6: #include <sys/types.h> 7: #include <sys/socket.h> 8: #include <netdb.h> 9: #include <netinet/in.h> 10: #include <sys/param.h> 11: #include <sys/uio.h> 12: #include <unistd.h> 13: #include <ctype.h> 14: 15: #define BUF_LEN 256 /* バッファのサイズ */ 16: 17: int debug_flg = 0; /* -d オプションを付けると turn on する */ 18: 19: 20: /*-------------------------------------------------- 21: * ソケットから1行読み込む 22: */ 23: char *read_line(int socket, char *p){ 24: char *org_p = p; 25: 26: while (1){ 27: if ( read(socket, p, 1) == 0 ) break; 28: if ( *p == '\n' ) break; 29: p++; 30: } 31: *(++p) = '\0'; 32: return org_p; 33: } 34: 35: 36: /*-------------------------------------------------- 37: * レスポンスを取得する。^\d\d\d- ならもう1行取得 38: */ 39: void read_response(int socket, char *p){ 40: do { 41: read_line(socket, p); 42: if ( debug_flg ){ 43: fprintf(stderr, "<-- %s", p); 44: } 45: } while ( isdigit(p[0]) && 46: isdigit(p[1]) && 47: isdigit(p[2]) && 48: p[3]=='-' ); 49: } 50: 51: 52: /*-------------------------------------------------- 53: * 指定されたソケット socket に文字列 p を送信。 54: * 文字列 p の終端は \0 で terminate されている 55: * 必要がある 56: */ 57: 58: void write_to_server(int socket, char *p){ 59: if ( debug_flg ){ 60: fprintf(stderr, "--> %s", p); 61: } 62: write(socket, p, strlen(p)); 63: } 64: 65: void error( char *message ){ 66: fprintf(stderr, message); 67: exit(1); 68: } 69: 70: 71: int main(int argc, char *argv[]){ 72: int command_socket; /* コマンド用ソケット */ 73: int data_socket; /* データ用ソケット */ 74: int data_waiting_socket; /* データコネクションの待ち受け用ソケット */ 75: struct hostent *servhost; /* ホスト名とIPアドレスを扱うための構造体 */ 76: struct sockaddr_in server; /* ソケットを扱うための構造体 */ 77: struct sockaddr_in sin; 78: int len; 79: 80: char send_mesg[BUF_LEN]; /* サーバに送るメッセージ */ 81: char user[BUF_LEN]; /* ftp サーバに送信するユーザ名 */ 82: char passwd[BUF_LEN]; /* ftp サーバに送信するパスワード */ 83: char host[BUF_LEN]; /* 接続するホスト名 */ 84: char path[BUF_LEN]; /* 要求するパス */ 85: char buf[BUF_LEN]; 86: 87: while (1){ 88: int c; 89: c = getopt(argc, argv, "d"); 90: if ( c == -1 ) break; 91: switch (c){ 92: case 'd': 93: debug_flg = 1; 94: argc--; 95: argv++; 96: break; 97: default: 98: break; 99: } 100: } 101: /* 引数解析 */ 102: if ( argc == 5 ){ 103: strncpy(user, argv[1], sizeof(user)); 104: strncpy(passwd, argv[2], sizeof(passwd)); 105: strncpy(host, argv[3], sizeof(host)); 106: strncpy(path, argv[4], sizeof(path)); 107: } else { 108: fprintf(stderr, "ftp-client ユーザ名 パスワード ホスト名 パス\n"); 109: exit(1); 110: } 111: /* ホストの情報 (IP アドレスなど) を取得 */ 112: servhost = gethostbyname(host); 113: if ( servhost == NULL ){ 114: fprintf(stderr, "Bad hostname [%s]\n", host); 115: exit(1); 116: } 117: 118: /* IP アドレスを示す構造体をコピー */ 119: bzero((char*)&server, sizeof(server)); 120: server.sin_family = AF_INET; 121: /* 構造体をゼロクリア */ 122: bcopy(servhost->h_addr, (char *)&server.sin_addr, servhost->h_length); 123: 124: /* ポート番号取得 */ 125: server.sin_port = (getservbyname("ftp", "tcp"))->s_port; 126: 127: /* ソケット生成 */ 128: command_socket = socket(AF_INET, SOCK_STREAM, 0); 129: 130: /* サーバに接続 */ 131: connect(command_socket, (struct sockaddr *)&server, sizeof(server)); 132: 133: /* welcome response を取得 */ 134: read_response(command_socket, buf); 135: 136: /* USER・PASS を送信 */ 137: sprintf(send_mesg, "USER %s\n", user); 138: write_to_server(command_socket, send_mesg); 139: read_response(command_socket, buf); 140: 141: sprintf(send_mesg, "PASS %s\n", passwd); 142: write_to_server(command_socket, send_mesg); 143: read_response(command_socket, buf); 144: 145: /* データコネクション用ソケットを作成し、 146: * bind・listen する 147: */ 148: data_waiting_socket = socket(AF_INET, SOCK_STREAM, 0); 149: 150: sin.sin_family = AF_INET; 151: sin.sin_port = 0; 152: sin.sin_addr.s_addr = htonl(INADDR_ANY); 153: 154: if ( bind(data_waiting_socket, (struct sockaddr *)&sin, sizeof sin) < 0 ){ 155: error("bind failed.\n"); 156: } 157: if ( listen(data_waiting_socket, SOMAXCONN) == -1 ){ 158: error("listen failed.\n"); 159: } 160: /* まだ accept はしない。PORT・LIST を送ってから */ 161: 162: 163: /* ----------------------------------------- */ 164: { 165: u_long local_ip; 166: 167: /* localhost の IP アドレスを取得。既に ESTABLISHED である 168: * command_socket から取得していることに注意。 169: */ 170: 171: len = sizeof(sin); 172: if ( getsockname(command_socket, 173: (struct sockaddr *)&sin, &len) < 0 ){ 174: error("getsockname failed.\n"); 175: } 176: local_ip = ntohl(sin.sin_addr.s_addr); 177: 178: /* ポート番号を取得 */ 179: if ( getsockname(data_waiting_socket, 180: (struct sockaddr *)&sin, &len) < 0 ){ 181: error("getsockname failed.\n"); 182: } 183: 184: sprintf(send_mesg, "PORT %d,%d,%d,%d,%d,%d\n", 185: (int)(local_ip >> 24) & 0xff, 186: (int)(local_ip >> 16) & 0xff, 187: (int)(local_ip >> 8) & 0xff, 188: (int)(local_ip) & 0xff, 189: /* 190: * ↑は inet_ntoa(local_ip) でもいいんだけど、 191: * その場合はピリオドをカンマに変換しないといけない。 192: */ 193: (ntohs(sin.sin_port) >> 8) & 0xff, 194: ntohs(sin.sin_port) & 0xff); 195: 196: /* PORT・RETR を送信 */ 197: write_to_server(command_socket, send_mesg); 198: read_response(command_socket, buf); 199: 200: sprintf(send_mesg, "RETR %s\n", path); 201: write_to_server(command_socket, send_mesg); 202: } 203: 204: /* データコネクションの確立 */ 205: len = sizeof(sin); 206: data_socket = accept(data_waiting_socket, (struct sockaddr *)&sin, &len); 207: if ( data_socket == -1 ){ 208: error("accept failed.\n"); 209: } 210: 211: /* ファイルの内容取得 */ 212: while (1){ 213: int read_size; 214: read_size = read(data_socket, buf, BUF_LEN); 215: if ( read_size > 0 ){ 216: write(1, buf, read_size); 217: } else { 218: break; 219: } 220: } 221: /* 150 Opening ASCII mode data connection ... 222: * のようなレスポンスを受け取る 223: */ 224: read_response(command_socket, buf); 225: /* 226 Transfer complete. のようなレスポンスを受け取る */ 226: read_response(command_socket, buf); 227: 228: /* QUIT 送って終了 */ 229: write_to_server(command_socket, "QUIT\n"); 230: read_response(command_socket, buf); 231: 232: close(data_waiting_socket); 233: close(command_socket); 234: 235: return 0; 236: }
ご意見・ご指摘は Twitter: @68user までお願いします。