[e16e8f2] | 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 | |
---|
| 23 | FEATURE ( 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 | */ |
---|
| 31 | enum 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 | */ |
---|
| 47 | struct 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 | */ |
---|
| 77 | static 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 | */ |
---|
| 93 | static 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 */ |
---|
| 113 | struct 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 | */ |
---|
| 130 | static 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 | */ |
---|
| 140 | static 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 | */ |
---|
| 151 | static 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 */ |
---|
| 157 | static 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 | */ |
---|
| 178 | static 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 | */ |
---|
| 204 | static 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 | */ |
---|
| 218 | static 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 | */ |
---|
| 245 | static 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 | */ |
---|
| 308 | static 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 */ |
---|
| 358 | static 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 | */ |
---|
| 385 | static 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 | */ |
---|
| 408 | static 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 */ |
---|
| 425 | static 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 | */ |
---|
| 446 | static 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 */ |
---|
| 457 | static 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 | */ |
---|
| 479 | static 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 */ |
---|
| 526 | struct uri_opener ftp_uri_opener __uri_opener = { |
---|
| 527 | .scheme = "ftp", |
---|
| 528 | .open = ftp_open, |
---|
| 529 | }; |
---|