Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected errors if smtp support is disabled (on RHEL-9) #15472

Closed
jeroen opened this issue Nov 1, 2024 · 6 comments
Closed

Unexpected errors if smtp support is disabled (on RHEL-9) #15472

jeroen opened this issue Nov 1, 2024 · 6 comments

Comments

@jeroen
Copy link
Contributor

jeroen commented Nov 1, 2024

RHEL 9: dual configuration

Users of the R bindings reported weird errors when sending sending emails on RHEL/Rockylinux version 9. It turns out the folks at Redhat have packaged libcurl in a unusual way.

RHEL version 9 ships a single curl-devel package, but two different libcurl runtime builds. The default version installed on most systems is called libcurl-minimal and it disables several protocols including smtp. An alternative more complete build is available in the libcurl package, but most servers don't have this installed.

The problem

When a user of libcurl bindings has the libcurl-minimal package (which does not support smtp) installed on the server, we see the following behavior:

  • Setting CURLOPT_URL to a smtp:// url does not error
  • The curl_easyoption option API makes it seem CURLOPT_MAIL_RCPT is supported, however:
  • Setting the CURLOPT_MAIL_RCPT gives a confusing error message An unknown option was passed in to libcurl

Example program

An example test.c program to demonstrate this:

#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
 
int main(void){
  curl_global_init(CURL_GLOBAL_DEFAULT);
  CURL *curl = curl_easy_init();
  CURLcode err = 0;

  // Check if SMTP supported is enabled
  int has_smtp = 0;
  const curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
  const char *const * temp = data->protocols;
  while(*++temp)
    if(strcmp(*temp, "smtp") == 0) has_smtp=1;
  printf("SMTP support: %s\n", has_smtp ? "YES" : "NO");

  // Test if CURLOPT_MAIL_RCPT exists
  const struct curl_easyoption *o = curl_easy_option_by_id(CURLOPT_MAIL_RCPT);
  printf("Found %s: type %d flags %d\n", o->name, o->type, o->flags);

  // should this not error if smtp is not supported?
  err = curl_easy_setopt(curl, CURLOPT_URL, "smtp://mail.example.com");
  printf("Setting CURLOPT_URL: %s\n",curl_easy_strerror(err));

  struct curl_slist *recipients = NULL;
  recipients = curl_slist_append(recipients, "<addressee@example.net>");
  err = curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
  printf("Setting CURLOPT_MAIL_RCPT: %s\n",curl_easy_strerror(err));
  return 0;
}

You can easily run a RHEL-9 container in docker:

docker run --rm -it rockylinux/rockylinux:9

Then in docker you need to install the compiler and curl headers:

dnf install -y gcc curl-devel
gcc test.c -lcurl
./a.out

curl/libcurl version

curl 7.76.1 (x86_64-redhat-linux-gnu) libcurl/7.76.1 OpenSSL/3.0.7 zlib/1.2.11 nghttp2/1.43.0

operating system

Redhat 9 / RockyLinux 9

@dfandrich
Copy link
Contributor

dfandrich commented Nov 1, 2024 via email

@bagder
Copy link
Member

bagder commented Nov 1, 2024

Setting CURLOPT_URL to a smtp:// url does not error

Let me quote a section of the CURLOPT_URL page:

   libcurl does not validate the syntax or use the URL until the transfer is started.
   Even if you set  a  crazy  value  here,  curl_easy_setopt(3)  might  still  return
   CURLE_OK.

@jeroen
Copy link
Contributor Author

jeroen commented Nov 4, 2024

The language bindings try help users at runtime by validating that we only expose options that exist in libcurl, and that the value passed by the user is of the correct type. If curl_easy_setopt() nevertheless returns an error, we want to present an informative message to the user.

It is confusing that an option is advertised by curl_easyoption but when using it at runtime you get a generic error that is the same error as when calling curl_easy_setopt() with a CURLoption that does not exist at all.

One improvement could be if curl_easyoption->flags would contain a flag for options that are disabled. Then bindings can warn users against trying to set that option. Currently we assume that users may set any of the options listed by curl_easy_option_next() which is seemingly too optimistic.

Alternatively an improvement could be that curl_easy_setopt() returns a special error code for a CURLoption that does exist but is not available. That way the bindings can distinguish it from non-existing options, and can tell the user: "the option you are trying to set exists, but is supported has been disabled by your local libcurl build".

The other would be to use curl_version_info() to look at the list of protocols supported.

I know now that in this case we should check if smtp is a supported protocol but we would need something general that would help for any option, not just CURLOPT_MAIL_RCPT. As author of the language bindings, I cannot know for all 300+ libcurl options which other things I need to check first to know if the option should actually be made available to the user at runtime.

@bagder
Copy link
Member

bagder commented Nov 5, 2024

background

When someone decides to deliberately strip a libcurl build from features it would normally have, that libcurl build no longer supports what a plain default build would otherwise support. There are a few different reasons why people decide to strip libcurl but ultimately it is the choice of the one who builds it.

How exactly libcurl should act when users try to use features that are explicitly disabled is not always consistent or perfected mostly because this is "off the mainstream". As one of the reasons people remove features is to reduce foot-print and size, we also don't want to add a lot of logic to handle cases where we remove features in the build.

on the issue

Yes, it would be nice to have curl_easyoption reflect the build status.

an improvement could be that curl_easy_setopt() returns a special error code for a CURLoption that does exist but is not available

CURLE_NOT_BUILT_IN was introduced for this purpose.

@jeroen
Copy link
Contributor Author

jeroen commented Nov 5, 2024

Yes, it would be nice to have curl_easyoption reflect the build status.

That would be very helpful.

CURLE_NOT_BUILT_IN was introduced for this purpose.

Ah ok that would be much better. Setting CURLOPT_MAIL_RCPT on RHEL-9 returns CURLE_UNKNOWN_OPTION.

@bagder
Copy link
Member

bagder commented Nov 5, 2024

Setting CURLOPT_MAIL_RCPT on RHEL-9 returns CURLE_UNKNOWN_OPTION.

Yes, for most/all disabled options.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

3 participants