|
@@ -12,25 +12,49 @@
|
12
|
12
|
*
|
13
|
13
|
*/
|
14
|
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] = "",
|
|
15
|
+/** An FTP control channel string */
|
|
16
|
+struct ftp_string {
|
|
17
|
+ /** String format */
|
|
18
|
+ const char *format;
|
|
19
|
+ /** Offset to string data
|
|
20
|
+ *
|
|
21
|
+ * This is the offset within the struct ftp_request to the
|
|
22
|
+ * pointer to the string data. Use ftp_string_data() to get a
|
|
23
|
+ * pointer to the actual data.
|
|
24
|
+ */
|
|
25
|
+ off_t data_offset;
|
24
|
26
|
};
|
25
|
27
|
|
26
|
|
-static inline struct ftp_request *
|
27
|
|
-tcp_to_ftp ( struct tcp_connection *conn ) {
|
28
|
|
- return container_of ( conn, struct ftp_request, tcp );
|
|
28
|
+#define ftp_string_offset( fieldname ) \
|
|
29
|
+ offsetof ( struct ftp_request, fieldname )
|
|
30
|
+
|
|
31
|
+/**
|
|
32
|
+ * Get data associated with an FTP control channel string
|
|
33
|
+ *
|
|
34
|
+ * @v ftp FTP request
|
|
35
|
+ * @v data_offset Data offset field from ftp_string structure
|
|
36
|
+ * @ret data Pointer to data
|
|
37
|
+ */
|
|
38
|
+static inline const void * ftp_string_data ( struct ftp_request *ftp,
|
|
39
|
+ off_t data_offset ) {
|
|
40
|
+ return * ( ( void ** ) ( ( ( void * ) ftp ) + data_offset ) );
|
29
|
41
|
}
|
30
|
42
|
|
|
43
|
+/** FTP control channel strings */
|
|
44
|
+const struct ftp_string ftp_strings[] = {
|
|
45
|
+ [FTP_CONNECT] = { "", 0 },
|
|
46
|
+ [FTP_USER] = { "USER anonymous\r\n", 0 },
|
|
47
|
+ [FTP_PASS] = { "PASS etherboot@etherboot.org\r\n", 0 },
|
|
48
|
+ [FTP_TYPE] = { "TYPE I\r\n", 0 },
|
|
49
|
+ [FTP_PASV] = { "PASV\r\n", 0 },
|
|
50
|
+ [FTP_RETR] = { "RETR %s\r\n", ftp_string_offset ( filename ) },
|
|
51
|
+ [FTP_QUIT] = { "QUIT\r\n", 0 },
|
|
52
|
+ [FTP_DONE] = { "", 0 },
|
|
53
|
+};
|
|
54
|
+
|
31
|
55
|
static inline struct ftp_request *
|
32
|
|
-tcp_to_ftp_data ( struct tcp_connection *conn ) {
|
33
|
|
- return container_of ( conn, struct ftp_request, tcp_data );
|
|
56
|
+tcp_to_ftp ( struct tcp_connection *conn ) {
|
|
57
|
+ return container_of ( conn, struct ftp_request, tcp );
|
34
|
58
|
}
|
35
|
59
|
|
36
|
60
|
static void ftp_complete ( struct ftp_request *ftp, int complete ) {
|
|
@@ -39,85 +63,62 @@ static void ftp_complete ( struct ftp_request *ftp, int complete ) {
|
39
|
63
|
tcp_close ( &ftp->tcp );
|
40
|
64
|
}
|
41
|
65
|
|
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_acked ( struct tcp_connection *conn, size_t len ) {
|
62
|
|
- struct ftp_request *ftp = tcp_to_ftp ( conn );
|
63
|
|
-
|
64
|
|
- ftp->already_sent += len;
|
65
|
|
-}
|
66
|
|
-
|
67
|
|
-int ftp_open_passive ( struct ftp_request *ftp ) {
|
68
|
|
- char *ptr = ftp->passive_text;
|
69
|
|
- uint8_t *byte = ( uint8_t * ) ( &ftp->tcp_data.sin );
|
70
|
|
- int i;
|
71
|
|
-
|
72
|
|
- /* Parse the IP address and port from the PASV repsonse */
|
73
|
|
- for ( i = 6 ; i ; i-- ) {
|
74
|
|
- if ( ! *ptr )
|
75
|
|
- return -EINVAL;
|
76
|
|
- *(byte++) = strtoul ( ptr, &ptr, 10 );
|
77
|
|
- if ( *ptr )
|
78
|
|
- ptr++;
|
79
|
|
- }
|
80
|
|
- if ( *ptr )
|
81
|
|
- return -EINVAL;
|
82
|
|
-
|
83
|
|
- tcp_connect ( &ftp->tcp_data );
|
84
|
|
- return 0;
|
|
66
|
+/**
|
|
67
|
+ * Parse FTP byte sequence value
|
|
68
|
+ *
|
|
69
|
+ * @v text Text string
|
|
70
|
+ * @v value Value buffer
|
|
71
|
+ * @v len Length of value buffer
|
|
72
|
+ *
|
|
73
|
+ * This parses an FTP byte sequence value (e.g. the "aaa,bbb,ccc,ddd"
|
|
74
|
+ * form for IP addresses in PORT commands) into a byte sequence. @c
|
|
75
|
+ * *text will be updated to point beyond the end of the parsed byte
|
|
76
|
+ * sequence.
|
|
77
|
+ *
|
|
78
|
+ * This function is safe in the presence of malformed data, though the
|
|
79
|
+ * output is undefined.
|
|
80
|
+ */
|
|
81
|
+static void ftp_parse_value ( char **text, uint8_t *value, size_t len ) {
|
|
82
|
+ do {
|
|
83
|
+ *(value++) = strtoul ( *text, text, 10 );
|
|
84
|
+ if ( **text )
|
|
85
|
+ (*text)++;
|
|
86
|
+ } while ( --len );
|
85
|
87
|
}
|
86
|
88
|
|
87
|
|
-void ftp_reply ( struct ftp_request *ftp ) {
|
|
89
|
+/**
|
|
90
|
+ * Handle a response from an FTP server
|
|
91
|
+ *
|
|
92
|
+ * @v ftp FTP request
|
|
93
|
+ *
|
|
94
|
+ * This is called once we have received a complete repsonse line.
|
|
95
|
+ */
|
|
96
|
+static void ftp_reply ( struct ftp_request *ftp ) {
|
88
|
97
|
char status_major = ftp->status_text[0];
|
89
|
|
- int success;
|
90
|
98
|
|
91
|
|
- /* Ignore "intermediate" messages */
|
|
99
|
+ /* Ignore "intermediate" responses (1xx codes) */
|
92
|
100
|
if ( status_major == '1' )
|
93
|
101
|
return;
|
94
|
102
|
|
95
|
|
- /* Check for success */
|
96
|
|
- success = ( status_major == '2' );
|
97
|
|
-
|
98
|
|
- /* Special-case the "USER"-"PASS" sequence */
|
99
|
|
- if ( ftp->state == FTP_USER ) {
|
100
|
|
- if ( success ) {
|
101
|
|
- /* No password was asked for; pretend we have
|
102
|
|
- * already entered it
|
103
|
|
- */
|
104
|
|
- ftp->state = FTP_PASS;
|
105
|
|
- } else if ( status_major == '3' ) {
|
106
|
|
- /* Password requested, treat this as success
|
107
|
|
- * for our purposes
|
108
|
|
- */
|
109
|
|
- success = 1;
|
110
|
|
- }
|
111
|
|
- }
|
112
|
|
-
|
113
|
|
- /* Abort on failure */
|
114
|
|
- if ( ! success )
|
|
103
|
+ /* Anything other than success (2xx) or, in the case of a
|
|
104
|
+ * repsonse to a "USER" command, a password prompt (3xx), is a
|
|
105
|
+ * fatal error.
|
|
106
|
+ */
|
|
107
|
+ if ( ! ( ( status_major == '2' ) ||
|
|
108
|
+ ( ( status_major == '3' ) && ( ftp->state == FTP_USER ) ) ) )
|
115
|
109
|
goto err;
|
116
|
110
|
|
117
|
111
|
/* Open passive connection when we get "PASV" response */
|
118
|
112
|
if ( ftp->state == FTP_PASV ) {
|
119
|
|
- if ( ftp_open_passive ( ftp ) != 0 )
|
120
|
|
- goto err;
|
|
113
|
+ char *ptr = ftp->passive_text;
|
|
114
|
+
|
|
115
|
+ ftp_parse_value ( &ptr,
|
|
116
|
+ ( uint8_t * ) &ftp->tcp_data.sin.sin_addr,
|
|
117
|
+ sizeof ( ftp->tcp_data.sin.sin_addr ) );
|
|
118
|
+ ftp_parse_value ( &ptr,
|
|
119
|
+ ( uint8_t * ) &ftp->tcp_data.sin.sin_port,
|
|
120
|
+ sizeof ( ftp->tcp_data.sin.sin_port ) );
|
|
121
|
+ tcp_connect ( &ftp->tcp_data );
|
121
|
122
|
}
|
122
|
123
|
|
123
|
124
|
/* Move to next state */
|
|
@@ -127,59 +128,96 @@ void ftp_reply ( struct ftp_request *ftp ) {
|
127
|
128
|
return;
|
128
|
129
|
|
129
|
130
|
err:
|
130
|
|
- ftp->complete = -EPROTO;
|
131
|
|
- tcp_close ( &ftp->tcp );
|
|
131
|
+ /* Flag protocol error and close connections */
|
|
132
|
+ ftp_complete ( ftp, -EPROTO );
|
132
|
133
|
}
|
133
|
134
|
|
134
|
135
|
static void ftp_newdata ( struct tcp_connection *conn,
|
135
|
136
|
void *data, size_t len ) {
|
136
|
137
|
struct ftp_request *ftp = tcp_to_ftp ( conn );
|
|
138
|
+ char *recvbuf = ftp->recvbuf;
|
|
139
|
+ size_t recvsize = ftp->recvsize;
|
137
|
140
|
char c;
|
138
|
|
-
|
139
|
|
- for ( ; len ; data++, len-- ) {
|
140
|
|
- c = * ( ( char * ) data );
|
141
|
|
- if ( ( c == '\r' ) || ( c == '\n' ) ) {
|
142
|
|
- if ( ftp->recvsize == 0 )
|
|
141
|
+
|
|
142
|
+ while ( len-- ) {
|
|
143
|
+ c = * ( ( char * ) data++ );
|
|
144
|
+ switch ( c ) {
|
|
145
|
+ case '\r' :
|
|
146
|
+ case '\n' :
|
|
147
|
+ /* End of line: call ftp_reply() to handle
|
|
148
|
+ * completed reply. Avoid calling ftp_reply()
|
|
149
|
+ * twice if we receive both \r and \n.
|
|
150
|
+ */
|
|
151
|
+ if ( recvsize == 0 )
|
143
|
152
|
ftp_reply ( ftp );
|
144
|
|
- ftp->recvbuf = ftp->status_text;
|
145
|
|
- ftp->recvsize = sizeof ( ftp->status_text ) - 1;
|
146
|
|
- } else if ( c == '(' ) {
|
147
|
|
- ftp->recvbuf = ftp->passive_text;
|
148
|
|
- ftp->recvsize = sizeof ( ftp->passive_text ) - 1;
|
149
|
|
- } else if ( c == ')' ) {
|
150
|
|
- ftp->recvsize = 0;
|
151
|
|
- } else if ( ftp->recvsize > 0 ) {
|
152
|
|
- *(ftp->recvbuf++) = c;
|
153
|
|
- ftp->recvsize--;
|
|
153
|
+ /* Start filling up the status code buffer */
|
|
154
|
+ recvbuf = ftp->status_text;
|
|
155
|
+ recvsize = sizeof ( ftp->status_text ) - 1;
|
|
156
|
+ break;
|
|
157
|
+ case '(' :
|
|
158
|
+ /* Start filling up the passive parameter buffer */
|
|
159
|
+ recvbuf = ftp->passive_text;
|
|
160
|
+ recvsize = sizeof ( ftp->passive_text ) - 1;
|
|
161
|
+ break;
|
|
162
|
+ case ')' :
|
|
163
|
+ /* Stop filling the passive parameter buffer */
|
|
164
|
+ recvsize = 0;
|
|
165
|
+ break;
|
|
166
|
+ default :
|
|
167
|
+ /* Fill up buffer if applicable */
|
|
168
|
+ if ( recvsize > 0 ) {
|
|
169
|
+ *(recvbuf++) = c;
|
|
170
|
+ recvsize--;
|
|
171
|
+ }
|
|
172
|
+ break;
|
154
|
173
|
}
|
155
|
174
|
}
|
|
175
|
+
|
|
176
|
+ /* Store for next invocation */
|
|
177
|
+ ftp->recvbuf = recvbuf;
|
|
178
|
+ ftp->recvsize = recvsize;
|
|
179
|
+}
|
|
180
|
+
|
|
181
|
+static void ftp_acked ( struct tcp_connection *conn, size_t len ) {
|
|
182
|
+ struct ftp_request *ftp = tcp_to_ftp ( conn );
|
|
183
|
+
|
|
184
|
+ /* Mark off ACKed portion of the currently-transmitted data */
|
|
185
|
+ ftp->already_sent += len;
|
156
|
186
|
}
|
157
|
187
|
|
158
|
188
|
static void ftp_senddata ( struct tcp_connection *conn ) {
|
159
|
189
|
struct ftp_request *ftp = tcp_to_ftp ( conn );
|
160
|
|
- const char *format;
|
161
|
|
- const char *data;
|
|
190
|
+ const struct ftp_string *string;
|
162
|
191
|
size_t len;
|
163
|
192
|
|
164
|
|
- /* Select message format string and data */
|
165
|
|
- format = ftp_strings[ftp->state];
|
166
|
|
- switch ( ftp->state ) {
|
167
|
|
- case FTP_RETR:
|
168
|
|
- data = ftp->filename;
|
169
|
|
- break;
|
170
|
|
- default:
|
171
|
|
- data = NULL;
|
172
|
|
- break;
|
173
|
|
- }
|
174
|
|
- if ( ! data )
|
175
|
|
- data = "";
|
176
|
|
-
|
177
|
|
- /* Build message */
|
178
|
|
- len = snprintf ( tcp_buffer, tcp_buflen, format, data );
|
|
193
|
+ /* Send the as-yet-unACKed portion of the string for the
|
|
194
|
+ * current state.
|
|
195
|
+ */
|
|
196
|
+ string = &ftp_strings[ftp->state];
|
|
197
|
+ len = snprintf ( tcp_buffer, tcp_buflen, string->format,
|
|
198
|
+ ftp_string_data ( ftp, string->data_offset ) );
|
179
|
199
|
tcp_send ( conn, tcp_buffer + ftp->already_sent,
|
180
|
200
|
len - ftp->already_sent );
|
181
|
201
|
}
|
182
|
202
|
|
|
203
|
+static void ftp_aborted ( struct tcp_connection *conn ) {
|
|
204
|
+ struct ftp_request *ftp = tcp_to_ftp ( conn );
|
|
205
|
+
|
|
206
|
+ ftp_complete ( ftp, -ECONNABORTED );
|
|
207
|
+}
|
|
208
|
+
|
|
209
|
+static void ftp_timedout ( struct tcp_connection *conn ) {
|
|
210
|
+ struct ftp_request *ftp = tcp_to_ftp ( conn );
|
|
211
|
+
|
|
212
|
+ ftp_complete ( ftp, -ETIMEDOUT );
|
|
213
|
+}
|
|
214
|
+
|
|
215
|
+static void ftp_closed ( struct tcp_connection *conn ) {
|
|
216
|
+ struct ftp_request *ftp = tcp_to_ftp ( conn );
|
|
217
|
+
|
|
218
|
+ ftp_complete ( ftp, 1 );
|
|
219
|
+}
|
|
220
|
+
|
183
|
221
|
static struct tcp_operations ftp_tcp_operations = {
|
184
|
222
|
.aborted = ftp_aborted,
|
185
|
223
|
.timedout = ftp_timedout,
|
|
@@ -189,6 +227,11 @@ static struct tcp_operations ftp_tcp_operations = {
|
189
|
227
|
.senddata = ftp_senddata,
|
190
|
228
|
};
|
191
|
229
|
|
|
230
|
+static inline struct ftp_request *
|
|
231
|
+tcp_to_ftp_data ( struct tcp_connection *conn ) {
|
|
232
|
+ return container_of ( conn, struct ftp_request, tcp_data );
|
|
233
|
+}
|
|
234
|
+
|
192
|
235
|
static void ftp_data_aborted ( struct tcp_connection *conn ) {
|
193
|
236
|
struct ftp_request *ftp = tcp_to_ftp_data ( conn );
|
194
|
237
|
|