How can I conditionality load and use a shared library if it is installed, but still run without that functionality if it is not there? more specifically, can I do that without using that library as a plugin? I prefer failing during build time rather than runtime if possible.
if I build it with the library flag -lfoo, it builds. But then it fails to run it libfoo.so.2 is not installed in the target system. But if I don't add the library flag it fails in linking.
here is some code snipets for better picture.
myAdapter.cpp
#include "newlib/foo.h" //this is from the shared library
...
bool myAdapter::isAvailable()
{
handle_ = dlopen("libfoo.so.2", RTLD_LAZY);
if (!handle_)
{
return false;
}
return true;
}
...
bool myAdapter::init()
{
if (!isAvailable())
{
return false;
}
isInitilized = false;
isConnected = false;
if (!fooInit()) // shared library function
{
fooCleanup(); //shared library function
return false;
}
// these are my private functions but they call shared library functions.
if (!createUserParams_() || !setCallbacks_() || !createContext_() || !connect_())
{
return false;
}
return true;
}
...
myApp.cpp
#include "myAdapter.h"
...
int main()
{
...
foo = new myAdapter();
if (!foo.init())
{
cout << "Foo function is not available;
isFooAvailable = false;
}
}
...
}
First, remove any linkage you have to libfoo.a, libfoo.sa, or any -lfoo parameter in your build. Everything is to be dynamically loaded.
Second, after dlopen succeeds, you need to get the address, via dlsym, of all the functions you need to use - and call through into those.
Third, instead of making a direct call to your functions, you call through to them via the pointer you loaded via dlsym.
Finally, your isAvailable has a side effect (loading the library) and will re-attempt every time. You just need to load the library once.
Here's an improved version of your code. I made some hypothetical assumptions of what your params to those library functions would be. Change it to meet your needs:
class myAdapter
{
void* handle_;
typedef bool (*CREATE_USER_PARAMS)(UserParams*);
typedef int (*CONNECT)(int);
CREATE_USER_PARAMS createUserParams_;
CONNECT connect_;
myAdapter() :
handle_(),
createUserParams_(),
connect_()
{
handle_ = dlopen("libfoo.so.2", RTLD_NOW);
if (handle_)
{
createUserParams = (CREATE_USER_PARAMS)dlsym(handle_, "createUserParams");
connect_ = (CONNECT)dlsym(handle_, "connect");
if (!createUserParams_ || !connect_)
{
// error
dlclose(handle_);
handle_ = nullptr;
connect = nullptr_;
createUserParams_ = nullptr;
}
}
}
~myAdapter()
{
if (handle_)
{
dlclose(handle_);
handle = nullptr;
}
}
bool createUserParams(UserParams* params)
{
if (createUserParams_ != nullptr)
{
return createUserParams_(params);
}
else
{
return false;
}
}
int connect(int sock)
{
if (connect_ != nullptr)
{
return (connect_(sock));
}
else
{
return -1;
}
}
};
Related
I'm attempting to refactor my code using clang-tidy and it flags a number of changes that should not be applied. For example, I've defined for my cpp files the format:
{ key: readability-identifier-naming.FunctionCase, value: camelBack }
But it flags fixes for functions outside my control like
static void from_json(const nlohmann::json& aJson,...)
Everything I'm working with is contained within a namespace, so is it possible to only apply readability-identifier-naming to content within a namespace? Can this be done with existing functions or will I need to define my own format checker in the clang-tools-extra?
Update
I solved the issue by modifying IdentifierNamingCheck::getDeclFailureInfo and adding a namespace filter to the FileStyle. I pulled some code from another place. It's not a general solution but it works in my case.
// ...
if (!FileStyle.namespaceFilter().empty()) {
auto testNamespace = [&FileStyle](const DeclContext *DC) {
if (!DC->isNamespace())
return false;
const auto *ND = cast<NamespaceDecl>(DC);
if (ND->isInline()) {
return false;
}
if (!DC->getParent()->getRedeclContext()->isTranslationUnit())
return false;
const IdentifierInfo *II = ND->getIdentifier();
return II && II->isStr(FileStyle.namespaceFilter());
};
bool isNamespace = false;
for (const DeclContext *DC = Decl->getDeclContext(); DC;
DC = DC->getParent()) {
isNamespace |= testNamespace(DC);
if (isNamespace)
break;
}
if (!isNamespace)
return llvm::None;
}
// ...
I have a fairly good template (as in snippet of code) I pull out whenever I need a singleton class. I am now trying to apply it within my project to allow me to control a single instance of a web server. I can make a web server without encasing it in my class. When I try to encase it within the class I'm apparently too unskilled to pull it off.
I've tried the obvious Googling and searching here. I've read relevant posts. I am sure this does not mean I have a unique problem, just that I've not figured out the right way to fix it. Here's what I am working with:
webserver.h:
#include <ESP8266WebServer.h>
#include <FS.h>
class WebServer {
private:
// Singleton Declarations
static bool instanceFlag;
static WebServer *single;
WebServer() {}
// Other Declarations
FS *filesystem;
ESP8266WebServer server();
String getContentType(String);
bool handleFileRead(String);
public:
// Singleton Declarations
static WebServer* getInstance();
~WebServer() {instanceFlag = false;}
// Other Declarations
void initialize(int);
void handleLoop();
};
webserver.cpp:
#include "webserver.h"
bool WebServer::instanceFlag = false;
WebServer* WebServer::single = NULL;
WebServer* WebServer::getInstance() {
if(!instanceFlag) {
single = new WebServer();
instanceFlag = true;
return single;
} else {
return single;
}
}
void WebServer::initialize (int port) {
ESP8266WebServer server(port);
FS *filesystem;
filesystem->begin();
Serial.print("Open: http://");
Serial.print(WiFi.hostname().c_str());
Serial.println(".local");
server.onNotFound([]() {
if (!single->handleFileRead(single->server.uri())) {
single->server.send(404, "text/plain", "404: File not found.");
}
});
server.begin();
Serial.print("HTTP server started on port ");
Serial.print(port);
Serial.println(".");
}
String WebServer::getContentType(String filename) {
if (single->server.hasArg("download")) {
return "application/octet-stream";
} else if (filename.endsWith(".htm")) {
return "text/html";
} else if (filename.endsWith(".html")) {
return "text/html";
} else if (filename.endsWith(".css")) {
return "text/css";
} else if (filename.endsWith(".js")) {
return "application/javascript";
} else if (filename.endsWith(".png")) {
return "image/png";
} else if (filename.endsWith(".gif")) {
return "image/gif";
} else if (filename.endsWith(".jpg")) {
return "image/jpeg";
} else if (filename.endsWith(".ico")) {
return "image/x-icon";
} else if (filename.endsWith(".xml")) {
return "text/xml";
} else if (filename.endsWith(".pdf")) {
return "application/x-pdf";
} else if (filename.endsWith(".zip")) {
return "application/x-zip";
} else if (filename.endsWith(".gz")) {
return "application/x-gzip";
} else {
return "text/plain";
}
}
bool WebServer::handleFileRead(String path) {
Serial.println("handleFileRead: " + path);
if (path.endsWith("/")) {
path += "index.htm";
}
String contentType = getContentType(path);
String pathWithGz = path + ".gz";
if (filesystem->exists(pathWithGz) || filesystem->exists(path)) {
if (filesystem->exists(pathWithGz)) {
path += ".gz";
}
File file = filesystem->open(path, "r");
single->server.streamFile(file, contentType);
file.close();
return true;
}
return false;
}
void WebServer::handleLoop() {
single->server.handleClient();
}
The errors I am getting are all similar to the following:
src\webserver.cpp: In member function 'bool WebServer::handleFileRead(String)':
src\webserver.cpp:81:23: error: 'WebServer::single->WebServer::server' does not have class type
single->server.streamFile(file, contentType);
I get the idea of "does not have a class type", I just have no idea what it means here. In my mind, "single" is a pointer to the class so I'm unclear what that reference is not working.
Obviously, there are ample examples out there how to do a web server without encapsulating it. Other things I need to do for this project lend itself to creating that requirement.
There are some mistake in your code.
In webserver.h:
...
private:
// Singleton Declarations
static bool instanceFlag;
static WebServer *single;
WebServer() {}
// Other Declarations
FS *filesystem;
ESP8266WebServer *server; // <--- remove the parentheses and make it a pointer
String getContentType(String);
bool handleFileRead(String);
...
In webserver.cpp:
In WebServer::initialize I am guessing you want to initialize the class server and filesystem not locals, so it should probably look like this:
void WebServer::initialize (int port) {
server = new ESP8266WebServer(port);
filesystem = new FS();
...
}
And now everywhere you use the server you have to use the -> operator.
For example:
void WebServer::handleLoop() {
single->server->handleClient();
}
Please keep in mind that server and filesystem objects have to be deleted to avoid memory leaks.
EDIT:
You get the new error because FS has no constructor without arguments.
FS's constructor looks like this: FS(FSImplPtr impl) : _impl(impl) { }, here you can see that FSImplPtr is a typedef for std::shared_ptr<FileImpl>, so you need to provide this as a parameter.
It works your way, because SPIFFS's existence is declared here and is of type FS.
If you want to use SPIFFS, you have to use it like this: filesystem = &SPIFFS;, not like you mentioned in the comments (FS* filesystem = &SPIFFS;) because your way creates a new temporary variable named filesystem, and probably you expect to initiate the filesystem in the class, not a local one.
I've been trying to call an external function with the GetProcAddress function but everytime i call the function it crashes the console, ive been looking but in every post i get the same final solution but when i try it in my DLL it crashes the app.
Here's the code:
#include <Windows.h>
#include <vector>
#include "SDK\plugin.h"
typedef void (*logprintf_t)(char* format, ...);
logprintf_t logprintf;
// static void* m_AMXExports[44];
typedef bool (PLUGIN_CALL *ServerPluginLoad_t)(void **data);
typedef void (PLUGIN_CALL *ServerPluginUnload_t)();
typedef unsigned int (PLUGIN_CALL *ServerPluginSupports_t)();
typedef void (PLUGIN_CALL *ServerPluginProcessTick_t)();
typedef int (PLUGIN_CALL *ServerPluginAmxLoad_t)(AMX *amx);
typedef int (PLUGIN_CALL *ServerPluginAmxUnload_t)(AMX *amx);
struct Plugins
{
void* AppData[256];
SUPPORTS_FLAGS FlagSupport;
HMODULE Module;
ServerPluginLoad_t LOAD;
ServerPluginUnload_t UNLOAD;
ServerPluginSupports_t SUPPORTS;
ServerPluginProcessTick_t PROCESSTICK;
// AMX Plugin Interface
ServerPluginAmxLoad_t AMXLOAD;
ServerPluginAmxUnload_t AMXUNLOAD;
};
Plugins* ServerPlugins;
void **ppPluginData ;
extern void *pAMXFunctions;
//native LoadLibrary(libraryname[]);
static cell AMX_NATIVE_CALL my_LoadLibrary(AMX* amx, cell* params)
{
bool validfunc = false;
char *path;
amx_StrParam(amx, params[1], path);
logprintf("Loading plugin %s", path);
ServerPlugins = new Plugins();
ServerPlugins->Module = LoadLibraryA(path);
if (ServerPlugins->Module == NULL)
{
delete ServerPlugins;
logprintf("Failed loading plugin %s (Error: %d)", path, GetLastError());
return 0;
}
logprintf("NULL");
ServerPlugins->LOAD = (ServerPluginLoad_t)GetProcAddress(ServerPlugins->Module, "Load");
ServerPlugins->UNLOAD = (ServerPluginUnload_t)GetProcAddress(ServerPlugins->Module, "Unload");
ServerPlugins->SUPPORTS = (ServerPluginSupports_t)GetProcAddress(ServerPlugins->Module, "Supports");
if (ServerPlugins->LOAD == NULL || ServerPlugins->SUPPORTS == NULL || ServerPlugins->UNLOAD == NULL)
{
logprintf(" Plugin doesnt conform to architecture");
FreeLibrary(ServerPlugins->Module);
delete ServerPlugins;
return false;
}
logprintf("NULL 1");
ServerPlugins->FlagSupport = (SUPPORTS_FLAGS)ServerPlugins->SUPPORTS();
if ((ServerPlugins->FlagSupport & SUPPORTS_VERSION_MASK) > SUPPORTS_VERSION)
{
logprintf("Unsupported Version; unloading.");
FreeLibrary(ServerPlugins->Module);
delete ServerPlugins;
return false;
}
logprintf("NULL 2");
if ((ServerPlugins->FlagSupport & SUPPORTS_AMX_NATIVES) > SUPPORTS_VERSION)
{
ServerPlugins->AMXLOAD = (ServerPluginAmxLoad_t)GetProcAddress(ServerPlugins->Module, "AmxLoad");
ServerPlugins->AMXUNLOAD = (ServerPluginAmxUnload_t)GetProcAddress(ServerPlugins->Module, "AmxUnload");
}
else
{
ServerPlugins->AMXLOAD = NULL;
ServerPlugins->AMXUNLOAD = NULL;
logprintf("Any Abstract Machine has been loaded");
}
logprintf("NULL 3");
if ((ServerPlugins->FlagSupport & SUPPORTS_PROCESS_TICK) != 0)
{
ServerPlugins->PROCESSTICK = (ServerPluginProcessTick_t)GetProcAddress(ServerPlugins->Module, "ProcessTick");
}
else
{
ServerPlugins->PROCESSTICK = NULL;
}
logprintf("NULL 4"); //debugging
ServerPlugins->AppData[PLUGIN_DATA_AMX_EXPORTS] = pAMXFunctions;
ServerPlugins->AppData[PLUGIN_DATA_LOGPRINTF] = &logprintf;
if (!(ServerPlugins->LOAD)(ServerPlugins->AppData)) //i didnt put it as &ServerPlugins->AppData because it causes an error
{
logprintf("Initialized failed loading plugin %s", path);
FreeLibrary(ServerPlugins->Module);
logprintf("NULL 5");
delete ServerPlugins;
return false;
}
logprintf("Plugin %s loaded", path);
return true;
}
//native UnloadLibrary(libraryname[]);
static cell AMX_NATIVE_CALL my_UnloadLibrary(AMX*amx, cell*params)
{
char *path;
amx_StrParam(amx, params[1], path);
ServerPlugins->Module = GetModuleHandle((LPCTSTR)path);
if (ServerPlugins->Module != NULL)
{
ServerPlugins->UNLOAD = (ServerPluginUnload_t)GetProcAddress(ServerPlugins->Module, "Unload");
if (ServerPlugins->UNLOAD != NULL)
{
ServerPlugins->UNLOAD();
FreeLibrary(GetModuleHandleA(path));
logprintf("Library %s has been unloaded correctly", path);
return 1;
}
else
{
logprintf("Unloading library %s failed (Error: %d)", GetLastError());
return 0;
}
}
return 1;
}
PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData)
{
pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];
return 1;
}
PLUGIN_EXPORT void PLUGIN_CALL Unload()
{
}
PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports()
{
return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}
AMX_NATIVE_INFO projectNatives[] =
{
{ "LoadLibrary", my_LoadLibrary },
{ "UnloadLibrary", my_UnloadLibrary }
};
PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx)
{
return amx_Register(amx, projectNatives, -1);
}
PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx)
{
return AMX_ERR_NONE;
}
You have a memory leak in convertCharArrayToLPCWSTR(). You are never freeing the wchar_t* that you allocate. The convertCharArrayToLPCWSTR() function itself is not needed, you can simply pass the char* path as-is to LoadLibraryA() instead:
char *path;
amx_StrParam(amx, params[1], path);
...
ServerPlugins->Module = LoadLibraryA(path);
You are not checking if ServerPlugins->UNLOAD is successfully loaded by GetProcAddress("Unload") or not.
You are using GetProcAddress("Load") for both ServerPlugins->LOAD and ServerPlugins->AMXLOAD, and GetProcAddress("Unload") for both ServerPlugins->UNLOAD and ServerPlugins->AMXUNLOAD. That is very fishy to me. Does the DLL really use the same exports for AMX and non-AMX entry points? If so, that is very bad design, considering that ServerPluginLoad_t has a very different signature than ServerPluginAmxLoad_t, and the same for ServerPlugin(Amx)Unload_t. That is a corrupted call stack waiting to happen. It would be much safer to have the DLL export separate AmxLoad() and AmxUnload() functions instead.
For that matter, the SUPPORTS_AMX_NATIVES and SUPPORTS_PROCESS_TICK flags are redundant, since GetProcAddress() would tell you if those exports are available or not.
As for the crash when calling ServerPlugins->LOAD, I do not see you initializing ppData with any data before passing it to Load(). Certainly not the PLUGIN_DATA_AMX_EXPORTS and PLUGIN_DATA_LOGPRINTF slots, at least:
ppData[PLUGIN_DATA_AMX_EXPORTS] = pAMXFunctions;
ppData[PLUGIN_DATA_LOGPRINTF] = &logprintf;
if (!(ServerPlugins->LOAD)(ppData))
So even if the call to Load() itself did not crash, the DLL would still likely crash at a later time when it tries to use its local pAMXFunctions and logprintf pointers that were assigned in Load().
For that matter, why are you passing things like that as a void* array instead of a struct? That would have been much safer, eg:
struct PluginInitData
{
void* pAMXFunctions;
logprintf_t logprintf;
...
};
typedef bool (__stdcall *ServerPluginLoad_t)(PluginInitData* data);
PluginInitData pInitData;
pInitData.pAMXFunctions = pAMXFunctions;
pInitData.logprintf = &logprintf;
...
if (!(ServerPlugins->LOAD)(&pInitData))
extern "C" bool __stdcall Load(PluginInitData* data)
{
pAMXFunctions = data->pAMXFunctions;
logprintf = data->logprintf;
...
return true;
}
Update: you have fixed most of the issues I mentioned, but now I see that your my_UnloadLibrary() function is implemented wrong. DO NOT call GetModuleHandle() or GetProcAddress() at all, use the existing Module and UNLOAD pointers that were initialized earlier in my_LoadLibrary().
static cell AMX_NATIVE_CALL my_LoadLibrary(AMX* amx, cell* params)
{
char *path;
amx_StrParam(amx, params[1], path);
...
ServerPlugins->Path = path;
...
}
static cell AMX_NATIVE_CALL my_UnloadLibrary(AMX*amx, cell*params)
{
if (ServerPlugins)
{
if (ServerPlugins->UNLOAD != NULL)
ServerPlugins->UNLOAD();
if (ServerPlugins->Module != NULL)
{
FreeLibrary(ServerPlugins->Module);
ServerPlugins->Module = NULL;
}
logprintf("Library %s has been unloaded", ServerPlugins->Path);
delete ServerPlugins;
ServerPlugins = NULL;
}
return 1;
}
If you are still having problems with Load() crashing, then you are just going to have to use your compiler's debugger to find out what is actually happening at run-time. The code shown so far should not be crashing, so either you have a calling convention mismatch, or a data alignment mismatch, or corrupted memory, or something like that. We can't run you debugger for you.
If I desire to run a piece of code in a function, only from the second invocation of the function onwards,
Questions:
Is there something wrong to do that?
How can I possibly achieve this ? Is using a static variable to do this a good idea ?
There's two answers to this question, depending on whether you have to deal with multi-threaded serialization or not.
No threading:
void doSomething() {
static bool firstTime = true;
if (firstTime) {
// do code specific to first pass
firstTime = false;
} else {
// do code specific to 2nd+ pass
}
// do any code that is common
}
With threading:
I'll write the generic boilerplate, but this code is system specific (requiring some variant of an atomic compareAndSet).
void doSomethingThreadSafe() {
static volatile atomic<int> passState = 0;
do {
if ( passState == 2 ) {
//perform pass 2+ code
break;
} else
if ( passState.compareAndSet(0,1) ) { // if passState==0 set passState=1 return true else return false
//perform pass 1 initialization code
passState = 2;
break;
} else {
//loser in setup collision, delay (wait for init code to finish) then retry
sleep(1);
}
} while(1);
//perform code common to all passes
}
Multi-threading will be a problem. To prevent this, if required, you'll probably need something like a mutex.
Like this:
void someFunction()
{
static bool firstRun = true;
if (!firstRun)
{
// code to execute from the second time onwards
}
else
{
firstRun = false;
}
// other code
}
Add a global counter.
eg:-
static int counter = 0;
public void testFunc(){
if(counter==1){
........
<Execute the functionality>
........
}
counter++;
}
How can I find the current depth inside a recursive function in C++ without passing in the previous level? i.e. is it possible to know how many times the function was called without using a parameter to keep track of the level and passing that number in as a parameter each time the function is called?
For example my recursive function looks like this:
DoSomething(int level)
{
print level;
if (level > 10)
return;
DoSomething(++level);
}
main
{
DoSomething(0);
}
Building on the answer already given by JoshD:
void recursive()
{
static int calls = 0;
static int max_calls = 0;
calls++;
if (calls > max_calls)
max_calls = calls;
recursive();
calls--;
}
This resets the counter after the recursive function is complete, but still tracks the maximum depth of the recursion.
I wouldn't use static variables like this for anything but a quick test, to be deleted soon after. If you really need to track this on an ongoing basis there are better methods.
You could use a static variable in the function...
void recursive()
{
static int calls = 0;
calls++;
recursive();
}
Of course, this will keep counting when you start a new originating call....
If you want it to be re-entrant and thread-safe, why not:
void rec(int &level) // reference to your level var
{
// do work
rec(++level); // go down one level
}
main()
{
//and you call it like
int level=0;
rec(level);
cout<<level<<" levels."<<endl;
}
No static/global variables to mess up threading and you can use different variables for different recursive chains for re-entrancy issues.
You can use a local static variable, if you don't care about thread-safety.
Although, this will only give you a proper count the first time you run your recursive routine. A better technique would be a RAII guard-type class which contains an internal static variable. At the start of the recursive routine, construct the guard class. The constructor would increment the internal static variable, and the destructor would decrement it. This way, when you create a new stack-frame the counter increments by one, and when you return from each stack-frame the counter would decrement by one.
struct recursion_guard
{
recursion_guard() { ++counter; }
~recursion_guard() { --counter; }
static int counter;
};
int recursion_guard::counter = 0;
void recurse(int x)
{
recursion_guard rg;
if (x > 10) return;
recurse(x + 1);
}
int main()
{
recurse(0);
recurse(0);
}
Note however, that this is still not thread-safe. If you need thread-safety, you can replace the static-storage variable with a thread-local-storage variable, either using boost::thread_specific_ptr or the C++0x thread local facilities.
You could also pass in the level as a template parameter, if it can be determined at compile-time. You could also use a function object. This is by far and away the best option - less hassle, and static variables should be avoided wherever possible.
struct DoSomething {
DoSomething() {
calls = 0;
}
void operator()() {
std::cout << calls;
calls++;
if (calls < 10)
return operator()();
return;
}
int calls;
};
int main() {
DoSomething()(); // note the double ().
std::cin.get();
}
convert level to an instance variable of a new object (typically a template) capable of containing the arguments and (possibly) the function. then you can reuse the recursion accumulator interface.
You can also try using a global variable to log the depth.
var depth = 0;
DoSomething()
{
print ++depth;
if (depth > 10)
return;
DoSomething();
}
main
{
DoSomething(0);
}
I came here when I sensed that some recursion is required, because I was implementing a function that can validate the chain of trust in a certificate chain. This is not X.509 but instead it is just the basics wherein the issuer key of a certificate must match the public key of the signer.
bool verify_chain(std::vector<Cert>& chain,
Cert* certificate,
unsigned char* pOrigin = nullptr, int depth = 0)
{
bool flag = false;
if (certificate == nullptr) {
// use first element in case parameter is null
certificate = &chain[0];
}
if (pOrigin == nullptr) {
pOrigin = certificate->pubkey;
} else {
if (std::memcmp(pOrigin, certificate->pubkey, 32) == 0) {
return false; // detected circular chain
}
}
if (certificate->hasValidSignature()) {
if (!certificate->isRootCA()) {
Cert* issuerCert = certificate->getIssuer(chain);
if (issuerCert) {
flag = verify_chain(chain, issuerCert, pOrigin, depth+1);
}
} else {
flag = true;
}
}
if (pOrigin && depth == 1) {
pOrigin = nullptr;
}
return flag;
}
I needed to know the recursion depth so that I can correctly clean up pOrigin. at the right stack frame during the unwinding of the call stack.
I used pOrigin to detect a circular chain, without which the recursive call can go on forever. For example,
cert0 signs cert1
cert1 signs cert2
cert2 signs cert0
I later realized that a simple for-loop can do it for simple cases when there is only one common chain.
bool verify_chain2(std::vector<Cert> &chain, Cert& cert)
{
Cert *pCert = &cert;
unsigned char *startkey = cert.pubkey;
while (pCert != nullptr) {
if (pCert->hasValidSignature()) {
if (!pCert->isRootCA()) {
pCert = pCert->getIssuer(chain);
if (pCert == nullptr
|| std::memcmp(pCert->pubkey, startkey, 32) == 0) {
return false;
}
continue;
} else {
return true;
}
} else {
return false;
}
}
return false;
}
But recursion is a must when there is not one common chain but instead the chain is within each certificate. I welcome any comments. Thank you.