% 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 までお願いします。