Webserver+3d print
Embed:
(wiki syntax)
Show/hide line numbers
ftp_client.c
Go to the documentation of this file.
00001 /** 00002 * @file ftp_client.c 00003 * @brief FTP client (File Transfer Protocol) 00004 * 00005 * @section License 00006 * 00007 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. 00008 * 00009 * This file is part of CycloneTCP Open. 00010 * 00011 * This program is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU General Public License 00013 * as published by the Free Software Foundation; either version 2 00014 * of the License, or (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License 00022 * along with this program; if not, write to the Free Software Foundation, 00023 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00024 * 00025 * @section Description 00026 * 00027 * File Transfer Protocol (FTP) is a standard network protocol used to 00028 * transfer files from one host to another host over a TCP-based network. 00029 * Refer to the following RFCs for complete details: 00030 * - RFC 959: File Transfer Protocol (FTP) 00031 * - RFC 2428: FTP Extensions for IPv6 and NATs 00032 * 00033 * @author Oryx Embedded SARL (www.oryx-embedded.com) 00034 * @version 1.7.6 00035 **/ 00036 00037 //Switch to the appropriate trace level 00038 #define TRACE_LEVEL FTP_TRACE_LEVEL 00039 00040 //Dependencies 00041 #include <stdlib.h> 00042 #include <ctype.h> 00043 #include "ftp/ftp_client.h" 00044 #include "str.h" 00045 #include "error.h" 00046 #include "debug.h" 00047 00048 //Check TCP/IP stack configuration 00049 #if (FTP_CLIENT_SUPPORT == ENABLED) 00050 00051 00052 /** 00053 * @brief Set the port to be used in data connection 00054 * @param[in] context Pointer to the FTP client context 00055 * @param[in] ipAddr Host address 00056 * @param[in] port Port number 00057 * @return Error code 00058 **/ 00059 00060 error_t ftpSetPort(FtpClientContext *context, const IpAddr *ipAddr, uint16_t port) 00061 { 00062 error_t error; 00063 uint_t replyCode; 00064 char_t *p; 00065 00066 //Invalid context? 00067 if(context == NULL) 00068 return ERROR_INVALID_PARAMETER; 00069 00070 #if (IPV4_SUPPORT == ENABLED) 00071 //IPv4 FTP client? 00072 if(ipAddr->length == sizeof(Ipv4Addr)) 00073 { 00074 //Format the PORT command 00075 strcpy(context->buffer, "PORT "); 00076 00077 //Append host address 00078 ipv4AddrToString(ipAddr->ipv4Addr, context->buffer + 5); 00079 00080 //Parse the resulting string 00081 for(p = context->buffer; *p != '\0'; p++) 00082 { 00083 //Change dots to commas 00084 if(*p == '.') *p = ','; 00085 } 00086 00087 //Append port number 00088 sprintf(p, ",%" PRIu8 ",%" PRIu8 "\r\n", MSB(port), LSB(port)); 00089 } 00090 else 00091 #endif 00092 #if (IPV6_SUPPORT == ENABLED) 00093 //IPv6 FTP client? 00094 if(ipAddr->length == sizeof(Ipv6Addr)) 00095 { 00096 //Format the EPRT command 00097 strcpy(context->buffer, "EPRT |2|"); 00098 00099 //Append host address 00100 ipv6AddrToString(&ipAddr->ipv6Addr, context->buffer + 8); 00101 00102 //Point to the end of the resulting string 00103 p = context->buffer + strlen(context->buffer); 00104 //Append port number 00105 sprintf(p, "|%" PRIu16 "|\r\n", port); 00106 } 00107 else 00108 #endif 00109 //Invalid IP address? 00110 { 00111 //Report an error 00112 return ERROR_INVALID_ADDRESS; 00113 } 00114 00115 //Send the command to the server 00116 error = ftpSendCommand(context, context->buffer, &replyCode); 00117 //Any error to report? 00118 if(error) 00119 return error; 00120 00121 //Check FTP response code 00122 if(!FTP_REPLY_CODE_2YZ(replyCode)) 00123 return ERROR_UNEXPECTED_RESPONSE; 00124 00125 //Successful processing 00126 return NO_ERROR; 00127 } 00128 00129 00130 /** 00131 * @brief Enter passive mode 00132 * @param[in] context Pointer to the FTP client context 00133 * @param[out] port The port number the server is listening on 00134 * @return Error code 00135 **/ 00136 00137 error_t ftpSetPassiveMode(FtpClientContext *context, uint16_t *port) 00138 { 00139 error_t error; 00140 uint_t replyCode; 00141 char_t delimiter; 00142 char_t *p; 00143 00144 //Invalid context? 00145 if(context == NULL) 00146 return ERROR_INVALID_PARAMETER; 00147 00148 #if (IPV4_SUPPORT == ENABLED) 00149 //IPv4 FTP server? 00150 if(context->serverIpAddr.length == sizeof(Ipv4Addr)) 00151 { 00152 //Send the command to the server 00153 error = ftpSendCommand(context, "PASV\r\n", &replyCode); 00154 //Any error to report? 00155 if(error) 00156 return error; 00157 00158 //Check FTP response code 00159 if(!FTP_REPLY_CODE_2YZ(replyCode)) 00160 return ERROR_UNEXPECTED_RESPONSE; 00161 00162 //Delimiter character 00163 delimiter = ','; 00164 00165 //Retrieve the low byte of the port number 00166 p = strrchr(context->buffer, delimiter); 00167 //Failed to parse the response? 00168 if(!p) 00169 return ERROR_INVALID_SYNTAX; 00170 00171 //Convert the resulting string 00172 *port = atoi(p + 1); 00173 //Split the string 00174 *p = '\0'; 00175 00176 //Retrieve the high byte of the port number 00177 p = strrchr(context->buffer, delimiter); 00178 //Failed to parse the response? 00179 if(!p) 00180 return ERROR_INVALID_SYNTAX; 00181 00182 //Convert the resulting string 00183 *port |= atoi(p + 1) << 8; 00184 } 00185 else 00186 #endif 00187 #if (IPV6_SUPPORT == ENABLED) 00188 //IPv6 FTP server? 00189 if(context->serverIpAddr.length == sizeof(Ipv6Addr)) 00190 { 00191 //Send the command to the server 00192 error = ftpSendCommand(context, "EPSV\r\n", &replyCode); 00193 //Any error to report? 00194 if(error) 00195 return error; 00196 00197 //Check FTP response code 00198 if(!FTP_REPLY_CODE_2YZ(replyCode)) 00199 return ERROR_UNEXPECTED_RESPONSE; 00200 00201 //Search for the opening parenthesis 00202 p = strrchr(context->buffer, '('); 00203 //Failed to parse the response? 00204 if(!p || p[1] == '\0') 00205 return ERROR_INVALID_SYNTAX; 00206 00207 //Retrieve the delimiter character 00208 delimiter = p[1]; 00209 00210 //Search for the last delimiter character 00211 p = strrchr(context->buffer, delimiter); 00212 //Failed to parse the response? 00213 if(!p) 00214 return ERROR_INVALID_SYNTAX; 00215 00216 //Split the string 00217 *p = '\0'; 00218 00219 //Search for the last but one delimiter character 00220 p = strrchr(context->buffer, delimiter); 00221 //Failed to parse the response? 00222 if(!p) 00223 return ERROR_INVALID_SYNTAX; 00224 00225 //Convert the resulting string 00226 *port = atoi(p + 1); 00227 } 00228 else 00229 #endif 00230 //Invalid IP address? 00231 { 00232 //Report an error 00233 return ERROR_INVALID_ADDRESS; 00234 } 00235 00236 //Successful processing 00237 return NO_ERROR; 00238 } 00239 00240 00241 /** 00242 * @brief Set representation type 00243 * @param[in] context Pointer to the FTP client context 00244 * @param[in] type Single character identifying the desired type 00245 * @return Error code 00246 **/ 00247 00248 error_t ftpSetType(FtpClientContext *context, char_t type) 00249 { 00250 error_t error; 00251 uint_t replyCode; 00252 00253 //Invalid context? 00254 if(context == NULL) 00255 return ERROR_INVALID_PARAMETER; 00256 00257 //Format the TYPE command 00258 sprintf(context->buffer, "TYPE %c\r\n", type); 00259 00260 //Send the command to the server 00261 error = ftpSendCommand(context, context->buffer, &replyCode); 00262 //Any error to report? 00263 if(error) 00264 return error; 00265 00266 //Check FTP response code 00267 if(!FTP_REPLY_CODE_2YZ(replyCode)) 00268 return ERROR_UNEXPECTED_RESPONSE; 00269 00270 //Successful processing 00271 return NO_ERROR; 00272 } 00273 00274 00275 /** 00276 * @brief Set protection buffer size 00277 * @param[in] context Pointer to the FTP client context 00278 * @param[in] size Protection buffer size 00279 * @return Error code 00280 **/ 00281 00282 error_t ftpSetProtectionBufferSize(FtpClientContext *context, uint32_t size) 00283 { 00284 error_t error; 00285 uint_t replyCode; 00286 00287 //Invalid context? 00288 if(context == NULL) 00289 return ERROR_INVALID_PARAMETER; 00290 00291 //Format the PBSZ command 00292 sprintf(context->buffer, "PBSZ %" PRIu32 "\r\n", size); 00293 00294 //Send the command to the server 00295 error = ftpSendCommand(context, context->buffer, &replyCode); 00296 //Any error to report? 00297 if(error) 00298 return error; 00299 00300 //Check FTP response code 00301 if(!FTP_REPLY_CODE_2YZ(replyCode)) 00302 return ERROR_UNEXPECTED_RESPONSE; 00303 00304 //Successful processing 00305 return NO_ERROR; 00306 } 00307 00308 00309 /** 00310 * @brief Set data channel protection level 00311 * @param[in] context Pointer to the FTP client context 00312 * @param[in] level Character specifying the data channel protection level 00313 * @return Error code 00314 **/ 00315 00316 error_t ftpSetDataChannelProtectionLevel(FtpClientContext *context, char_t level) 00317 { 00318 error_t error; 00319 uint_t replyCode; 00320 00321 //Invalid context? 00322 if(context == NULL) 00323 return ERROR_INVALID_PARAMETER; 00324 00325 //Format the PROT command 00326 sprintf(context->buffer, "PROT %c\r\n", level); 00327 00328 //Send the command to the server 00329 error = ftpSendCommand(context, context->buffer, &replyCode); 00330 //Any error to report? 00331 if(error) 00332 return error; 00333 00334 //Check FTP response code 00335 if(!FTP_REPLY_CODE_2YZ(replyCode)) 00336 return ERROR_UNEXPECTED_RESPONSE; 00337 00338 //Successful processing 00339 return NO_ERROR; 00340 } 00341 00342 00343 /** 00344 * @brief Establish a connection with the specified FTP server 00345 * @param[in] context Pointer to the FTP client context 00346 * @param[in] interface Underlying network interface (optional parameter) 00347 * @param[in] serverIpAddr IP address of the FTP server 00348 * @param[in] serverPort Port number 00349 * @param[in] flags Connection options 00350 * @return Error code 00351 **/ 00352 00353 error_t ftpConnect(FtpClientContext *context, NetInterface *interface, 00354 const IpAddr *serverIpAddr, uint16_t serverPort, uint_t flags) 00355 { 00356 error_t error; 00357 uint_t replyCode; 00358 00359 //Invalid context? 00360 if(context == NULL) 00361 return ERROR_INVALID_PARAMETER; 00362 00363 //Initialize context 00364 context->passiveMode = FALSE; 00365 context->controlSocket = NULL; 00366 context->dataSocket = NULL; 00367 00368 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED) 00369 context->controlTlsContext = NULL; 00370 context->dataTlsContext = NULL; 00371 #endif 00372 00373 //Underlying network interface 00374 context->interface = interface; 00375 //Save the IP address of the FTP server 00376 context->serverIpAddr = *serverIpAddr; 00377 00378 //Use passive mode? 00379 if(flags & FTP_PASSIVE_MODE) 00380 context->passiveMode = TRUE; 00381 00382 //Open control socket 00383 context->controlSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP); 00384 //Failed to open socket? 00385 if(!context->controlSocket) 00386 return ERROR_OPEN_FAILED; 00387 00388 //Start of exception handling block 00389 do 00390 { 00391 //Bind the socket to a particular network interface? 00392 if(context->interface != NULL) 00393 { 00394 //Associate the socket with the relevant interface 00395 error = socketBindToInterface(context->controlSocket, context->interface); 00396 //Any error to report? 00397 if(error) 00398 break; 00399 } 00400 00401 //Set timeout for blocking operations 00402 error = socketSetTimeout(context->controlSocket, FTP_CLIENT_DEFAULT_TIMEOUT); 00403 //Any error to report? 00404 if(error) 00405 break; 00406 00407 //Specify the size of the send buffer 00408 error = socketSetTxBufferSize(context->controlSocket, 00409 FTP_CLIENT_SOCKET_MIN_TX_BUFFER_SIZE); 00410 //Any error to report? 00411 if(error) 00412 break; 00413 00414 //Specify the size of the receive buffer 00415 error = socketSetRxBufferSize(context->controlSocket, 00416 FTP_CLIENT_SOCKET_MIN_RX_BUFFER_SIZE); 00417 //Any error to report? 00418 if(error) 00419 break; 00420 00421 //Connect to the FTP server 00422 error = socketConnect(context->controlSocket, serverIpAddr, serverPort); 00423 //Connection to server failed? 00424 if(error) 00425 break; 00426 00427 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED) 00428 //Use implicit FTPS? 00429 if(flags & FTP_IMPLICIT_SECURITY) 00430 { 00431 //SSL initialization 00432 error = ftpInitControlTlsContext(context); 00433 //Any error to report? 00434 if(error) 00435 break; 00436 } 00437 #endif 00438 00439 //Wait for the connection greeting reply 00440 error = ftpSendCommand(context, NULL, &replyCode); 00441 //Any communication error to report? 00442 if(error) 00443 break; 00444 00445 //Check FTP response code 00446 if(!FTP_REPLY_CODE_2YZ(replyCode)) 00447 error = ERROR_UNEXPECTED_RESPONSE; 00448 00449 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED) 00450 //Use explicit FTPS? 00451 if(flags & FTP_EXPLICIT_SECURITY) 00452 { 00453 //Sanity check 00454 if(context->controlTlsContext == NULL) 00455 { 00456 //The client issues an AUTH TLS command 00457 error = ftpAuth(context); 00458 //Any error to report? 00459 if(error) 00460 break; 00461 00462 //SSL initialization 00463 error = ftpInitControlTlsContext(context); 00464 //Any error to report? 00465 if(error) 00466 break; 00467 } 00468 } 00469 #endif 00470 00471 //End of exception handling block 00472 } while(0); 00473 00474 //Any error to report? 00475 if(error) 00476 { 00477 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED) 00478 if(context->controlTlsContext != NULL) 00479 { 00480 //Release SSL context 00481 tlsFree(context->controlTlsContext); 00482 context->controlTlsContext = NULL; 00483 } 00484 #endif 00485 00486 //Close socket 00487 socketClose(context->controlSocket); 00488 context->controlSocket = NULL; 00489 } 00490 00491 //Return status code 00492 return error; 00493 } 00494 00495 00496 /** 00497 * @brief Request authentication 00498 * @param[in] context Pointer to the FTP client context 00499 * @return Error code 00500 **/ 00501 00502 error_t ftpAuth(FtpClientContext *context) 00503 { 00504 error_t error; 00505 uint_t replyCode; 00506 00507 //Invalid context? 00508 if(context == NULL) 00509 return ERROR_INVALID_PARAMETER; 00510 00511 //Issue the AUTH command 00512 error = ftpSendCommand(context, "AUTH TLS\r\n", &replyCode); 00513 //Any error to report? 00514 if(error) 00515 return error; 00516 00517 //Check FTP response code 00518 if(!FTP_REPLY_CODE_2YZ(replyCode)) 00519 return ERROR_UNEXPECTED_RESPONSE; 00520 00521 //Successful processing 00522 return NO_ERROR; 00523 } 00524 00525 00526 /** 00527 * @brief Login to the FTP server using the provided username and password 00528 * @param[in] context Pointer to the FTP client context 00529 * @param[in] username The username to login under 00530 * @param[in] password The password to use 00531 * @param[in] account Account name 00532 * @return Error code 00533 **/ 00534 00535 error_t ftpLogin(FtpClientContext *context, const char_t *username, 00536 const char_t *password, const char_t *account) 00537 { 00538 error_t error; 00539 uint_t replyCode; 00540 00541 //Invalid context? 00542 if(context == NULL) 00543 return ERROR_INVALID_PARAMETER; 00544 00545 //Format the USER command 00546 sprintf(context->buffer, "USER %s\r\n", username); 00547 00548 //Send the command to the server 00549 error = ftpSendCommand(context, context->buffer, &replyCode); 00550 //Any error to report? 00551 if(error) 00552 return error; 00553 00554 //Check FTP response code 00555 if(FTP_REPLY_CODE_2YZ(replyCode)) 00556 return NO_ERROR; 00557 else if(!FTP_REPLY_CODE_3YZ(replyCode)) 00558 return ERROR_UNEXPECTED_RESPONSE; 00559 00560 //Format the PASS command 00561 sprintf(context->buffer, "PASS %s\r\n", password); 00562 00563 //Send the command to the server 00564 error = ftpSendCommand(context, context->buffer, &replyCode); 00565 //Any error to report? 00566 if(error) 00567 return error; 00568 00569 //Check FTP response code 00570 if(FTP_REPLY_CODE_2YZ(replyCode)) 00571 return NO_ERROR; 00572 else if(!FTP_REPLY_CODE_3YZ(replyCode)) 00573 return ERROR_UNEXPECTED_RESPONSE; 00574 00575 //Format the ACCT command 00576 sprintf(context->buffer, "ACCT %s\r\n", account); 00577 00578 //Send the command to the server 00579 error = ftpSendCommand(context, context->buffer, &replyCode); 00580 //Any error to report? 00581 if(error) 00582 return error; 00583 00584 //Check FTP response code 00585 if(!FTP_REPLY_CODE_2YZ(replyCode)) 00586 return ERROR_UNEXPECTED_RESPONSE; 00587 00588 //Successful processing 00589 return NO_ERROR; 00590 } 00591 00592 00593 /** 00594 * @brief Get the working directory from the FTP server 00595 * @param[in] context Pointer to the FTP client context 00596 * @param[out] path Output buffer where to store the current directory 00597 * @param[in] size Size of the output buffer 00598 * @return Error code 00599 **/ 00600 00601 error_t ftpGetWorkingDir(FtpClientContext *context, char_t *path, size_t size) 00602 { 00603 error_t error; 00604 size_t length; 00605 uint_t replyCode; 00606 char_t *p; 00607 00608 //Invalid context? 00609 if(context == NULL) 00610 return ERROR_INVALID_PARAMETER; 00611 //Check parameters 00612 if(path == NULL || size == 0) 00613 return ERROR_INVALID_PARAMETER; 00614 00615 //Send the command to the server 00616 error = ftpSendCommand(context, "PWD\r\n", &replyCode); 00617 //Any error to report? 00618 if(error) 00619 return error; 00620 00621 //Check FTP response code 00622 if(!FTP_REPLY_CODE_2YZ(replyCode)) 00623 return ERROR_UNEXPECTED_RESPONSE; 00624 00625 //Search for the last double quote 00626 p = strrchr(context->buffer, '\"'); 00627 //Failed to parse the response? 00628 if(!p) 00629 return ERROR_INVALID_SYNTAX; 00630 00631 //Split the string 00632 *p = '\0'; 00633 00634 //Search for the first double quote 00635 p = strchr(context->buffer, '\"'); 00636 //Failed to parse the response? 00637 if(!p) 00638 return ERROR_INVALID_SYNTAX; 00639 00640 //Retrieve the length of the working directory 00641 length = strlen(p + 1); 00642 //Limit the number of characters to copy 00643 length = MIN(length, size - 1); 00644 00645 //Copy the string 00646 strncpy(path, p + 1, length); 00647 //Properly terminate the string with a NULL character 00648 path[length] = '\0'; 00649 00650 //Successful processing 00651 return NO_ERROR; 00652 } 00653 00654 00655 /** 00656 * @brief Change the current working directory of the FTP session 00657 * @param[in] context Pointer to the FTP client context 00658 * @param[in] path The new current working directory 00659 * @return Error code 00660 **/ 00661 00662 error_t ftpChangeWorkingDir(FtpClientContext *context, const char_t *path) 00663 { 00664 error_t error; 00665 uint_t replyCode; 00666 00667 //Invalid context? 00668 if(context == NULL) 00669 return ERROR_INVALID_PARAMETER; 00670 00671 //Format the CWD command 00672 sprintf(context->buffer, "CWD %s\r\n", path); 00673 00674 //Send the command to the server 00675 error = ftpSendCommand(context, context->buffer, &replyCode); 00676 //Any error to report? 00677 if(error) 00678 return error; 00679 00680 //Check FTP response code 00681 if(!FTP_REPLY_CODE_2YZ(replyCode)) 00682 return ERROR_UNEXPECTED_RESPONSE; 00683 00684 //Successful processing 00685 return NO_ERROR; 00686 } 00687 00688 00689 /** 00690 * @brief Change the current working directory to the parent directory 00691 * @param[in] context Pointer to the FTP client context 00692 * @return Error code 00693 **/ 00694 00695 error_t ftpChangeToParentDir(FtpClientContext *context) 00696 { 00697 error_t error; 00698 uint_t replyCode; 00699 00700 //Invalid context? 00701 if(context == NULL) 00702 return ERROR_INVALID_PARAMETER; 00703 00704 //Send the command to the server 00705 error = ftpSendCommand(context, "CDUP\r\n", &replyCode); 00706 //Any error to report? 00707 if(error) 00708 return error; 00709 00710 //Check FTP response code 00711 if(!FTP_REPLY_CODE_2YZ(replyCode)) 00712 return ERROR_UNEXPECTED_RESPONSE; 00713 00714 //Successful processing 00715 return NO_ERROR; 00716 } 00717 00718 00719 /** 00720 * @brief Create a new directory 00721 * @param[in] context Pointer to the FTP client context 00722 * @param[in] path The name of the new directory 00723 * @return Error code 00724 **/ 00725 00726 error_t ftpMakeDir(FtpClientContext *context, const char_t *path) 00727 { 00728 error_t error; 00729 uint_t replyCode; 00730 00731 //Invalid context? 00732 if(context == NULL) 00733 return ERROR_INVALID_PARAMETER; 00734 00735 //Format the MKD command 00736 sprintf(context->buffer, "MKD %s\r\n", path); 00737 00738 //Send the command to the server 00739 error = ftpSendCommand(context, context->buffer, &replyCode); 00740 //Any error to report? 00741 if(error) 00742 return error; 00743 00744 //Check FTP response code 00745 if(!FTP_REPLY_CODE_2YZ(replyCode)) 00746 return ERROR_UNEXPECTED_RESPONSE; 00747 00748 //Successful processing 00749 return NO_ERROR; 00750 } 00751 00752 00753 /** 00754 * @brief Remove a directory on the FTP server 00755 * @param[in] context Pointer to the FTP client context 00756 * @param[in] path Path to the directory to be removed 00757 * @return Error code 00758 **/ 00759 00760 error_t ftpRemoveDir(FtpClientContext *context, const char_t *path) 00761 { 00762 error_t error; 00763 uint_t replyCode; 00764 00765 //Invalid context? 00766 if(context == NULL) 00767 return ERROR_INVALID_PARAMETER; 00768 00769 //Format the RMD command 00770 sprintf(context->buffer, "RMD %s\r\n", path); 00771 00772 //Send the command to the server 00773 error = ftpSendCommand(context, context->buffer, &replyCode); 00774 //Any error to report? 00775 if(error) 00776 return error; 00777 00778 //Check FTP response code 00779 if(!FTP_REPLY_CODE_2YZ(replyCode)) 00780 return ERROR_UNEXPECTED_RESPONSE; 00781 00782 //Successful processing 00783 return NO_ERROR; 00784 } 00785 00786 00787 /** 00788 * @brief Open a file for reading, writing, or appending 00789 * @param[in] context Pointer to the FTP client context 00790 * @param[in] path Path to the file to be be opened 00791 * @param[in] flags Access mode 00792 * @return Error code 00793 **/ 00794 00795 error_t ftpOpenFile(FtpClientContext *context, const char_t *path, uint_t flags) 00796 { 00797 error_t error; 00798 uint_t replyCode; 00799 IpAddr ipAddr; 00800 uint16_t port; 00801 00802 //Invalid context? 00803 if(context == NULL) 00804 return ERROR_INVALID_PARAMETER; 00805 00806 //Open data socket 00807 context->dataSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP); 00808 //Failed to open socket? 00809 if(!context->dataSocket) 00810 return ERROR_OPEN_FAILED; 00811 00812 //Start of exception handling block 00813 do 00814 { 00815 //Bind the socket to a particular network interface? 00816 if(context->interface != NULL) 00817 { 00818 //Associate the socket with the relevant interface 00819 error = socketBindToInterface(context->dataSocket, context->interface); 00820 //Any error to report? 00821 if(error) 00822 break; 00823 } 00824 00825 //Set timeout for blocking operations 00826 error = socketSetTimeout(context->dataSocket, FTP_CLIENT_DEFAULT_TIMEOUT); 00827 //Any error to report? 00828 if(error) 00829 break; 00830 00831 //Check data transfer direction 00832 if(flags & (FTP_FOR_WRITING | FTP_FOR_APPENDING)) 00833 { 00834 //Maximize transmission throughput by using a large buffer 00835 error = socketSetTxBufferSize(context->dataSocket, 00836 FTP_CLIENT_SOCKET_MAX_TX_BUFFER_SIZE); 00837 //Any error to report? 00838 if(error) 00839 break; 00840 00841 //Use a small buffer for the reception path 00842 error = socketSetRxBufferSize(context->dataSocket, 00843 FTP_CLIENT_SOCKET_MIN_RX_BUFFER_SIZE); 00844 //Any error to report? 00845 if(error) 00846 break; 00847 } 00848 else 00849 { 00850 //Use a small buffer for the transmission path 00851 error = socketSetTxBufferSize(context->dataSocket, 00852 FTP_CLIENT_SOCKET_MIN_TX_BUFFER_SIZE); 00853 //Any error to report? 00854 if(error) 00855 break; 00856 00857 //Maximize reception throughput by using a large buffer 00858 error = socketSetRxBufferSize(context->dataSocket, 00859 FTP_CLIENT_SOCKET_MAX_RX_BUFFER_SIZE); 00860 //Any error to report? 00861 if(error) 00862 break; 00863 } 00864 00865 //Set representation type 00866 if(flags & FTP_TEXT_TYPE) 00867 { 00868 //Use ASCII type 00869 error = ftpSetType(context, 'A'); 00870 //Any error to report? 00871 if(error) 00872 break; 00873 } 00874 else 00875 { 00876 //Use image type 00877 error = ftpSetType(context, 'I'); 00878 //Any error to report? 00879 if(error) 00880 break; 00881 } 00882 00883 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED) 00884 //Use SSL? 00885 if(context->controlTlsContext != NULL) 00886 { 00887 //A PBSZ command must be issued, but must have a parameter of '0' to 00888 //indicate that no buffering is taking place and the data connection 00889 //should not be encapsulated 00890 error = ftpSetProtectionBufferSize(context, 0); 00891 //Any communication error to report? 00892 if(error) 00893 break; 00894 00895 //If the data connection security level is 'Private', then an SSL 00896 //negotiation must take place on the data connection 00897 error = ftpSetDataChannelProtectionLevel(context, 'P'); 00898 //Any communication error to report? 00899 if(error) 00900 break; 00901 } 00902 #endif 00903 00904 //Check transfer mode 00905 if(!context->passiveMode) 00906 { 00907 //Place the data socket in the listening state 00908 error = socketListen(context->dataSocket, 1); 00909 //Any error to report? 00910 if(error) 00911 break; 00912 00913 //Retrieve local IP address 00914 error = socketGetLocalAddr(context->controlSocket, &ipAddr, NULL); 00915 //Any error to report? 00916 if(error) 00917 break; 00918 00919 //Retrieve local port number 00920 error = socketGetLocalAddr(context->dataSocket, NULL, &port); 00921 //Any error to report? 00922 if(error) 00923 break; 00924 00925 //Set the port to be used in data connection 00926 error = ftpSetPort(context, &ipAddr, port); 00927 //Any error to report? 00928 if(error) 00929 break; 00930 } 00931 else 00932 { 00933 //Enter passive mode 00934 error = ftpSetPassiveMode(context, &port); 00935 //Any error to report? 00936 if(error) 00937 break; 00938 00939 //Establish data connection 00940 error = socketConnect(context->dataSocket, &context->serverIpAddr, port); 00941 //Connection to server failed? 00942 if(error) 00943 break; 00944 } 00945 00946 //Format the command 00947 if(flags & FTP_FOR_WRITING) 00948 sprintf(context->buffer, "STOR %s\r\n", path); 00949 else if(flags & FTP_FOR_APPENDING) 00950 sprintf(context->buffer, "APPE %s\r\n", path); 00951 else 00952 sprintf(context->buffer, "RETR %s\r\n", path); 00953 00954 //Send the command to the server 00955 error = ftpSendCommand(context, context->buffer, &replyCode); 00956 //Any error to report? 00957 if(error) 00958 break; 00959 00960 //Check FTP response code 00961 if(!FTP_REPLY_CODE_1YZ(replyCode)) 00962 { 00963 //Report an error 00964 error = ERROR_UNEXPECTED_RESPONSE; 00965 break; 00966 } 00967 00968 //Check transfer mode 00969 if(!context->passiveMode) 00970 { 00971 //Wait for the server to connect back to the client's data port 00972 Socket *socket = socketAccept(context->dataSocket, NULL, NULL); 00973 00974 //No connection request? 00975 if(socket == NULL) 00976 { 00977 //Report an error 00978 error = ERROR_FAILURE; 00979 break; 00980 } 00981 00982 //Close the listening socket 00983 socketClose(context->dataSocket); 00984 //Save socket handle 00985 context->dataSocket = socket; 00986 00987 //Set timeout for blocking operations 00988 error = socketSetTimeout(context->dataSocket, FTP_CLIENT_DEFAULT_TIMEOUT); 00989 //Any error to report? 00990 if(error) 00991 break; 00992 } 00993 00994 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED) 00995 //Use SSL? 00996 if(context->controlTlsContext != NULL) 00997 { 00998 //SSL initialization 00999 error = ftpInitDataTlsContext(context); 01000 //Any error to report? 01001 if(error) 01002 break; 01003 } 01004 #endif 01005 01006 //End of exception handling block 01007 } while(0); 01008 01009 //Any error to report? 01010 if(error) 01011 { 01012 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED) 01013 if(context->dataTlsContext != NULL) 01014 { 01015 //Release SSL context 01016 tlsFree(context->dataTlsContext); 01017 context->dataTlsContext = NULL; 01018 } 01019 #endif 01020 01021 //Close socket 01022 socketClose(context->dataSocket); 01023 context->dataSocket = NULL; 01024 } 01025 01026 //Return status code 01027 return error; 01028 } 01029 01030 01031 /** 01032 * @brief Write to a remote file 01033 * @param[in] context Pointer to the FTP client context 01034 * @param[in] data Pointer to a buffer containing the data to be written 01035 * @param[in] length Number of data bytes to write 01036 * @param[in] flags Set of flags that influences the behavior of this function 01037 * @return Error code 01038 **/ 01039 01040 error_t ftpWriteFile(FtpClientContext *context, 01041 const void *data, size_t length, uint_t flags) 01042 { 01043 error_t error; 01044 01045 //Invalid context? 01046 if(context == NULL) 01047 return ERROR_INVALID_PARAMETER; 01048 01049 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED) 01050 if(context->dataTlsContext != NULL) 01051 { 01052 //Transmit data to the FTP server 01053 error = tlsWrite(context->dataTlsContext, data, length, NULL, flags); 01054 } 01055 else 01056 #endif 01057 { 01058 //Transmit data to the FTP server 01059 error = socketSend(context->dataSocket, data, length, NULL, flags); 01060 } 01061 01062 //Return status code 01063 return error; 01064 } 01065 01066 01067 /** 01068 * @brief Read from a remote file 01069 * @param[in] context Pointer to the FTP client context 01070 * @param[out] data Buffer where to store the incoming data 01071 * @param[in] size Maximum number of bytes that can be read 01072 * @param[out] length Actual number of bytes that have been read 01073 * @param[in] flags Set of flags that influences the behavior of this function 01074 * @return Error code 01075 **/ 01076 01077 error_t ftpReadFile(FtpClientContext *context, 01078 void *data, size_t size, size_t *length, uint_t flags) 01079 { 01080 error_t error; 01081 01082 //Invalid context? 01083 if(context == NULL) 01084 return ERROR_INVALID_PARAMETER; 01085 01086 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED) 01087 if(context->dataTlsContext != NULL) 01088 { 01089 //Receive data from the FTP server 01090 error = tlsRead(context->dataTlsContext, data, size, length, flags); 01091 } 01092 else 01093 #endif 01094 { 01095 //Receive data from the FTP server 01096 error = socketReceive(context->dataSocket, data, size, length, flags); 01097 } 01098 01099 //Return status code 01100 return error; 01101 } 01102 01103 01104 /** 01105 * @brief Close file 01106 * @param[in] context Pointer to the FTP client context 01107 * @return Error code 01108 **/ 01109 01110 error_t ftpCloseFile(FtpClientContext *context) 01111 { 01112 error_t error; 01113 uint_t replyCode; 01114 01115 //Invalid context? 01116 if(context == NULL) 01117 return ERROR_INVALID_PARAMETER; 01118 01119 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED) 01120 if(context->dataTlsContext != NULL) 01121 { 01122 //Gracefully close the data connection 01123 tlsShutdown(context->dataTlsContext); 01124 01125 //Release SSL context (data connection) 01126 tlsFree(context->dataTlsContext); 01127 context->dataTlsContext = NULL; 01128 } 01129 #endif 01130 01131 //Graceful shutdown 01132 socketShutdown(context->dataSocket, SOCKET_SD_BOTH); 01133 01134 //Close the data socket 01135 socketClose(context->dataSocket); 01136 context->dataSocket = NULL; 01137 01138 //Check the transfer status 01139 error = ftpSendCommand(context, NULL, &replyCode); 01140 //Any error to report? 01141 if(error) 01142 return error; 01143 01144 //Check FTP response code 01145 if(!FTP_REPLY_CODE_2YZ(replyCode)) 01146 return ERROR_UNEXPECTED_RESPONSE; 01147 01148 //Successful processing 01149 return NO_ERROR; 01150 } 01151 01152 01153 /** 01154 * @brief Rename a remote file 01155 * @param[in] context Pointer to the FTP client context 01156 * @param[in] oldName The name of the remote file to rename 01157 * @param[in] newName The new name of the remote file 01158 * @return Error code 01159 **/ 01160 01161 error_t ftpRenameFile(FtpClientContext *context, 01162 const char_t *oldName, const char_t *newName) 01163 { 01164 error_t error; 01165 uint_t replyCode; 01166 01167 //Invalid context? 01168 if(context == NULL) 01169 return ERROR_INVALID_PARAMETER; 01170 01171 //Format the RNFR command 01172 sprintf(context->buffer, "RNFR %s\r\n", oldName); 01173 01174 //Send the command to the server 01175 error = ftpSendCommand(context, context->buffer, &replyCode); 01176 //Any error to report? 01177 if(error) 01178 return error; 01179 01180 //Check FTP response code 01181 if(!FTP_REPLY_CODE_3YZ(replyCode)) 01182 return ERROR_UNEXPECTED_RESPONSE; 01183 01184 //Format the RNTO command 01185 sprintf(context->buffer, "RNTO %s\r\n", newName); 01186 01187 //Send the command to the server 01188 error = ftpSendCommand(context, context->buffer, &replyCode); 01189 //Any error to report? 01190 if(error) 01191 return error; 01192 01193 //Check FTP response code 01194 if(!FTP_REPLY_CODE_2YZ(replyCode)) 01195 return ERROR_UNEXPECTED_RESPONSE; 01196 01197 //Successful processing 01198 return NO_ERROR; 01199 } 01200 01201 01202 /** 01203 * @brief Delete a file 01204 * @param[in] context Pointer to the FTP client context 01205 * @param[in] path Path to the file to be be deleted 01206 * @return Error code 01207 **/ 01208 01209 error_t ftpDeleteFile(FtpClientContext *context, const char_t *path) 01210 { 01211 error_t error; 01212 uint_t replyCode; 01213 01214 //Invalid context? 01215 if(context == NULL) 01216 return ERROR_INVALID_PARAMETER; 01217 01218 //Format the DELE command 01219 sprintf(context->buffer, "DELE %s\r\n", path); 01220 01221 //Send the command to the server 01222 error = ftpSendCommand(context, context->buffer, &replyCode); 01223 //Any error to report? 01224 if(error) 01225 return error; 01226 01227 //Check FTP response code 01228 if(!FTP_REPLY_CODE_2YZ(replyCode)) 01229 return ERROR_UNEXPECTED_RESPONSE; 01230 01231 //Successful processing 01232 return NO_ERROR; 01233 } 01234 01235 01236 /** 01237 * @brief Close the connection with the FTP server 01238 * @param[in] context Pointer to the FTP client context 01239 * @return Error code 01240 **/ 01241 01242 error_t ftpClose(FtpClientContext *context) 01243 { 01244 //Invalid context? 01245 if(context == NULL) 01246 return ERROR_INVALID_PARAMETER; 01247 01248 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED) 01249 if(context->dataTlsContext != NULL) 01250 { 01251 //Gracefully close the data connection 01252 tlsShutdown(context->dataTlsContext); 01253 01254 //Release SSL context (data connection) 01255 tlsFree(context->dataTlsContext); 01256 context->dataTlsContext = NULL; 01257 } 01258 #endif 01259 01260 //Close data socket 01261 if(context->dataSocket) 01262 { 01263 socketClose(context->dataSocket); 01264 context->dataSocket = NULL; 01265 } 01266 01267 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED) 01268 if(context->controlTlsContext != NULL) 01269 { 01270 //Gracefully close the control connection 01271 tlsShutdown(context->controlTlsContext); 01272 01273 //Release SSL context (control connection) 01274 tlsFree(context->controlTlsContext); 01275 context->controlTlsContext = NULL; 01276 } 01277 #endif 01278 01279 //Close control socket 01280 if(context->controlSocket) 01281 { 01282 socketClose(context->controlSocket); 01283 context->controlSocket = NULL; 01284 } 01285 01286 //Successful processing 01287 return NO_ERROR; 01288 } 01289 01290 01291 /** 01292 * @brief Send FTP command and wait for a reply 01293 * @param[in] context Pointer to the FTP client context 01294 * @param[in] command Command line 01295 * @param[out] replyCode Response code from the FTP server 01296 * @return Error code 01297 **/ 01298 01299 error_t ftpSendCommand(FtpClientContext *context, 01300 const char_t *command, uint_t *replyCode) 01301 { 01302 error_t error; 01303 size_t length; 01304 char_t *p; 01305 01306 //Any command line to send? 01307 if(command) 01308 { 01309 //Debug message 01310 TRACE_DEBUG("FTP client: %s", command); 01311 01312 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED) 01313 if(context->controlTlsContext != NULL) 01314 { 01315 //Send the command to the FTP server 01316 error = tlsWrite(context->controlTlsContext, command, 01317 strlen(command), NULL, SOCKET_FLAG_WAIT_ACK); 01318 } 01319 else 01320 #endif 01321 { 01322 //Send the command to the FTP server 01323 error = socketSend(context->controlSocket, command, 01324 strlen(command), NULL, SOCKET_FLAG_WAIT_ACK); 01325 } 01326 01327 //Failed to send command? 01328 if(error) 01329 return error; 01330 } 01331 01332 //Multiline replies are allowed for any command 01333 while(1) 01334 { 01335 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED) 01336 if(context->controlTlsContext != NULL) 01337 { 01338 //Wait for a response from the server 01339 error = tlsRead(context->controlTlsContext, context->buffer, 01340 FTP_CLIENT_BUFFER_SIZE - 1, &length, SOCKET_FLAG_BREAK_CRLF); 01341 } 01342 else 01343 #endif 01344 { 01345 //Wait for a response from the server 01346 error = socketReceive(context->controlSocket, context->buffer, 01347 FTP_CLIENT_BUFFER_SIZE - 1, &length, SOCKET_FLAG_BREAK_CRLF); 01348 } 01349 01350 //The remote server did not respond as expected? 01351 if(error) 01352 return error; 01353 01354 //Point to the beginning of the buffer 01355 p = context->buffer; 01356 //Properly terminate the string with a NULL character 01357 p[length] = '\0'; 01358 01359 //Remove trailing whitespace from the response 01360 strRemoveTrailingSpace(p); 01361 01362 //Debug message 01363 TRACE_DEBUG("FTP server: %s\r\n", p); 01364 01365 //Check the length of the response 01366 if(strlen(p) >= 3) 01367 { 01368 //All replies begin with a three digit numeric code 01369 if(isdigit((uint8_t) p[0]) && isdigit((uint8_t) p[1]) && isdigit((uint8_t) p[2])) 01370 { 01371 //A space character follows the response code for the last line 01372 if(p[3] == ' ' || p[3] == '\0') 01373 { 01374 //Get the server response code 01375 *replyCode = strtoul(p, NULL, 10); 01376 //Exit immediately 01377 break; 01378 } 01379 } 01380 } 01381 } 01382 01383 //Successful processing 01384 return NO_ERROR; 01385 } 01386 01387 01388 #if (FTP_CLIENT_TLS_SUPPORT == ENABLED) 01389 01390 /** 01391 * @brief Register SSL initialization callback function 01392 * @param[in] context Pointer to the FTP client context 01393 * @param[in] callback SSL initialization callback function 01394 * @return Error code 01395 **/ 01396 01397 error_t ftpRegisterTlsInitCallback(FtpClientContext *context, 01398 FtpClientTlsInitCallback callback) 01399 { 01400 //Check parameters 01401 if(context == NULL || callback == NULL) 01402 return ERROR_INVALID_PARAMETER; 01403 01404 //Save callback function 01405 context->tlsInitCallback = callback; 01406 01407 //Successful processing 01408 return NO_ERROR; 01409 } 01410 01411 01412 /** 01413 * @brief SSL initialization (control connection) 01414 * @param[in] context Pointer to the FTP client context 01415 * @return Error code 01416 **/ 01417 01418 error_t ftpInitControlTlsContext(FtpClientContext *context) 01419 { 01420 error_t error; 01421 01422 //Debug message 01423 TRACE_DEBUG("FTP Client: Initializing SSL session (control)...\r\n"); 01424 01425 //Allocate SSL context 01426 context->controlTlsContext = tlsInit(); 01427 01428 //Initialization failed? 01429 if(context->controlTlsContext == NULL) 01430 return ERROR_OUT_OF_MEMORY; 01431 01432 //Start of exception handling block 01433 do 01434 { 01435 //Select the relevant operation mode 01436 error = tlsSetConnectionEnd(context->controlTlsContext, TLS_CONNECTION_END_CLIENT); 01437 //Any error to report? 01438 if(error) 01439 break; 01440 01441 //Bind SSL to the relevant socket 01442 error = tlsSetSocket(context->controlTlsContext, context->controlSocket); 01443 //Any error to report? 01444 if(error) 01445 break; 01446 01447 //Check whether the SSL initialization callback has been 01448 //properly registered? 01449 if(context->tlsInitCallback != NULL) 01450 { 01451 //Perform SSL related initialization 01452 error = context->tlsInitCallback(context, context->controlTlsContext); 01453 //Any error to report? 01454 if(error) 01455 break; 01456 } 01457 else 01458 { 01459 //No callback function registered 01460 error = ERROR_NOT_CONFIGURED; 01461 break; 01462 } 01463 01464 //Establish a secure session 01465 error = tlsConnect(context->controlTlsContext); 01466 //Any error to report? 01467 if(error) 01468 break; 01469 01470 //Save SSL session 01471 error = tlsSaveSession(context->controlTlsContext, &context->tlsSession); 01472 //Any error to report? 01473 if(error) 01474 break; 01475 01476 //End of exception handling block 01477 } while(0); 01478 01479 //Any error to report? 01480 if(error) 01481 { 01482 //Clean up side effects 01483 tlsFree(context->controlTlsContext); 01484 context->controlTlsContext = NULL; 01485 } 01486 01487 //Return status code 01488 return error; 01489 } 01490 01491 01492 /** 01493 * @brief SSL initialization (data connection) 01494 * @param[in] context Pointer to the FTP client context 01495 * @return Error code 01496 **/ 01497 01498 error_t ftpInitDataTlsContext(FtpClientContext *context) 01499 { 01500 error_t error; 01501 01502 //Debug message 01503 TRACE_DEBUG("FTP Client: Initializing SSL session (data)...\r\n"); 01504 01505 //Allocate SSL context 01506 context->dataTlsContext = tlsInit(); 01507 01508 //Initialization failed? 01509 if(context->dataTlsContext == NULL) 01510 return ERROR_OUT_OF_MEMORY; 01511 01512 //Start of exception handling block 01513 do 01514 { 01515 //Select the relevant operation mode 01516 error = tlsSetConnectionEnd(context->dataTlsContext, TLS_CONNECTION_END_CLIENT); 01517 //Any error to report? 01518 if(error) 01519 break; 01520 01521 //Bind SSL to the relevant socket 01522 error = tlsSetSocket(context->dataTlsContext, context->dataSocket); 01523 //Any error to report? 01524 if(error) 01525 break; 01526 01527 //Check whether the SSL initialization callback has been 01528 //properly registered? 01529 if(context->tlsInitCallback != NULL) 01530 { 01531 //Perform SSL related initialization 01532 error = context->tlsInitCallback(context, context->dataTlsContext); 01533 //Any error to report? 01534 if(error) 01535 break; 01536 } 01537 else 01538 { 01539 //No callback function registered 01540 error = ERROR_NOT_CONFIGURED; 01541 break; 01542 } 01543 01544 //Restore SSL session 01545 error = tlsRestoreSession(context->dataTlsContext, &context->tlsSession); 01546 //Any error to report? 01547 if(error) 01548 break; 01549 01550 //Establish a secure session 01551 error = tlsConnect(context->dataTlsContext); 01552 //Any error to report? 01553 if(error) 01554 break; 01555 01556 //End of exception handling block 01557 } while(0); 01558 01559 //Any error to report? 01560 if(error) 01561 { 01562 //Clean up side effects 01563 tlsFree(context->dataTlsContext); 01564 context->dataTlsContext = NULL; 01565 } 01566 01567 //Return status code 01568 return error; 01569 } 01570 01571 #endif 01572 01573 #endif 01574
Generated on Tue Jul 12 2022 17:10:13 by
