]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
netrc: return code cleanup, fix missing file error
authorDaniel Stenberg <daniel@haxx.se>
Tue, 4 Feb 2025 10:25:18 +0000 (11:25 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 6 Feb 2025 09:34:02 +0000 (10:34 +0100)
Made the netrc parser return a more consistent set of error codes and
error messages, and also return error properly if the .netrc file is
missing.

Add test 697 to verify error on missing netrc file.

Fixes #16163
Reported-by: arlt on github
Closes #16165

lib/netrc.c
lib/netrc.h
lib/url.c
tests/data/Makefile.am
tests/data/test697 [new file with mode: 0644]

index 7ad81ece229f0aba095ae6e03e2fb320c01e1dce..ba6708991dfde444f91621b50929653ed9ec1d94 100644 (file)
@@ -59,23 +59,26 @@ enum found_state {
 #define FOUND_LOGIN    1
 #define FOUND_PASSWORD 2
 
-#define NETRC_FILE_MISSING 1
-#define NETRC_FAILED -1
-#define NETRC_SUCCESS 0
-
 #define MAX_NETRC_LINE 16384
 #define MAX_NETRC_FILE (128*1024)
 #define MAX_NETRC_TOKEN 4096
 
-static CURLcode file2memory(const char *filename, struct dynbuf *filebuf)
+/* convert a dynbuf call CURLcode error to a NETRCcode error */
+#define curl2netrc(result)                     \
+  (((result) == CURLE_OUT_OF_MEMORY) ?         \
+   NETRC_OUT_OF_MEMORY : NETRC_SYNTAX_ERROR)
+
+static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf)
 {
-  CURLcode result = CURLE_OK;
+  NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */
   FILE *file = fopen(filename, FOPEN_READTEXT);
   struct dynbuf linebuf;
   Curl_dyn_init(&linebuf, MAX_NETRC_LINE);
 
   if(file) {
+    ret = NETRC_OK;
     while(Curl_get_line(&linebuf, file)) {
+      CURLcode result;
       const char *line = Curl_dyn_ptr(&linebuf);
       /* skip comments on load */
       while(ISBLANK(*line))
@@ -83,27 +86,29 @@ static CURLcode file2memory(const char *filename, struct dynbuf *filebuf)
       if(*line == '#')
         continue;
       result = Curl_dyn_add(filebuf, line);
-      if(result)
+      if(result) {
+        ret = curl2netrc(result);
         goto done;
+      }
     }
   }
 done:
   Curl_dyn_free(&linebuf);
   if(file)
     fclose(file);
-  return result;
+  return ret;
 }
 
 /*
  * Returns zero on success.
  */
-static int parsenetrc(struct store_netrc *store,
-                      const char *host,
-                      char **loginp, /* might point to a username */
-                      char **passwordp,
-                      const char *netrcfile)
+static NETRCcode parsenetrc(struct store_netrc *store,
+                            const char *host,
+                            char **loginp, /* might point to a username */
+                            char **passwordp,
+                            const char *netrcfile)
 {
-  int retcode = NETRC_FILE_MISSING;
+  NETRCcode retcode = NETRC_NO_MATCH;
   char *login = *loginp;
   char *password = NULL;
   bool specific_login = !!login; /* points to something */
@@ -120,8 +125,9 @@ static int parsenetrc(struct store_netrc *store,
   Curl_dyn_init(&token, MAX_NETRC_TOKEN);
 
   if(!store->loaded) {
-    if(file2memory(netrcfile, filebuf))
-      return NETRC_FAILED;
+    NETRCcode ret = file2memory(netrcfile, filebuf);
+    if(ret)
+      return ret;
     store->loaded = TRUE;
   }
 
@@ -151,12 +157,18 @@ static int parsenetrc(struct store_netrc *store,
       tok_end = tok;
       if(!quoted) {
         size_t len = 0;
+        CURLcode result;
         while(!ISSPACE(*tok_end)) {
           tok_end++;
           len++;
         }
-        if(!len || Curl_dyn_addn(&token, tok, len)) {
-          retcode = NETRC_FAILED;
+        if(!len) {
+          retcode = NETRC_SYNTAX_ERROR;
+          goto out;
+        }
+        result = Curl_dyn_addn(&token, tok, len);
+        if(result) {
+          retcode = curl2netrc(result);
           goto out;
         }
       }
@@ -165,6 +177,7 @@ static int parsenetrc(struct store_netrc *store,
         bool endquote = FALSE;
         tok_end++; /* pass the leading quote */
         while(*tok_end) {
+          CURLcode result;
           char s = *tok_end;
           if(escape) {
             escape = FALSE;
@@ -190,15 +203,16 @@ static int parsenetrc(struct store_netrc *store,
             endquote = TRUE;
             break;
           }
-          if(Curl_dyn_addn(&token, &s, 1)) {
-            retcode = NETRC_FAILED;
+          result = Curl_dyn_addn(&token, &s, 1);
+          if(result) {
+            retcode = curl2netrc(result);
             goto out;
           }
           tok_end++;
         }
         if(escape || !endquote) {
           /* bad syntax, get out */
-          retcode = NETRC_FAILED;
+          retcode = NETRC_SYNTAX_ERROR;
           goto out;
         }
       }
@@ -226,7 +240,7 @@ static int parsenetrc(struct store_netrc *store,
         }
         else if(strcasecompare("default", tok)) {
           state = HOSTVALID;
-          retcode = NETRC_SUCCESS; /* we did find our host */
+          retcode = NETRC_OK; /* we did find our host */
         }
         break;
       case MACDEF:
@@ -237,7 +251,7 @@ static int parsenetrc(struct store_netrc *store,
         if(strcasecompare(host, tok)) {
           /* and yes, this is our host! */
           state = HOSTVALID;
-          retcode = NETRC_SUCCESS; /* we did find our host */
+          retcode = NETRC_OK; /* we did find our host */
         }
         else
           /* not our host */
@@ -253,7 +267,7 @@ static int parsenetrc(struct store_netrc *store,
             free(login);
             login = strdup(tok);
             if(!login) {
-              retcode = NETRC_FAILED; /* allocation failed */
+              retcode = NETRC_OUT_OF_MEMORY; /* allocation failed */
               goto out;
             }
           }
@@ -264,7 +278,7 @@ static int parsenetrc(struct store_netrc *store,
           free(password);
           password = strdup(tok);
           if(!password) {
-            retcode = NETRC_FAILED; /* allocation failed */
+            retcode = NETRC_OUT_OF_MEMORY; /* allocation failed */
             goto out;
           }
           if(!specific_login || our_login)
@@ -290,7 +304,7 @@ static int parsenetrc(struct store_netrc *store,
         }
         else if(strcasecompare("default", tok)) {
           state = HOSTVALID;
-          retcode = NETRC_SUCCESS; /* we did find our host */
+          retcode = NETRC_OK; /* we did find our host */
           Curl_safefree(password);
           if(!specific_login)
             Curl_safefree(login);
@@ -321,11 +335,11 @@ out:
       /* success without a password, set a blank one */
       password = strdup("");
       if(!password)
-        retcode = 1; /* out of memory */
+        retcode = NETRC_OUT_OF_MEMORY; /* out of memory */
     }
     else if(!login && !password)
       /* a default with no credentials */
-      retcode = NETRC_FILE_MISSING;
+      retcode = NETRC_NO_MATCH;
   }
   if(!retcode) {
     /* success */
@@ -343,17 +357,34 @@ out:
   return retcode;
 }
 
+const char *Curl_netrc_strerror(NETRCcode ret)
+{
+  switch(ret) {
+  default:
+    return ""; /* not a legit error */
+  case NETRC_FILE_MISSING:
+    return "no such file";
+  case NETRC_NO_MATCH:
+    return "no matching entry";
+  case NETRC_OUT_OF_MEMORY:
+    return "out of memory";
+  case NETRC_SYNTAX_ERROR:
+    return "syntax error";
+  }
+  /* never reached */
+}
+
 /*
  * @unittest: 1304
  *
  * *loginp and *passwordp MUST be allocated if they are not NULL when passed
  * in.
  */
-int Curl_parsenetrc(struct store_netrc *store, const char *host,
-                    char **loginp, char **passwordp,
-                    char *netrcfile)
+NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host,
+                          char **loginp, char **passwordp,
+                          char *netrcfile)
 {
-  int retcode = 1;
+  NETRCcode retcode = NETRC_OK;
   char *filealloc = NULL;
 
   if(!netrcfile) {
@@ -391,23 +422,23 @@ int Curl_parsenetrc(struct store_netrc *store, const char *host,
     }
 
     if(!home)
-      return retcode; /* no home directory found (or possibly out of
-                         memory) */
+      return NETRC_FILE_MISSING; /* no home directory found (or possibly out
+                                    of memory) */
 
     filealloc = aprintf("%s%s.netrc", home, DIR_CHAR);
     if(!filealloc) {
       free(homea);
-      return -1;
+      return NETRC_OUT_OF_MEMORY;
     }
     retcode = parsenetrc(store, host, loginp, passwordp, filealloc);
     free(filealloc);
 #ifdef _WIN32
-    if((retcode == NETRC_FILE_MISSING) || (retcode == NETRC_FAILED)) {
+    if(retcode == NETRC_FILE_MISSING) {
       /* fallback to the old-style "_netrc" file */
       filealloc = aprintf("%s%s_netrc", home, DIR_CHAR);
       if(!filealloc) {
         free(homea);
-        return -1;
+        return NETRC_OUT_OF_MEMORY;
       }
       retcode = parsenetrc(store, host, loginp, passwordp, filealloc);
       free(filealloc);
index 0ef9ff78ea2a0e4905bab9776153cc83538a9854..ac0f88622b79f461bd2ddb67919a0698a69e3eff 100644 (file)
@@ -34,12 +34,21 @@ struct store_netrc {
   BIT(loaded);
 };
 
+typedef enum {
+  NETRC_OK,
+  NETRC_NO_MATCH,      /* no matching entry in the file */
+  NETRC_SYNTAX_ERROR,  /* in the netrc file */
+  NETRC_FILE_MISSING,  /* the netrc file does not exist */
+  NETRC_OUT_OF_MEMORY, /* while parsing netrc */
+  NETRC_LAST /* never used */
+} NETRCcode;
+
+const char *Curl_netrc_strerror(NETRCcode ret);
 void Curl_netrc_init(struct store_netrc *s);
 void Curl_netrc_cleanup(struct store_netrc *s);
 
-/* returns -1 on failure, 0 if the host is found, 1 is the host is not found */
-int Curl_parsenetrc(struct store_netrc *s, const char *host, char **loginp,
-                    char **passwordp, char *filename);
+NETRCcode Curl_parsenetrc(struct store_netrc *s, const char *host,
+                          char **loginp, char **passwordp, char *filename);
   /* Assume: (*passwordp)[0]=0, host[0] != 0.
    * If (*loginp)[0] = 0, search for login and password within a machine
    * section in the netrc.
index 0f3d1002b4cbd15cc3a72e9c9282f08b3d53b5b5..fb3f83f8b359c856622efaa797b057b9b4a5603c 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -2690,7 +2690,6 @@ static CURLcode override_login(struct Curl_easy *data,
   }
   conn->bits.netrc = FALSE;
   if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) {
-    int ret;
     bool url_provided = FALSE;
 
     if(data->state.aptr.user &&
@@ -2702,17 +2701,19 @@ static CURLcode override_login(struct Curl_easy *data,
     }
 
     if(!*passwdp) {
-      ret = Curl_parsenetrc(&data->state.netrc, conn->host.name,
-                            userp, passwdp,
-                            data->set.str[STRING_NETRC_FILE]);
-      if(ret > 0) {
+      NETRCcode ret = Curl_parsenetrc(&data->state.netrc, conn->host.name,
+                                      userp, passwdp,
+                                      data->set.str[STRING_NETRC_FILE]);
+      if(ret && ((ret == NETRC_NO_MATCH) ||
+                 (data->set.use_netrc == CURL_NETRC_OPTIONAL))) {
         infof(data, "Couldn't find host %s in the %s file; using defaults",
               conn->host.name,
               (data->set.str[STRING_NETRC_FILE] ?
                data->set.str[STRING_NETRC_FILE] : ".netrc"));
       }
-      else if(ret < 0) {
-        failf(data, ".netrc parser error");
+      else if(ret) {
+        const char *m = Curl_netrc_strerror(ret);
+        failf(data, ".netrc error: %s", m);
         return CURLE_READ_ERROR;
       }
       else {
index 1fc6c66817b18d167baa72495317a6969ada319e..65f7712f23701fb663ac1a383175efa64f1e1d6c 100644 (file)
@@ -101,7 +101,7 @@ test652 test653 test654 test655 test656 test658 test659 test660 test661 \
 test662 test663 test664 test665 test666 test667 test668 test669 test670 \
 test671 test672 test673 test674 test675 test676 test677 test678 test679 \
 test680 test681 test682 test683 test684 test685 test686 test687 test688 \
-test689 test690 test691 test692 test693 test694 test695 test696 \
+test689 test690 test691 test692 test693 test694 test695 test696 test697 \
 \
 test700 test701 test702 test703 test704 test705 test706 test707 test708 \
 test709 test710 test711 test712 test713 test714 test715 test716 test717 \
diff --git a/tests/data/test697 b/tests/data/test697
new file mode 100644 (file)
index 0000000..5b97761
--- /dev/null
@@ -0,0 +1,37 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+netrc
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+none
+</server>
+<features>
+http
+</features>
+<name>
+netrc with missing netrc file
+</name>
+<command>
+--netrc --netrc-file %LOGDIR/netrc%TESTNUMBER http://user1@http.example/
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<errorcode>
+26
+</errorcode>
+</verify>
+</testcase>