1#include <stdint.h> 2#include <stdlib.h> 3#include <stdio.h> 4#include <string.h> 5#include <assert.h> 6#include <errno.h> 7#include <byteswap.h> 8#include <gpxe/socket.h> 9#include <gpxe/tcpip.h> 10#include <gpxe/in.h> 11#include <gpxe/xfer.h> 12#include <gpxe/open.h> 13#include <gpxe/uri.h> 14#include <gpxe/features.h> 15#include <gpxe/ftp.h> 16 17/** @file 18 * 19 * File transfer protocol 20 * 21 */ 22 23FEATURE ( FEATURE_PROTOCOL, "FTP", DHCP_EB_FEATURE_FTP, 1 ); 24 25/** 26 * FTP states 27 * 28 * These @b must be sequential, i.e. a successful FTP session must 29 * pass through each of these states in order. 30 */ 31enum ftp_state { 32 FTP_CONNECT = 0, 33 FTP_USER, 34 FTP_PASS, 35 FTP_TYPE, 36 FTP_PASV, 37 FTP_RETR, 38 FTP_WAIT, 39 FTP_QUIT, 40 FTP_DONE, 41}; 42 43/** 44 * An FTP request 45 * 46 */ 47struct ftp_request { 48 /** Reference counter */ 49 struct refcnt refcnt; 50 /** Data transfer interface */ 51 struct xfer_interface xfer; 52 53 /** URI being fetched */ 54 struct uri *uri; 55 /** FTP control channel interface */ 56 struct xfer_interface control; 57 /** FTP data channel interface */ 58 struct xfer_interface data; 59 60 /** Current state */ 61 enum ftp_state state; 62 /** Buffer to be filled with data received via the control channel */ 63 char *recvbuf; 64 /** Remaining size of recvbuf */ 65 size_t recvsize; 66 /** FTP status code, as text */ 67 char status_text[5]; 68 /** Passive-mode parameters, as text */ 69 char passive_text[24]; /* "aaa,bbb,ccc,ddd,eee,fff" */ 70}; 71 72/** 73 * Free FTP request 74 * 75 * @v refcnt Reference counter 76 */ 77static void ftp_free ( struct refcnt *refcnt ) { 78 struct ftp_request *ftp = 79 container_of ( refcnt, struct ftp_request, refcnt ); 80 81 DBGC ( ftp, "FTP %p freed\n", ftp ); 82 83 uri_put ( ftp->uri ); 84 free ( ftp ); 85} 86 87/** 88 * Mark FTP operation as complete 89 * 90 * @v ftp FTP request 91 * @v rc Return status code 92 */ 93static void ftp_done ( struct ftp_request *ftp, int rc ) { 94 95 DBGC ( ftp, "FTP %p completed (%s)\n", ftp, strerror ( rc ) ); 96 97 /* Close all data transfer interfaces */ 98 xfer_nullify ( &ftp->xfer ); 99 xfer_close ( &ftp->xfer, rc ); 100 xfer_nullify ( &ftp->control ); 101 xfer_close ( &ftp->control, rc ); 102 xfer_nullify ( &ftp->data ); 103 xfer_close ( &ftp->data, rc ); 104} 105 106/***************************************************************************** 107 * 108 * FTP control channel 109 * 110 */ 111 112/** An FTP control channel string */ 113struct ftp_control_string { 114 /** Literal portion */ 115 const char *literal; 116 /** Variable portion 117 * 118 * @v ftp FTP request 119 * @ret string Variable portion of string 120 */ 121 const char * ( *variable ) ( struct ftp_request *ftp ); 122}; 123 124/** 125 * Retrieve FTP pathname 126 * 127 * @v ftp FTP request 128 * @ret path FTP pathname 129 */ 130static const char * ftp_uri_path ( struct ftp_request *ftp ) { 131 return ftp->uri->path; 132} 133 134/** 135 * Retrieve FTP user 136 * 137 * @v ftp FTP request 138 * @ret user FTP user 139 */ 140static const char * ftp_user ( struct ftp_request *ftp ) { 141 static char *ftp_default_user = "anonymous"; 142 return ftp->uri->user ? ftp->uri->user : ftp_default_user; 143} 144 145/** 146 * Retrieve FTP password 147 * 148 * @v ftp FTP request 149 * @ret password FTP password 150 */ 151static const char * ftp_password ( struct ftp_request *ftp ) { 152 static char *ftp_default_password = "etherboot@etherboot.org"; 153 return ftp->uri->password ? ftp->uri->password : ftp_default_password; 154} 155 156/** FTP control channel strings */ 157static struct ftp_control_string ftp_strings[] = { 158 [FTP_CONNECT] = { NULL, NULL }, 159 [FTP_USER] = { "USER ", ftp_user }, 160 [FTP_PASS] = { "PASS ", ftp_password }, 161 [FTP_TYPE] = { "TYPE I", NULL }, 162 [FTP_PASV] = { "PASV", NULL }, 163 [FTP_RETR] = { "RETR ", ftp_uri_path }, 164 [FTP_WAIT] = { NULL, NULL }, 165 [FTP_QUIT] = { "QUIT", NULL }, 166 [FTP_DONE] = { NULL, NULL }, 167}; 168 169/** 170 * Handle control channel being closed 171 * 172 * @v control FTP control channel interface 173 * @v rc Reason for close 174 * 175 * When the control channel is closed, the data channel must also be 176 * closed, if it is currently open. 177 */ 178static void ftp_control_close ( struct xfer_interface *control, int rc ) { 179 struct ftp_request *ftp = 180 container_of ( control, struct ftp_request, control ); 181 182 DBGC ( ftp, "FTP %p control connection closed: %s\n", 183 ftp, strerror ( rc ) ); 184 185 /* Complete FTP operation */ 186 ftp_done ( ftp, rc ); 187} 188 189/** 190 * Parse FTP byte sequence value 191 * 192 * @v text Text string 193 * @v value Value buffer 194 * @v len Length of value buffer 195 * 196 * This parses an FTP byte sequence value (e.g. the "aaa,bbb,ccc,ddd" 197 * form for IP addresses in PORT commands) into a byte sequence. @c 198 * *text will be updated to point beyond the end of the parsed byte 199 * sequence. 200 * 201 * This function is safe in the presence of malformed data, though the 202 * output is undefined. 203 */ 204static void ftp_parse_value ( char **text, uint8_t *value, size_t len ) { 205 do { 206 *(value++) = strtoul ( *text, text, 10 ); 207 if ( **text ) 208 (*text)++; 209 } while ( --len ); 210} 211 212/** 213 * Move to next state and send the appropriate FTP control string 214 * 215 * @v ftp FTP request 216 * 217 */ 218static void ftp_next_state ( struct ftp_request *ftp ) { 219 struct ftp_control_string *ftp_string; 220 const char *literal; 221 const char *variable; 222 223 /* Move to next state */ 224 if ( ftp->state < FTP_DONE ) 225 ftp->state++; 226 227 /* Send control string if needed */ 228 ftp_string = &ftp_strings[ftp->state]; 229 literal = ftp_string->literal; 230 variable = ( ftp_string->variable ? 231 ftp_string->variable ( ftp ) : "" ); 232 if ( literal ) { 233 DBGC ( ftp, "FTP %p sending %s%s\n", ftp, literal, variable ); 234 xfer_printf ( &ftp->control, "%s%s\r\n", literal, variable ); 235 } 236} 237 238/** 239 * Handle an FTP control channel response 240 * 241 * @v ftp FTP request 242 * 243 * This is called once we have received a complete response line. 244 */ 245static void ftp_reply ( struct ftp_request *ftp ) { 246 char status_major = ftp->status_text[0]; 247 char separator = ftp->status_text[3]; 248 249 DBGC ( ftp, "FTP %p received status %s\n", ftp, ftp->status_text ); 250 251 /* Ignore malformed lines */ 252 if ( separator != ' ' ) 253 return; 254 255 /* Ignore "intermediate" responses (1xx codes) */ 256 if ( status_major == '1' ) 257 return; 258 259 /* Anything other than success (2xx) or, in the case of a 260 * repsonse to a "USER" command, a password prompt (3xx), is a 261 * fatal error. 262 */ 263 if ( ! ( ( status_major == '2' ) || 264 ( ( status_major == '3' ) && ( ftp->state == FTP_USER ) ) ) ){ 265 /* Flag protocol error and close connections */ 266 ftp_done ( ftp, -EPROTO ); 267 return; 268 } 269 270 /* Open passive connection when we get "PASV" response */ 271 if ( ftp->state == FTP_PASV ) { 272 char *ptr = ftp->passive_text; 273 union { 274 struct sockaddr_in sin; 275 struct sockaddr sa; 276 } sa; 277 int rc; 278 279 sa.sin.sin_family = AF_INET; 280 ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_addr, 281 sizeof ( sa.sin.sin_addr ) ); 282 ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_port, 283 sizeof ( sa.sin.sin_port ) ); 284 if ( ( rc = xfer_open_socket ( &ftp->data, SOCK_STREAM, 285 &sa.sa, NULL ) ) != 0 ) { 286 DBGC ( ftp, "FTP %p could not open data connection\n", 287 ftp ); 288 ftp_done ( ftp, rc ); 289 return; 290 } 291 } 292 293 /* Move to next state and send control string */ 294 ftp_next_state ( ftp ); 295 296} 297 298/** 299 * Handle new data arriving on FTP control channel 300 * 301 * @v control FTP control channel interface 302 * @v data New data 303 * @v len Length of new data 304 * 305 * Data is collected until a complete line is received, at which point 306 * its information is passed to ftp_reply(). 307 */ 308static int ftp_control_deliver_raw ( struct xfer_interface *control, 309 const void *data, size_t len ) { 310 struct ftp_request *ftp = 311 container_of ( control, struct ftp_request, control ); 312 char *recvbuf = ftp->recvbuf; 313 size_t recvsize = ftp->recvsize; 314 char c; 315 316 while ( len-- ) { 317 c = * ( ( char * ) data++ ); 318 switch ( c ) { 319 case '\r' : 320 case '\n' : 321 /* End of line: call ftp_reply() to handle 322 * completed reply. Avoid calling ftp_reply() 323 * twice if we receive both \r and \n. 324 */ 325 if ( recvsize == 0 ) 326 ftp_reply ( ftp ); 327 /* Start filling up the status code buffer */ 328 recvbuf = ftp->status_text; 329 recvsize = sizeof ( ftp->status_text ) - 1; 330 break; 331 case '(' : 332 /* Start filling up the passive parameter buffer */ 333 recvbuf = ftp->passive_text; 334 recvsize = sizeof ( ftp->passive_text ) - 1; 335 break; 336 case ')' : 337 /* Stop filling the passive parameter buffer */ 338 recvsize = 0; 339 break; 340 default : 341 /* Fill up buffer if applicable */ 342 if ( recvsize > 0 ) { 343 *(recvbuf++) = c; 344 recvsize--; 345 } 346 break; 347 } 348 } 349 350 /* Store for next invocation */ 351 ftp->recvbuf = recvbuf; 352 ftp->recvsize = recvsize; 353 354 return 0; 355} 356 357/** FTP control channel operations */ 358static struct xfer_interface_operations ftp_control_operations = { 359 .close = ftp_control_close, 360 .vredirect = xfer_vreopen, 361 .window = unlimited_xfer_window, 362 .alloc_iob = default_xfer_alloc_iob, 363 .deliver_iob = xfer_deliver_as_raw, 364 .deliver_raw = ftp_control_deliver_raw, 365}; 366 367/***************************************************************************** 368 * 369 * FTP data channel 370 * 371 */ 372 373/** 374 * Handle FTP data channel being closed 375 * 376 * @v data FTP data channel interface 377 * @v rc Reason for closure 378 * 379 * When the data channel is closed, the control channel should be left 380 * alone; the server will send a completion message via the control 381 * channel which we'll pick up. 382 * 383 * If the data channel is closed due to an error, we abort the request. 384 */ 385static void ftp_data_closed ( struct xfer_interface *data, int rc ) { 386 struct ftp_request *ftp = 387 container_of ( data, struct ftp_request, data ); 388 389 DBGC ( ftp, "FTP %p data connection closed: %s\n", 390 ftp, strerror ( rc ) ); 391 392 /* If there was an error, close control channel and record status */ 393 if ( rc ) { 394 ftp_done ( ftp, rc ); 395 } else { 396 ftp_next_state ( ftp ); 397 } 398} 399 400/** 401 * Handle data delivery via FTP data channel 402 * 403 * @v xfer FTP data channel interface 404 * @v iobuf I/O buffer 405 * @v meta Data transfer metadata 406 * @ret rc Return status code 407 */ 408static int ftp_data_deliver_iob ( struct xfer_interface *data, 409 struct io_buffer *iobuf, 410 struct xfer_metadata *meta __unused ) { 411 struct ftp_request *ftp = 412 container_of ( data, struct ftp_request, data ); 413 int rc; 414 415 if ( ( rc = xfer_deliver_iob ( &ftp->xfer, iobuf ) ) != 0 ) { 416 DBGC ( ftp, "FTP %p failed to deliver data: %s\n", 417 ftp, strerror ( rc ) ); 418 return rc; 419 } 420 421 return 0; 422} 423 424/** FTP data channel operations */ 425static struct xfer_interface_operations ftp_data_operations = { 426 .close = ftp_data_closed, 427 .vredirect = xfer_vreopen, 428 .window = unlimited_xfer_window, 429 .alloc_iob = default_xfer_alloc_iob, 430 .deliver_iob = ftp_data_deliver_iob, 431 .deliver_raw = xfer_deliver_as_iob, 432}; 433 434/***************************************************************************** 435 * 436 * Data transfer interface 437 * 438 */ 439 440/** 441 * Close FTP data transfer interface 442 * 443 * @v xfer FTP data transfer interface 444 * @v rc Reason for close 445 */ 446static void ftp_xfer_closed ( struct xfer_interface *xfer, int rc ) { 447 struct ftp_request *ftp = 448 container_of ( xfer, struct ftp_request, xfer ); 449 450 DBGC ( ftp, "FTP %p data transfer interface closed: %s\n", 451 ftp, strerror ( rc ) ); 452 453 ftp_done ( ftp, rc ); 454} 455 456/** FTP data transfer interface operations */ 457static struct xfer_interface_operations ftp_xfer_operations = { 458 .close = ftp_xfer_closed, 459 .vredirect = ignore_xfer_vredirect, 460 .window = unlimited_xfer_window, 461 .alloc_iob = default_xfer_alloc_iob, 462 .deliver_iob = xfer_deliver_as_raw, 463 .deliver_raw = ignore_xfer_deliver_raw, 464}; 465 466/***************************************************************************** 467 * 468 * URI opener 469 * 470 */ 471 472/** 473 * Initiate an FTP connection 474 * 475 * @v xfer Data transfer interface 476 * @v uri Uniform Resource Identifier 477 * @ret rc Return status code 478 */ 479static int ftp_open ( struct xfer_interface *xfer, struct uri *uri ) { 480 struct ftp_request *ftp; 481 struct sockaddr_tcpip server; 482 int rc; 483 484 /* Sanity checks */ 485 if ( ! uri->path ) 486 return -EINVAL; 487 if ( ! uri->host ) 488 return -EINVAL; 489 490 /* Allocate and populate structure */ 491 ftp = zalloc ( sizeof ( *ftp ) ); 492 if ( ! ftp ) 493 return -ENOMEM; 494 ftp->refcnt.free = ftp_free; 495 xfer_init ( &ftp->xfer, &ftp_xfer_operations, &ftp->refcnt ); 496 ftp->uri = uri_get ( uri ); 497 xfer_init ( &ftp->control, &ftp_control_operations, &ftp->refcnt ); 498 xfer_init ( &ftp->data, &ftp_data_operations, &ftp->refcnt ); 499 ftp->recvbuf = ftp->status_text; 500 ftp->recvsize = sizeof ( ftp->status_text ) - 1; 501 502 DBGC ( ftp, "FTP %p fetching %s\n", ftp, ftp->uri->path ); 503 504 /* Open control connection */ 505 memset ( &server, 0, sizeof ( server ) ); 506 server.st_port = htons ( uri_port ( uri, FTP_PORT ) ); 507 if ( ( rc = xfer_open_named_socket ( &ftp->control, SOCK_STREAM, 508 ( struct sockaddr * ) &server, 509 uri->host, NULL ) ) != 0 ) 510 goto err; 511 512 /* Attach to parent interface, mortalise self, and return */ 513 xfer_plug_plug ( &ftp->xfer, xfer ); 514 ref_put ( &ftp->refcnt ); 515 return 0; 516 517 err: 518 DBGC ( ftp, "FTP %p could not create request: %s\n", 519 ftp, strerror ( rc ) ); 520 ftp_done ( ftp, rc ); 521 ref_put ( &ftp->refcnt ); 522 return rc; 523} 524 525/** FTP URI opener */ 526struct uri_opener ftp_uri_opener __uri_opener = { 527 .scheme = "ftp", 528 .open = ftp_open, 529}; 530