bool Connection::JoinParent() { if (!Connect()) { LogError(wxString().Format(wxT(" Handshake failed in con id %d"),id_)); KillSocket(-1); return FALSE; } /* Tell our parent hello and it has just born a new child. */ Packet packet = GenPacket_HelloNext(); if (!Send(packet)) { LogError(wxString().Format(wxT(" Send failed in con id %d when trying to say hello to parent"),id_)); KillSocket(-1); return FALSE; } /* Receive all the hashes we are responsible for. */ Packet* rcv; if (!Receive(rcv) || rcv == 0) { LogError(wxString().Format(wxT(" Receive failed when getting responsible hashes from parent on con id %d"), id_)); KillSocket(-1); return FALSE; } KillSocket(-1); // add all the hashes we are responsible for to our hash table for (int x = 0; x < rcv->Count(Packet::HEADER_HASH);x++) { wxString hash = rcv->Get(Packet::HEADER_HASH,x); wxString node = rcv->Get(Packet::HEADER_NODE,x); wxString info = rcv->Get(Packet::HEADER_INFO,x); Network::Instance()->hashTable->Add(HashValue(node,hash,info)); } delete rcv; return TRUE; }
LookupResponse Connection::DirectQuery(wxString key) { LookupResponse answer(LookupResponse::FAIL); if (!Connect()) { LogError(wxString().Format(wxT("Connect() failed when doing a direct query in con id %d"),id_)); KillSocket(-1); return answer; } /* Send QUERY. */ Packet snd = GenPacket_Query(key); if (!Send(snd)) { LogError(wxString().Format(wxT("Send failed when trying to direct query on con id %d"),id_)); KillSocket(-1); return answer; } /* Receive some sort of query response. */ Packet* rcv; if (!Receive(rcv) || rcv == 0) { LogError(wxString().Format(wxT("Receive failed when trying to direct query on con id %d"), id_)); KillSocket(-1); return answer; } KillSocket(-1); switch(rcv->Type()) { case Packet::TYPE_RESPONSE_FOUND: { answer = LookupResponse(LookupResponse::FOUND,Node(currentNid_,currentAddr_)); for (int x = 0; x < rcv->Count(Packet::HEADER_NODE);x++) { wxString node = rcv->Get(Packet::HEADER_NODE,x); wxString info = rcv->Get(Packet::HEADER_INFO,x); answer.AddResponse(HashValue(node,key,info)); } break; } case Packet::TYPE_RESPONSE_JUMP: { answer = LookupResponse(LookupResponse::JUMP,Node(wxT("UNDEFINED"),rcv->Get(Packet::HEADER_JUMP))); break; } case Packet::TYPE_RESPONSE_SUCCESSOR: { answer = LookupResponse(LookupResponse::SUCCESSOR,Node(currentNid_,currentAddr_)); break; } default: { LogError(wxString().Format(wxT("Received an invalid lookup response from node on con id %d"), id_)); break; } } delete rcv; return answer; }
// this function does not send events bool Connection::ReceiveHandShake() { // TODO check the banned list! /* Get the SYN or JOIN packet. */ Packet* rcv; if (!Receive(rcv) || rcv == 0) { LogError(wxString().Format(wxT("Receive failed on incomming con id %d"), id_)); return FALSE; } switch(rcv->Type()) { // node is a network node and wants to handshake case Packet::TYPE_SYN: { #ifdef ENABLE_ENCRYPTION rsaPub_ = rcv->Get(Packet::HEADER_PUBLIC_KEY); if (rsaPub_ == wxT("")) { LogError(wxString().Format(wxT("Got empty RSA public key on incomming con id %d"), id_)); return false; } #endif delete rcv; #ifdef ENABLE_ENCRYPTION /* Init encryption. */ symetric_->Init(crypto::RandomString().c_str()/*,(unsigned char*)"blah"*/); #endif /* Send the ACK packet. */ #ifdef ENABLE_ENCRYPTION Packet snd = GenPacket_Ack(wxString(symetric_->Key().c_str()),wxString(socket_->GetPeerHost().c_str())); #else Packet snd = GenPacket_Ack(wxString(),wxString(socket_->GetPeerHost().c_str())); #endif if (!Send(snd)) { LogError(wxString().Format(wxT("ACK send failed on incomming con id %d"),id_)); return FALSE; } if (!Receive(rcv) || rcv == 0) { LogError(wxString().Format(wxT("Receive confirm packet failed on incomming con id %d"), id_)); return FALSE; } if (rcv->Type() != Packet::TYPE_CONFIRM) { LogError(wxString().Format(wxT("Did not get confirm packet on incomming con id %d, got packet type %d instead"), id_,rcv->Type())); return FALSE; } // TODO get the correct hostname if none is supplied currentAddr_ = rcv->Get(Packet::HEADER_SRC_ADDR); currentNid_ = rcv->Get(Packet::HEADER_SRC_NID); wxString wrkGrpKey = rcv->Get(Packet::HEADER_WRKGRPKEY); Network::Instance()->SetGateway(rcv->Get(Packet::HEADER_DEST_HOST)); if (Prefs::Instance()->Get(Prefs::WRKGRPKEY) != wxT("") && wrkGrpKey != Prefs::Instance()->Get(Prefs::WRKGRPKEY)) { LogError(wxString().Format(wxT("Node tried to connect with invalid workgroup %s key on incomming con id %d packet: %s"), wrkGrpKey.c_str(),id_,rcv->Raw())); delete rcv; return FALSE; } if (currentAddr_ == wxT("") || currentNid_ == wxT("")) { LogError(wxString().Format(wxT("(3) Blank address or nid from node when trying to handshake on con id %d"),id_)); delete rcv; return FALSE; } delete rcv; /* Now that handshaking is out of the way, lets figure out what this node wants. */ if (!Receive(rcv) || rcv == 0) { LogError(wxString().Format(wxT("Receive failed on incomming con id %d"), id_)); return FALSE; } switch(rcv->Type()) { case Packet::TYPE_QUERY: { // get the hash we are being queried for wxString hash = rcv->Get(Packet::HEADER_HASH); LogDebug(wxString().Format(wxT("Responding to a query for '%s' on incomming con id %d"),hash.c_str(),id_)); delete rcv; if (Network::Instance()->IsSuccessor(Prefs::Instance()->Get(Prefs::NID), Network::Instance()->Prev().Nid(),hash)) { Packet answer = GenPacket_ResponseSuccess(); bool found = FALSE; HashValue value; for (int x = 1; ;x++) { value = Network::Instance()->hashTable->GetNext(hash,x); if (value.hash == wxT("")) break; // responsible node information answer.Add(Packet::HEADER_NODE, value.node); answer.Add(Packet::HEADER_INFO, value.info); // yes, we are responsible for this mess found = TRUE; } if (found) // we are responsible for the hash and we have it { LogDebug(wxString().Format(wxT("We have the hash %s"),hash.c_str())); if (!Send(answer)) { return FALSE; } } else // we are responsible for the hash but don't have it { LogDebug(wxString().Format(wxT("We have no entry matching hash %s"),hash.c_str())); Packet snd = GenPacket_ResponseEnd(); if (!Send(snd)) { LogError(wxString().Format(wxT(" Send failed on incomming con id %d"),id_)); return FALSE; } } } else // not resposible, so tell them where to jump to next { // TODO use the finger table to find a jump node Node next; if (!Network::Instance()->GetNext(0,next)) { LogError(wxT("Could not get next to generate jump packet.")); } Packet snd = GenPacket_ResponseJump(next.Address()); if (!Send(snd)) { LogError(wxString().Format(wxT(" Send jump failed on incomming con id %d"),id_)); return FALSE; } } break; } case Packet::TYPE_HELLO_NEXT: { delete rcv; LogDebug(wxString().Format(wxT("Got a hello next from %s"),currentNid_.c_str())); bool dojoin = FALSE; //if (Network::Instance()->Prev().Nid() == wxT("") && // Network::Instance()->seed_) Node next; Network::Instance()->GetNext(0,next); if (next.Nid() == Prefs::Instance()->Get(Prefs::NID)) { dojoin = TRUE; } /* We now have a new previous! */ Network::Instance()->Prev(Node(currentNid_,currentAddr_)); // TODO do a lookup and make sure we are the real successor! /* A new node just said it is my new child, so I have to: - send it all the hashes < than the new node's nid */ Packet insert = GenPacket_Insert(); HashValue value; //bool found = FALSE; for (unsigned int x = 1;;++x) { value = Network::Instance()->hashTable->Get(x); if (value.hash == wxT("")) break; if (!Network::Instance()->IsSuccessor(Prefs::Instance()->Get(Prefs::NID), Network::Instance()->Prev().Nid(),value.hash)) { LogDebug(wxString().Format(wxT("Giving up hash %s to prev %s"),value.hash.c_str(),currentNid_.c_str())); insert.Add(Packet::HEADER_HASH,value.hash); insert.Add(Packet::HEADER_NODE,value.node); insert.Add(Packet::HEADER_INFO,value.info); } //found = TRUE; } // send an insert even if we have no hashes, this means "OK" if (!Send(insert)) { LogError(wxString().Format(wxT("Send insert failed on incomming con id %d"),id_)); return FALSE; } /* If we don't have a next pointer then use the first new child (this will happen if we are a seed) */ if (dojoin) { LogDebug(wxString().Format(wxT("Seed node, so joining parent %s"),currentNid_.c_str())); /* Now that we have a next pointer, lets say hey to it. */ if (!HelloParent(Node(currentNid_,currentAddr_))) { // if we can't connect to the node that just connected to us we lost our next PostConEvent(EVENT_NEXT_LOST); return FALSE; } else { //PostConEvent(EVENT_NEXT_CONNECTED); } } PostConEvent(EVENT_PREV_CONNECTED); break; } case Packet::TYPE_STAT: { delete rcv; Packet snd = GenPacket_StatResponse(); if (!Send(snd)) { LogError(wxString().Format(wxT("STAT_RESPONSE send failed on incomming con id %d"),id_)); return FALSE; } break; } case Packet::TYPE_REQUEST: { /* Client is requesting a file we have. ... No need to check if we have the file or not here, the filetransferthread will fail if we don't have the file. */ // get the filename and an md5 of the file requested wxString md5 = rcv->Get(Packet::HEADER_FILEMD5); wxString filename = rcv->Get(Packet::HEADER_FILENAME); delete rcv; if (currentAddr_ == wxT("") || currentNid_ == wxT("")) { LogError(wxString().Format(wxT("(4) Blank address or nid from node when trying to handshake on con id %d"),id_)); delete rcv; return FALSE; } bool found = FALSE; for (unsigned int x = 0; x < Prefs::Instance()->sharedFiles.size();x++) { if (Prefs::Instance()->sharedFiles[x].Name() == filename && Prefs::Instance()->sharedFiles[x].MD5() == md5) { LogDebug(wxString().Format(wxT("Sending file %s with MD5 %s on incomming con id %d"), filename.c_str(),md5.c_str(),id_)); ShareFile* file = new ShareFile(Prefs::Instance()->sharedFiles[x]); Put(file); found = TRUE; break; } } if (!found) { LogError(wxString().Format(wxT(" Don't have request for file %s with MD5 %s on incomming con id %d"), filename.c_str(),md5.c_str(),id_)); return FALSE; } break; } case Packet::TYPE_HELLO_BUDDY: { /* A buddy has signed on and is now saying hello to us. Set his status to online and save some info about him. */ wxString info = rcv->Get(Packet::HEADER_INFO); delete rcv; Buddy* buddy = 0; wxStringTokenizer tokens(info, wxT(":")); // TODO make sure the buddy info is valid (we have all the tokens) if (tokens.GetNextToken() == wxT("BUDDY")) { wxMutexGuiEnter(); BuddyTreeCtrl* tree = wxGetApp().GetMainWindow()->GetTreeCtrl(); wxString alias = tokens.GetNextToken(); wxString publicKey = tokens.GetNextToken(); wxString profile = tokens.GetNextToken(); tree->SetBuddyData(publicKey, alias, profile, currentAddr_); buddy = tree->GetBuddy(publicKey); if (buddy != 0) buddy->SetStatusOn(Buddy::STATUS_ONLINE); wxMutexGuiLeave(); LogMsg(wxString().Format(wxT("Found that buddy '%s' with id '%s' is online."),alias.c_str(),publicKey.c_str())); } else { LogError(wxT("Got an invalid info header on buddy hello.")); } break; } case Packet::TYPE_IM_CONNECT: { /* A buddy wants to connect to us. */ wxString info = rcv->Get(Packet::HEADER_INFO); delete rcv; Buddy* buddy = 0; wxStringTokenizer tokens(info, wxT(":")); // TODO make sure the buddy info is valid (we have all the tokens) if (tokens.GetNextToken() == wxT("BUDDY")) { wxString alias = tokens.GetNextToken(); wxString publicKey = tokens.GetNextToken(); wxString profile = tokens.GetNextToken(); wxMutexGuiEnter(); BuddyTreeCtrl* tree = wxGetApp().GetMainWindow()->GetTreeCtrl(); buddy = tree->GetBuddy(publicKey); if (buddy != 0) { tree->SetBuddyData(publicKey, alias, profile, currentAddr_); LogMsg(wxString().Format(wxT("Buddy '%s' with id '%s' wants to connect."),alias.c_str(),publicKey.c_str())); //buddy->SetStatusOn(Buddy::STATUS_ONLINE); } else { wxMessageDialog diag(0,wxT("Buddy '")+ alias + wxT("' wants to make an instant message connection to you. Do you want to accept this connection and add this buddy to your list?"),wxT("New Buddy Connection"),wxYES|wxNO); if (diag.ShowModal() == wxYES) { } } wxMutexGuiLeave(); } else { LogError(wxT("Got an invalid info header on buddy hello.")); } if (buddy != 0) { parent_ = buddy; parentId_ = buddy->id(); ImEventQueue* queue = new ImEventQueue(); PostConEvent(ImEvent::EVENT_MADE,(void*)queue); /* Process the IM events. */ HandleImEvents(queue); PostConEvent(ImEvent::EVENT_LOST); //delete queue; } break; } case Packet::TYPE_INSERT: { // somebody wants to give us some hashes to be responsible for // TODO we should validate if we are responsible for them or not for (int x = 0; x < rcv->Count(Packet::HEADER_HASH); x++) { wxString hash = rcv->Get(Packet::HEADER_HASH,x); wxString node = rcv->Get(Packet::HEADER_NODE,x); wxString info = rcv->Get(Packet::HEADER_INFO,x); Network::Instance()->hashTable->Add(HashValue(node,hash,info)); LogDebug(wxString().Format(wxT("Now responsible for hash: %s with owner %s and info %s"), hash.c_str(),node.c_str(),info.c_str())); } delete rcv; break; } default: { LogError(wxString().Format(wxT("Invalid packet on incomming con id %d"), id_)); delete rcv; return FALSE; } } break; } // node is not a network node and wants to join case Packet::TYPE_JOIN: { //currentAddr_ = rcv->Get(Packet::HEADER_SRC_ADDR); currentAddr_ = wxString().Format(wxT("%s/%d"),socket_->GetPeerHost().c_str(),ParsePort(rcv->Get(Packet::HEADER_SRC_ADDR))); rsaPub_ = rcv->Get(Packet::HEADER_PUBLIC_KEY); wxString wrkGrpKey = rcv->Get(Packet::HEADER_WRKGRPKEY); if (Prefs::Instance()->Get(Prefs::WRKGRPKEY) != wxT("") && wrkGrpKey != Prefs::Instance()->Get(Prefs::WRKGRPKEY)) { LogError(wxString().Format(wxT(" Node tried to connect with invalid workgroup %s key on incomming con id %d packet: %s"), wrkGrpKey.c_str(),id_,rcv->Raw())); delete rcv; return FALSE; } // compute an NID for the new node currentNid_ = Network::Instance()->ComputeNid(currentAddr_); delete rcv; if (currentAddr_ == wxT("")) { LogError(wxString().Format(wxT("(5) Blank address or nid from node when trying to handshake on con id %d"),id_)); delete rcv; return FALSE; } #ifdef DEBUG // make sure the nid we computed is valid if (currentNid_.size() != 40) { // this should never happen, but whatever LogError(wxT("Computed am invalid blank nid for joining node.")); return FALSE; } #endif /* Now, we need to do a lookup and find the parent of the node. */ LookupResponse answer = Lookup(currentNid_); switch(answer.type_) { case LookupResponse::SUCCESSOR: case LookupResponse::FOUND: { /* Found your successor. If we get anything else from the lookup it means we can't do it and drop the connection (this should never happen!) */ Packet snd = GenPacket_JoinResponse(currentNid_,answer.successor,wxString(socket_->GetPeerHost().c_str())); if (!Send(snd)) { LogError(wxString().Format(wxT("JOIN_RESPONSE send failed on incomming con id %d"),id_)); return FALSE; } break; } default: { LogError(wxString().Format(wxT("Lookup failed when trying to find parent for new node on incomming con id %d"),id_)); return FALSE; } } break; } default: { LogError(wxString().Format(wxT("Invalid packet type %d on incomming con id %d"),rcv->Type(), id_)); delete rcv; return FALSE; } } /* Save the node in the finger table. */ Network::Instance()->finger.Add(Node(currentNid_,currentAddr_)); return TRUE; }