Refresh MYSQL UDF - mysql

I am trying to use my own UDF (written in c++) as a mysql extension. I was able to install the function via
CREATE FUNCTION decrypt RETURNS STRING SONAME "libudf_func.so";
and use it. I could also drop the function via
DROP FUNCTION decrypt;
After that it would not be listed in the mysql.func table, so Im pretty sure it was successfully deleted.
Now I want to use an updated version of my function, and therefore reload it in mysql. However, mysql only reloads the old version of my file (which I completely removed from the computer). Even if I remove ALL files named "libudf_func.so" it will be reloaded.
I'm using mysql version 8.0.28 for macos12.0 on localhost.
How can I remove this cached version or force mysql to reload?
EDIT
All my steps, from loading the first version to my attempt to load the updated version:
I used: CREATE FUNCTION decrypt RETURNS STRING SONAME "libudf.so"; to initially create the function decrypt
I changed something in my udf.cpp file.
I used: DROP FUNCTION decrypt; to remove the function decrypt
I used CREATE FUNCTION decrypt RETURNS STRING SONAME "libudf.so"; as an attempt to reload/refresh the function decrypt (NO ERRORS)
the behaviors did not change (among other things I wanted to output a different error message, so that was pretty obviously the old behavior)
I renamed libudf.so to lubudf_func.so
I replicated this with the new name.
I deleted ALL files with the names libudf.so or lubudf_func.so from my machine
I could still execute CREATE FUNCTION decrypt RETURNS STRING SONAME "libudf.so"; without any errors
I tried to call CREATE FUNCTION decrypt RETURNS STRING SONAME "libudf_xyz.so"; (never existed), which failed. Error message: "Error Code: 1126. Can't open shared library 'libudf_xyz.so' (errno: 2 dlopen(/usr/local/opt/mysql/lib/plugin/libudf_xyz.so, 0x0002): tried: '/usr/local/opt/mysql/lib/plugin/libudf_xyz.so' (no such f)"
plugin_dir: /usr/local/opt/mysql/lib/plugin/
--->(this is where the .so file WAS located, but is not anymore)
How did I notice the old behavior was still present? The dead giveaway was that the function used the old error message ("Please use plaintext(int), iv(string), key(string)"), and not the new message ("Please use plaintext(string), iv(string), key(string)") after step no. 4
Also add the content of the plugins/ directory, which CREATE FUNCTION uses to load the function: I wrote the library in c++, then built it as a shared library. The c++ code would be as follows (but Im not sure this is relevant):
extern "C" {
bool decrypt_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
char *decrypt(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error);
void decrypt_deinit(UDF_INIT *initid);
}
bool decrypt_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
if (args->arg_count != 3 or
args->arg_type[0] != STRING_RESULT or
args->arg_type[1] != STRING_RESULT or
args->arg_type[2] != STRING_RESULT
) {
strcpy(message, "Usage: decrypt_poly(string ctext, string key, string iv)");
return 1;
}
if (args->lengths[2] != 48) {
strcpy(message, "Only 24-length IVs are allowed. Make sure IV is hexed.");
return 1;
}
initid->ptr = new char[args->lengths[0]];
initid->maybe_null = 1;
return 0;
}
char *decrypt(UDF_INIT *initid, UDF_ARGS *args,
char *result, unsigned long *length,
char *is_null, char *error) {
std::string dvalue;
if (NULL != args->args[0]) {
std::string value = std::string(args->args[0], args->lengths[0]);
std::string key = std::string(args->args[1], args->lengths[1]);
std::string iv = std::string(args->args[2], args->lengths[2]);
try {
dvalue = ((encryption::poly_based_encryption::decrypt(value, key, iv))));
}
catch (CryptoPP::Exception &e) {
std::cerr << e.what() << std::endl;
*error = 1;
}
} else {
dvalue = "";
*is_null = 1;
*error = 1;
return NULL;
}
strcpy(initid->ptr, dvalue.c_str());
*length = dvalue.length();
result = initid->ptr;
*is_null = 0;
*error = 0;
return result;
}
void decrypt_deinit(UDF_INIT *initid) {
if (initid->ptr)
delete[] initid->ptr;
}

Related

DotNet Interop -- is AllocHGlobal my only option

I inherited some old Interop code and wanted to update it for the newer guidance, as it uses StringBuilder.
https://learn.microsoft.com/en-us/dotnet/standard/native-interop/best-practices
I do not have access to the source code to the native method.
[DllImport(NativeLib, CharSet = CharSet.Ansi, ExactSpelling = true)]
static extern int native_func(double input, StringBuilder result);
public string Convert(double input)
{
var res = new StringBuilder();
if (native_func(input, res) == 0)
return res.ToString();
throw ...;
}
I was able to convert things to manually use AllocateHGlobal instead of StringBuilder.
[DllImport(NativeLib, CharSet = CharSet.Ansi, ExactSpelling = true)]
static extern int native_func(double input, IntPtr result);
public string Convert(input val)
{
var res = Marshal.AllocHGlobal(100);
// pretend we are calling Marshal.FreeHGlobal later...
if (native_func(input, res) == 0)
return Marshal.PtrToStringAnsi(res);
throw ...;
}
But that's not the new ref/span/etc hotness.
However, no matter what I seem to try for the result parameter, I either get just the first character, or a System.AccessViolationException.
I've tried MemoryMarshal.GetReference, I've tried pinning, I've tried ref-ing, I've tried out-ing; whenever I'm trying to use a managed buffer, it just doesnt' work.
Are there scenarios where the new interop just doesn't work? Is AllocHGlobal the best I can do?

Gettinig an INT at the end of the Serialized JSON

So I'm updating this as I've gotten JSON OUT however the JSON is followed by an INT
{"function":"timereport","values":545028}41
#include <ArduinoJson.h>
char owner[] = "";
const int capacity = JSON_OBJECT_SIZE(2);
DynamicJsonDocument doc(capacity);
void setup() {
Serial.begin(115200);
}
void loop() {
doc["function"]="timereport";
doc["values"]=millis();
Serial.println(serializeJson(doc, Serial));
delay(5000);
}
Second TRY:(Post Still has the INT at the end)
char json_string[256];
http.begin("http://192.168.1.103:2000/hydroapi"); //Specify destination for HTTP request
http.addHeader("Content-Type","application/json");
doc["function"]="timereport";
doc["values"]=millis();
serializeJson(doc, json_string);
int httpResponseCode = http.POST(json_string); //Send the actual POST request
http.end();
The call to serializeJson prints the json to Serial (as that's the stream your passing in) and returns the number of bytes printed, which you're then explicitly printing using Serial.println.
Remove the call to Serial.println to avoid printing the number of bytes, just call serializeJson(doc, Serial);

Connecting Arduino to wunderground using ESP8266

I'm trying to use an ESP8266 and Arduino Uno to connect to wunderground and get the JSON file to get the current weather. With my code I am connecting to the server fine. What seems to be the issue is that it's not giving me the whole return file.
#include <SoftwareSerial.h>
#include <ArduinoJson.h>
SoftwareSerial esp8266(8, 9);
bool flag = true;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
esp8266.begin(9600);
}
void loop() {
if (flag) {
String cmd;
int length;
cmd = "AT+CIPSTART=\"TCP\",\"";
cmd += "api.wunderground.com";
cmd += "\",80";
esp8266.println(cmd);
Serial.println(cmd);
delay(2000);
Serial.write(esp8266.read());
if (esp8266.find("CONNECT")) {
Serial.println("CONNECT found so your connected");
}
String action;
action = "GET http://api.wunderground.com/api/APIKEY/conditions/q/Canada/Regina.json HTTP/1.0\r\n\r\n";
length = action.length();
cmd = "AT+CIPSEND=";
cmd += length;
esp8266.println(cmd);
Serial.println(cmd);
delay(5000);
if (esp8266.find(">")) {
Serial.print(">");
} else {
esp8266.println("AT+CIPCLOSE");
Serial.println(F("connect timeout"));
}
esp8266.println(action);
Serial.println(action);
delay(700);
String test = "";
while (esp8266.available()) {
char c = esp8266.read();
test += c;
}
Serial.println(test);
flag = false;
Serial.println("Flag is false");
}
}
Running this code give me the following result:
AT+CIPSTART="TCP","api.wunderground.com",80
ACONNECT found so your connected
AT+CIPSEND=97
GET http://api.wunderground.com/api/7287eb3ace065563/conditions/q/Canada/Regina.json HTTP/1.0
busy s...
Recv 97 bytes
SEND OK
+IPD,1460:HTTP/1.0:"0.1",
"termsofService":"http://www.wunderground.com/weather/api/d/terms.html",
"
Flag is false
As you can see I only get a snippet of the JSON file. I'm not sure what the problem is.
It's not sending JSON at all. It detected that your Arduino/ESP combo was not a human, and is scolding you, letting you know that you are in breach of the Terms of Service, as described in http://www.wunderground.com/weather/api/d/terms.html. You need to set some headers, to masquerade as a browser and thus pass as a human user.

objective-c implementation of mySQL encrypt() function

I have a mySQL database with hashed passwords that I cannot abandon. I need to duplicate the encrypt() function of mySQL so that I can be consistent in my hash creation for login in an iOS app I'm creating. (I'm using the first 2 characters of the password as the salt for the encrypt function)
Has anyone done this before? I tried to add the following category to NSString based on code I found elsewhere, but the resulting string isn't even close. (I have a base64 category on NSData and yes, I'm new to the CCCrypt call)
-(NSString*) encryptWithSalt:(NSString *)salt {
NSString *token = self;
const void *vplainText;
size_t plainTextBufferSize = [token length];
vplainText = (const void *) [token UTF8String];
CCCryptorStatus ccStatus;
uint8_t *bufferPtr = NULL;
size_t bufferPtrSize = 0;
size_t movedBytes = 0;
bufferPtrSize = (plainTextBufferSize + kCCBlockSizeDES) & ~(kCCBlockSizeDES - 1);
bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
memset((void *)bufferPtr, 0x0, bufferPtrSize);
uint8_t iv[kCCBlockSizeDES];
memset((void *) iv, 0x0, (size_t) sizeof(iv)); // zero out iv
const void *vkey = (const void *) [salt UTF8String];
ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding | kCCModeCBC,
vkey,
kCCKeySizeDES,
iv,
vplainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
&movedBytes);
NSData *myData = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
NSString* hash;
if (ccStatus == kCCSuccess) {
hash = [myData base64EncodedString];
}
return hash;
}
The crypt() function cannot be emulated using CCCrypt, as the salt is used to alter the DES E-box. This makes the cipher in use no longer really DES.
However, it's still just the crypt() function. Call it directly:
- (NSString*) encryptWithSalt:(NSString *)salt {
return [NSString stringWithUTF8String:crypt([self UTF8String], [salt UTF8String])];
}
Incidentally, using the first two characters of the password as the salt is a gigantic security hole, as it narrows the password search space down to six characters (because the first two are given away by the salt).

my_udf_init not called

This is my first time trying to create an udf for mysql. The docs state that my_func_init gets called prior to executing the main function, yet in my environment this does not seem to happen.
long long charmatch(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)
{
return 42;
}
my_bool charmatch_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
if (args->arg_count != 2)
{
strncpy(message, "charmatch() requires two arguments", 34);
return EXIT_FAILURE;
// was return EXIT_SUCCESS;
}
if(args->arg_type[0] != STRING_RESULT)
{
strncpy(message, "argument 1 must be a string", 27);
return EXIT_FAILURE;
// was return EXIT_SUCCESS;
}
if(args->arg_type[0] != STRING_RESULT)
{
strncpy(message, "argument 2 must be a string", 27);
return EXIT_FAILURE;
// was return EXIT_SUCCESS;
}
return EXIT_SUCCESS;
}
As you can see, I'm checking whether there are two arguments. In theory SELECT CHARMATCH() should return a message saying I must set two arguments, but it's not: it return 42.
Why doesn't it return the message?
EDIT Seems like the problem was in my return values for errors being 0 instead of 1. I fixed it and now calling SELECT CHARMATCH() with the wrong number of arguments returns an empty set. Why are the errors not being displayed?
You always return EXIT_SUCCESS in charmatch_init(), but
"The initialization function should return 0 if no error occurred and 1 otherwise. If an error occurs, xxx_init() should store a null-terminated error message in the message parameter. "
http://dev.mysql.com/doc/refman/5.0/en/udf-return-values.html
I guess that's why your error messages are ignored.