LCOV - code coverage report
Current view: top level - external/libfetch - ftp.c (source / functions) Hit Total Coverage
Test: rapport Lines: 0 599 0.0 %
Date: 2021-12-10 16:22:55 Functions: 0 26 0.0 %
Branches: 0 526 0.0 %

           Branch data     Line data    Source code
       1                 :            : /*-
       2                 :            :  * SPDX-License-Identifier: (BSD-3-Clause AND Beerware)
       3                 :            :  *
       4                 :            :  * Copyright (c) 1998-2011 Dag-Erling Smørgrav
       5                 :            :  * All rights reserved.
       6                 :            :  *
       7                 :            :  * Redistribution and use in source and binary forms, with or without
       8                 :            :  * modification, are permitted provided that the following conditions
       9                 :            :  * are met:
      10                 :            :  * 1. Redistributions of source code must retain the above copyright
      11                 :            :  *    notice, this list of conditions and the following disclaimer
      12                 :            :  *    in this position and unchanged.
      13                 :            :  * 2. Redistributions in binary form must reproduce the above copyright
      14                 :            :  *    notice, this list of conditions and the following disclaimer in the
      15                 :            :  *    documentation and/or other materials provided with the distribution.
      16                 :            :  * 3. The name of the author may not be used to endorse or promote products
      17                 :            :  *    derived from this software without specific prior written permission
      18                 :            :  *
      19                 :            :  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
      20                 :            :  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
      21                 :            :  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
      22                 :            :  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
      23                 :            :  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
      24                 :            :  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      25                 :            :  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      26                 :            :  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      27                 :            :  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
      28                 :            :  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      29                 :            :  */
      30                 :            : 
      31                 :            : #include <sys/cdefs.h>
      32                 :            : #include "bsd_compat.h"
      33                 :            : __FBSDID("$FreeBSD: head/lib/libfetch/ftp.c 341013 2018-11-27 10:45:14Z des $");
      34                 :            : 
      35                 :            : /*
      36                 :            :  * Portions of this code were taken from or based on ftpio.c:
      37                 :            :  *
      38                 :            :  * ----------------------------------------------------------------------------
      39                 :            :  * "THE BEER-WARE LICENSE" (Revision 42):
      40                 :            :  * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
      41                 :            :  * can do whatever you want with this stuff. If we meet some day, and you think
      42                 :            :  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
      43                 :            :  * ----------------------------------------------------------------------------
      44                 :            :  *
      45                 :            :  * Major Changelog:
      46                 :            :  *
      47                 :            :  * Dag-Erling Smørgrav
      48                 :            :  * 9 Jun 1998
      49                 :            :  *
      50                 :            :  * Incorporated into libfetch
      51                 :            :  *
      52                 :            :  * Jordan K. Hubbard
      53                 :            :  * 17 Jan 1996
      54                 :            :  *
      55                 :            :  * Turned inside out. Now returns xfers as new file ids, not as a special
      56                 :            :  * `state' of FTP_t
      57                 :            :  *
      58                 :            :  * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $
      59                 :            :  *
      60                 :            :  */
      61                 :            : 
      62                 :            : #include <sys/param.h>
      63                 :            : #include <sys/socket.h>
      64                 :            : #include <netinet/in.h>
      65                 :            : 
      66                 :            : #include <ctype.h>
      67                 :            : #include <err.h>
      68                 :            : #include <errno.h>
      69                 :            : #include <fcntl.h>
      70                 :            : #include <netdb.h>
      71                 :            : #include <stdarg.h>
      72                 :            : #include <stdint.h>
      73                 :            : #include <stdio.h>
      74                 :            : #include <stdlib.h>
      75                 :            : #include <string.h>
      76                 :            : #include <time.h>
      77                 :            : #include <unistd.h>
      78                 :            : 
      79                 :            : #include "fetch.h"
      80                 :            : #include "common.h"
      81                 :            : #include "ftperr.h"
      82                 :            : 
      83                 :            : #define FTP_ANONYMOUS_USER      "anonymous"
      84                 :            : 
      85                 :            : #define FTP_CONNECTION_ALREADY_OPEN     125
      86                 :            : #define FTP_OPEN_DATA_CONNECTION        150
      87                 :            : #define FTP_OK                          200
      88                 :            : #define FTP_FILE_STATUS                 213
      89                 :            : #define FTP_SERVICE_READY               220
      90                 :            : #define FTP_TRANSFER_COMPLETE           226
      91                 :            : #define FTP_PASSIVE_MODE                227
      92                 :            : #define FTP_LPASSIVE_MODE               228
      93                 :            : #define FTP_EPASSIVE_MODE               229
      94                 :            : #define FTP_LOGGED_IN                   230
      95                 :            : #define FTP_FILE_ACTION_OK              250
      96                 :            : #define FTP_DIRECTORY_CREATED           257 /* multiple meanings */
      97                 :            : #define FTP_FILE_CREATED                257 /* multiple meanings */
      98                 :            : #define FTP_WORKING_DIRECTORY           257 /* multiple meanings */
      99                 :            : #define FTP_NEED_PASSWORD               331
     100                 :            : #define FTP_NEED_ACCOUNT                332
     101                 :            : #define FTP_FILE_OK                     350
     102                 :            : #define FTP_SYNTAX_ERROR                500
     103                 :            : #define FTP_PROTOCOL_ERROR              999
     104                 :            : 
     105                 :            : static struct url cached_host;
     106                 :            : static conn_t   *cached_connection;
     107                 :            : 
     108                 :            : #define isftpreply(foo)                         \
     109                 :            :         (isdigit((unsigned char)foo[0]) &&      \
     110                 :            :             isdigit((unsigned char)foo[1]) &&   \
     111                 :            :             isdigit((unsigned char)foo[2]) &&   \
     112                 :            :             (foo[3] == ' ' || foo[3] == '\0'))
     113                 :            : #define isftpinfo(foo) \
     114                 :            :         (isdigit((unsigned char)foo[0]) &&      \
     115                 :            :             isdigit((unsigned char)foo[1]) &&   \
     116                 :            :             isdigit((unsigned char)foo[2]) &&   \
     117                 :            :             foo[3] == '-')
     118                 :            : 
     119                 :            : /*
     120                 :            :  * Translate IPv4 mapped IPv6 address to IPv4 address
     121                 :            :  */
     122                 :            : static void
     123                 :          0 : unmappedaddr(struct sockaddr_in6 *sin6)
     124                 :            : {
     125                 :            :         struct sockaddr_in *sin4;
     126                 :            :         u_int32_t addr;
     127                 :            :         int port;
     128                 :            : 
     129   [ #  #  #  # ]:          0 :         if (sin6->sin6_family != AF_INET6 ||
     130   [ #  #  #  # ]:          0 :             !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
     131                 :          0 :                 return;
     132                 :          0 :         sin4 = (struct sockaddr_in *)sin6;
     133                 :          0 :         addr = *(u_int32_t *)(uintptr_t)&sin6->sin6_addr.s6_addr[12];
     134                 :          0 :         port = sin6->sin6_port;
     135                 :          0 :         memset(sin4, 0, sizeof(struct sockaddr_in));
     136                 :          0 :         sin4->sin_addr.s_addr = addr;
     137                 :          0 :         sin4->sin_port = port;
     138                 :          0 :         sin4->sin_family = AF_INET;
     139                 :            : #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LE
     140                 :            :         sin4->sin_len = sizeof(struct sockaddr_in);
     141                 :            : #endif
     142                 :          0 : }
     143                 :            : 
     144                 :            : /*
     145                 :            :  * Get server response
     146                 :            :  */
     147                 :            : static int
     148                 :          0 : ftp_chkerr(conn_t *conn)
     149                 :            : {
     150         [ #  # ]:          0 :         if (fetch_getln(conn) == -1) {
     151                 :          0 :                 fetch_syserr();
     152                 :          0 :                 return (-1);
     153                 :            :         }
     154   [ #  #  #  #  :          0 :         if (isftpinfo(conn->buf)) {
             #  #  #  # ]
     155   [ #  #  #  #  :          0 :                 while (conn->buflen && !isftpreply(conn->buf)) {
          #  #  #  #  #  
                #  #  # ]
     156         [ #  # ]:          0 :                         if (fetch_getln(conn) == -1) {
     157                 :          0 :                                 fetch_syserr();
     158                 :          0 :                                 return (-1);
     159                 :            :                         }
     160                 :            :                 }
     161                 :          0 :         }
     162                 :            : 
     163   [ #  #  #  # ]:          0 :         while (conn->buflen &&
     164                 :          0 :             isspace((unsigned char)conn->buf[conn->buflen - 1]))
     165                 :          0 :                 conn->buflen--;
     166                 :          0 :         conn->buf[conn->buflen] = '\0';
     167                 :            : 
     168   [ #  #  #  #  :          0 :         if (!isftpreply(conn->buf)) {
          #  #  #  #  #  
                      # ]
     169                 :          0 :                 ftp_seterr(FTP_PROTOCOL_ERROR);
     170                 :          0 :                 return (-1);
     171                 :            :         }
     172                 :            : 
     173                 :          0 :         conn->err = (conn->buf[0] - '0') * 100
     174                 :          0 :             + (conn->buf[1] - '0') * 10
     175                 :          0 :             + (conn->buf[2] - '0');
     176                 :            : 
     177                 :          0 :         return (conn->err);
     178                 :          0 : }
     179                 :            : 
     180                 :            : /*
     181                 :            :  * Send a command and check reply
     182                 :            :  */
     183                 :            : static int
     184                 :          0 : ftp_cmd(conn_t *conn, const char *fmt, ...)
     185                 :            : {
     186                 :            :         va_list ap;
     187                 :            :         size_t len;
     188                 :            :         char *msg;
     189                 :            :         int r;
     190                 :            : 
     191                 :          0 :         va_start(ap, fmt);
     192                 :          0 :         len = vasprintf(&msg, fmt, ap);
     193                 :          0 :         va_end(ap);
     194                 :            : 
     195         [ #  # ]:          0 :         if (msg == NULL) {
     196                 :          0 :                 errno = ENOMEM;
     197                 :          0 :                 fetch_syserr();
     198                 :          0 :                 return (-1);
     199                 :            :         }
     200                 :            : 
     201                 :          0 :         r = fetch_putln(conn, msg, len);
     202                 :          0 :         free(msg);
     203                 :            : 
     204         [ #  # ]:          0 :         if (r == -1) {
     205                 :          0 :                 fetch_syserr();
     206                 :          0 :                 return (-1);
     207                 :            :         }
     208                 :            : 
     209                 :          0 :         return (ftp_chkerr(conn));
     210                 :          0 : }
     211                 :            : 
     212                 :            : /*
     213                 :            :  * Return a pointer to the filename part of a path
     214                 :            :  */
     215                 :            : static const char *
     216                 :          0 : ftp_filename(const char *file, int *len, int *type)
     217                 :            : {
     218                 :            :         const char *s;
     219                 :            : 
     220         [ #  # ]:          0 :         if ((s = strrchr(file, '/')) == NULL)
     221                 :          0 :                 s = file;
     222                 :            :         else
     223                 :          0 :                 s = s + 1;
     224                 :          0 :         *len = strlen(s);
     225   [ #  #  #  # ]:          0 :         if (*len > 7 && strncmp(s + *len - 7, ";type=", 6) == 0) {
     226                 :          0 :                 *type = s[*len - 1];
     227                 :          0 :                 *len -= 7;
     228                 :          0 :         } else {
     229                 :          0 :                 *type = '\0';
     230                 :            :         }
     231                 :          0 :         return (s);
     232                 :            : }
     233                 :            : 
     234                 :            : /*
     235                 :            :  * Get current working directory from the reply to a CWD, PWD or CDUP
     236                 :            :  * command.
     237                 :            :  */
     238                 :            : static int
     239                 :          0 : ftp_pwd(conn_t *conn, char *pwd, size_t pwdlen)
     240                 :            : {
     241                 :            :         char *src, *dst, *end;
     242                 :            :         int q;
     243                 :            : 
     244   [ #  #  #  # ]:          0 :         if (conn->err != FTP_WORKING_DIRECTORY &&
     245                 :          0 :             conn->err != FTP_FILE_ACTION_OK)
     246                 :          0 :                 return (FTP_PROTOCOL_ERROR);
     247                 :          0 :         end = conn->buf + conn->buflen;
     248                 :          0 :         src = conn->buf + 4;
     249   [ #  #  #  # ]:          0 :         if (src >= end || *src++ != '"')
     250                 :          0 :                 return (FTP_PROTOCOL_ERROR);
     251   [ #  #  #  # ]:          0 :         for (q = 0, dst = pwd; src < end && pwdlen--; ++src) {
     252   [ #  #  #  # ]:          0 :                 if (!q && *src == '"')
     253                 :          0 :                         q = 1;
     254   [ #  #  #  # ]:          0 :                 else if (q && *src != '"')
     255                 :          0 :                         break;
     256         [ #  # ]:          0 :                 else if (q)
     257                 :          0 :                         *dst++ = '"', q = 0;
     258                 :            :                 else
     259                 :          0 :                         *dst++ = *src;
     260                 :          0 :         }
     261         [ #  # ]:          0 :         if (!pwdlen)
     262                 :          0 :                 return (FTP_PROTOCOL_ERROR);
     263                 :          0 :         *dst = '\0';
     264                 :            : #if 0
     265                 :            :         DEBUGF("pwd: [%s]\n", pwd);
     266                 :            : #endif
     267                 :          0 :         return (FTP_OK);
     268                 :          0 : }
     269                 :            : 
     270                 :            : /*
     271                 :            :  * Change working directory to the directory that contains the specified
     272                 :            :  * file.
     273                 :            :  */
     274                 :            : static int
     275                 :          0 : ftp_cwd(conn_t *conn, const char *file)
     276                 :            : {
     277                 :            :         const char *beg, *end;
     278                 :            :         char pwd[PATH_MAX];
     279                 :            :         int e, i, len;
     280                 :            : 
     281                 :            :         /* If no slashes in name, no need to change dirs. */
     282         [ #  # ]:          0 :         if ((end = strrchr(file, '/')) == NULL)
     283                 :          0 :                 return (0);
     284   [ #  #  #  # ]:          0 :         if ((e = ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY ||
     285                 :          0 :             (e = ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) {
     286                 :          0 :                 ftp_seterr(e);
     287                 :          0 :                 return (-1);
     288                 :            :         }
     289                 :          0 :         for (;;) {
     290                 :          0 :                 len = strlen(pwd);
     291                 :            : 
     292                 :            :                 /* Look for a common prefix between PWD and dir to fetch. */
     293   [ #  #  #  # ]:          0 :                 for (i = 0; i <= len && i <= end - file; ++i)
     294         [ #  # ]:          0 :                         if (pwd[i] != file[i])
     295                 :          0 :                                 break;
     296                 :            : #if 0
     297                 :            :                 DEBUGF("have: [%.*s|%s]\n", i, pwd, pwd + i);
     298                 :            :                 DEBUGF("want: [%.*s|%s]\n", i, file, file + i);
     299                 :            : #endif
     300                 :            :                 /* Keep going up a dir until we have a matching prefix. */
     301   [ #  #  #  #  :          0 :                 if (pwd[i] == '\0' && (file[i - 1] == '/' || file[i] == '/'))
                   #  # ]
     302                 :          0 :                         break;
     303   [ #  #  #  # ]:          0 :                 if ((e = ftp_cmd(conn, "CDUP")) != FTP_FILE_ACTION_OK ||
     304         [ #  # ]:          0 :                     (e = ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY ||
     305                 :          0 :                     (e = ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) {
     306                 :          0 :                         ftp_seterr(e);
     307                 :          0 :                         return (-1);
     308                 :            :                 }
     309                 :            :         }
     310                 :            : 
     311                 :            : #ifdef FTP_COMBINE_CWDS
     312                 :            :         /* Skip leading slashes, even "////". */
     313                 :            :         for (beg = file + i; beg < end && *beg == '/'; ++beg, ++i)
     314                 :            :                 /* nothing */ ;
     315                 :            : 
     316                 :            :         /* If there is no trailing dir, we're already there. */
     317                 :            :         if (beg >= end)
     318                 :            :                 return (0);
     319                 :            : 
     320                 :            :         /* Change to the directory all in one chunk (e.g., foo/bar/baz). */
     321                 :            :         e = ftp_cmd(conn, "CWD %.*s", (int)(end - beg), beg);
     322                 :            :         if (e == FTP_FILE_ACTION_OK)
     323                 :            :                 return (0);
     324                 :            : #endif /* FTP_COMBINE_CWDS */
     325                 :            : 
     326                 :            :         /* That didn't work so go back to legacy behavior (multiple CWDs). */
     327         [ #  # ]:          0 :         for (beg = file + i; beg < end; beg = file + i + 1) {
     328         [ #  # ]:          0 :                 while (*beg == '/')
     329                 :          0 :                         ++beg, ++i;
     330   [ #  #  #  # ]:          0 :                 for (++i; file + i < end && file[i] != '/'; ++i)
     331                 :            :                         /* nothing */ ;
     332                 :          0 :                 e = ftp_cmd(conn, "CWD %.*s", file + i - beg, beg);
     333         [ #  # ]:          0 :                 if (e != FTP_FILE_ACTION_OK) {
     334                 :          0 :                         ftp_seterr(e);
     335                 :          0 :                         return (-1);
     336                 :            :                 }
     337                 :          0 :         }
     338                 :          0 :         return (0);
     339                 :          0 : }
     340                 :            : 
     341                 :            : /*
     342                 :            :  * Set transfer mode and data type
     343                 :            :  */
     344                 :            : static int
     345                 :          0 : ftp_mode_type(conn_t *conn, int mode, int type)
     346                 :            : {
     347                 :            :         int e;
     348                 :            : 
     349      [ #  #  # ]:          0 :         switch (mode) {
     350                 :            :         case 0:
     351                 :            :         case 's':
     352                 :          0 :                 mode = 'S';
     353                 :            :         case 'S':
     354                 :          0 :                 break;
     355                 :            :         default:
     356                 :          0 :                 return (FTP_PROTOCOL_ERROR);
     357                 :            :         }
     358         [ #  # ]:          0 :         if ((e = ftp_cmd(conn, "MODE %c", mode)) != FTP_OK) {
     359         [ #  # ]:          0 :                 if (mode == 'S') {
     360                 :            :                         /*
     361                 :            :                          * Stream mode is supposed to be the default - so
     362                 :            :                          * much so that some servers not only do not
     363                 :            :                          * support any other mode, but do not support the
     364                 :            :                          * MODE command at all.
     365                 :            :                          *
     366                 :            :                          * If "MODE S" fails, it is unlikely that we
     367                 :            :                          * previously succeeded in setting a different
     368                 :            :                          * mode.  Therefore, we simply hope that the
     369                 :            :                          * server is already in the correct mode, and
     370                 :            :                          * silently ignore the failure.
     371                 :            :                          */
     372                 :          0 :                 } else {
     373                 :          0 :                         return (e);
     374                 :            :                 }
     375                 :          0 :         }
     376                 :            : 
     377   [ #  #  #  #  :          0 :         switch (type) {
                #  #  # ]
     378                 :            :         case 0:
     379                 :            :         case 'i':
     380                 :          0 :                 type = 'I';
     381                 :            :         case 'I':
     382                 :          0 :                 break;
     383                 :            :         case 'a':
     384                 :          0 :                 type = 'A';
     385                 :            :         case 'A':
     386                 :          0 :                 break;
     387                 :            :         case 'd':
     388                 :          0 :                 type = 'D';
     389                 :            :         case 'D':
     390                 :            :                 /* can't handle yet */
     391                 :            :         default:
     392                 :          0 :                 return (FTP_PROTOCOL_ERROR);
     393                 :            :         }
     394         [ #  # ]:          0 :         if ((e = ftp_cmd(conn, "TYPE %c", type)) != FTP_OK)
     395                 :          0 :                 return (e);
     396                 :            : 
     397                 :          0 :         return (FTP_OK);
     398                 :          0 : }
     399                 :            : 
     400                 :            : /*
     401                 :            :  * Request and parse file stats
     402                 :            :  */
     403                 :            : static int
     404                 :          0 : ftp_stat(conn_t *conn, const char *file, struct url_stat *us)
     405                 :            : {
     406                 :            :         char *ln;
     407                 :            :         const char *filename;
     408                 :            :         int filenamelen, type;
     409                 :            :         struct tm tm;
     410                 :            :         time_t t;
     411                 :            :         int e;
     412                 :            : 
     413                 :          0 :         us->size = -1;
     414                 :          0 :         us->atime = us->mtime = 0;
     415                 :            : 
     416                 :          0 :         filename = ftp_filename(file, &filenamelen, &type);
     417                 :            : 
     418         [ #  # ]:          0 :         if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) {
     419                 :          0 :                 ftp_seterr(e);
     420                 :          0 :                 return (-1);
     421                 :            :         }
     422                 :            : 
     423                 :          0 :         e = ftp_cmd(conn, "SIZE %.*s", filenamelen, filename);
     424         [ #  # ]:          0 :         if (e != FTP_FILE_STATUS) {
     425                 :          0 :                 ftp_seterr(e);
     426                 :          0 :                 return (-1);
     427                 :            :         }
     428   [ #  #  #  # ]:          0 :         for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
     429                 :            :                 /* nothing */ ;
     430   [ #  #  #  # ]:          0 :         for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++)
     431                 :          0 :                 us->size = us->size * 10 + *ln - '0';
     432   [ #  #  #  # ]:          0 :         if (*ln && !isspace((unsigned char)*ln)) {
     433                 :          0 :                 ftp_seterr(FTP_PROTOCOL_ERROR);
     434                 :          0 :                 us->size = -1;
     435                 :          0 :                 return (-1);
     436                 :            :         }
     437         [ #  # ]:          0 :         if (us->size == 0)
     438                 :          0 :                 us->size = -1;
     439         [ #  # ]:          0 :         DEBUGF("size: [%lld]\n", (long long)us->size);
     440                 :            : 
     441                 :          0 :         e = ftp_cmd(conn, "MDTM %.*s", filenamelen, filename);
     442         [ #  # ]:          0 :         if (e != FTP_FILE_STATUS) {
     443                 :          0 :                 ftp_seterr(e);
     444                 :          0 :                 return (-1);
     445                 :            :         }
     446   [ #  #  #  # ]:          0 :         for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++)
     447                 :            :                 /* nothing */ ;
     448      [ #  #  # ]:          0 :         switch (strspn(ln, "0123456789")) {
     449                 :            :         case 14:
     450                 :          0 :                 break;
     451                 :            :         case 15:
     452                 :          0 :                 ln++;
     453                 :          0 :                 ln[0] = '2';
     454                 :          0 :                 ln[1] = '0';
     455                 :          0 :                 break;
     456                 :            :         default:
     457                 :          0 :                 ftp_seterr(FTP_PROTOCOL_ERROR);
     458                 :          0 :                 return (-1);
     459                 :            :         }
     460   [ #  #  #  #  :          0 :         if (sscanf(ln, "%04d%02d%02d%02d%02d%02d",
                   #  # ]
     461                 :          0 :             &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
     462                 :          0 :             &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
     463                 :          0 :                 ftp_seterr(FTP_PROTOCOL_ERROR);
     464                 :          0 :                 return (-1);
     465                 :            :         }
     466                 :          0 :         tm.tm_mon--;
     467                 :          0 :         tm.tm_year -= 1900;
     468                 :          0 :         tm.tm_isdst = -1;
     469                 :          0 :         t = timegm(&tm);
     470         [ #  # ]:          0 :         if (t == (time_t)-1)
     471                 :          0 :                 t = time(NULL);
     472                 :          0 :         us->mtime = t;
     473                 :          0 :         us->atime = t;
     474         [ #  # ]:          0 :         DEBUGF("last modified: [%04d-%02d-%02d %02d:%02d:%02d]\n",
     475                 :            :             tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
     476                 :            :             tm.tm_hour, tm.tm_min, tm.tm_sec);
     477                 :          0 :         return (0);
     478                 :          0 : }
     479                 :            : 
     480                 :            : /*
     481                 :            :  * I/O functions for FTP
     482                 :            :  */
     483                 :            : struct ftpio {
     484                 :            :         conn_t  *cconn;         /* Control connection */
     485                 :            :         conn_t  *dconn;         /* Data connection */
     486                 :            :         int      dir;           /* Direction */
     487                 :            :         int      eof;           /* EOF reached */
     488                 :            :         int      err;           /* Error code */
     489                 :            : };
     490                 :            : 
     491                 :            : static int       ftp_readfn(void *, char *, int);
     492                 :            : static int       ftp_writefn(void *, const char *, int);
     493                 :            : static off_t     ftp_seekfn(void *, off_t, int);
     494                 :            : static int       ftp_closefn(void *);
     495                 :            : 
     496                 :            : static int
     497                 :          0 : ftp_readfn(void *v, char *buf, int len)
     498                 :            : {
     499                 :            :         struct ftpio *io;
     500                 :            :         int r;
     501                 :            : 
     502                 :          0 :         io = (struct ftpio *)v;
     503         [ #  # ]:          0 :         if (io == NULL) {
     504                 :          0 :                 errno = EBADF;
     505                 :          0 :                 return (-1);
     506                 :            :         }
     507   [ #  #  #  #  :          0 :         if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) {
                   #  # ]
     508                 :          0 :                 errno = EBADF;
     509                 :          0 :                 return (-1);
     510                 :            :         }
     511         [ #  # ]:          0 :         if (io->err) {
     512                 :          0 :                 errno = io->err;
     513                 :          0 :                 return (-1);
     514                 :            :         }
     515         [ #  # ]:          0 :         if (io->eof)
     516                 :          0 :                 return (0);
     517                 :          0 :         r = fetch_read(io->dconn, buf, len);
     518         [ #  # ]:          0 :         if (r > 0)
     519                 :          0 :                 return (r);
     520         [ #  # ]:          0 :         if (r == 0) {
     521                 :          0 :                 io->eof = 1;
     522                 :          0 :                 return (0);
     523                 :            :         }
     524         [ #  # ]:          0 :         if (errno != EINTR)
     525                 :          0 :                 io->err = errno;
     526                 :          0 :         return (-1);
     527                 :          0 : }
     528                 :            : 
     529                 :            : static int
     530                 :          0 : ftp_writefn(void *v, const char *buf, int len)
     531                 :            : {
     532                 :            :         struct ftpio *io;
     533                 :            :         int w;
     534                 :            : 
     535                 :          0 :         io = (struct ftpio *)v;
     536         [ #  # ]:          0 :         if (io == NULL) {
     537                 :          0 :                 errno = EBADF;
     538                 :          0 :                 return (-1);
     539                 :            :         }
     540   [ #  #  #  #  :          0 :         if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) {
                   #  # ]
     541                 :          0 :                 errno = EBADF;
     542                 :          0 :                 return (-1);
     543                 :            :         }
     544         [ #  # ]:          0 :         if (io->err) {
     545                 :          0 :                 errno = io->err;
     546                 :          0 :                 return (-1);
     547                 :            :         }
     548                 :          0 :         w = fetch_write(io->dconn, buf, len);
     549         [ #  # ]:          0 :         if (w >= 0)
     550                 :          0 :                 return (w);
     551         [ #  # ]:          0 :         if (errno != EINTR)
     552                 :          0 :                 io->err = errno;
     553                 :          0 :         return (-1);
     554                 :          0 : }
     555                 :            : 
     556                 :            : static off_t
     557                 :          0 : ftp_seekfn(void *v, off_t pos __unused, int whence __unused)
     558                 :            : {
     559                 :            :         struct ftpio *io;
     560                 :            : 
     561                 :          0 :         io = (struct ftpio *)v;
     562         [ #  # ]:          0 :         if (io == NULL) {
     563                 :          0 :                 errno = EBADF;
     564                 :          0 :                 return ((off_t)-1);
     565                 :            :         }
     566                 :          0 :         errno = ESPIPE;
     567                 :          0 :         return ((off_t)-1);
     568                 :          0 : }
     569                 :            : 
     570                 :            : static int
     571                 :          0 : ftp_closefn(void *v)
     572                 :            : {
     573                 :            :         struct ftpio *io;
     574                 :            :         int r;
     575                 :            : 
     576                 :          0 :         io = (struct ftpio *)v;
     577         [ #  # ]:          0 :         if (io == NULL) {
     578                 :          0 :                 errno = EBADF;
     579                 :          0 :                 return (-1);
     580                 :            :         }
     581         [ #  # ]:          0 :         if (io->dir == -1)
     582                 :          0 :                 return (0);
     583   [ #  #  #  # ]:          0 :         if (io->cconn == NULL || io->dconn == NULL) {
     584                 :          0 :                 errno = EBADF;
     585                 :          0 :                 return (-1);
     586                 :            :         }
     587                 :          0 :         fetch_close(io->dconn);
     588                 :          0 :         io->dir = -1;
     589                 :          0 :         io->dconn = NULL;
     590         [ #  # ]:          0 :         DEBUGF("Waiting for final status\n");
     591                 :          0 :         r = ftp_chkerr(io->cconn);
     592   [ #  #  #  # ]:          0 :         if (io->cconn == cached_connection && io->cconn->ref == 1)
     593                 :          0 :                 cached_connection = NULL;
     594                 :          0 :         fetch_close(io->cconn);
     595                 :          0 :         free(io);
     596                 :          0 :         return (r == FTP_TRANSFER_COMPLETE) ? 0 : -1;
     597                 :          0 : }
     598                 :            : 
     599                 :            : static FILE *
     600                 :          0 : ftp_setup(conn_t *cconn, conn_t *dconn, int mode)
     601                 :            : {
     602                 :            :         struct ftpio *io;
     603                 :            :         FILE *f;
     604                 :            : 
     605   [ #  #  #  # ]:          0 :         if (cconn == NULL || dconn == NULL)
     606                 :          0 :                 return (NULL);
     607         [ #  # ]:          0 :         if ((io = malloc(sizeof(*io))) == NULL)
     608                 :          0 :                 return (NULL);
     609                 :          0 :         io->cconn = cconn;
     610                 :          0 :         io->dconn = dconn;
     611                 :          0 :         io->dir = mode;
     612                 :          0 :         io->eof = io->err = 0;
     613                 :          0 :         f = funopen(io, ftp_readfn, ftp_writefn, ftp_seekfn, ftp_closefn);
     614         [ #  # ]:          0 :         if (f == NULL)
     615                 :          0 :                 free(io);
     616                 :          0 :         return (f);
     617                 :          0 : }
     618                 :            : 
     619                 :            : /*
     620                 :            :  * Transfer file
     621                 :            :  */
     622                 :            : static FILE *
     623                 :          0 : ftp_transfer(conn_t *conn, const char *oper, const char *file,
     624                 :            :     int mode, off_t offset, const char *flags)
     625                 :            : {
     626                 :            :         struct sockaddr_storage sa;
     627                 :            :         struct sockaddr_in6 *sin6;
     628                 :            :         struct sockaddr_in *sin4;
     629                 :            :         const char *bindaddr;
     630                 :            :         const char *filename;
     631                 :            :         int filenamelen, type;
     632                 :            :         int low, pasv, verbose;
     633                 :          0 :         int e, sd = -1;
     634                 :            :         socklen_t l;
     635                 :            :         char *s;
     636                 :            :         FILE *df;
     637                 :            : 
     638                 :            :         /* check flags */
     639         [ #  # ]:          0 :         low = CHECK_FLAG('l');
     640   [ #  #  #  #  :          0 :         pasv = CHECK_FLAG('p') || !CHECK_FLAG('P');
                   #  # ]
     641         [ #  # ]:          0 :         verbose = CHECK_FLAG('v');
     642                 :            : 
     643                 :            :         /* passive mode */
     644         [ #  # ]:          0 :         if ((s = getenv("FTP_PASSIVE_MODE")) != NULL)
     645                 :          0 :                 pasv = (strncasecmp(s, "no", 2) != 0);
     646                 :            : 
     647                 :            :         /* isolate filename */
     648                 :          0 :         filename = ftp_filename(file, &filenamelen, &type);
     649                 :            : 
     650                 :            :         /* set transfer mode and data type */
     651         [ #  # ]:          0 :         if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK)
     652                 :          0 :                 goto ouch;
     653                 :            : 
     654                 :            :         /* find our own address, bind, and listen */
     655                 :          0 :         l = sizeof(sa);
     656         [ #  # ]:          0 :         if (getsockname(conn->sd, (struct sockaddr *)&sa, &l) == -1)
     657                 :          0 :                 goto sysouch;
     658         [ #  # ]:          0 :         if (sa.ss_family == AF_INET6)
     659                 :          0 :                 unmappedaddr((struct sockaddr_in6 *)&sa);
     660                 :            : 
     661                 :            :         /* open data socket */
     662         [ #  # ]:          0 :         if ((sd = socket(sa.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
     663                 :          0 :                 fetch_syserr();
     664                 :          0 :                 return (NULL);
     665                 :            :         }
     666                 :            : 
     667         [ #  # ]:          0 :         if (pasv) {
     668                 :            :                 u_char addr[64];
     669                 :            :                 char *ln, *p;
     670                 :            :                 unsigned int i;
     671                 :            :                 int port;
     672                 :            : 
     673                 :            :                 /* send PASV command */
     674         [ #  # ]:          0 :                 if (verbose)
     675                 :          0 :                         fetch_info("setting passive mode");
     676      [ #  #  # ]:          0 :                 switch (sa.ss_family) {
     677                 :            :                 case AF_INET:
     678         [ #  # ]:          0 :                         if ((e = ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE)
     679                 :          0 :                                 goto ouch;
     680                 :          0 :                         break;
     681                 :            :                 case AF_INET6:
     682         [ #  # ]:          0 :                         if ((e = ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) {
     683         [ #  # ]:          0 :                                 if (e == -1)
     684                 :          0 :                                         goto ouch;
     685         [ #  # ]:          0 :                                 if ((e = ftp_cmd(conn, "LPSV")) !=
     686                 :            :                                     FTP_LPASSIVE_MODE)
     687                 :          0 :                                         goto ouch;
     688                 :          0 :                         }
     689                 :          0 :                         break;
     690                 :            :                 default:
     691                 :          0 :                         e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
     692                 :          0 :                         goto ouch;
     693                 :            :                 }
     694                 :            : 
     695                 :            :                 /*
     696                 :            :                  * Find address and port number. The reply to the PASV command
     697                 :            :                  * is IMHO the one and only weak point in the FTP protocol.
     698                 :            :                  */
     699                 :          0 :                 ln = conn->buf;
     700      [ #  #  # ]:          0 :                 switch (e) {
     701                 :            :                 case FTP_PASSIVE_MODE:
     702                 :            :                 case FTP_LPASSIVE_MODE:
     703   [ #  #  #  # ]:          0 :                         for (p = ln + 3; *p && !isdigit((unsigned char)*p); p++)
     704                 :            :                                 /* nothing */ ;
     705         [ #  # ]:          0 :                         if (!*p) {
     706                 :          0 :                                 e = FTP_PROTOCOL_ERROR;
     707                 :          0 :                                 goto ouch;
     708                 :            :                         }
     709                 :          0 :                         l = (e == FTP_PASSIVE_MODE ? 6 : 21);
     710   [ #  #  #  # ]:          0 :                         for (i = 0; *p && i < l; i++, p++)
     711                 :          0 :                                 addr[i] = strtol(p, &p, 10);
     712         [ #  # ]:          0 :                         if (i < l) {
     713                 :          0 :                                 e = FTP_PROTOCOL_ERROR;
     714                 :          0 :                                 goto ouch;
     715                 :            :                         }
     716                 :          0 :                         break;
     717                 :            :                 case FTP_EPASSIVE_MODE:
     718   [ #  #  #  # ]:          0 :                         for (p = ln + 3; *p && *p != '('; p++)
     719                 :            :                                 /* nothing */ ;
     720         [ #  # ]:          0 :                         if (!*p) {
     721                 :          0 :                                 e = FTP_PROTOCOL_ERROR;
     722                 :          0 :                                 goto ouch;
     723                 :            :                         }
     724                 :          0 :                         ++p;
     725         [ #  # ]:          0 :                         if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2],
     726   [ #  #  #  # ]:          0 :                                 &port, &addr[3]) != 5 ||
     727         [ #  # ]:          0 :                             addr[0] != addr[1] ||
     728         [ #  # ]:          0 :                             addr[0] != addr[2] || addr[0] != addr[3]) {
     729                 :          0 :                                 e = FTP_PROTOCOL_ERROR;
     730                 :          0 :                                 goto ouch;
     731                 :            :                         }
     732                 :          0 :                         break;
     733                 :            :                 }
     734                 :            : 
     735                 :            :                 /* seek to required offset */
     736         [ #  # ]:          0 :                 if (offset)
     737         [ #  # ]:          0 :                         if (ftp_cmd(conn, "REST %lu", (u_long)offset) != FTP_FILE_OK)
     738                 :          0 :                                 goto sysouch;
     739                 :            : 
     740                 :            :                 /* construct sockaddr for data socket */
     741                 :          0 :                 l = sizeof(sa);
     742         [ #  # ]:          0 :                 if (getpeername(conn->sd, (struct sockaddr *)&sa, &l) == -1)
     743                 :          0 :                         goto sysouch;
     744         [ #  # ]:          0 :                 if (sa.ss_family == AF_INET6)
     745                 :          0 :                         unmappedaddr((struct sockaddr_in6 *)&sa);
     746      [ #  #  # ]:          0 :                 switch (sa.ss_family) {
     747                 :            :                 case AF_INET6:
     748                 :          0 :                         sin6 = (struct sockaddr_in6 *)&sa;
     749         [ #  # ]:          0 :                         if (e == FTP_EPASSIVE_MODE)
     750                 :          0 :                                 sin6->sin6_port = htons(port);
     751                 :            :                         else {
     752                 :          0 :                                 memcpy(&sin6->sin6_addr, addr + 2, 16);
     753                 :          0 :                                 memcpy(&sin6->sin6_port, addr + 19, 2);
     754                 :            :                         }
     755                 :          0 :                         break;
     756                 :            :                 case AF_INET:
     757                 :          0 :                         sin4 = (struct sockaddr_in *)&sa;
     758         [ #  # ]:          0 :                         if (e == FTP_EPASSIVE_MODE)
     759                 :          0 :                                 sin4->sin_port = htons(port);
     760                 :            :                         else {
     761                 :          0 :                                 memcpy(&sin4->sin_addr, addr, 4);
     762                 :          0 :                                 memcpy(&sin4->sin_port, addr + 4, 2);
     763                 :            :                         }
     764                 :          0 :                         break;
     765                 :            :                 default:
     766                 :          0 :                         e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
     767                 :          0 :                         break;
     768                 :            :                 }
     769                 :            : 
     770                 :            :                 /* connect to data port */
     771         [ #  # ]:          0 :                 if (verbose)
     772                 :          0 :                         fetch_info("opening data connection");
     773                 :          0 :                 bindaddr = getenv("FETCH_BIND_ADDRESS");
     774   [ #  #  #  #  :          0 :                 if (bindaddr != NULL && *bindaddr != '\0' &&
                   #  # ]
     775                 :          0 :                     (e = fetch_bind(sd, sa.ss_family, bindaddr)) != 0)
     776                 :          0 :                         goto ouch;
     777         [ #  # ]:          0 :                 if (connect(sd, (struct sockaddr *)&sa, l) == -1)
     778                 :          0 :                         goto sysouch;
     779                 :            : 
     780                 :            :                 /* make the server initiate the transfer */
     781         [ #  # ]:          0 :                 if (verbose)
     782                 :          0 :                         fetch_info("initiating transfer");
     783                 :          0 :                 e = ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename);
     784   [ #  #  #  # ]:          0 :                 if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
     785                 :          0 :                         goto ouch;
     786                 :            : 
     787                 :          0 :         } else {
     788                 :            :                 u_int32_t a;
     789                 :            :                 u_short p;
     790                 :            :                 int arg, d;
     791                 :            :                 char *ap;
     792                 :            :                 char hname[INET6_ADDRSTRLEN];
     793                 :            : 
     794      [ #  #  # ]:          0 :                 switch (sa.ss_family) {
     795                 :            :                 case AF_INET6:
     796                 :          0 :                         ((struct sockaddr_in6 *)&sa)->sin6_port = 0;
     797                 :            : #ifdef IPV6_PORTRANGE
     798                 :          0 :                         arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH;
     799   [ #  #  #  #  :          0 :                         if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE,
                   #  # ]
     800                 :          0 :                                 (char *)&arg, sizeof(arg)) == -1)
     801                 :          0 :                                 goto sysouch;
     802                 :            : #endif
     803                 :          0 :                         break;
     804                 :            :                 case AF_INET:
     805                 :          0 :                         ((struct sockaddr_in *)&sa)->sin_port = 0;
     806                 :            : #ifdef IP_PORTRANGE
     807                 :          0 :                         arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH;
     808   [ #  #  #  #  :          0 :                         if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE,
                   #  # ]
     809                 :          0 :                                 (char *)&arg, sizeof(arg)) == -1)
     810                 :          0 :                                 goto sysouch;
     811                 :            : #endif
     812                 :          0 :                         break;
     813                 :            :                 }
     814         [ #  # ]:          0 :                 if (verbose)
     815                 :          0 :                         fetch_info("binding data socket");
     816         [ #  # ]:          0 :                 if (bind(sd, (struct sockaddr *)&sa, l) == -1)
     817                 :          0 :                         goto sysouch;
     818         [ #  # ]:          0 :                 if (listen(sd, 1) == -1)
     819                 :          0 :                         goto sysouch;
     820                 :            : 
     821                 :            :                 /* find what port we're on and tell the server */
     822         [ #  # ]:          0 :                 if (getsockname(sd, (struct sockaddr *)&sa, &l) == -1)
     823                 :          0 :                         goto sysouch;
     824      [ #  #  # ]:          0 :                 switch (sa.ss_family) {
     825                 :            :                 case AF_INET:
     826                 :          0 :                         sin4 = (struct sockaddr_in *)&sa;
     827                 :          0 :                         a = ntohl(sin4->sin_addr.s_addr);
     828                 :          0 :                         p = ntohs(sin4->sin_port);
     829                 :          0 :                         e = ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d",
     830                 :          0 :                             (a >> 24) & 0xff, (a >> 16) & 0xff,
     831                 :          0 :                             (a >> 8) & 0xff, a & 0xff,
     832                 :          0 :                             (p >> 8) & 0xff, p & 0xff);
     833                 :          0 :                         break;
     834                 :            :                 case AF_INET6:
     835                 :            : #define UC(b)   (((int)b)&0xff)
     836                 :          0 :                         e = -1;
     837                 :          0 :                         sin6 = (struct sockaddr_in6 *)&sa;
     838                 :          0 :                         sin6->sin6_scope_id = 0;
     839   [ #  #  #  #  :          0 :                         if (getnameinfo((struct sockaddr *)&sa, l,
                   #  # ]
     840                 :          0 :                                 hname, sizeof(hname),
     841                 :          0 :                                 NULL, 0, NI_NUMERICHOST) == 0) {
     842                 :          0 :                                 e = ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname,
     843                 :          0 :                                     htons(sin6->sin6_port));
     844         [ #  # ]:          0 :                                 if (e == -1)
     845                 :          0 :                                         goto ouch;
     846                 :          0 :                         }
     847         [ #  # ]:          0 :                         if (e != FTP_OK) {
     848                 :          0 :                                 ap = (char *)&sin6->sin6_addr;
     849                 :          0 :                                 e = ftp_cmd(conn,
     850                 :            :                                     "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
     851                 :            :                                     6, 16,
     852                 :          0 :                                     UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]),
     853                 :          0 :                                     UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]),
     854                 :          0 :                                     UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]),
     855                 :          0 :                                     UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]),
     856                 :            :                                     2,
     857                 :          0 :                                     (ntohs(sin6->sin6_port) >> 8) & 0xff,
     858                 :          0 :                                     ntohs(sin6->sin6_port)        & 0xff);
     859                 :          0 :                         }
     860                 :          0 :                         break;
     861                 :            :                 default:
     862                 :          0 :                         e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */
     863                 :          0 :                         goto ouch;
     864                 :            :                 }
     865         [ #  # ]:          0 :                 if (e != FTP_OK)
     866                 :          0 :                         goto ouch;
     867                 :            : 
     868                 :            :                 /* seek to required offset */
     869         [ #  # ]:          0 :                 if (offset)
     870         [ #  # ]:          0 :                         if (ftp_cmd(conn, "REST %ju", (uintmax_t)offset) != FTP_FILE_OK)
     871                 :          0 :                                 goto sysouch;
     872                 :            : 
     873                 :            :                 /* make the server initiate the transfer */
     874         [ #  # ]:          0 :                 if (verbose)
     875                 :          0 :                         fetch_info("initiating transfer");
     876                 :          0 :                 e = ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename);
     877   [ #  #  #  # ]:          0 :                 if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)
     878                 :          0 :                         goto ouch;
     879                 :            : 
     880                 :            :                 /* accept the incoming connection and go to town */
     881         [ #  # ]:          0 :                 if ((d = accept(sd, NULL, NULL)) == -1)
     882                 :          0 :                         goto sysouch;
     883                 :          0 :                 close(sd);
     884                 :          0 :                 sd = d;
     885                 :            :         }
     886                 :            : 
     887         [ #  # ]:          0 :         if ((df = ftp_setup(conn, fetch_reopen(sd), mode)) == NULL)
     888                 :          0 :                 goto sysouch;
     889                 :          0 :         return (df);
     890                 :            : 
     891                 :            : sysouch:
     892                 :          0 :         fetch_syserr();
     893         [ #  # ]:          0 :         if (sd >= 0)
     894                 :          0 :                 close(sd);
     895                 :          0 :         return (NULL);
     896                 :            : 
     897                 :            : ouch:
     898         [ #  # ]:          0 :         if (e != -1)
     899                 :          0 :                 ftp_seterr(e);
     900         [ #  # ]:          0 :         if (sd >= 0)
     901                 :          0 :                 close(sd);
     902                 :          0 :         return (NULL);
     903                 :          0 : }
     904                 :            : 
     905                 :            : /*
     906                 :            :  * Authenticate
     907                 :            :  */
     908                 :            : static int
     909                 :          0 : ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)
     910                 :            : {
     911                 :            :         const char *user, *pwd, *logname;
     912                 :            :         char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1];
     913                 :            :         int e, len;
     914                 :            : 
     915                 :            :         /* XXX FTP_AUTH, and maybe .netrc */
     916                 :            : 
     917                 :            :         /* send user name and password */
     918         [ #  # ]:          0 :         if (url->user[0] == '\0')
     919                 :          0 :                 fetch_netrc_auth(url);
     920                 :          0 :         user = url->user;
     921         [ #  # ]:          0 :         if (*user == '\0')
     922         [ #  # ]:          0 :                 if ((user = getenv("FTP_LOGIN")) != NULL)
     923         [ #  # ]:          0 :                         DEBUGF("FTP_LOGIN=%s\n", user);
     924   [ #  #  #  # ]:          0 :         if (user == NULL || *user == '\0')
     925                 :          0 :                 user = FTP_ANONYMOUS_USER;
     926   [ #  #  #  # ]:          0 :         if (purl && url->port == fetch_default_port(url->scheme))
     927                 :          0 :                 e = ftp_cmd(conn, "USER %s@%s", user, url->host);
     928         [ #  # ]:          0 :         else if (purl)
     929                 :          0 :                 e = ftp_cmd(conn, "USER %s@%s@%d", user, url->host, url->port);
     930                 :            :         else
     931                 :          0 :                 e = ftp_cmd(conn, "USER %s", user);
     932                 :            : 
     933                 :            :         /* did the server request a password? */
     934         [ #  # ]:          0 :         if (e == FTP_NEED_PASSWORD) {
     935                 :          0 :                 pwd = url->pwd;
     936         [ #  # ]:          0 :                 if (*pwd == '\0')
     937         [ #  # ]:          0 :                         if ((pwd = getenv("FTP_PASSWORD")) != NULL)
     938         [ #  # ]:          0 :                                 DEBUGF("FTP_PASSWORD=%s\n", pwd);
     939   [ #  #  #  # ]:          0 :                 if (pwd == NULL || *pwd == '\0') {
     940         [ #  # ]:          0 :                         if ((logname = getlogin()) == NULL)
     941                 :          0 :                                 logname = FTP_ANONYMOUS_USER;
     942         [ #  # ]:          0 :                         if ((len = snprintf(pbuf, MAXLOGNAME + 1, "%s@", logname)) < 0)
     943                 :          0 :                                 len = 0;
     944         [ #  # ]:          0 :                         else if (len > MAXLOGNAME)
     945                 :          0 :                                 len = MAXLOGNAME;
     946                 :          0 :                         gethostname(pbuf + len, sizeof(pbuf) - len);
     947                 :          0 :                         pwd = pbuf;
     948                 :          0 :                 }
     949                 :          0 :                 e = ftp_cmd(conn, "PASS %s", pwd);
     950                 :          0 :         }
     951                 :            : 
     952                 :          0 :         return (e);
     953                 :            : }
     954                 :            : 
     955                 :            : /*
     956                 :            :  * Log on to FTP server
     957                 :            :  */
     958                 :            : static conn_t *
     959                 :          0 : ftp_connect(struct url *url, struct url *purl, const char *flags)
     960                 :            : {
     961                 :            :         conn_t *conn;
     962                 :            :         int e, direct, verbose;
     963                 :            : #ifdef INET6
     964                 :          0 :         int af = AF_UNSPEC;
     965                 :            : #else
     966                 :            :         int af = AF_INET;
     967                 :            : #endif
     968                 :            : 
     969         [ #  # ]:          0 :         direct = CHECK_FLAG('d');
     970         [ #  # ]:          0 :         verbose = CHECK_FLAG('v');
     971   [ #  #  #  # ]:          0 :         if (CHECK_FLAG('4'))
     972                 :          0 :                 af = AF_INET;
     973   [ #  #  #  # ]:          0 :         else if (CHECK_FLAG('6'))
     974                 :          0 :                 af = AF_INET6;
     975                 :            : 
     976         [ #  # ]:          0 :         if (direct)
     977                 :          0 :                 purl = NULL;
     978                 :            : 
     979                 :            :         /* check for proxy */
     980         [ #  # ]:          0 :         if (purl) {
     981                 :            :                 /* XXX proxy authentication! */
     982                 :          0 :                 conn = fetch_connect(purl, af, verbose);
     983                 :          0 :         } else {
     984                 :            :                 /* no proxy, go straight to target */
     985                 :          0 :                 conn = fetch_connect(url, af, verbose);
     986                 :          0 :                 purl = NULL;
     987                 :            :         }
     988                 :            : 
     989                 :            :         /* check connection */
     990         [ #  # ]:          0 :         if (conn == NULL)
     991                 :            :                 /* fetch_connect() has already set an error code */
     992                 :          0 :                 return (NULL);
     993                 :            : 
     994                 :            :         /* expect welcome message */
     995         [ #  # ]:          0 :         if ((e = ftp_chkerr(conn)) != FTP_SERVICE_READY)
     996                 :          0 :                 goto fouch;
     997                 :            : 
     998                 :            :         /* authenticate */
     999         [ #  # ]:          0 :         if ((e = ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN)
    1000                 :          0 :                 goto fouch;
    1001                 :            : 
    1002                 :            :         /* TODO: Request extended features supported, if any (RFC 3659). */
    1003                 :            : 
    1004                 :            :         /* done */
    1005                 :          0 :         return (conn);
    1006                 :            : 
    1007                 :            : fouch:
    1008         [ #  # ]:          0 :         if (e != -1)
    1009                 :          0 :                 ftp_seterr(e);
    1010                 :          0 :         fetch_close(conn);
    1011                 :          0 :         return (NULL);
    1012                 :          0 : }
    1013                 :            : 
    1014                 :            : /*
    1015                 :            :  * Disconnect from server
    1016                 :            :  */
    1017                 :            : static void
    1018                 :          0 : ftp_disconnect(conn_t *conn)
    1019                 :            : {
    1020                 :          0 :         (void)ftp_cmd(conn, "QUIT");
    1021   [ #  #  #  # ]:          0 :         if (conn == cached_connection && conn->ref == 1)
    1022                 :          0 :                 cached_connection = NULL;
    1023                 :          0 :         fetch_close(conn);
    1024                 :          0 : }
    1025                 :            : 
    1026                 :            : /*
    1027                 :            :  * Check if we're already connected
    1028                 :            :  */
    1029                 :            : static int
    1030                 :          0 : ftp_isconnected(struct url *url)
    1031                 :            : {
    1032                 :          0 :         return (cached_connection
    1033         [ #  # ]:          0 :             && (strcmp(url->host, cached_host.host) == 0)
    1034         [ #  # ]:          0 :             && (strcmp(url->user, cached_host.user) == 0)
    1035         [ #  # ]:          0 :             && (strcmp(url->pwd, cached_host.pwd) == 0)
    1036         [ #  # ]:          0 :             && (url->port == cached_host.port));
    1037                 :            : }
    1038                 :            : 
    1039                 :            : /*
    1040                 :            :  * Check the cache, reconnect if no luck
    1041                 :            :  */
    1042                 :            : static conn_t *
    1043                 :          0 : ftp_cached_connect(struct url *url, struct url *purl, const char *flags)
    1044                 :            : {
    1045                 :            :         conn_t *conn;
    1046                 :            :         int e;
    1047                 :            : 
    1048                 :            :         /* set default port */
    1049         [ #  # ]:          0 :         if (!url->port)
    1050                 :          0 :                 url->port = fetch_default_port(url->scheme);
    1051                 :            : 
    1052                 :            :         /* try to use previously cached connection */
    1053         [ #  # ]:          0 :         if (ftp_isconnected(url)) {
    1054                 :          0 :                 e = ftp_cmd(cached_connection, "NOOP");
    1055   [ #  #  #  # ]:          0 :                 if (e == FTP_OK || e == FTP_SYNTAX_ERROR)
    1056                 :          0 :                         return (fetch_ref(cached_connection));
    1057                 :          0 :         }
    1058                 :            : 
    1059                 :            :         /* connect to server */
    1060         [ #  # ]:          0 :         if ((conn = ftp_connect(url, purl, flags)) == NULL)
    1061                 :          0 :                 return (NULL);
    1062         [ #  # ]:          0 :         if (cached_connection)
    1063                 :          0 :                 ftp_disconnect(cached_connection);
    1064                 :          0 :         cached_connection = fetch_ref(conn);
    1065                 :          0 :         memcpy(&cached_host, url, sizeof(*url));
    1066                 :          0 :         return (conn);
    1067                 :          0 : }
    1068                 :            : 
    1069                 :            : /*
    1070                 :            :  * Check the proxy settings
    1071                 :            :  */
    1072                 :            : static struct url *
    1073                 :          0 : ftp_get_proxy(struct url * url, const char *flags)
    1074                 :            : {
    1075                 :            :         struct url *purl;
    1076                 :            :         char *p;
    1077                 :            : 
    1078   [ #  #  #  # ]:          0 :         if (flags != NULL && strchr(flags, 'd') != NULL)
    1079                 :          0 :                 return (NULL);
    1080         [ #  # ]:          0 :         if (fetch_no_proxy_match(url->host))
    1081                 :          0 :                 return (NULL);
    1082   [ #  #  #  #  :          0 :         if (((p = getenv("FTP_PROXY")) || (p = getenv("ftp_proxy")) ||
                   #  # ]
    1083         [ #  # ]:          0 :                 (p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) &&
    1084         [ #  # ]:          0 :             *p && (purl = fetchParseURL(p)) != NULL) {
    1085         [ #  # ]:          0 :                 if (!*purl->scheme) {
    1086   [ #  #  #  # ]:          0 :                         if (getenv("FTP_PROXY") || getenv("ftp_proxy"))
    1087                 :          0 :                                 strcpy(purl->scheme, SCHEME_FTP);
    1088                 :            :                         else
    1089                 :          0 :                                 strcpy(purl->scheme, SCHEME_HTTP);
    1090                 :          0 :                 }
    1091         [ #  # ]:          0 :                 if (!purl->port)
    1092                 :          0 :                         purl->port = fetch_default_proxy_port(purl->scheme);
    1093   [ #  #  #  # ]:          0 :                 if (strcmp(purl->scheme, SCHEME_FTP) == 0 ||
    1094                 :          0 :                     strcmp(purl->scheme, SCHEME_HTTP) == 0)
    1095                 :          0 :                         return (purl);
    1096                 :          0 :                 fetchFreeURL(purl);
    1097                 :          0 :         }
    1098                 :          0 :         return (NULL);
    1099                 :          0 : }
    1100                 :            : 
    1101                 :            : /*
    1102                 :            :  * Process an FTP request
    1103                 :            :  */
    1104                 :            : FILE *
    1105                 :          0 : ftp_request(struct url *url, const char *op, struct url_stat *us,
    1106                 :            :     struct url *purl, const char *flags)
    1107                 :            : {
    1108                 :            :         conn_t *conn;
    1109                 :            :         int oflag;
    1110                 :            : 
    1111                 :            :         /* check if we should use HTTP instead */
    1112   [ #  #  #  #  :          0 :         if (purl && (strcmp(purl->scheme, SCHEME_HTTP) == 0 ||
                   #  # ]
    1113                 :          0 :             strcmp(purl->scheme, SCHEME_HTTPS) == 0)) {
    1114         [ #  # ]:          0 :                 if (strcmp(op, "STAT") == 0)
    1115                 :          0 :                         return (http_request(url, "HEAD", us, purl, flags));
    1116         [ #  # ]:          0 :                 else if (strcmp(op, "RETR") == 0)
    1117                 :          0 :                         return (http_request(url, "GET", us, purl, flags));
    1118                 :            :                 /*
    1119                 :            :                  * Our HTTP code doesn't support PUT requests yet, so try
    1120                 :            :                  * a direct connection.
    1121                 :            :                  */
    1122                 :          0 :         }
    1123                 :            : 
    1124                 :            :         /* connect to server */
    1125                 :          0 :         conn = ftp_cached_connect(url, purl, flags);
    1126         [ #  # ]:          0 :         if (purl)
    1127                 :          0 :                 fetchFreeURL(purl);
    1128         [ #  # ]:          0 :         if (conn == NULL)
    1129                 :          0 :                 return (NULL);
    1130                 :            : 
    1131                 :            :         /* change directory */
    1132         [ #  # ]:          0 :         if (ftp_cwd(conn, url->doc) == -1)
    1133                 :          0 :                 goto errsock;
    1134                 :            : 
    1135                 :            :         /* stat file */
    1136   [ #  #  #  # ]:          0 :         if (us && ftp_stat(conn, url->doc, us) == -1
    1137         [ #  # ]:          0 :             && fetchLastErrCode != FETCH_PROTO
    1138         [ #  # ]:          0 :             && fetchLastErrCode != FETCH_UNAVAIL)
    1139                 :          0 :                 goto errsock;
    1140                 :            : 
    1141                 :            :         /* just a stat */
    1142         [ #  # ]:          0 :         if (strcmp(op, "STAT") == 0) {
    1143                 :          0 :                 --conn->ref;
    1144                 :          0 :                 ftp_disconnect(conn);
    1145                 :          0 :                 return (FILE *)1; /* bogus return value */
    1146                 :            :         }
    1147   [ #  #  #  # ]:          0 :         if (strcmp(op, "STOR") == 0 || strcmp(op, "APPE") == 0)
    1148                 :          0 :                 oflag = O_WRONLY;
    1149                 :            :         else
    1150                 :          0 :                 oflag = O_RDONLY;
    1151                 :            : 
    1152                 :            :         /* initiate the transfer */
    1153                 :          0 :         return (ftp_transfer(conn, op, url->doc, oflag, url->offset, flags));
    1154                 :            : 
    1155                 :            : errsock:
    1156                 :          0 :         ftp_disconnect(conn);
    1157                 :          0 :         return (NULL);
    1158                 :          0 : }
    1159                 :            : 
    1160                 :            : /*
    1161                 :            :  * Get and stat file
    1162                 :            :  */
    1163                 :            : FILE *
    1164                 :          0 : fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags)
    1165                 :            : {
    1166                 :          0 :         return (ftp_request(url, "RETR", us, ftp_get_proxy(url, flags), flags));
    1167                 :            : }
    1168                 :            : 
    1169                 :            : /*
    1170                 :            :  * Get file
    1171                 :            :  */
    1172                 :            : FILE *
    1173                 :          0 : fetchGetFTP(struct url *url, const char *flags)
    1174                 :            : {
    1175                 :          0 :         return (fetchXGetFTP(url, NULL, flags));
    1176                 :            : }
    1177                 :            : 
    1178                 :            : /*
    1179                 :            :  * Put file
    1180                 :            :  */
    1181                 :            : FILE *
    1182                 :          0 : fetchPutFTP(struct url *url, const char *flags)
    1183                 :            : {
    1184         [ #  # ]:          0 :         return (ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL,
    1185                 :          0 :             ftp_get_proxy(url, flags), flags));
    1186                 :            : }
    1187                 :            : 
    1188                 :            : /*
    1189                 :            :  * Get file stats
    1190                 :            :  */
    1191                 :            : int
    1192                 :          0 : fetchStatFTP(struct url *url, struct url_stat *us, const char *flags)
    1193                 :            : {
    1194                 :            :         FILE *f;
    1195                 :            : 
    1196                 :          0 :         f = ftp_request(url, "STAT", us, ftp_get_proxy(url, flags), flags);
    1197         [ #  # ]:          0 :         if (f == NULL)
    1198                 :          0 :                 return (-1);
    1199                 :            :         /*
    1200                 :            :          * When op is "STAT", ftp_request() will return either NULL or
    1201                 :            :          * (FILE *)1, never a valid FILE *, so we mustn't fclose(f) before
    1202                 :            :          * returning, as it would cause a segfault.
    1203                 :            :          */
    1204                 :          0 :         return (0);
    1205                 :          0 : }
    1206                 :            : 
    1207                 :            : /*
    1208                 :            :  * List a directory
    1209                 :            :  */
    1210                 :            : struct url_ent *
    1211                 :          0 : fetchListFTP(struct url *url __unused, const char *flags __unused)
    1212                 :            : {
    1213                 :          0 :         warnx("fetchListFTP(): not implemented");
    1214                 :          0 :         return (NULL);
    1215                 :            : }

Generated by: LCOV version 1.15