+ }
+ }
+
+ if(devclass < 0 || devclass >= DEV_CLASS_COUNT) {
+ logger(NULL, MESHLINK_ERROR, "Invalid devclass given!\n");
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;
+ }
+
+ meshlink_open_params_t *params = xzalloc(sizeof * params);
+
+ params->confbase = xstrdup(confbase);
+ params->name = xstrdup(name);
+ params->appname = xstrdup(appname);
+ params->devclass = devclass;
+ params->netns = -1;
+
+ return params;
+}
+
+bool meshlink_open_params_set_netns(meshlink_open_params_t *params, int netns) {
+ if(!params) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return false;
+ }
+
+ params->netns = netns;
+
+ return true;
+}
+
+bool meshlink_open_params_set_storage_key(meshlink_open_params_t *params, const void *key, size_t keylen) {
+ if(!params) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return false;
+ }
+
+ if((!key && keylen) || (key && !keylen)) {
+ logger(NULL, MESHLINK_ERROR, "Invalid key length!\n");
+ meshlink_errno = MESHLINK_EINVAL;
+ return false;
+ }
+
+ params->key = key;
+ params->keylen = keylen;
+
+ return true;
+}
+
+bool meshlink_encrypted_key_rotate(meshlink_handle_t *mesh, const void *new_key, size_t new_keylen) {
+ if(!mesh || !new_key || !new_keylen) {
+ logger(mesh, MESHLINK_ERROR, "Invalid arguments given!\n");
+ meshlink_errno = MESHLINK_EINVAL;
+ return false;
+ }
+
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+
+ // Create hash for the new key
+ void *new_config_key;
+ new_config_key = xmalloc(CHACHA_POLY1305_KEYLEN);
+
+ if(!prf(new_key, new_keylen, "MeshLink configuration key", 26, new_config_key, CHACHA_POLY1305_KEYLEN)) {
+ logger(mesh, MESHLINK_ERROR, "Error creating new configuration key!\n");
+ meshlink_errno = MESHLINK_EINTERNAL;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ // Copy contents of the "current" confbase sub-directory to "new" confbase sub-directory with the new key
+
+ if(!config_copy(mesh, "current", mesh->config_key, "new", new_config_key)) {
+ logger(mesh, MESHLINK_ERROR, "Could not set up configuration in %s/old: %s\n", mesh->confbase, strerror(errno));
+ meshlink_errno = MESHLINK_ESTORAGE;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ devtool_keyrotate_probe(1);
+
+ main_config_unlock(mesh);
+
+ // Rename confbase/current/ to confbase/old
+
+ if(!config_rename(mesh, "current", "old")) {
+ logger(mesh, MESHLINK_ERROR, "Cannot rename %s/current to %s/old\n", mesh->confbase, mesh->confbase);
+ meshlink_errno = MESHLINK_ESTORAGE;
+ main_config_lock(mesh);
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ devtool_keyrotate_probe(2);
+
+ // Rename confbase/new/ to confbase/current
+
+ if(!config_rename(mesh, "new", "current")) {
+ logger(mesh, MESHLINK_ERROR, "Cannot rename %s/new to %s/current\n", mesh->confbase, mesh->confbase);
+ meshlink_errno = MESHLINK_ESTORAGE;
+ main_config_lock(mesh);
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ devtool_keyrotate_probe(3);
+
+ if(!main_config_lock(mesh)) {
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ // Cleanup the "old" confbase sub-directory
+
+ if(!config_destroy(mesh->confbase, "old")) {
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ // Change the mesh handle key with new key
+
+ free(mesh->config_key);
+ mesh->config_key = new_config_key;
+
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+
+ return true;
+}
+
+void meshlink_open_params_free(meshlink_open_params_t *params) {
+ if(!params) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return;
+ }
+
+ free(params->confbase);
+ free(params->name);
+ free(params->appname);
+
+ free(params);
+}
+
+/// Device class traits
+static const dev_class_traits_t default_class_traits[DEV_CLASS_COUNT] = {
+ { .pingtimeout = 5, .pinginterval = 60, .min_connects = 3, .max_connects = 10000, .edge_weight = 1 }, // DEV_CLASS_BACKBONE
+ { .pingtimeout = 5, .pinginterval = 60, .min_connects = 3, .max_connects = 100, .edge_weight = 3 }, // DEV_CLASS_STATIONARY
+ { .pingtimeout = 5, .pinginterval = 60, .min_connects = 3, .max_connects = 3, .edge_weight = 6 }, // DEV_CLASS_PORTABLE
+ { .pingtimeout = 5, .pinginterval = 60, .min_connects = 1, .max_connects = 1, .edge_weight = 9 }, // DEV_CLASS_UNKNOWN
+};
+
+meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const char *appname, dev_class_t devclass) {
+ if(!confbase || !*confbase) {
+ logger(NULL, MESHLINK_ERROR, "No confbase given!\n");
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;
+ }
+
+ /* Create a temporary struct on the stack, to avoid allocating and freeing one. */
+ meshlink_open_params_t params;
+ memset(¶ms, 0, sizeof(params));
+
+ params.confbase = (char *)confbase;
+ params.name = (char *)name;
+ params.appname = (char *)appname;
+ params.devclass = devclass;
+ params.netns = -1;
+
+ return meshlink_open_ex(¶ms);
+}
+
+meshlink_handle_t *meshlink_open_encrypted(const char *confbase, const char *name, const char *appname, dev_class_t devclass, const void *key, size_t keylen) {
+ if(!confbase || !*confbase) {
+ logger(NULL, MESHLINK_ERROR, "No confbase given!\n");
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;
+ }
+
+ /* Create a temporary struct on the stack, to avoid allocating and freeing one. */
+ meshlink_open_params_t params = {NULL};
+
+ params.confbase = (char *)confbase;
+ params.name = (char *)name;
+ params.appname = (char *)appname;
+ params.devclass = devclass;
+ params.netns = -1;
+
+ if(!meshlink_open_params_set_storage_key(¶ms, key, keylen)) {
+ return false;
+ }
+
+ return meshlink_open_ex(¶ms);
+}
+
+meshlink_handle_t *meshlink_open_ephemeral(const char *name, const char *appname, dev_class_t devclass) {
+ /* Create a temporary struct on the stack, to avoid allocating and freeing one. */
+ meshlink_open_params_t params = {NULL};
+
+ params.name = (char *)name;
+ params.appname = (char *)appname;
+ params.devclass = devclass;
+ params.netns = -1;
+
+ return meshlink_open_ex(¶ms);
+}
+
+meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) {
+ // Validate arguments provided by the application
+ bool usingname = false;
+
+ logger(NULL, MESHLINK_DEBUG, "meshlink_open called\n");
+
+ if(!params->appname || !*params->appname) {
+ logger(NULL, MESHLINK_ERROR, "No appname given!\n");
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;
+ }
+
+ if(strchr(params->appname, ' ')) {
+ logger(NULL, MESHLINK_ERROR, "Invalid appname given!\n");
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;
+ }
+
+ if(!params->name || !*params->name) {
+ logger(NULL, MESHLINK_ERROR, "No name given!\n");
+ //return NULL;
+ } else { //check name only if there is a name != NULL
+
+ if(!check_id(params->name)) {
+ logger(NULL, MESHLINK_ERROR, "Invalid name given!\n");
+ meshlink_errno = MESHLINK_EINVAL;
+ return NULL;