Using C to modify response in VCL - c

The varnish docs say that we can include C snippets inside a VCL file, like
sub vcl_hash {
C{
int i = /* Some logic to get a number */
}C
}
But now how can I use the value of the integer i to set a response header, or cookie

See varnish.vcc
And the functions:
VRT_SetHdr
VRT_GetHdr
in varnish 4 there is ctx variabl defined for the context (as opposed to sp in varnish 3 ) (source)
example:
sub vcl_hash {
C{
const char *hash = calc_hash(...);
const struct gethdr_s hdr = {
HDR_BERESP,
"\010X-Hash:" // length prefixed string, in octal
};
VRT_SetHdr(ctx, &hdr, hash, vrt_magic_string_end);
}C
}
see here for another example

Why don't you just use VCL?
set resp.http.x-header = header to set any header you wanna set.

I'd encourage you to write a vmod directly, It'll be way more comfortable. You can find a (old but still relevant) guide here: https://info.varnish-software.com/blog/creating-a-vmod-vmod-str

Related

SNMP Agent: Could mib2c generate code for InetAddress or String type (ie something not an integer type)

I was able to transform 95% of a dedicated MIB to C code and make it run in a sub-agent like described in the last part of this Net-SNMP tutorial
For this I naturally use the mib2c.mfd.conf (I just read that mfd stands for Mib For Dummies ... all is said ...)
mib2c -I -c mib2c.mfd.conf my_mib_node
It generated a long .c file with almost all the oids like the one below.
Almost no lines were generated for the VideoInetAddr OID
//ABSTRACT OF SOURCE FILE GENERATED BY MIB2C
//...
long VideoFormat = 0; /* XXX: set default value */
// <<<=== NOTHING GENERATED HERE FOR VideoInetAddr OF TYPE INETADDRESS
// WHEREAS OTHER INTEGERS ARE NORMALLY PRESENT
long VideoInetPort = 0; /* XXX: set default value */
//...
void init_my_mib_node(void)
{
//...
const oid VideoFormat_oid[] = { 1,3,6,1,4,1,a,b,c,d,e };
static netsnmp_watcher_info VideoFormat_winfo;
// <<<=== NO OID GENERATED for VideoInetAddr OF TYPE INETADDRESS
// WHEREAS OTHER OIDs ARE NORMALLY GENERATED
static netsnmp_watcher_info VideoInetAddr_winfo; //We have the winfo after all
const oid VideoInetPort_oid[] = { 1,3,6,1,4,1,a,b,c,d,g };
static netsnmp_watcher_info VideoInetPort_winfo;
DEBUGMSGTL(("my_mib_node",
"Initializing VideoFormat scalar integer. Default value = %d\n",
VideoFormat));
reg = netsnmp_create_handler_registration(
"VideoFormat", NULL,
VideoFormat_oid, OID_LENGTH(VideoFormat_oid),
HANDLER_CAN_RWRITE);
netsnmp_init_watcher_info(&VideoFormat_winfo, &VideoFormat,
sizeof(long),ASN_INTEGER, WATCHER_FIXED_SIZE);
if (netsnmp_register_watched_scalar( reg, &VideoFormat_winfo ) < 0 ) {
snmp_log( LOG_ERR, "Failed to register watched VideoFormat" );
//...
}
This worked fine and needed 5 minutes (no code to write, just call the init() function), I was able to GET and SET all ... integers ...
Some oids are of Type InetAddress were not generated, neither were strings
Question
Is there a mib conf file able to generate code for every type
I tried the mib2c.old-api.conf which generates code also for the non-integer oids but I find it not as convenient. There is more boilerplate code to write.
Yes, mib2c could generate code for IP addresses. I cannot say that mfd does this, but, definitely, some mib2c.iterate.conf (for tables) does this.
The type of IP in SNMP is ASN_IPADDRESS represented by unint32_t in C.
Also,You need to make sure that in MIB-file for object, which represents IP, you have "SYNTAX IpAddress".
Have a look:
at the MIB file with IP object and implementation in C
Piece of answer but I am very far from comprehension and so side problems persist
Very pragmatically I managed to add by hand
//I put here ONLY what I added, see question above to complete code
#define STR_LENGTH_IPV4 sizeof("xxx.yyy.zzz.www")
char VideoInetAddr[STR_LENGTH_IPV4] = "192.168.2.3";
//...
const oid VideoInetAddr_oid[] = { 1,3,6,1,4,1,a,b,c,d,f };
reg = netsnmp_create_handler_registration(
"VideoInetAddr", NULL,
VideoInetAddr_oid, OID_LENGTH(VideoInetAddr_oid),
HANDLER_CAN_RWRITE);
netsnmp_init_watcher_info(&VideoInetAddr_winfo, &VideoInetAddr, sizeof(VideoInetAddr),
ASN_OCTET_STR, WATCHER_MAX_SIZE );
if (netsnmp_register_watched_scalar( reg, &VideoInetAddr_winfo ) < 0 ) {
snmp_log( LOG_ERR, "Failed to register watched VideoInetAddr" );
}
It still need to understand exactly the option like WATCHER_MAX_SIZE (is-it the good one ?)

EVP_get_cipherbyname always returns null

I have a problem when calling EVP_get_cipherbyname on macOS:
const char *cipher_str = "aes-256-cbc";
const evp_cipher_st *cipher1 = EVP_aes_256_cbc();
const evp_cipher_st *cipher2 = EVP_get_cipherbyname(cipher_str);
In the code above, cipher1 will always be set to a valid evp_cipher_st * object, and cipher2 will always be null. I haven't found a single instance of cipher_str that produces a non-null cipher2.
Am I doing something wrong? Are there some other calls I should be making to get this to work?
You need to initialize the OpenSSL library first. If you just use libcrypto,
call:
OpenSSL_add_all_algorithms();
Refer to https://wiki.openssl.org/index.php/Library_Initialization for how to handle other situations or openssl versions.

How to read URLs containing question marks in C using microhttpd.h

I am trying to parse the URL in C using microhttpd library.
daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, PORT, NULL, NULL, &answer_to_connection, NULL, MHD_OPTION_HTTPS_MEM_KEY, key_pem, MHD_OPTION_HTTPS_MEM_CERT, cert_pem, MHD_OPTION_END);
When I run the function MHD_start_daemon a call back function answer_to_connection is called.
static int answer_to_connection(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls)
{
printf("URL:%s\n", url);
}
One of the parameters of answer_to_connection is const char *url. The url variable contains the string after https://localhost:port example: for http://128.19.24.123:8888/cars/ferrari the url value will be /cars/ferrari
But in case of http://128.19.24.123:8888/cars?value=ferrari the url is printing only cars.
I want to print cars?value=ferrari. How can I do that?
There is a tutorial on microhttpd library at https://www.gnu.org/software/libmicrohttpd/tutorial.html
But I can't find any solution to this problem there.
CAVEAT EMPTOR: I have not used this library, this answer is based on a quick perusal of the API.
It looks like you can't access the whole original URL, because microhttpd parses it for you. Instead you access the individual query string values using MHD_lookup_connection_value, like this:
value = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "value");
That would return a pointer to the value of the query string argument, or null if not found.
You can also use MHD_get_connection_values to iterate over the query string components. In that case you would call it like this:
num = MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, iterator, cls);
iterator would be a callback function to receive the GET query arguments, one by one.
See also: the Handling requests section in the manual.

Lowercase urls in Varnish (inline C)

In Varnish (3.0), urls are treated in a case sensitive way. By that I mean http://test.com/user/a4556 is treated differently from http://test.com/user/A4556. On my web server they're treated as the same url. What I'd like to do is have varnish lowercase all request urls as they come in.
I managed to find this discussion but the creator of Varnish indicates that I will have to use inline C to do it. I could achieve this in a simplistic way using multiple regexes but that just seems like it's bound to fail.
Ideally, what I'd like is a VCL configuration to do this (an example of this can be found here) but I'd settle for a C function that takes in a const char * and returns const char * (I'm not a C programmer so forgive me if I get the syntax wrong).
It must be mentioned that Varnish includes the ability to uppercase and lowercase strings in the std vmod ( https://www.varnish-cache.org/docs/trunk/reference/vmod_std.generated.html#func-tolower )
This is much cleaner than the embedded C route (which is disabled by default in Varnish 4). Here's an example I use to normalize the request Host and url;
import std;
sub vcl_recv {
# normalize Host header
set req.http.Host = std.tolower(regsub(req.http.Host, ":[0-9]+", ""));
....
}
sub vcl_hash {
# set cache key to lowercased req.url
hash_data(std.tolower(req.url));
....
}
Okay, I went ahead and solved this for myself. Here's the VCL:
C{
#include <ctype.h>
//lovingly lifted from:
//https://github.com/cosimo/varnish-accept-language/blob/master/examples/accept-language.vcl
static void strtolower(const char *s) {
register char *c;
for (c=s; *c; c++) {
if (isupper(*c)) {
*c = tolower(*c);
}
}
return;
}
}C
sub vcl_recv {
C{
strtolower(VRT_r_req_url(sp));
}C
}
I put this in a separate VCL file and then added an include for it.
I'll just share my solution, which expands Richard's code into a complete solution.
If URL contains upper case letters, we redirect user to the correct URL, instead of simply normalizing the URL before entering the cache machinery. This prevents search engines from indexing mixed case URLs separately from lower-case.
# Define a function that converts a string to lower-case in-place.
# http://stackoverflow.com/questions/6857445
C{
#include <ctype.h>
static void strtolower(char *c) {
for (; *c; c++) {
if (isupper(*c)) {
*c = tolower(*c);
}
}
}
}C
sub vcl_recv {
if (req.http.host ~ "[A-Z]" || req.url ~ "[A-Z]") {
# Convert host and path to lowercase in-place.
C{
strtolower(VRT_GetHdr(sp, HDR_REQ, "\005host:"));
strtolower((char *)VRT_r_req_url(sp));
}C
# Use req.http.location as a scratch register; any header will do.
set req.http.location = "http://" req.http.host req.url;
error 999 req.http.location;
}
# Fall-through to default
}
sub vcl_error {
# Check for redirects - redirects are performed using: error 999 "http://target-url/"
# Thus we piggyback the redirect target in the error response variable.
if (obj.status == 999) {
set obj.http.location = obj.response;
set obj.status = 301;
set obj.response = "Moved permanently";
return(deliver);
}
# Fall-through to default
}
There's an ugly cast from const char * to char * when converting req.url to lower-case... basically, we're modifying the string in-place despite Varnish telling us not to. It seems to work. :-)
Nearly 5 years after the original question was asked I think we have a cleaner answer available now. This SO question still comes up top in a search for "lowercase Varnish".
Here is a simplified variation on the example that Fastly recommends:
# at the top of your VCL
import std;
sub vcl_recv {
# Lowercase all incoming URLs. It will also be lowercase by the time the hash is computed.
set req.url = std.tolower(req.url);
}
https://www.fastly.com/blog/varnish-tip-case-insensitivity
If you are looking for a C function that converts an upper case string to lower case, this will do:
#include <ctype.h>
static char *
to_lower (char *str)
{
char *s = str;
while (*s)
{
if (isupper (*s))
*s = tolower (*s);
s++;
}
return str;
}
Note that this modifies the string in-place. So you may want to pass a copy of the original string as argument.
Note that to set the URL from the C block and avoid crashing use:
VRT_l_req_url(sp,"new-string", vrt_magic_string_end);
(Pulled this detail from "varnishd -C" output.) Here's an untested revision to the first answer:
C{
#include <ctype.h>
//lovingly lifted from:
//https://github.com/cosimo/varnish-accept-language/blob/master/examples/accept-language.vcl
static void strtolower(const char *s) {
register char *c;
for (c=s; *c; c++) {
if (isupper(*c)) {
*c = tolower(*c);
}
}
return;
}
}C
sub vcl_recv {
C{
const char *url = VRT_r_req_url(sp);
char urlRewritten[1000];
strcat(urlRewritten, url);
strtolower(urlRewritten);
VRT_l_req_url(sp, urlRewritten, vrt_magic_string_end);
}C
}

SetProp problem

Can anybody tell me why the following code doesn't work? I don't get any compiler errors.
short value = 10;
SetProp(hCtl, "value", (short*) value);
The third parameter is typed as a HANDLE, so IMO to meet the explicit contract of the function you should save the property as a HANDLE by allocating a HGLOBAL memory block. However, as noted in the comments below, MSDN states that any value can be specified, and indeed when I try it on Windows 7 using...
SetProp(hWnd, _T("TestProp"), (HANDLE)(10)); // or (HANDLE)(short*)(10)
...
(short)GetProp(hWnd, _T("TestProp"));
... I get back 10 from GetProp. I suspect somewhere between your SetProp and GetProp one of two things happens: (1) the value of hWnd is different -- you're checking a different window or (2) a timing issue -- the property hasn't been set yet or had been removed.
If you wanted to use an HGLOBAL instead to follow the specific types of the function signature, you can follow this example in MSDN.
Even though a HANDLE is just a pointer, it's a specific data type that is allocated by calls into the Windows API. Lots of things have handles: icons, cursors, files, ... Unless the documentation explicitly states otherwise, to use a blob of data such as a short when the function calls for a HANDLE, you need a memory handle (an HGLOBAL).
The sample code linked above copies data as a string, but you can instead set it as another data type:
// TODO: Add error handling
hMem = GlobalAlloc(GPTR, sizeof(short));
lpMem = GlobalLock(hMem);
if (lpMem != NULL)
{
*((short*)lpMem) = 10;
GlobalUnlock(hMem);
}
To read it back, when you GetProp to get the HANDLE you must lock it to read the memory:
// TODO: Add error handling
short val;
hMem = (HGLOBAL)GetProp(hwnd, ...);
if (hMem)
{
lpMem = GlobalLock(hMem);
if (lpMem)
{
val = *((short*)lpMem);
}
}
I would create the short on the heap, so that it continues to exist, or perhaps make it global, which is perhaps what you did. Also the cast for the short address needs to be void *, or HANDLE.

Resources