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