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

Set a user agent for http/https tdnf requests #453

Merged
merged 1 commit into from
Nov 2, 2023

Conversation

shivania2
Copy link
Contributor

tdnf does not set a user agent for http/https requests and the repository pkgs.k8s.io did block requests without user agents (because its the default policy in AWS WAF). This issue is fixed in kubernetes upstream repo. But to avoid this kind of issue in future, adding user agent for http/https tdnf requests

@vmwclabot
Copy link
Member

@shivania2, you must sign our contributor license agreement before your changes are merged. Click here to sign the agreement. If you are a VMware employee, read this for further instruction.

Copy link
Contributor

@oliverkurth oliverkurth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need a test.

client/config.c Outdated Show resolved Hide resolved
client/config.c Outdated Show resolved Hide resolved
client/config.c Outdated

while (fgets(line, 2*UserAgentHeaderMaxLength, file)) {
if (strstr(line, "ID=") != NULL) {
sscanf(line, "ID=\"%[^\"]\"", pConf->pszOSName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested that this works with both unquoted and quoted settings?

client/config.c Outdated Show resolved Hide resolved
Comment on lines 546 to 547
uint32_t
TDNFParseOSInfo(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can make this a static function in config.c.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use config parser for parsing /etc/os-release?

Also, we have some info on OS with uname APIs? can we use that here as well?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use config parser for parsing /etc/os-release?

No.

Also, we have some info on OS with uname APIs? can we use that here as well?

I don't think it's needed.

client/config.c Outdated
strcat(pszUserAgentHeader, " ");
}
else
strcat(pszUserAgentHeader, "UNKNOWN ");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: use "unknown". No need to yell.

client/config.c Outdated Show resolved Hide resolved
client/config.c Outdated Show resolved Hide resolved
client/config.c Outdated
@@ -91,6 +139,8 @@ TDNFReadConfig(
}
}

TDNFParseOSInfo(pConf);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validate return value here

client/config.c Outdated
dwError = TDNFAllocateMemory(UserAgentHeaderMaxLength, sizeof(char), (void**)&pszUserAgentHeader);
BAIL_ON_TDNF_ERROR(dwError);

strcpy(pszUserAgentHeader, "User-Agent:tdnf/");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"User-Agent:tdnf/" storing these keywords in macros would help

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use TDNFAllocateStringPrintf() it will be a format string.

Comment on lines 546 to 547
uint32_t
TDNFParseOSInfo(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use config parser for parsing /etc/os-release?

Also, we have some info on OS with uname APIs? can we use that here as well?

client/config.c Outdated
strcat(pszUserAgentHeader, "/");

if (pConf->pszOSVersion != NULL)
strcat(pszUserAgentHeader, pConf->pszOSVersion);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use snprintf here.

client/config.c Outdated Show resolved Hide resolved
client/config.c Outdated
char *line = NULL;
uint32_t dwError = 0;
FILE *file = NULL;
file = fopen("/etc/os-release", "r");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line before line 39 and store etc/os-release path in macro

client/config.c Outdated
dwError = TDNFAllocateMemory(2*UserAgentHeaderMaxLength, sizeof(char), (void**)&line);
BAIL_ON_TDNF_ERROR(dwError);

dwError = TDNFAllocateMemory(UserAgentHeaderMaxLength, sizeof(char), (void**)&pConf->pszOSName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

50 bytes for name & version seems like an overkill and allocate memory based on line length inside loop.

@shivania2
Copy link
Contributor Author

We also need a test.

Exploring the possible test case

@shivania2 shivania2 force-pushed the topic/shivania2/useragent branch from 46bc356 to 1293042 Compare October 10, 2023 08:25
client/config.c Outdated
}

while (fgets(temp, USERAGENTHEADERMAXLENGTH, file)) {
if (strstr(temp, "ID=") != NULL && name_found == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use strncmp("ID=", temp, sizeof("ID=")-1);

no need of name_found and it will make this logic bit more robust

client/config.c Outdated
BAIL_ON_TDNF_SYSTEM_ERROR(dwError);
}

while (fgets(temp, USERAGENTHEADERMAXLENGTH, file)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fgets() is just fine.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fgets works here but we assume that max line length is 50 bytes, with getline we don't need not make any assumptions and fits perfectly for this use case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

50 characters is too short, we can just create a 256 byte buffer on the stack, and no serious /etc/os-release file will have lines any longer, and even if we can skip the rest. We can use getline(), I just think it's overkill.

client/config.c Outdated
static uint32_t TDNFParseOSInfo(PTDNF_CONF pConf)
{
char temp[USERAGENTHEADERMAXLENGTH];
char name[10];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dynamically allocate memory for name and version and simply assign them to pConf->pszOSName and version.
Later it gets freed anyway.

@shivania2 shivania2 force-pushed the topic/shivania2/useragent branch from 1293042 to 8238803 Compare October 10, 2023 10:31
client/config.c Outdated
@@ -13,6 +13,9 @@
#include "../llconf/entry.h"
#include "../llconf/ini.h"

#define USERAGENTHEADERMAXLENGTH 50
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make this better readable, use like USERAGENT_HEADER_MAXLENGTH

client/config.c Outdated
BAIL_ON_TDNF_SYSTEM_ERROR(dwError);
}

dwError = TDNFAllocateMemory(10, sizeof(char), (void**)&name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't pre-allocate, parse the strings into the temp buffer first, then use TDNFAllocateString() to allocate the exact amount you need.

client/config.c Outdated
if (pConf->pszOSVersion == NULL)
TDNFAllocateString("UNKNOWN", &pConf->pszOSVersion);

dwError = TDNFAllocateStringPrintf(&pConf->pszUserAgentHeader, "User-Agent:tdnf/%s %s/%s", pszTdnfVersion, pConf->pszOSName, pConf->pszOSVersion);
Copy link
Contributor

@oliverkurth oliverkurth Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need a space after "User-Agent:". But, I think it doesn't need to be part of the string at all. See example at https://curl.se/libcurl/c/CURLOPT_USERAGENT.html

curl_easy_setopt(curl, CURLOPT_USERAGENT, "Dark Secret Ninja/1.0");

Also make sure it's compliant with RFC 7231, see https://www.rfc-editor.org/rfc/rfc7231#page-46

Also, I don't see anywhere where you are setting the header for curl. At some point you will have to call curl_easy_setopt(curl, CURLOPT_USERAGENT, ...

@shivania2 shivania2 force-pushed the topic/shivania2/useragent branch from 8238803 to 12d3d44 Compare October 16, 2023 06:21
client/config.c Outdated
file = fopen(OS_CONF_FILE, "r");

if (!file) {
dwError = errno;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

print a warning here and return 0 perhaps?

client/config.c Outdated
@@ -26,6 +29,50 @@ TDNFConfGetRpmVerbosity(
return nLogLevel;
}

static uint32_t TDNFParseOSInfo(PTDNF_CONF pConf)
{
char temp[USERAGENT_HEADER_MAX_LENGTH];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would still say we should avoid static allocation here and use getline. It's upto you on what you want to use.

Also, be aware that fgets doesn't always null terminate the buffer. By chance if there is a line which is more than 50 bytes in os-release file, this might end up crashing tdnf.

Copy link
Contributor

@oliverkurth oliverkurth Oct 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not what the man page says:

fgets()  reads in at most one less than size characters from stream and stores them into the buffer pointed to by s.  Reading stops after an EOF or a newline.  If a newline is read, it is stored into
       the buffer.  A terminating null byte ('\0') is stored after the last character in the buffer.

Instead it seems to take care that it is aways terminated with 0.

client/config.c Show resolved Hide resolved
client/config.c Outdated
@@ -91,6 +139,11 @@ TDNFReadConfig(
}
}

dwError = TDNFParseOSInfo(pConf);
if (dwError != ENOENT) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a question. If we the os-release file doesn't exist and it's considered not a fatal error, why should we consider other errors in TDNFParseOSInfo as fatal? why not use some default values in case of failure and make the return type void?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think /etc/os-release not present is an acceptable condition, but failing to parse it for any reason is not. It may be some deeper issue (a bug in the parser, file corrupted or whatever) , and we shouldn't proceed.

@shivania2 shivania2 force-pushed the topic/shivania2/useragent branch 4 times, most recently from a9414c2 to 74df3aa Compare October 17, 2023 06:58
client/config.c Outdated
TDNF_SAFE_FREE_MEMORY(name);
TDNF_SAFE_FREE_MEMORY(version);
goto cleanup;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete the empty line please

client/config.c Show resolved Hide resolved
client/config.c Show resolved Hide resolved
client/config.c Outdated
Comment on lines 49 to 51
if (strncmp("ID=", buf, sizeof("ID=")-1) == 0)
{
sscanf(buf, "ID=%s\n", buf);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to take care of quotes, which may or may not be present. For example this is in Ubuntu:

$ cat /etc/os-release 
PRETTY_NAME="Ubuntu 22.04.3 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.3 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
...

This is in Photon:

# cat /etc/os-release 
NAME="VMware Photon OS"
VERSION="5.0"
ID=photon
VERSION_ID=5.0
...

Note that Ubuntu has VERSION_ID in quotes, but Photon does not. You need to check for quotes and remove them if there.

@shivania2 shivania2 force-pushed the topic/shivania2/useragent branch 4 times, most recently from e547037 to d6c7975 Compare October 20, 2023 07:08
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for writing a test! Are you going to finish this? I don't see where we are testing the header sent by tdnf (we are not calling it at all here).

@shivania2 shivania2 force-pushed the topic/shivania2/useragent branch 2 times, most recently from 7a52e76 to ed0a329 Compare October 26, 2023 09:42
Copy link
Contributor

@oliverkurth oliverkurth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As stated per DM, let's drop the test change, it takes too much effort. I verified locally that the header is sent on requests. The change is good, I can merge this, without a test.

client/config.c Show resolved Hide resolved
@shivania2
Copy link
Contributor Author

As stated per DM, let's drop the test change, it takes too much effort. I verified locally that the header is sent on requests. The change is good, I can merge this, without a test.

Thank You. I will remove the test case

@shivania2 shivania2 force-pushed the topic/shivania2/useragent branch from ed0a329 to 95c1054 Compare November 2, 2023 05:10
@oliverkurth oliverkurth merged commit ee028e5 into vmware:dev Nov 2, 2023
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants