+static bool finalize_join(meshlink_handle_t *mesh) {
+ char *name = xstrdup(get_value(mesh->data, "Name"));
+ if(!name) {
+ fprintf(stderr, "No Name found in invitation!\n");
+ return false;
+ }
+
+ if(!check_id(name)) {
+ fprintf(stderr, "Invalid Name found in invitation: %s!\n", name);
+ return false;
+ }
+
+ char filename[PATH_MAX];
+ snprintf(filename, sizeof filename, "%s" SLASH "meshlink.conf", mesh->confbase);
+
+ FILE *f = fopen(filename, "w");
+ if(!f) {
+ fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
+ return false;
+ }
+
+ fprintf(f, "Name = %s\n", name);
+
+ snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
+ FILE *fh = fopen(filename, "w");
+ if(!fh) {
+ fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
+ fclose(f);
+ return false;
+ }
+
+ // Filter first chunk on approved keywords, split between tinc.conf and hosts/Name
+ // Other chunks go unfiltered to their respective host config files
+ const char *p = mesh->data;
+ char *l, *value;
+
+ while((l = get_line(&p))) {
+ // Ignore comments
+ if(*l == '#')
+ continue;
+
+ // Split line into variable and value
+ int len = strcspn(l, "\t =");
+ value = l + len;
+ value += strspn(value, "\t ");
+ if(*value == '=') {
+ value++;
+ value += strspn(value, "\t ");
+ }
+ l[len] = 0;
+
+ // Is it a Name?
+ if(!strcasecmp(l, "Name"))
+ if(strcmp(value, name))
+ break;
+ else
+ continue;
+ else if(!strcasecmp(l, "NetName"))
+ continue;
+
+ // Check the list of known variables //TODO: most variables will not be available in meshlink, only name and key will be absolutely necessary
+ bool found = false;
+ int i;
+ for(i = 0; variables[i].name; i++) {
+ if(strcasecmp(l, variables[i].name))
+ continue;
+ found = true;
+ break;
+ }
+
+ // Ignore unknown and unsafe variables
+ if(!found) {
+ fprintf(stderr, "Ignoring unknown variable '%s' in invitation.\n", l);
+ continue;
+ } else if(!(variables[i].type & VAR_SAFE)) {
+ fprintf(stderr, "Ignoring unsafe variable '%s' in invitation.\n", l);
+ continue;
+ }
+
+ // Copy the safe variable to the right config file
+ fprintf(variables[i].type & VAR_HOST ? fh : f, "%s = %s\n", l, value);
+ }
+
+ fclose(f);
+
+ while(l && !strcasecmp(l, "Name")) {
+ if(!check_id(value)) {
+ fprintf(stderr, "Invalid Name found in invitation.\n");
+ return false;
+ }
+
+ if(!strcmp(value, name)) {
+ fprintf(stderr, "Secondary chunk would overwrite our own host config file.\n");
+ return false;
+ }
+
+ snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, value);
+ f = fopen(filename, "w");
+
+ if(!f) {
+ fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
+ return false;
+ }
+
+ while((l = get_line(&p))) {
+ if(!strcmp(l, "#---------------------------------------------------------------#"))
+ continue;
+ int len = strcspn(l, "\t =");
+ if(len == 4 && !strncasecmp(l, "Name", 4)) {
+ value = l + len;
+ value += strspn(value, "\t ");
+ if(*value == '=') {
+ value++;
+ value += strspn(value, "\t ");
+ }
+ l[len] = 0;
+ break;
+ }
+
+ fputs(l, f);
+ fputc('\n', f);
+ }
+
+ fclose(f);
+ }
+
+ char *b64key = ecdsa_get_base64_public_key(mesh->self->connection->ecdsa);
+ if(!b64key)
+ return false;
+
+ fprintf(fh, "ECDSAPublicKey = %s\n", b64key);
+ fprintf(fh, "Port = %s\n", mesh->myport);
+
+ fclose(fh);
+
+ sptps_send_record(&(mesh->sptps), 1, b64key, strlen(b64key));
+ free(b64key);
+
+ free(mesh->self->name);
+ free(mesh->self->connection->name);
+ mesh->self->name = xstrdup(name);
+ mesh->self->connection->name = xstrdup(name);
+
+ fprintf(stderr, "Configuration stored in: %s\n", mesh->confbase);
+
+ load_all_nodes(mesh);
+
+ return true;
+}
+
+static bool invitation_send(void *handle, uint8_t type, const void *data, size_t len) {
+ meshlink_handle_t* mesh = handle;
+ while(len) {
+ int result = send(mesh->sock, data, len, 0);
+ if(result == -1 && errno == EINTR)
+ continue;
+ else if(result <= 0)
+ return false;
+ data += result;
+ len -= result;
+ }
+ return true;
+}
+
+static bool invitation_receive(void *handle, uint8_t type, const void *msg, uint16_t len) {
+ meshlink_handle_t* mesh = handle;
+ switch(type) {
+ case SPTPS_HANDSHAKE:
+ return sptps_send_record(&(mesh->sptps), 0, mesh->cookie, sizeof mesh->cookie);
+
+ case 0:
+ mesh->data = xrealloc(mesh->data, mesh->thedatalen + len + 1);
+ memcpy(mesh->data + mesh->thedatalen, msg, len);
+ mesh->thedatalen += len;
+ mesh->data[mesh->thedatalen] = 0;
+ break;
+
+ case 1:
+ return finalize_join(mesh);
+
+ case 2:
+ fprintf(stderr, "Invitation succesfully accepted.\n");
+ shutdown(mesh->sock, SHUT_RDWR);
+ mesh->success = true;
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static bool recvline(meshlink_handle_t* mesh, size_t len) {
+ char *newline = NULL;
+
+ if(!mesh->sock)
+ abort();
+
+ while(!(newline = memchr(mesh->buffer, '\n', mesh->blen))) {
+ int result = recv(mesh->sock, mesh->buffer + mesh->blen, sizeof mesh->buffer - mesh->blen, 0);
+ if(result == -1 && errno == EINTR)
+ continue;
+ else if(result <= 0)
+ return false;
+ mesh->blen += result;
+ }
+
+ if(newline - mesh->buffer >= len)
+ return false;
+
+ len = newline - mesh->buffer;
+
+ memcpy(mesh->line, mesh->buffer, len);
+ mesh->line[len] = 0;
+ memmove(mesh->buffer, newline + 1, mesh->blen - len - 1);
+ mesh->blen -= len + 1;
+
+ return true;
+}
+static bool sendline(int fd, char *format, ...) {
+ static char buffer[4096];
+ char *p = buffer;
+ int blen = 0;
+ va_list ap;
+
+ va_start(ap, format);
+ blen = vsnprintf(buffer, sizeof buffer, format, ap);
+ va_end(ap);
+
+ if(blen < 1 || blen >= sizeof buffer)
+ return false;
+
+ buffer[blen] = '\n';
+ blen++;
+
+ while(blen) {
+ int result = send(fd, p, blen, MSG_NOSIGNAL);
+ if(result == -1 && errno == EINTR)
+ continue;
+ else if(result <= 0)
+ return false;
+ p += result;
+ blen -= result;
+ }
+
+ return true;
+}
+
+static const char *errstr[] = {
+ [MESHLINK_OK] = "No error",
+ [MESHLINK_ENOMEM] = "Out of memory",
+ [MESHLINK_ENOENT] = "No such node",
+};
+
+const char *meshlink_strerror(meshlink_errno_t errno) {
+ return errstr[errno];
+}
+
+static bool ecdsa_keygen(meshlink_handle_t *mesh) {
+ ecdsa_t *key;
+ FILE *f;
+ char pubname[PATH_MAX], privname[PATH_MAX];
+
+ fprintf(stderr, "Generating ECDSA keypair:\n");
+
+ if(!(key = ecdsa_generate())) {
+ fprintf(stderr, "Error during key generation!\n");
+ return false;
+ } else
+ fprintf(stderr, "Done.\n");
+
+ snprintf(privname, sizeof privname, "%s" SLASH "ecdsa_key.priv", mesh->confbase);
+ f = fopen(privname, "w");
+
+ if(!f)
+ return false;
+
+#ifdef HAVE_FCHMOD
+ fchmod(fileno(f), 0600);
+#endif
+
+ if(!ecdsa_write_pem_private_key(key, f)) {
+ fprintf(stderr, "Error writing private key!\n");
+ ecdsa_free(key);
+ fclose(f);
+ return false;
+ }
+
+ fclose(f);
+
+
+ snprintf(pubname, sizeof pubname, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, mesh->name);
+ f = fopen(pubname, "a");
+
+ if(!f)
+ return false;
+
+ char *pubkey = ecdsa_get_base64_public_key(key);
+ fprintf(f, "ECDSAPublicKey = %s\n", pubkey);
+ free(pubkey);
+
+ fclose(f);
+ ecdsa_free(key);
+
+ return true;
+}