/* handle received packets for not yet established crypto connections. */ static void receive_crypto(void) { uint32_t i; for (i = 0; i < MAX_CRYPTO_CONNECTIONS; ++i) { if (crypto_connections[i].status == CONN_HANDSHAKE_SENT) { uint8_t temp_data[MAX_DATA_SIZE]; uint8_t secret_nonce[crypto_box_NONCEBYTES]; uint8_t public_key[crypto_box_PUBLICKEYBYTES]; uint8_t session_key[crypto_box_PUBLICKEYBYTES]; uint16_t len; if (id_packet(crypto_connections[i].number) == 1) /* if the packet is a friend request drop it (because we are already friends) */ len = read_packet(crypto_connections[i].number, temp_data); if (id_packet(crypto_connections[i].number) == 2) { /* handle handshake packet. */ len = read_packet(crypto_connections[i].number, temp_data); if (handle_cryptohandshake(public_key, secret_nonce, session_key, temp_data, len)) { if (memcmp(public_key, crypto_connections[i].public_key, crypto_box_PUBLICKEYBYTES) == 0) { memcpy(crypto_connections[i].sent_nonce, secret_nonce, crypto_box_NONCEBYTES); memcpy(crypto_connections[i].peersessionpublic_key, session_key, crypto_box_PUBLICKEYBYTES); increment_nonce(crypto_connections[i].sent_nonce); uint32_t zero = 0; encrypt_precompute(crypto_connections[i].peersessionpublic_key, crypto_connections[i].sessionsecret_key, crypto_connections[i].shared_key); crypto_connections[i].status = CONN_ESTABLISHED; /* connection status needs to be 3 for write_cryptpacket() to work */ write_cryptpacket(i, ((uint8_t *)&zero), sizeof(zero)); crypto_connections[i].status = CONN_NOT_CONFIRMED; /* set it to its proper value right after. */ } } } else if (id_packet(crypto_connections[i].number) != -1) // This should not happen kill the connection if it does crypto_kill(crypto_connections[i].number); } if (crypto_connections[i].status == CONN_NOT_CONFIRMED) { if (id_packet(crypto_connections[i].number) == 3) { uint8_t temp_data[MAX_DATA_SIZE]; uint8_t data[MAX_DATA_SIZE]; int length = read_packet(crypto_connections[i].number, temp_data); int len = decrypt_data(crypto_connections[i].peersessionpublic_key, crypto_connections[i].sessionsecret_key, crypto_connections[i].recv_nonce, temp_data + 1, length - 1, data); uint32_t zero = 0; if (len == sizeof(uint32_t) && memcmp(((uint8_t *)&zero), data, sizeof(uint32_t)) == 0) { increment_nonce(crypto_connections[i].recv_nonce); encrypt_precompute(crypto_connections[i].peersessionpublic_key, crypto_connections[i].sessionsecret_key, crypto_connections[i].shared_key); crypto_connections[i].status = CONN_ESTABLISHED; /* connection is accepted so we disable the auto kill by setting it to about 1 month from now. */ kill_connection_in(crypto_connections[i].number, 3000000); } else crypto_kill(crypto_connections[i].number); // This should not happen kill the connection if it does } else if(id_packet(crypto_connections[i].number) != -1) /* This should not happen kill the connection if it does */ crypto_kill(crypto_connections[i].number); } } }
/* Callback for dht public key changes. */ static void dht_pk_callback(void *object, int32_t number, const uint8_t *dht_public_key, void *userdata) { Friend_Connections *const fr_c = (Friend_Connections *)object; Friend_Conn *const friend_con = get_conn(fr_c, number); if (!friend_con) { return; } if (public_key_cmp(friend_con->dht_temp_pk, dht_public_key) == 0) { return; } change_dht_pk(fr_c, number, dht_public_key); /* if pk changed, create a new connection.*/ if (friend_con->crypt_connection_id != -1) { crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id); friend_con->crypt_connection_id = -1; handle_status(object, number, 0, userdata); /* Going offline. */ } friend_new_connection(fr_c, number); onion_set_friend_DHT_pubkey(fr_c->onion_c, friend_con->onion_friendnum, dht_public_key); }
void kill_net_crypto(Net_Crypto *c) { uint32_t i; for (i = 0; i < c->crypto_connections_length; ++i) { crypto_kill(c, i); } kill_lossless_udp(c->lossless_udp); memset(c, 0, sizeof(Net_Crypto)); free(c); }
/* main friend_connections loop. */ void do_friend_connections(Friend_Connections *fr_c, void *userdata) { const uint64_t temp_time = mono_time_get(fr_c->mono_time); for (uint32_t i = 0; i < fr_c->num_cons; ++i) { Friend_Conn *const friend_con = get_conn(fr_c, i); if (friend_con) { if (friend_con->status == FRIENDCONN_STATUS_CONNECTING) { if (friend_con->dht_pk_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) { if (friend_con->dht_lock) { dht_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock); friend_con->dht_lock = 0; memset(friend_con->dht_temp_pk, 0, CRYPTO_PUBLIC_KEY_SIZE); } } if (friend_con->dht_ip_port_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) { friend_con->dht_ip_port.ip.family = net_family_unspec; } if (friend_con->dht_lock) { if (friend_new_connection(fr_c, i) == 0) { set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->dht_ip_port, 0); connect_to_saved_tcp_relays(fr_c, i, (MAX_FRIEND_TCP_CONNECTIONS / 2)); /* Only fill it half up. */ } } } else if (friend_con->status == FRIENDCONN_STATUS_CONNECTED) { if (friend_con->ping_lastsent + FRIEND_PING_INTERVAL < temp_time) { send_ping(fr_c, i); } if (friend_con->share_relays_lastsent + SHARE_RELAYS_INTERVAL < temp_time) { send_relays(fr_c, i); } if (friend_con->ping_lastrecv + FRIEND_CONNECTION_TIMEOUT < temp_time) { /* If we stopped receiving ping packets, kill it. */ crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id); friend_con->crypt_connection_id = -1; handle_status(fr_c, i, 0, userdata); /* Going offline. */ } } } } if (fr_c->local_discovery_enabled) { lan_discovery(fr_c); } }
/* main friend_connections loop. */ void do_friend_connections(Friend_Connections *fr_c) { uint32_t i; uint64_t temp_time = unix_time(); for (i = 0; i < fr_c->num_cons; ++i) { Friend_Conn *friend_con = get_conn(fr_c, i); if (friend_con) { if (friend_con->status == FRIENDCONN_STATUS_CONNECTING) { if (friend_con->dht_pk_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) { if (friend_con->dht_lock) { DHT_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock); friend_con->dht_lock = 0; } } if (friend_con->dht_ip_port_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) { friend_con->dht_ip_port.ip.family = 0; } if (friend_con->dht_lock) { if (friend_new_connection(fr_c, i) == 0) { set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->dht_ip_port, 0); connect_to_saved_tcp_relays(fr_c, i, (MAX_FRIEND_TCP_CONNECTIONS / 2)); /* Only fill it half up. */ } } } else if (friend_con->status == FRIENDCONN_STATUS_CONNECTED) { if (friend_con->ping_lastsent + FRIEND_PING_INTERVAL < temp_time) { send_ping(fr_c, i); } if (friend_con->share_relays_lastsent + SHARE_RELAYS_INTERVAL < temp_time) { send_relays(fr_c, i); } if (friend_con->ping_lastrecv + FRIEND_CONNECTION_TIMEOUT < temp_time) { /* If we stopped receiving ping packets, kill it. */ crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id); friend_con->crypt_connection_id = -1; handle_status(fr_c, i, 0); /* Going offline. */ } } } } LANdiscovery(fr_c); }
/* remove a friend return 0 if success return -1 if failure */ int m_delfriend(int friendnumber) { if (friendnumber >= numfriends || friendnumber < 0) return -1; DHT_delfriend(friendlist[friendnumber].client_id); crypto_kill(friendlist[friendnumber].crypt_connection_id); free(friendlist[friendnumber].userstatus); memset(&friendlist[friendnumber], 0, sizeof(Friend)); uint32_t i; for (i = numfriends; i != 0; --i) { if (friendlist[i].status != 0) break; } numfriends = i; return 0; }
static void doInbound(void) { uint8_t secret_nonce[crypto_box_NONCEBYTES]; uint8_t public_key[crypto_box_PUBLICKEYBYTES]; uint8_t session_key[crypto_box_PUBLICKEYBYTES]; int inconnection = crypto_inbound(public_key, secret_nonce, session_key); if (inconnection != -1) { int friend_id = getfriend_id(public_key); if (friend_id != -1) { crypto_kill(friendlist[friend_id].crypt_connection_id); friendlist[friend_id].crypt_connection_id = accept_crypto_inbound(inconnection, public_key, secret_nonce, session_key); set_friend_status(friend_id, FRIEND_CONFIRMED); } } }
/* main friend_connections loop. */ void do_friend_connections(Friend_Connections *fr_c) { uint32_t i; uint64_t temp_time = unix_time(); for (i = 0; i < fr_c->num_cons; ++i) { Friend_Conn *friend_con = get_conn(fr_c, i); if (friend_con) { if (friend_con->status == FRIENDCONN_STATUS_CONNECTING) { if (friend_con->dht_ping_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) { if (friend_con->dht_lock) { DHT_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock); friend_con->dht_lock = 0; } } if (friend_con->dht_ip_port_lastrecv + FRIEND_DHT_TIMEOUT < temp_time) { friend_con->dht_ip_port.ip.family = 0; } if (friend_con->dht_lock) { if (friend_new_connection(fr_c, i) == 0) { set_connection_dht_public_key(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->dht_temp_pk); set_direct_ip_port(fr_c->net_crypto, friend_con->crypt_connection_id, friend_con->dht_ip_port); } } } else if (friend_con->status == FRIENDCONN_STATUS_CONNECTED) { if (friend_con->ping_lastsent + FRIEND_PING_INTERVAL < temp_time) { send_ping(fr_c, i); } if (friend_con->ping_lastrecv + FRIEND_CONNECTION_TIMEOUT < temp_time) { /* If we stopped receiving ping packets, kill it. */ crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id); friend_con->crypt_connection_id = -1; handle_status(fr_c, i, 0); /* Going offline. */ } } } } }
/* remove a friend return 0 if success return -1 if failure */ int m_delfriend(int friendnumber) { if (friendnumber >= numfriends || friendnumber < 0) return -1; DHT_delfriend(friendlist[friendnumber].client_id); crypto_kill(friendlist[friendnumber].crypt_connection_id); free(friendlist[friendnumber].statusmessage); memset(&friendlist[friendnumber], 0, sizeof(Friend)); uint32_t i; for (i = numfriends; i != 0; --i) { if (friendlist[i-1].status != NOFRIEND) break; } numfriends = i; realloc_friendlist(numfriends + 1); return 0; }
/* Kill a friend connection. * * return -1 on failure. * return 0 on success. */ int kill_friend_connection(Friend_Connections *fr_c, int friendcon_id) { Friend_Conn *friend_con = get_conn(fr_c, friendcon_id); if (!friend_con) return -1; if (friend_con->lock_count) { --friend_con->lock_count; return 0; } onion_delfriend(fr_c->onion_c, friend_con->onion_friendnum); crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id); if (friend_con->dht_lock) { DHT_delfriend(fr_c->dht, friend_con->dht_temp_pk, friend_con->dht_lock); } return wipe_friend_conn(fr_c, friendcon_id); }
/* Callback for dht public key changes. */ static void dht_pk_callback(void *object, int32_t number, const uint8_t *dht_public_key) { Friend_Connections *fr_c = object; Friend_Conn *friend_con = get_conn(fr_c, number); if (!friend_con) return; if (memcmp(friend_con->dht_temp_pk, dht_public_key, crypto_box_PUBLICKEYBYTES) == 0) return; change_dht_pk(fr_c, number, dht_public_key); /* if pk changed, create a new connection.*/ if (friend_con->crypt_connection_id != -1) { crypto_kill(fr_c->net_crypto, friend_con->crypt_connection_id); friend_con->crypt_connection_id = -1; } friend_new_connection(fr_c, number); onion_set_friend_DHT_pubkey(fr_c->onion_c, friend_con->onion_friendnum, dht_public_key); }
/* remove a friend return 0 if success return -1 if failure */ int m_delfriend(Messenger *m, int friendnumber) { if (friendnumber >= m->numfriends || friendnumber < 0) return -1; DHT_delfriend(m->friendlist[friendnumber].client_id); crypto_kill(m->friendlist[friendnumber].crypt_connection_id); free(m->friendlist[friendnumber].statusmessage); memset(&(m->friendlist[friendnumber]), 0, sizeof(Friend)); uint32_t i; for (i = m->numfriends; i != 0; --i) { if (m->friendlist[i - 1].status != NOFRIEND) break; } m->numfriends = i; if (realloc_friendlist(m, m->numfriends) != 0) return FAERR_NOMEM; return 0; }
int main(int argc, char *argv[]) { if (argc < 4) { printf("usage %s ip port filename(of file to send)\n", argv[0]); exit(0); } new_keys(); printf("OUR ID: "); uint32_t i; for(i = 0; i < 32; i++) { if(self_public_key[i] < 16) printf("0"); printf("%hhX",self_public_key[i]); } printf("\n"); memcpy(self_client_id, self_public_key, 32); char temp_id[128]; printf("Enter the client_id of the friend to connect to (32 bytes HEX format):\n"); scanf("%s", temp_id); uint8_t friend_id[32]; memcpy(friend_id, hex_string_to_bin(temp_id), 32); /* memcpy(self_client_id, "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", 32); */ DHT_addfriend(friend_id); IP_Port friend_ip; int connection = -1; int inconnection = -1; uint8_t acceptedfriend_public_key[crypto_box_PUBLICKEYBYTES]; int friendrequest = -1; uint8_t request_data[512]; /* initialize networking * bind to ip 0.0.0.0:PORT */ IP ip; ip.i = 0; init_networking(ip, PORT); initNetCrypto(); perror("Initialization"); IP_Port bootstrap_ip_port; bootstrap_ip_port.port = htons(atoi(argv[2])); bootstrap_ip_port.ip.i = inet_addr(argv[1]); DHT_bootstrap(bootstrap_ip_port); IP_Port ip_port; uint8_t data[MAX_UDP_PACKET_SIZE]; uint32_t length; uint8_t buffer1[128]; int read1 = 0; uint8_t buffer2[128]; int read2 = 0; FILE *file1 = fopen(argv[3], "rb"); if ( file1==NULL ){printf("Error opening file.\n");return 1;} FILE *file2 = fopen("received.txt", "wb"); if ( file2==NULL ){return 1;} read1 = fread(buffer1, 1, 128, file1); while(1) { while(receivepacket(&ip_port, data, &length) != -1) { if(rand() % 3 != 1) { /* simulate packet loss */ if(DHT_handlepacket(data, length, ip_port) && LosslessUDP_handlepacket(data, length, ip_port)) { /* if packet is not recognized */ printf("Received unhandled packet with length: %u\n", length); } else { printf("Received handled packet with length: %u\n", length); } } } friend_ip = DHT_getfriendip(friend_id); if(friend_ip.ip.i != 0) { if(connection == -1 && friendrequest == -1) { printf("Sending friend request to peer:"); printip(friend_ip); friendrequest = send_friendrequest(friend_id, friend_ip,(uint8_t *) "Hello World", 12); /* connection = crypto_connect((uint8_t *)friend_id, friend_ip); */ /* connection = new_connection(friend_ip); */ } if(check_friendrequest(friendrequest) == 1) { printf("Started connecting to friend:"); connection = crypto_connect(friend_id, friend_ip); } } if(inconnection == -1) { uint8_t secret_nonce[crypto_box_NONCEBYTES]; uint8_t public_key[crypto_box_PUBLICKEYBYTES]; uint8_t session_key[crypto_box_PUBLICKEYBYTES]; inconnection = crypto_inbound(public_key, secret_nonce, session_key); inconnection = accept_crypto_inbound(inconnection, acceptedfriend_public_key, secret_nonce, session_key); /* inconnection = incoming_connection(); */ if(inconnection != -1) { printf("Someone connected to us:\n"); /* printip(connection_ip(inconnection)); */ } } if(handle_friendrequest(acceptedfriend_public_key, request_data) > 1) { printf("RECIEVED FRIEND REQUEST: %s\n", request_data); } /* if someone connected to us write what he sends to a file * also send him our file. */ if(inconnection != -1) { if(write_cryptpacket(inconnection, buffer1, read1)) { printf("Wrote data1.\n"); read1 = fread(buffer1, 1, 128, file1); } read2 = read_cryptpacket(inconnection, buffer2); if(read2 != 0) { printf("Received data1.\n"); if(!fwrite(buffer2, read2, 1, file2)) { printf("file write error1\n"); } if(read2 < 128) { printf("Closed file1 %u\n", read2); fclose(file2); } } /* if buffer is empty and the connection timed out. */ else if(is_cryptoconnected(inconnection) == 4) { crypto_kill(inconnection); } } /* if we are connected to a friend send him data from the file. * also put what he sends us in a file. */ if(is_cryptoconnected(connection) >= 3) { if(write_cryptpacket(0, buffer1, read1)) { printf("Wrote data2.\n"); read1 = fread(buffer1, 1, 128, file1); } read2 = read_cryptpacket(0, buffer2); if(read2 != 0) { printf("Received data2.\n"); if(!fwrite(buffer2, read2, 1, file2)) { printf("file write error2\n"); } if(read2 < 128) { printf("Closed file2 %u\n", read2); fclose(file2); } } /* if buffer is empty and the connection timed out. */ else if(is_cryptoconnected(connection) == 4) { crypto_kill(connection); } } doDHT(); doLossless_UDP(); doNetCrypto(); /*print_clientlist(); *print_friendlist(); *c_sleep(300); */ c_sleep(1); } shutdown_networking(); return 0; }
//TODO: make this function not suck. static void doFriends(void) { /* TODO: add incoming connections and some other stuff. */ uint32_t i; int len; uint8_t temp[MAX_DATA_SIZE]; for (i = 0; i < numfriends; ++i) { if (friendlist[i].status == FRIEND_ADDED) { int fr = send_friendrequest(friendlist[i].client_id, friendlist[i].info, friendlist[i].info_size); if (fr == 0) /* TODO: This needs to be fixed so that it sends the friend requests a couple of times in case of packet loss */ set_friend_status(i, FRIEND_REQUESTED); else if (fr > 0) set_friend_status(i, FRIEND_REQUESTED); } if (friendlist[i].status == FRIEND_REQUESTED || friendlist[i].status == FRIEND_CONFIRMED) { /* friend is not online */ if (friendlist[i].status == FRIEND_REQUESTED) { if (friendlist[i].friend_request_id + 10 < unix_time()) { /*I know this is hackish but it should work.*/ send_friendrequest(friendlist[i].client_id, friendlist[i].info, friendlist[i].info_size); friendlist[i].friend_request_id = unix_time(); } } IP_Port friendip = DHT_getfriendip(friendlist[i].client_id); switch (is_cryptoconnected(friendlist[i].crypt_connection_id)) { case 0: if (friendip.ip.i > 1) friendlist[i].crypt_connection_id = crypto_connect(friendlist[i].client_id, friendip); break; case 3: /* Connection is established */ set_friend_status(i, FRIEND_ONLINE); friendlist[i].name_sent = 0; friendlist[i].userstatus_sent = 0; friendlist[i].statusmessage_sent = 0; break; case 4: crypto_kill(friendlist[i].crypt_connection_id); friendlist[i].crypt_connection_id = -1; break; default: break; } } while (friendlist[i].status == FRIEND_ONLINE) { /* friend is online */ if (friendlist[i].name_sent == 0) { if (m_sendname(i, self_name, self_name_length)) friendlist[i].name_sent = 1; } if (friendlist[i].statusmessage_sent == 0) { if (send_statusmessage(i, self_statusmessage, self_statusmessage_length)) friendlist[i].statusmessage_sent = 1; } if (friendlist[i].userstatus_sent == 0) { if (send_userstatus(i, self_userstatus)) friendlist[i].userstatus_sent = 1; } len = read_cryptpacket(friendlist[i].crypt_connection_id, temp); uint8_t packet_id = temp[0]; uint8_t* data = temp + 1; int data_length = len - 1; if (len > 0) { switch (packet_id) { case PACKET_ID_NICKNAME: { if (data_length >= MAX_NAME_LENGTH || data_length == 0) break; if(friend_namechange_isset) friend_namechange(i, data, data_length); memcpy(friendlist[i].name, data, data_length); friendlist[i].name[data_length - 1] = 0; /* make sure the NULL terminator is present. */ break; } case PACKET_ID_STATUSMESSAGE: { if (data_length == 0) break; uint8_t *status = calloc(MIN(data_length, MAX_STATUSMESSAGE_LENGTH), 1); memcpy(status, data, MIN(data_length, MAX_STATUSMESSAGE_LENGTH)); if (friend_statusmessagechange_isset) friend_statusmessagechange(i, status, MIN(data_length, MAX_STATUSMESSAGE_LENGTH)); set_friend_statusmessage(i, status, MIN(data_length, MAX_STATUSMESSAGE_LENGTH)); free(status); break; } case PACKET_ID_USERSTATUS: { if (data_length != 1) break; USERSTATUS status = data[0]; if (friend_userstatuschange_isset) friend_userstatuschange(i, status); set_friend_userstatus(i, status); break; } case PACKET_ID_MESSAGE: { uint8_t *message_id = data; uint8_t message_id_length = 4; uint8_t *message = data + message_id_length; uint16_t message_length = data_length - message_id_length; if (friendlist[i].receives_read_receipts) { write_cryptpacket_id(i, PACKET_ID_RECEIPT, message_id, message_id_length); } if (friend_message_isset) (*friend_message)(i, message, message_length); break; } case PACKET_ID_ACTION: { if (friend_action_isset) (*friend_action)(i, data, data_length); break; } case PACKET_ID_RECEIPT: { uint32_t msgid; if (data_length < sizeof(msgid)) break; memcpy(&msgid, data, sizeof(msgid)); msgid = ntohl(msgid); if (read_receipt_isset) (*read_receipt)(i, msgid); break; } } } else { if (is_cryptoconnected(friendlist[i].crypt_connection_id) == 4) { /* if the connection timed out, kill it */ crypto_kill(friendlist[i].crypt_connection_id); friendlist[i].crypt_connection_id = -1; set_friend_status(i, FRIEND_CONFIRMED); } break; } } } }
//TODO: make this function not suck. static void doFriends() { /* TODO: add incoming connections and some other stuff. */ uint32_t i; int len; uint8_t temp[MAX_DATA_SIZE]; for (i = 0; i < numfriends; ++i) { if (friendlist[i].status == 1) { int fr = send_friendrequest(friendlist[i].client_id, friendlist[i].info, friendlist[i].info_size); if (fr == 0) /* TODO: This needs to be fixed so that it sends the friend requests a couple of times in case of packet loss */ friendlist[i].status = 2; else if (fr > 0) friendlist[i].status = 2; } if (friendlist[i].status == 2 || friendlist[i].status == 3) { /* friend is not online */ if (friendlist[i].status == 2) { if (friendlist[i].friend_request_id + 10 < unix_time()) { /*I know this is hackish but it should work.*/ send_friendrequest(friendlist[i].client_id, friendlist[i].info, friendlist[i].info_size); friendlist[i].friend_request_id = unix_time(); } } IP_Port friendip = DHT_getfriendip(friendlist[i].client_id); switch (is_cryptoconnected(friendlist[i].crypt_connection_id)) { case 0: if (friendip.ip.i > 1) friendlist[i].crypt_connection_id = crypto_connect(friendlist[i].client_id, friendip); break; case 3: /* Connection is established */ friendlist[i].status = 4; break; case 4: crypto_kill(friendlist[i].crypt_connection_id); friendlist[i].crypt_connection_id = -1; break; default: break; } } while (friendlist[i].status == 4) { /* friend is online */ if (friendlist[i].name_sent == 0) { if (m_sendname(i, self_name, self_name_length)) friendlist[i].name_sent = 1; } if (friendlist[i].userstatus_sent == 0) { if (send_userstatus(i, self_userstatus, self_userstatus_len)) friendlist[i].userstatus_sent = 1; } len = read_cryptpacket(friendlist[i].crypt_connection_id, temp); if (len > 0) { switch (temp[0]) { case PACKET_ID_NICKNAME: { if (len >= MAX_NAME_LENGTH + 1 || len == 1) break; if(friend_namechange_isset) friend_namechange(i, temp + 1, len - 1); memcpy(friendlist[i].name, temp + 1, len - 1); friendlist[i].name[len - 2] = 0; /* make sure the NULL terminator is present. */ break; } case PACKET_ID_USERSTATUS: { uint8_t *status = calloc(MIN(len - 1, MAX_USERSTATUS_LENGTH), 1); memcpy(status, temp + 1, MIN(len - 1, MAX_USERSTATUS_LENGTH)); if (friend_statuschange_isset) friend_statuschange(i, status, MIN(len - 1, MAX_USERSTATUS_LENGTH)); set_friend_userstatus(i, status, MIN(len - 1, MAX_USERSTATUS_LENGTH)); free(status); break; } case PACKET_ID_MESSAGE: { if (friend_message_isset) (*friend_message)(i, temp + 1, len - 1); break; } } } else { if (is_cryptoconnected(friendlist[i].crypt_connection_id) == 4) { /* if the connection timed out, kill it */ crypto_kill(friendlist[i].crypt_connection_id); friendlist[i].crypt_connection_id = -1; friendlist[i].status = 3; } break; } } } }
//TODO: make this function not suck. void doFriends(Messenger *m) { /* TODO: add incoming connections and some other stuff. */ uint32_t i; int len; uint8_t temp[MAX_DATA_SIZE]; uint64_t temp_time = unix_time(); for (i = 0; i < m->numfriends; ++i) { if (m->friendlist[i].status == FRIEND_ADDED) { int fr = send_friendrequest(m->friendlist[i].client_id, m->friendlist[i].friendrequest_nospam, m->friendlist[i].info, m->friendlist[i].info_size); if (fr >= 0) { set_friend_status(m, i, FRIEND_REQUESTED); m->friendlist[i].friendrequest_lastsent = temp_time; } } if (m->friendlist[i].status == FRIEND_REQUESTED || m->friendlist[i].status == FRIEND_CONFIRMED) { /* friend is not online */ if (m->friendlist[i].status == FRIEND_REQUESTED) { /* If we didn't connect to friend after successfully sending him a friend request the request is deemed unsuccessful so we set the status back to FRIEND_ADDED and try again.*/ if (m->friendlist[i].friendrequest_lastsent + m->friendlist[i].friendrequest_timeout < temp_time) { set_friend_status(m, i, FRIEND_ADDED); /* Double the default timeout everytime if friendrequest is assumed to have been sent unsuccessfully. */ m->friendlist[i].friendrequest_timeout *= 2; } } IP_Port friendip = DHT_getfriendip(m->friendlist[i].client_id); switch (is_cryptoconnected(m->friendlist[i].crypt_connection_id)) { case 0: if (friendip.ip.i > 1) m->friendlist[i].crypt_connection_id = crypto_connect(m->friendlist[i].client_id, friendip); break; case 3: /* Connection is established */ set_friend_status(m, i, FRIEND_ONLINE); m->friendlist[i].name_sent = 0; m->friendlist[i].userstatus_sent = 0; m->friendlist[i].statusmessage_sent = 0; m->friendlist[i].ping_lastrecv = temp_time; break; case 4: crypto_kill(m->friendlist[i].crypt_connection_id); m->friendlist[i].crypt_connection_id = -1; break; default: break; } } while (m->friendlist[i].status == FRIEND_ONLINE) { /* friend is online */ if (m->friendlist[i].name_sent == 0) { if (m_sendname(m, i, m->name, m->name_length)) m->friendlist[i].name_sent = 1; } if (m->friendlist[i].statusmessage_sent == 0) { if (send_statusmessage(m, i, m->statusmessage, m->statusmessage_length)) m->friendlist[i].statusmessage_sent = 1; } if (m->friendlist[i].userstatus_sent == 0) { if (send_userstatus(m, i, m->userstatus)) m->friendlist[i].userstatus_sent = 1; } if (m->friendlist[i].ping_lastsent + FRIEND_PING_INTERVAL < temp_time) { send_ping(m, i); } len = read_cryptpacket(m->friendlist[i].crypt_connection_id, temp); uint8_t packet_id = temp[0]; uint8_t *data = temp + 1; int data_length = len - 1; if (len > 0) { switch (packet_id) { case PACKET_ID_PING: { m->friendlist[i].ping_lastrecv = temp_time; break; } case PACKET_ID_NICKNAME: { if (data_length >= MAX_NAME_LENGTH || data_length == 0) break; if (m->friend_namechange) m->friend_namechange(m, i, data, data_length, m->friend_namechange_userdata); memcpy(m->friendlist[i].name, data, data_length); m->friendlist[i].name[data_length - 1] = 0; /* make sure the NULL terminator is present. */ break; } case PACKET_ID_STATUSMESSAGE: { if (data_length == 0) break; uint8_t *status = calloc(MIN(data_length, MAX_STATUSMESSAGE_LENGTH), 1); memcpy(status, data, MIN(data_length, MAX_STATUSMESSAGE_LENGTH)); if (m->friend_statusmessagechange) m->friend_statusmessagechange(m, i, status, MIN(data_length, MAX_STATUSMESSAGE_LENGTH), m->friend_statuschange_userdata); set_friend_statusmessage(m, i, status, MIN(data_length, MAX_STATUSMESSAGE_LENGTH)); free(status); break; } case PACKET_ID_USERSTATUS: { if (data_length != 1) break; USERSTATUS status = data[0]; if (m->friend_userstatuschange) m->friend_userstatuschange(m, i, status, m->friend_userstatuschange_userdata); set_friend_userstatus(m, i, status); break; } case PACKET_ID_MESSAGE: { uint8_t *message_id = data; uint8_t message_id_length = 4; uint8_t *message = data + message_id_length; uint16_t message_length = data_length - message_id_length; if (m->friendlist[i].receives_read_receipts) { write_cryptpacket_id(m, i, PACKET_ID_RECEIPT, message_id, message_id_length); } if (m->friend_message) (*m->friend_message)(m, i, message, message_length, m->friend_message_userdata); break; } case PACKET_ID_ACTION: { if (m->friend_action) (*m->friend_action)(m, i, data, data_length, m->friend_action_userdata); break; } case PACKET_ID_RECEIPT: { uint32_t msgid; if (data_length < sizeof(msgid)) break; memcpy(&msgid, data, sizeof(msgid)); msgid = ntohl(msgid); if (m->read_receipt) (*m->read_receipt)(m, i, msgid, m->read_receipt_userdata); break; } } } else { if (is_cryptoconnected(m->friendlist[i].crypt_connection_id) == 4) { /* if the connection timed out, kill it */ crypto_kill(m->friendlist[i].crypt_connection_id); m->friendlist[i].crypt_connection_id = -1; set_friend_status(m, i, FRIEND_CONFIRMED); } break; } if (m->friendlist[i].ping_lastrecv + FRIEND_CONNECTION_TIMEOUT < temp_time) { /* if we stopped recieving ping packets kill it */ crypto_kill(m->friendlist[i].crypt_connection_id); m->friendlist[i].crypt_connection_id = -1; set_friend_status(m, i, FRIEND_CONFIRMED); } } } }
/* Handle received packets for not yet established crypto connections. */ static void receive_crypto(Net_Crypto *c) { uint32_t i; for (i = 0; i < c->crypto_connections_length; ++i) { if (c->crypto_connections[i].status == CONN_HANDSHAKE_SENT) { uint8_t temp_data[MAX_DATA_SIZE]; uint8_t secret_nonce[crypto_box_NONCEBYTES]; uint8_t public_key[crypto_box_PUBLICKEYBYTES]; uint8_t session_key[crypto_box_PUBLICKEYBYTES]; uint16_t len; if (id_packet(c->lossless_udp, c->crypto_connections[i].number) == 2) { /* Handle handshake packet. */ len = read_packet(c->lossless_udp, c->crypto_connections[i].number, temp_data); if (handle_cryptohandshake(c, public_key, secret_nonce, session_key, temp_data, len)) { if (memcmp(public_key, c->crypto_connections[i].public_key, crypto_box_PUBLICKEYBYTES) == 0) { memcpy(c->crypto_connections[i].sent_nonce, secret_nonce, crypto_box_NONCEBYTES); memcpy(c->crypto_connections[i].peersessionpublic_key, session_key, crypto_box_PUBLICKEYBYTES); increment_nonce(c->crypto_connections[i].sent_nonce); uint32_t zero = 0; encrypt_precompute(c->crypto_connections[i].peersessionpublic_key, c->crypto_connections[i].sessionsecret_key, c->crypto_connections[i].shared_key); c->crypto_connections[i].status = CONN_ESTABLISHED; /* Connection status needs to be 3 for write_cryptpacket() to work. */ write_cryptpacket(c, i, ((uint8_t *)&zero), sizeof(zero)); c->crypto_connections[i].status = CONN_NOT_CONFIRMED; /* Set it to its proper value right after. */ } } } else if (id_packet(c->lossless_udp, c->crypto_connections[i].number) != -1) { // This should not happen, kill the connection if it does. crypto_kill(c, i); return; } } if (c->crypto_connections[i].status == CONN_NOT_CONFIRMED) { if (id_packet(c->lossless_udp, c->crypto_connections[i].number) == 3) { uint8_t temp_data[MAX_DATA_SIZE]; uint8_t data[MAX_DATA_SIZE]; int length = read_packet(c->lossless_udp, c->crypto_connections[i].number, temp_data); int len = decrypt_data(c->crypto_connections[i].peersessionpublic_key, c->crypto_connections[i].sessionsecret_key, c->crypto_connections[i].recv_nonce, temp_data + 1, length - 1, data); uint32_t zero = 0; if (len == sizeof(uint32_t) && memcmp(((uint8_t *)&zero), data, sizeof(uint32_t)) == 0) { increment_nonce(c->crypto_connections[i].recv_nonce); encrypt_precompute(c->crypto_connections[i].peersessionpublic_key, c->crypto_connections[i].sessionsecret_key, c->crypto_connections[i].shared_key); c->crypto_connections[i].status = CONN_ESTABLISHED; /* Connection is accepted so we disable the auto kill by setting it to about 1 month from now. */ kill_connection_in(c->lossless_udp, c->crypto_connections[i].number, 3000000); } else { /* This should not happen, kill the connection if it does. */ crypto_kill(c, i); return; } } else if (id_packet(c->lossless_udp, c->crypto_connections[i].number) != -1) /* This should not happen, kill the connection if it does. */ crypto_kill(c, i); return; } } }