Browse Source

Half-way tidy

tags/v0.9.3
Michael Brown 18 years ago
parent
commit
8afb6303fb
1 changed files with 159 additions and 116 deletions
  1. 159
    116
      src/net/tcp/ftp.c

+ 159
- 116
src/net/tcp/ftp.c View File

@@ -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
 

Loading…
Cancel
Save