Преглед изворни кода

Proof-of-concept FTP implementation

tags/v0.9.3
Michael Brown пре 18 година
родитељ
комит
aec0e127d2
2 измењених фајлова са 331 додато и 0 уклоњено
  1. 68
    0
      src/include/gpxe/ftp.h
  2. 263
    0
      src/net/tcp/ftp.c

+ 68
- 0
src/include/gpxe/ftp.h Прегледај датотеку

@@ -0,0 +1,68 @@
1
+#ifndef _GPXE_FTP_H
2
+#define _GPXE_FTP_H
3
+
4
+/** @file
5
+ *
6
+ * File transfer protocol
7
+ *
8
+ */
9
+
10
+#include <stdint.h>
11
+#include <gpxe/tcp.h>
12
+
13
+enum ftp_state {
14
+	FTP_CONNECT = 0,
15
+	FTP_USER,
16
+	FTP_PASS,
17
+	FTP_TYPE,
18
+	FTP_PASV,
19
+	FTP_RETR,
20
+	FTP_QUIT,
21
+	FTP_DONE,
22
+};
23
+
24
+/**
25
+ * An FTP request
26
+ *
27
+ */
28
+struct ftp_request {
29
+	/** TCP connection for this request */
30
+	struct tcp_connection tcp;
31
+	/** File to download */
32
+	const char *filename;
33
+	/** Callback function
34
+	 *
35
+	 * @v data	Received data
36
+	 * @v len	Length of received data
37
+	 *
38
+	 * This function is called for all data received from the
39
+	 * remote server.
40
+	 */
41
+	void ( *callback ) ( char *data, size_t len );
42
+	/** Completion indicator
43
+	 *
44
+	 * This will be set to a non-zero value when the transfer is
45
+	 * complete.  A negative value indicates an error.
46
+	 */
47
+	int complete;
48
+
49
+	/** Current state */
50
+	enum ftp_state state;
51
+	/** Amount of current message already transmitted */
52
+	size_t already_sent;
53
+	/** Buffer to be filled with data received via the control channel */
54
+	char *recvbuf;
55
+	/** Remaining size of recvbuf */
56
+	size_t recvsize;
57
+	/** FTP status code, as text */
58
+	char status_text[4];
59
+	/** Passive-mode parameters, as text */
60
+	char passive_text[24]; /* "aaa,bbb,ccc,ddd,eee,fff" */
61
+
62
+	/** TCP connection for the data channel */
63
+	struct tcp_connection tcp_data;
64
+};
65
+
66
+extern void ftp_connect ( struct ftp_request *ftp );
67
+
68
+#endif

+ 263
- 0
src/net/tcp/ftp.c Прегледај датотеку

@@ -0,0 +1,263 @@
1
+#include <stddef.h>
2
+#include <stdlib.h>
3
+#include <string.h>
4
+#include <vsprintf.h>
5
+#include <assert.h>
6
+#include <errno.h>
7
+#include <gpxe/ftp.h>
8
+
9
+/** @file
10
+ *
11
+ * File transfer protocol
12
+ *
13
+ */
14
+
15
+const char *ftp_strings[] = {
16
+	[FTP_CONNECT] = "",
17
+	[FTP_USER] = "USER anonymous\r\n",
18
+	[FTP_PASS] = "PASS etherboot@etherboot.org\r\n",
19
+	[FTP_TYPE] = "TYPE I\r\n",
20
+	[FTP_PASV] = "PASV\r\n",
21
+	[FTP_RETR] = "RETR %s\r\n",
22
+	[FTP_QUIT] = "QUIT\r\n",
23
+	[FTP_DONE] = "",
24
+};
25
+
26
+static inline struct ftp_request *
27
+tcp_to_ftp ( struct tcp_connection *conn ) {
28
+	return container_of ( conn, struct ftp_request, tcp );
29
+}
30
+
31
+static inline struct ftp_request *
32
+tcp_to_ftp_data ( struct tcp_connection *conn ) {
33
+	return container_of ( conn, struct ftp_request, tcp_data );
34
+}
35
+
36
+static void ftp_complete ( struct ftp_request *ftp, int complete ) {
37
+	ftp->complete = complete;
38
+	tcp_close ( &ftp->tcp_data );
39
+	tcp_close ( &ftp->tcp );
40
+}
41
+
42
+
43
+static void ftp_aborted ( struct tcp_connection *conn ) {
44
+	struct ftp_request *ftp = tcp_to_ftp ( conn );
45
+
46
+	ftp_complete ( ftp, -ECONNABORTED );
47
+}
48
+
49
+static void ftp_timedout ( struct tcp_connection *conn ) {
50
+	struct ftp_request *ftp = tcp_to_ftp ( conn );
51
+
52
+	ftp_complete ( ftp, -ETIMEDOUT );
53
+}
54
+
55
+static void ftp_closed ( struct tcp_connection *conn ) {
56
+	struct ftp_request *ftp = tcp_to_ftp ( conn );
57
+
58
+	ftp_complete ( ftp, 1 );
59
+}
60
+
61
+static void ftp_connected ( struct tcp_connection *conn ) {
62
+	struct ftp_request *ftp = tcp_to_ftp ( conn );
63
+
64
+	/* Nothing to do */
65
+}
66
+
67
+static void ftp_acked ( struct tcp_connection *conn, size_t len ) {
68
+	struct ftp_request *ftp = tcp_to_ftp ( conn );
69
+	
70
+	ftp->already_sent += len;
71
+}
72
+
73
+static int ftp_open_passive ( struct ftp_request *ftp ) {
74
+	char *ptr = ftp->passive_text;
75
+	uint8_t *byte = ( uint8_t * ) ( &ftp->tcp_data.sin );
76
+	int i;
77
+
78
+	/* Parse the IP address and port from the PASV repsonse */
79
+	for ( i = 6 ; i ; i-- ) {
80
+		if ( ! *ptr )
81
+			return -EINVAL;
82
+		*(byte++) = strtoul ( ptr, &ptr, 10 );
83
+		if ( *ptr )
84
+			ptr++;
85
+	}
86
+	if ( *ptr )
87
+		return -EINVAL;
88
+
89
+	tcp_connect ( &ftp->tcp_data );
90
+	return 0;
91
+}
92
+
93
+static void ftp_reply ( struct ftp_request *ftp ) {
94
+	char status_major = ftp->status_text[0];
95
+	int success;
96
+
97
+	/* Ignore "intermediate" messages */
98
+	if ( status_major == '1' )
99
+		return;
100
+
101
+	/* Check for success */
102
+	success = ( status_major == '2' );
103
+
104
+	/* Special-case the "USER"-"PASS" sequence */
105
+	if ( ftp->state == FTP_USER ) {
106
+		if ( success ) {
107
+			/* No password was asked for; pretend we have
108
+			 * already entered it
109
+			 */
110
+			ftp->state = FTP_PASS;
111
+		} else if ( status_major == '3' ) {
112
+			/* Password requested, treat this as success
113
+			 * for our purposes
114
+			 */
115
+			success = 1;
116
+		}
117
+	}
118
+	
119
+	/* Abort on failure */
120
+	if ( ! success )
121
+		goto err;
122
+
123
+	/* Open passive connection when we get "PASV" response */
124
+	if ( ftp->state == FTP_PASV ) {
125
+		if ( ftp_open_passive ( ftp ) != 0 )
126
+			goto err;
127
+	}
128
+
129
+	/* Move to next state */
130
+	if ( ftp->state < FTP_DONE )
131
+		ftp->state++;
132
+	ftp->already_sent = 0;
133
+	return;
134
+
135
+ err:
136
+	ftp->complete = -EPROTO;
137
+	tcp_close ( &ftp->tcp );
138
+}
139
+
140
+static void ftp_newdata ( struct tcp_connection *conn,
141
+			  void *data, size_t len ) {
142
+	struct ftp_request *ftp = tcp_to_ftp ( conn );
143
+	char c;
144
+
145
+	for ( ; len ; data++, len-- ) {
146
+		c = * ( ( char * ) data );
147
+		if ( ( c == '\r' ) || ( c == '\n' ) ) {
148
+			if ( ftp->recvsize == 0 )
149
+				ftp_reply ( ftp );
150
+			ftp->recvbuf = ftp->status_text;
151
+			ftp->recvsize = sizeof ( ftp->status_text ) - 1;
152
+		} else if ( c == '(' ) {
153
+			ftp->recvbuf = ftp->passive_text;
154
+			ftp->recvsize = sizeof ( ftp->passive_text ) - 1;
155
+		} else if ( c == ')' ) {
156
+			ftp->recvsize = 0;
157
+		} else if ( ftp->recvsize > 0 ) {
158
+			*(ftp->recvbuf++) = c;
159
+			ftp->recvsize--;
160
+		}
161
+	}
162
+}
163
+
164
+static void ftp_senddata ( struct tcp_connection *conn ) {
165
+	struct ftp_request *ftp = tcp_to_ftp ( conn );
166
+	const char *format;
167
+	const char *data;
168
+	size_t len;
169
+
170
+	/* Select message format string and data */
171
+	format = ftp_strings[ftp->state];
172
+	switch ( ftp->state ) {
173
+	case FTP_RETR:
174
+		data = ftp->filename;
175
+		break;
176
+	default:
177
+		data = NULL;
178
+		break;
179
+	}
180
+	if ( ! data )
181
+		data = "";
182
+	
183
+	/* Build message */
184
+	len = snprintf ( tcp_buffer, tcp_buflen, format, data );
185
+	tcp_send ( conn, tcp_buffer + ftp->already_sent,
186
+		   len - ftp->already_sent );
187
+}
188
+
189
+static struct tcp_operations ftp_tcp_operations = {
190
+	.aborted	= ftp_aborted,
191
+	.timedout	= ftp_timedout,
192
+	.closed		= ftp_closed,
193
+	.connected	= ftp_connected,
194
+	.acked		= ftp_acked,
195
+	.newdata	= ftp_newdata,
196
+	.senddata	= ftp_senddata,
197
+};
198
+
199
+static void ftp_data_aborted ( struct tcp_connection *conn ) {
200
+	struct ftp_request *ftp = tcp_to_ftp_data ( conn );
201
+
202
+	ftp_complete ( ftp, -ECONNABORTED );
203
+}
204
+
205
+static void ftp_data_timedout ( struct tcp_connection *conn ) {
206
+	struct ftp_request *ftp = tcp_to_ftp_data ( conn );
207
+
208
+	ftp_complete ( ftp, -ETIMEDOUT );
209
+}
210
+
211
+static void ftp_data_closed ( struct tcp_connection *conn ) {
212
+	struct ftp_request *ftp = tcp_to_ftp_data ( conn );
213
+
214
+	/* Nothing to do */
215
+}
216
+
217
+static void ftp_data_connected ( struct tcp_connection *conn ) {
218
+	struct ftp_request *ftp = tcp_to_ftp_data ( conn );
219
+
220
+	/* Nothing to do */
221
+}
222
+
223
+static void ftp_data_acked ( struct tcp_connection *conn, size_t len ) {
224
+	struct ftp_request *ftp = tcp_to_ftp_data ( conn );
225
+	
226
+	/* Nothing to do */
227
+}
228
+
229
+static void ftp_data_newdata ( struct tcp_connection *conn,
230
+			       void *data, size_t len ) {
231
+	struct ftp_request *ftp = tcp_to_ftp_data ( conn );
232
+
233
+	ftp->callback ( data, len );
234
+}
235
+
236
+static void ftp_data_senddata ( struct tcp_connection *conn ) {
237
+	struct ftp_request *ftp = tcp_to_ftp_data ( conn );
238
+	
239
+	/* Nothing to do */
240
+}
241
+
242
+static struct tcp_operations ftp_data_tcp_operations = {
243
+	.aborted	= ftp_data_aborted,
244
+	.timedout	= ftp_data_timedout,
245
+	.closed		= ftp_data_closed,
246
+	.connected	= ftp_data_connected,
247
+	.acked		= ftp_data_acked,
248
+	.newdata	= ftp_data_newdata,
249
+	.senddata	= ftp_data_senddata,
250
+};
251
+
252
+/**
253
+ * Initiate an FTP connection
254
+ *
255
+ * @v ftp	FTP request
256
+ */
257
+void ftp_connect ( struct ftp_request *ftp ) {
258
+	ftp->tcp.tcp_op = &ftp_tcp_operations;
259
+	ftp->tcp_data.tcp_op = &ftp_data_tcp_operations;
260
+	ftp->recvbuf = ftp->status_text;
261
+	ftp->recvsize = sizeof ( ftp->status_text ) - 1;
262
+	tcp_connect ( &ftp->tcp );
263
+}

Loading…
Откажи
Сачувај