//TODO: this can go away completely
const var_t variables[] = {
/* Server configuration */
- {"AddressFamily", VAR_SERVER},
- {"AutoConnect", VAR_SERVER | VAR_SAFE},
- {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
- {"BindToInterface", VAR_SERVER},
- {"Broadcast", VAR_SERVER | VAR_SAFE},
{"ConnectTo", VAR_SERVER | VAR_MULTIPLE | VAR_SAFE},
- {"DecrementTTL", VAR_SERVER},
- {"Device", VAR_SERVER},
- {"DeviceType", VAR_SERVER},
- {"DirectOnly", VAR_SERVER},
- {"ECDSAPrivateKeyFile", VAR_SERVER},
- {"ExperimentalProtocol", VAR_SERVER},
- {"Forwarding", VAR_SERVER},
- {"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
- {"Hostnames", VAR_SERVER},
- {"IffOneQueue", VAR_SERVER},
- {"Interface", VAR_SERVER},
- {"KeyExpire", VAR_SERVER},
- {"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
- {"LocalDiscovery", VAR_SERVER},
- {"MACExpire", VAR_SERVER},
- {"MaxConnectionBurst", VAR_SERVER},
- {"MaxOutputBufferSize", VAR_SERVER},
- {"MaxTimeout", VAR_SERVER},
- {"Mode", VAR_SERVER | VAR_SAFE},
{"Name", VAR_SERVER},
- {"PingInterval", VAR_SERVER},
- {"PingTimeout", VAR_SERVER},
- {"PriorityInheritance", VAR_SERVER},
- {"PrivateKey", VAR_SERVER | VAR_OBSOLETE},
- {"PrivateKeyFile", VAR_SERVER},
- {"ProcessPriority", VAR_SERVER},
- {"Proxy", VAR_SERVER},
- {"ReplayWindow", VAR_SERVER},
- {"ScriptsExtension", VAR_SERVER},
- {"ScriptsInterpreter", VAR_SERVER},
- {"StrictSubnets", VAR_SERVER},
- {"TunnelServer", VAR_SERVER},
- {"VDEGroup", VAR_SERVER},
- {"VDEPort", VAR_SERVER},
/* Host configuration */
+ {"CanonicalAddress", VAR_HOST},
{"Address", VAR_HOST | VAR_MULTIPLE},
- {"Cipher", VAR_SERVER | VAR_HOST},
- {"ClampMSS", VAR_SERVER | VAR_HOST},
- {"Compression", VAR_SERVER | VAR_HOST},
- {"Digest", VAR_SERVER | VAR_HOST},
{"ECDSAPublicKey", VAR_HOST},
- {"ECDSAPublicKeyFile", VAR_SERVER | VAR_HOST},
- {"IndirectData", VAR_SERVER | VAR_HOST},
- {"MACLength", VAR_SERVER | VAR_HOST},
- {"PMTU", VAR_SERVER | VAR_HOST},
- {"PMTUDiscovery", VAR_SERVER | VAR_HOST},
{"Port", VAR_HOST},
- {"PublicKey", VAR_HOST | VAR_OBSOLETE},
- {"PublicKeyFile", VAR_SERVER | VAR_HOST | VAR_OBSOLETE},
- {"Subnet", VAR_HOST | VAR_MULTIPLE | VAR_SAFE},
- {"TCPOnly", VAR_SERVER | VAR_HOST},
- {"Weight", VAR_HOST | VAR_SAFE},
{NULL, 0}
};
static void scan_for_hostname(const char *filename, char **hostname, char **port) {
char line[4096];
+ bool canonical = false;
if(!filename || (*hostname && *port)) {
return;
p += strspn(p, "\t ");
p[strcspn(p, "\t ")] = 0;
- // p is now pointing to the port
-
- // Check that the hostname is a symbolic name (it's not a numeric IPv4 or IPv6 address)
- if(!q[strspn(q, "0123456789.")] || strchr(q, ':')) {
- continue;
- }
+ // p is now pointing to the port, if present
if(!*port && !strcasecmp(line, "Port")) {
*port = xstrdup(q);
- } else if(!*hostname && !strcasecmp(line, "Address")) {
+ } else if(!canonical && !*hostname && !strcasecmp(line, "Address")) {
+ // Check that the hostname is a symbolic name (it's not a numeric IPv4 or IPv6 address)
+ if(!q[strspn(q, "0123456789.")] || strchr(q, ':')) {
+ continue;
+ }
+
*hostname = xstrdup(q);
+ if(*p) {
+ free(*port);
+ *port = xstrdup(p);
+ }
+ } else if(!strcasecmp(line, "CanonicalAddress")) {
+ *hostname = xstrdup(q);
+ canonical = true;
+
if(*p) {
free(*port);
*port = xstrdup(p);
}
}
- if(*hostname && *port) {
+ if(canonical && *hostname && *port) {
break;
}
}
}
static bool is_valid_hostname(const char *hostname) {
+ if(!*hostname) {
+ return false;
+ }
+
for(const char *p = hostname; *p; p++) {
if(!(isalnum(*p) || *p == '-' || *p == '.' || *p == ':')) {
return false;
return true;
}
+static bool is_valid_port(const char *port) {
+ if(!*port) {
+ return false;
+ }
+
+ if(isdigit(*port)) {
+ char *end;
+ unsigned long int result = strtoul(port, &end, 10);
+ return result && result < 65536 && !*end;
+ }
+
+ for(const char *p = port; *p; p++) {
+ if(!(isalnum(*p) || *p == '-')) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
static void set_timeout(int sock, int timeout) {
#ifdef _WIN32
DWORD tv = timeout;
return 0;
}
+static void deltree(const char *dirname) {
+ DIR *d = opendir(dirname);
+
+ if(d) {
+ struct dirent *ent;
+
+ while((ent = readdir(d))) {
+ if(ent->d_name[0] == '.') {
+ continue;
+ }
+
+ char filename[PATH_MAX];
+ snprintf(filename, sizeof(filename), "%s" SLASH "%s", dirname, ent->d_name);
+
+ if(unlink(filename)) {
+ deltree(filename);
+ }
+ }
+
+ closedir(d);
+ }
+
+ rmdir(dirname);
+}
+
static bool finalize_join(meshlink_handle_t *mesh) {
char *name = xstrdup(get_value(mesh->data, "Name"));
fprintf(f, "Name = %s\n", name);
+ // Wipe all old host config files and invitations
+ snprintf(filename, sizeof(filename), "%s" SLASH "hosts", mesh->confbase);
+ deltree(filename);
+
+ if(mkdir(filename, 0777) && errno != EEXIST) {
+ logger(mesh, MESHLINK_DEBUG, "Could not create directory %s: %s\n", filename, strerror(errno));
+ return false;
+ }
+
+ snprintf(filename, sizeof(filename), "%s" SLASH "invitations", mesh->confbase);
+ deltree(filename);
+
+ // Create a new host config file for ourself
snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
FILE *fh = fopen(filename, "w");
continue;
}
- // Check the list of known variables //TODO: most variables will not be available in meshlink, only name and key will be absolutely necessary
+ // Check the list of known variables
bool found = false;
int i;
sptps_send_record(&(mesh->sptps), 1, b64key, strlen(b64key));
free(b64key);
+ free(mesh->name);
free(mesh->self->name);
free(mesh->self->connection->name);
+ mesh->name = xstrdup(name);
mesh->self->name = xstrdup(name);
mesh->self->connection->name = name;
mesh->appname = xstrdup(appname);
mesh->devclass = devclass;
mesh->discovery = true;
+ mesh->invitation_timeout = 604800; // 1 week
if(usingname) {
mesh->name = xstrdup(name);
logger(mesh, MESHLINK_DEBUG, "Could not start thread: %s\n", strerror(errno));
memset(&mesh->thread, 0, sizeof(mesh)->thread);
meshlink_errno = MESHLINK_EINTERNAL;
+ event_loop_stop(&mesh->loop);
pthread_mutex_unlock(&(mesh->mesh_mutex));
return false;
}
}
}
- // Wait for the main thread to finish
- pthread_mutex_unlock(&(mesh->mesh_mutex));
- pthread_join(mesh->thread, NULL);
- pthread_mutex_lock(&(mesh->mesh_mutex));
+ if(mesh->threadstarted) {
+ // Wait for the main thread to finish
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ pthread_join(mesh->thread, NULL);
+ pthread_mutex_lock(&(mesh->mesh_mutex));
- mesh->threadstarted = false;
+ mesh->threadstarted = false;
+ }
// Close all metaconnections
if(mesh->connections) {
if(mesh->outgoings) {
list_delete_list(mesh->outgoings);
+ mesh->outgoings = NULL;
}
- mesh->outgoings = NULL;
-
pthread_mutex_unlock(&(mesh->mesh_mutex));
}
free(mesh);
}
-static void deltree(const char *dirname) {
- DIR *d = opendir(dirname);
-
- if(d) {
- struct dirent *ent;
-
- while((ent = readdir(d))) {
- if(ent->d_name[0] == '.') {
- continue;
- }
-
- char filename[PATH_MAX];
- snprintf(filename, sizeof(filename), "%s" SLASH "%s", dirname, ent->d_name);
-
- if(unlink(filename)) {
- deltree(filename);
- }
- }
-
- closedir(d);
- }
-
- rmdir(dirname);
- return;
-}
-
bool meshlink_destroy(const char *confbase) {
if(!confbase) {
meshlink_errno = MESHLINK_EINVAL;
return mesh->invitation_key;
}
-bool meshlink_add_address(meshlink_handle_t *mesh, const char *address) {
- if(!mesh || !address) {
+bool meshlink_set_canonical_address(meshlink_handle_t *mesh, meshlink_node_t *node, const char *address, const char *port) {
+ if(!mesh || !node || !address) {
meshlink_errno = MESHLINK_EINVAL;
return false;
}
return false;
}
- bool rval = false;
+ if(port && !is_valid_port(port)) {
+ logger(mesh, MESHLINK_DEBUG, "Invalid character in port: %s\n", address);
+ meshlink_errno = MESHLINK_EINVAL;
+ return false;
+ }
+
+ char *canonical_address;
+
+ if(port) {
+ xasprintf(&canonical_address, "%s %s", address, port);
+ } else {
+ canonical_address = xstrdup(address);
+ }
pthread_mutex_lock(&(mesh->mesh_mutex));
- rval = append_config_file(mesh, mesh->self->name, "Address", address);
+ bool rval = modify_config_file(mesh, node->name, "CanonicalAddress", canonical_address, 1);
pthread_mutex_unlock(&(mesh->mesh_mutex));
+ free(canonical_address);
return rval;
}
+bool meshlink_add_address(meshlink_handle_t *mesh, const char *address) {
+ return meshlink_set_canonical_address(mesh, (meshlink_node_t *)mesh->self, address, NULL);
+}
+
bool meshlink_add_external_address(meshlink_handle_t *mesh) {
if(!mesh) {
meshlink_errno = MESHLINK_EINVAL;
return rval;
}
+void meshlink_set_invitation_timeout(meshlink_handle_t *mesh, int timeout) {
+ mesh->invitation_timeout = timeout;
+}
+
char *meshlink_invite(meshlink_handle_t *mesh, const char *name) {
if(!mesh) {
meshlink_errno = MESHLINK_EINVAL;
// Fill in the details.
fprintf(f, "Name = %s\n", name);
- //if(netname)
- // fprintf(f, "NetName = %s\n", netname);
fprintf(f, "ConnectTo = %s\n", mesh->self->name);
// Copy Broadcast and Mode
static ssize_t channel_send(struct utcp *utcp, const void *data, size_t len) {
node_t *n = utcp->priv;
+
+ if(n->status.destroyed) {
+ return -1;
+ }
+
meshlink_handle_t *mesh = n->mesh;
return meshlink_send(mesh, (meshlink_node_t *)n, data, len) ? (ssize_t)len : -1;
}