+ case PROXY_SOCKS4: {
+ if(c->address.sa.sa_family != AF_INET) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Cannot connect to an IPv6 host through a SOCKS 4 proxy!");
+ return false;
+ }
+
+ uint8_t s4req[9 + (proxyuser ? strlen(proxyuser) : 0)];
+ s4req[0] = 4;
+ s4req[1] = 1;
+ memcpy(s4req + 2, &c->address.in.sin_port, 2);
+ memcpy(s4req + 4, &c->address.in.sin_addr, 4);
+
+ if(proxyuser) {
+ memcpy(s4req + 8, proxyuser, strlen(proxyuser));
+ }
+
+ s4req[sizeof(s4req) - 1] = 0;
+ c->tcplen = 8;
+ return send_meta(c, s4req, sizeof(s4req));
+ }
+
+ case PROXY_SOCKS5: {
+ size_t len = 3 + 6 + (c->address.sa.sa_family == AF_INET ? 4 : 16);
+ c->tcplen = 2;
+
+ if(proxypass) {
+ len += 3 + strlen(proxyuser) + strlen(proxypass);
+ }
+
+ uint8_t s5req[len];
+ size_t i = 0;
+ s5req[i++] = 5;
+ s5req[i++] = 1;
+
+ if(proxypass) {
+ s5req[i++] = 2;
+ s5req[i++] = 1;
+ s5req[i++] = strlen(proxyuser);
+ memcpy(s5req + i, proxyuser, strlen(proxyuser));
+ i += strlen(proxyuser);
+ s5req[i++] = strlen(proxypass);
+ memcpy(s5req + i, proxypass, strlen(proxypass));
+ i += strlen(proxypass);
+ c->tcplen += 2;
+ } else {
+ s5req[i++] = 0;
+ }
+
+ s5req[i++] = 5;
+ s5req[i++] = 1;
+ s5req[i++] = 0;
+
+ if(c->address.sa.sa_family == AF_INET) {
+ s5req[i++] = 1;
+ memcpy(s5req + i, &c->address.in.sin_addr, 4);
+ i += 4;
+ memcpy(s5req + i, &c->address.in.sin_port, 2);
+ i += 2;
+ c->tcplen += 10;
+ } else if(c->address.sa.sa_family == AF_INET6) {
+ s5req[i++] = 3;
+ memcpy(s5req + i, &c->address.in6.sin6_addr, 16);
+ i += 16;
+ memcpy(s5req + i, &c->address.in6.sin6_port, 2);
+ i += 2;
+ c->tcplen += 22;
+ } else {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Address family %x not supported for SOCKS 5 proxies!", c->address.sa.sa_family);
+ return false;
+ }
+
+ if(i > len) {
+ abort();
+ }
+
+ return send_meta(c, s5req, sizeof(s5req));
+ }
+
+ case PROXY_SOCKS4A:
+ logger(DEBUG_ALWAYS, LOG_ERR, "Proxy type not implemented yet");
+ return false;
+
+ case PROXY_EXEC:
+ return true;
+
+ default:
+ logger(DEBUG_ALWAYS, LOG_ERR, "Unknown proxy type");
+ return false;
+ }
+}
+
+bool send_id(connection_t *c) {
+ gettimeofday(&c->start, NULL);
+
+ int minor = 0;
+
+ if(experimental) {
+ if(c->outgoing && !read_ecdsa_public_key(&c->ecdsa, &c->config_tree, c->name)) {
+ minor = 1;
+ } else {
+ minor = myself->connection->protocol_minor;
+ }
+ }
+
+ if(proxytype && c->outgoing)
+ if(!send_proxyrequest(c)) {
+ return false;
+ }
+
+ return send_request(c, "%d %s %d.%d", ID, myself->connection->name, myself->connection->protocol_major, minor);
+}
+
+static bool finalize_invitation(connection_t *c, const char *data, uint16_t len) {
+ (void)len;
+
+ if(strchr(data, '\n')) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Received invalid key from invited node %s (%s)!\n", c->name, c->hostname);
+ return false;
+ }
+
+ // Create a new host config file
+ char filename[PATH_MAX];
+ snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, c->name);
+
+ if(!access(filename, F_OK)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Host config file for %s (%s) already exists!\n", c->name, c->hostname);
+ return false;
+ }
+
+ FILE *f = fopen(filename, "w");
+
+ if(!f) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error trying to create %s: %s\n", filename, strerror(errno));
+ return false;
+ }
+
+ fprintf(f, "Ed25519PublicKey = %s\n", data);
+ fclose(f);
+
+ logger(DEBUG_CONNECTIONS, LOG_INFO, "Key successfully received from %s (%s)", c->name, c->hostname);
+
+ // Call invitation-accepted script
+ environment_t env;
+ char *address, *port;
+
+ environment_init(&env);
+ environment_add(&env, "NODE=%s", c->name);
+ sockaddr2str(&c->address, &address, &port);
+ environment_add(&env, "REMOTEADDRESS=%s", address);
+ environment_add(&env, "NAME=%s", myself->name);
+
+ free(address);
+ free(port);
+
+ execute_script("invitation-accepted", &env);
+
+ environment_exit(&env);
+
+ sptps_send_record(&c->sptps, 2, data, 0);
+ return true;
+}
+
+static bool receive_invitation_sptps(void *handle, uint8_t type, const void *data, uint16_t len) {
+ connection_t *c = handle;
+
+ if(type == 128) {
+ return true;
+ }
+
+ if(type == 1 && c->status.invitation_used) {
+ return finalize_invitation(c, data, len);
+ }
+
+ if(type != 0 || len != 18 || c->status.invitation_used) {
+ return false;
+ }
+
+ // Recover the filename from the cookie and the key
+ char *fingerprint = ecdsa_get_base64_public_key(invitation_key);
+ char hashbuf[18 + strlen(fingerprint)];
+ char cookie[64];
+ memcpy(hashbuf, data, 18);
+ memcpy(hashbuf + 18, fingerprint, sizeof(hashbuf) - 18);
+ sha512(hashbuf, sizeof(hashbuf), cookie);
+ b64encode_urlsafe(cookie, cookie, 18);
+ free(fingerprint);
+
+ char filename[PATH_MAX], usedname[PATH_MAX];
+ snprintf(filename, sizeof(filename), "%s" SLASH "invitations" SLASH "%s", confbase, cookie);
+ snprintf(usedname, sizeof(usedname), "%s" SLASH "invitations" SLASH "%s.used", confbase, cookie);
+
+ // Atomically rename the invitation file
+ if(rename(filename, usedname)) {
+ if(errno == ENOENT) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s tried to use non-existing invitation %s\n", c->hostname, cookie);
+ } else {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error trying to rename invitation %s\n", cookie);
+ }
+
+ return false;
+ }
+
+ // Check the timestamp of the invitation
+ struct stat st;
+
+ if(stat(usedname, &st)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Could not stat %s", usedname);
+ return false;
+ }
+
+ if(st.st_mtime + invitation_lifetime < now.tv_sec) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Peer %s tried to use expired invitation %s", c->hostname, cookie);
+ return false;
+ }
+
+ // Open the renamed file
+ FILE *f = fopen(usedname, "r");
+
+ if(!f) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error trying to open invitation %s\n", cookie);
+ return false;
+ }
+
+ // Read the new node's Name from the file
+ char buf[1024] = "";
+ fgets(buf, sizeof(buf), f);
+ size_t buflen = strlen(buf);
+
+ // Strip whitespace at the end
+ while(buflen && strchr(" \t\r\n", buf[buflen - 1])) {
+ buf[--buflen] = 0;
+ }
+
+ // Split the first line into variable and value
+ len = strcspn(buf, " \t=");
+ char *name = buf + len;
+ name += strspn(name, " \t");
+
+ if(*name == '=') {
+ name++;
+ name += strspn(name, " \t");
+ }