+ if(!mesh || !invitation) {
+ meshlink_errno = MESHLINK_EINVAL;
+ return false;
+ }
+
+ pthread_mutex_lock(&(mesh->mesh_mutex));
+
+ //TODO: think of a better name for this variable, or of a different way to tokenize the invitation URL.
+ char copy[strlen(invitation) + 1];
+ strcpy(copy, invitation);
+
+ // Split the invitation URL into hostname, port, key hash and cookie.
+
+ char *slash = strchr(copy, '/');
+ if(!slash)
+ goto invalid;
+
+ *slash++ = 0;
+
+ if(strlen(slash) != 48)
+ goto invalid;
+
+ char *address = copy;
+ char *port = NULL;
+ if(*address == '[') {
+ address++;
+ char *bracket = strchr(address, ']');
+ if(!bracket)
+ goto invalid;
+ *bracket = 0;
+ if(bracket[1] == ':')
+ port = bracket + 2;
+ } else {
+ port = strchr(address, ':');
+ if(port)
+ *port++ = 0;
+ }
+
+ if(!port)
+ goto invalid;
+
+ if(!b64decode(slash, mesh->hash, 18) || !b64decode(slash + 24, mesh->cookie, 18))
+ goto invalid;
+
+ // Generate a throw-away key for the invitation.
+ ecdsa_t *key = ecdsa_generate();
+ if(!key) {
+ meshlink_errno = MESHLINK_EINTERNAL;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ char *b64key = ecdsa_get_base64_public_key(key);
+
+ //Before doing meshlink_join make sure we are not connected to another mesh
+ if ( mesh->threadstarted ){
+ goto invalid;
+ }
+
+ // Connect to the meshlink daemon mentioned in the URL.
+ struct addrinfo *ai = str2addrinfo(address, port, SOCK_STREAM);
+ if(!ai) {
+ meshlink_errno = MESHLINK_ERESOLV;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ mesh->sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if(mesh->sock <= 0) {
+ logger(mesh, MESHLINK_DEBUG, "Could not open socket: %s\n", strerror(errno));
+ freeaddrinfo(ai);
+ meshlink_errno = MESHLINK_ENETWORK;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ if(connect(mesh->sock, ai->ai_addr, ai->ai_addrlen)) {
+ logger(mesh, MESHLINK_DEBUG, "Could not connect to %s port %s: %s\n", address, port, strerror(errno));
+ closesocket(mesh->sock);
+ freeaddrinfo(ai);
+ meshlink_errno = MESHLINK_ENETWORK;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ freeaddrinfo(ai);
+
+ logger(mesh, MESHLINK_DEBUG, "Connected to %s port %s...\n", address, port);
+
+ // Tell him we have an invitation, and give him our throw-away key.
+
+ mesh->blen = 0;
+
+ if(!sendline(mesh->sock, "0 ?%s %d.%d", b64key, PROT_MAJOR, 1)) {
+ logger(mesh, MESHLINK_DEBUG, "Error sending request to %s port %s: %s\n", address, port, strerror(errno));
+ closesocket(mesh->sock);
+ meshlink_errno = MESHLINK_ENETWORK;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ free(b64key);
+
+ char hisname[4096] = "";
+ int code, hismajor, hisminor = 0;
+
+ if(!recvline(mesh, sizeof mesh->line) || sscanf(mesh->line, "%d %s %d.%d", &code, hisname, &hismajor, &hisminor) < 3 || code != 0 || hismajor != PROT_MAJOR || !check_id(hisname) || !recvline(mesh, sizeof mesh->line) || !rstrip(mesh->line) || sscanf(mesh->line, "%d ", &code) != 1 || code != ACK || strlen(mesh->line) < 3) {
+ logger(mesh, MESHLINK_DEBUG, "Cannot read greeting from peer\n");
+ closesocket(mesh->sock);
+ meshlink_errno = MESHLINK_ENETWORK;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ // Check if the hash of the key he gave us matches the hash in the URL.
+ char *fingerprint = mesh->line + 2;
+ char hishash[64];
+ if(sha512(fingerprint, strlen(fingerprint), hishash)) {
+ logger(mesh, MESHLINK_DEBUG, "Could not create hash\n%s\n", mesh->line + 2);
+ meshlink_errno = MESHLINK_EINTERNAL;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+ if(memcmp(hishash, mesh->hash, 18)) {
+ logger(mesh, MESHLINK_DEBUG, "Peer has an invalid key!\n%s\n", mesh->line + 2);
+ meshlink_errno = MESHLINK_EPEER;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+
+ }
+
+ ecdsa_t *hiskey = ecdsa_set_base64_public_key(fingerprint);
+ if(!hiskey) {
+ meshlink_errno = MESHLINK_EINTERNAL;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ // Start an SPTPS session
+ if(!sptps_start(&mesh->sptps, mesh, true, false, key, hiskey, "meshlink invitation", 15, invitation_send, invitation_receive)) {
+ meshlink_errno = MESHLINK_EINTERNAL;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ // Feed rest of input buffer to SPTPS
+ if(!sptps_receive_data(&mesh->sptps, mesh->buffer, mesh->blen)) {
+ meshlink_errno = MESHLINK_EPEER;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ int len;
+
+ while((len = recv(mesh->sock, mesh->line, sizeof mesh->line, 0))) {
+ if(len < 0) {
+ if(errno == EINTR)
+ continue;
+ logger(mesh, MESHLINK_DEBUG, "Error reading data from %s port %s: %s\n", address, port, strerror(errno));
+ meshlink_errno = MESHLINK_ENETWORK;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ if(!sptps_receive_data(&mesh->sptps, mesh->line, len)) {
+ meshlink_errno = MESHLINK_EPEER;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+ }
+
+ sptps_stop(&mesh->sptps);
+ ecdsa_free(hiskey);
+ ecdsa_free(key);
+ closesocket(mesh->sock);
+
+ if(!mesh->success) {
+ logger(mesh, MESHLINK_DEBUG, "Connection closed by peer, invitation cancelled.\n");
+ meshlink_errno = MESHLINK_EPEER;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return false;
+ }
+
+ pthread_mutex_unlock(&(mesh->mesh_mutex));
+ return true;
+
+invalid:
+ logger(mesh, MESHLINK_DEBUG, "Invalid invitation URL or you are already connected to a Mesh ?\n");
+ meshlink_errno = MESHLINK_EINVAL;
+ pthread_mutex_unlock(&(mesh->mesh_mutex));