diff options
Diffstat (limited to 'core/DHT.c')
-rw-r--r-- | core/DHT.c | 367 |
1 files changed, 325 insertions, 42 deletions
@@ -46,6 +46,12 @@ typedef struct | |||
46 | Client_data client_list[MAX_FRIEND_CLIENTS]; | 46 | Client_data client_list[MAX_FRIEND_CLIENTS]; |
47 | uint32_t lastgetnode; /* time at which the last get_nodes request was sent. */ | 47 | uint32_t lastgetnode; /* time at which the last get_nodes request was sent. */ |
48 | 48 | ||
49 | /*Symetric NAT hole punching stuff*/ | ||
50 | uint8_t hole_punching; /*0 if not hole punching, 1 if currently hole punching */ | ||
51 | uint32_t punching_index; | ||
52 | uint32_t punching_timestamp; | ||
53 | uint64_t NATping_id; | ||
54 | uint32_t NATping_timestamp; | ||
49 | }Friend; | 55 | }Friend; |
50 | 56 | ||
51 | 57 | ||
@@ -78,7 +84,7 @@ static Friend * friends_list; | |||
78 | static uint16_t num_friends; | 84 | static uint16_t num_friends; |
79 | 85 | ||
80 | /* The list of ip ports along with the ping_id of what we sent them and a timestamp */ | 86 | /* The list of ip ports along with the ping_id of what we sent them and a timestamp */ |
81 | #define LPING_ARRAY 128 | 87 | #define LPING_ARRAY 256 |
82 | 88 | ||
83 | static Pinged pings[LPING_ARRAY]; | 89 | static Pinged pings[LPING_ARRAY]; |
84 | 90 | ||
@@ -160,10 +166,24 @@ int client_in_nodelist(Node_format * list, uint32_t length, uint8_t * client_id) | |||
160 | 166 | ||
161 | } | 167 | } |
162 | 168 | ||
169 | /*Return the friend number from the client_id | ||
170 | Return -1 if failure, number of friend if success*/ | ||
171 | static int friend_number(uint8_t * client_id) | ||
172 | { | ||
173 | uint32_t i; | ||
174 | for(i = 0; i < num_friends; ++i) | ||
175 | { | ||
176 | if(memcmp(friends_list[i].client_id, client_id, CLIENT_ID_SIZE) == 0) /* Equal */ | ||
177 | { | ||
178 | return i; | ||
179 | } | ||
180 | } | ||
181 | return -1; | ||
182 | } | ||
163 | 183 | ||
164 | 184 | ||
165 | /* the number of seconds for a non responsive node to become bad. */ | 185 | /* the number of seconds for a non responsive node to become bad. */ |
166 | #define BAD_NODE_TIMEOUT 130 | 186 | #define BAD_NODE_TIMEOUT 70 |
167 | /* the max number of nodes to send with send nodes. */ | 187 | /* the max number of nodes to send with send nodes. */ |
168 | #define MAX_SENT_NODES 8 | 188 | #define MAX_SENT_NODES 8 |
169 | 189 | ||
@@ -809,6 +829,7 @@ int DHT_addfriend(uint8_t * client_id) | |||
809 | friends_list = temp; | 829 | friends_list = temp; |
810 | memset(&friends_list[num_friends], 0, sizeof(Friend)); | 830 | memset(&friends_list[num_friends], 0, sizeof(Friend)); |
811 | memcpy(friends_list[num_friends].client_id, client_id, CLIENT_ID_SIZE); | 831 | memcpy(friends_list[num_friends].client_id, client_id, CLIENT_ID_SIZE); |
832 | friends_list[num_friends].NATping_id = ((uint64_t)random_int() << 32) + random_int(); | ||
812 | ++num_friends; | 833 | ++num_friends; |
813 | return 0; | 834 | return 0; |
814 | } | 835 | } |
@@ -872,30 +893,6 @@ IP_Port DHT_getfriendip(uint8_t * client_id) | |||
872 | 893 | ||
873 | 894 | ||
874 | 895 | ||
875 | int DHT_handlepacket(uint8_t * packet, uint32_t length, IP_Port source) | ||
876 | { | ||
877 | switch (packet[0]) { | ||
878 | case 0: | ||
879 | return handle_pingreq(packet, length, source); | ||
880 | |||
881 | case 1: | ||
882 | return handle_pingres(packet, length, source); | ||
883 | |||
884 | case 2: | ||
885 | return handle_getnodes(packet, length, source); | ||
886 | |||
887 | case 3: | ||
888 | return handle_sendnodes(packet, length, source); | ||
889 | |||
890 | default: | ||
891 | return 1; | ||
892 | |||
893 | } | ||
894 | |||
895 | return 0; | ||
896 | |||
897 | } | ||
898 | |||
899 | /* The timeout after which a node is discarded completely. */ | 896 | /* The timeout after which a node is discarded completely. */ |
900 | #define Kill_NODE_TIMEOUT 300 | 897 | #define Kill_NODE_TIMEOUT 300 |
901 | 898 | ||
@@ -988,11 +985,6 @@ void doClose() /* tested */ | |||
988 | 985 | ||
989 | 986 | ||
990 | 987 | ||
991 | void doDHT() | ||
992 | { | ||
993 | doClose(); | ||
994 | doDHTFriends(); | ||
995 | } | ||
996 | 988 | ||
997 | 989 | ||
998 | 990 | ||
@@ -1018,6 +1010,39 @@ int route_packet(uint8_t * client_id, uint8_t * packet, uint32_t length) | |||
1018 | return -1; | 1010 | return -1; |
1019 | } | 1011 | } |
1020 | 1012 | ||
1013 | |||
1014 | /* Puts all the different ips returned by the nodes for a friend_num into array ip_portlist | ||
1015 | ip_portlist must be at least MAX_FRIEND_CLIENTS big | ||
1016 | returns the number of ips returned | ||
1017 | return 0 if we are connected to friend or if no ips were found. | ||
1018 | returns -1 if no such friend*/ | ||
1019 | static int friend_iplist(IP_Port * ip_portlist, uint16_t friend_num) | ||
1020 | { | ||
1021 | int num_ips = 0; | ||
1022 | uint32_t i; | ||
1023 | uint32_t temp_time = unix_time(); | ||
1024 | if(friend_num >= num_friends) | ||
1025 | { | ||
1026 | return -1; | ||
1027 | } | ||
1028 | for(i = 0; i < MAX_FRIEND_CLIENTS; ++i) | ||
1029 | { | ||
1030 | if(friends_list[friend_num].client_list[i].ret_ip_port.ip.i != 0 && | ||
1031 | friends_list[friend_num].client_list[i].ret_timestamp + BAD_NODE_TIMEOUT > temp_time) | ||
1032 | /*If ip is not zero and node is good */ | ||
1033 | { | ||
1034 | if(memcmp(friends_list[friend_num].client_list[i].client_id, friends_list[friend_num].client_id, CLIENT_ID_SIZE) == 0 ) | ||
1035 | { | ||
1036 | return 0; | ||
1037 | } | ||
1038 | ip_portlist[num_ips] = friends_list[friend_num].client_list[i].ret_ip_port; | ||
1039 | ++num_ips; | ||
1040 | } | ||
1041 | } | ||
1042 | return num_ips; | ||
1043 | } | ||
1044 | |||
1045 | |||
1021 | /* Send the following packet to everyone who tells us they are connected to friend_id | 1046 | /* Send the following packet to everyone who tells us they are connected to friend_id |
1022 | returns the number of nodes it sent the packet to */ | 1047 | returns the number of nodes it sent the packet to */ |
1023 | int route_tofriend(uint8_t * friend_id, uint8_t * packet, uint32_t length) | 1048 | int route_tofriend(uint8_t * friend_id, uint8_t * packet, uint32_t length) |
@@ -1047,36 +1072,294 @@ int route_tofriend(uint8_t * friend_id, uint8_t * packet, uint32_t length) | |||
1047 | return 0; | 1072 | return 0; |
1048 | } | 1073 | } |
1049 | 1074 | ||
1075 | /* Send the following packet to one random person who tells us they are connected to friend_id | ||
1076 | returns the number of nodes it sent the packet to */ | ||
1077 | int routeone_tofriend(uint8_t * friend_id, uint8_t * packet, uint32_t length) | ||
1078 | { | ||
1079 | int num = friend_number(friend_id); | ||
1080 | if(num == -1) | ||
1081 | { | ||
1082 | return 0; | ||
1083 | } | ||
1084 | |||
1085 | IP_Port ip_list[MAX_FRIEND_CLIENTS]; | ||
1086 | int n = 0; | ||
1087 | uint32_t i; | ||
1088 | uint32_t temp_time = unix_time(); | ||
1089 | for(i = 0; i < MAX_FRIEND_CLIENTS; ++i) | ||
1090 | { | ||
1091 | if(friends_list[num].client_list[i].ret_ip_port.ip.i != 0 && | ||
1092 | friends_list[num].client_list[i].ret_timestamp + BAD_NODE_TIMEOUT > temp_time) | ||
1093 | /*If ip is not zero and node is good */ | ||
1094 | { | ||
1095 | ip_list[n] = friends_list[num].client_list[i].ip_port; | ||
1096 | ++n; | ||
1097 | } | ||
1098 | } | ||
1099 | if(n < 1) | ||
1100 | { | ||
1101 | return 0; | ||
1102 | } | ||
1103 | if(sendpacket(ip_list[rand() % n], packet, length) == length) | ||
1104 | { | ||
1105 | return 1; | ||
1106 | } | ||
1107 | return 0; | ||
1108 | } | ||
1109 | |||
1050 | /* Puts all the different ips returned by the nodes for a friend_id into array ip_portlist | 1110 | /* Puts all the different ips returned by the nodes for a friend_id into array ip_portlist |
1051 | ip_portlist must be at least MAX_FRIEND_CLIENTS big | 1111 | ip_portlist must be at least MAX_FRIEND_CLIENTS big |
1052 | returns the number of ips returned | 1112 | returns the number of ips returned |
1113 | return 0 if we are connected to friend or if no ips were found. | ||
1053 | returns -1 if no such friend*/ | 1114 | returns -1 if no such friend*/ |
1054 | int friend_ips(IP_Port * ip_portlist, uint8_t * friend_id) | 1115 | int friend_ips(IP_Port * ip_portlist, uint8_t * friend_id) |
1055 | { | 1116 | { |
1056 | int num_ips = 0; | 1117 | |
1118 | uint32_t i; | ||
1119 | for(i = 0; i < num_friends; ++i) | ||
1120 | { | ||
1121 | if(memcmp(friends_list[i].client_id, friend_id, CLIENT_ID_SIZE) == 0) /* Equal */ | ||
1122 | { | ||
1123 | return friend_iplist(ip_portlist, i); | ||
1124 | } | ||
1125 | } | ||
1126 | return -1; | ||
1127 | } | ||
1128 | |||
1129 | /*BEGINNING OF NAT PUNCHING FUNCTIONS*/ | ||
1130 | |||
1131 | int send_NATping(uint8_t * public_key, uint64_t ping_id, uint8_t type) | ||
1132 | { | ||
1133 | uint8_t data[sizeof(uint64_t) + 1]; | ||
1134 | data[0] = type; | ||
1135 | memcpy(data + 1, &ping_id, sizeof(uint64_t)); | ||
1136 | |||
1137 | uint8_t packet[MAX_DATA_SIZE]; | ||
1138 | int len = create_request(packet, public_key, data, sizeof(uint64_t) + 1, 254); /* 254 is NAT ping request packet id */ | ||
1139 | if(len == -1) | ||
1140 | { | ||
1141 | return -1; | ||
1142 | } | ||
1143 | int num = 0; | ||
1144 | if(type == 0) | ||
1145 | { | ||
1146 | num = route_tofriend(public_key, packet, len);/*If packet is request use many people to route it*/ | ||
1147 | } | ||
1148 | else if(type == 1) | ||
1149 | { | ||
1150 | num = routeone_tofriend(public_key, packet, len);/*If packet is response use only one person to route it*/ | ||
1151 | } | ||
1152 | if(num == 0) | ||
1153 | { | ||
1154 | return -1; | ||
1155 | } | ||
1156 | return num; | ||
1157 | } | ||
1158 | |||
1159 | |||
1160 | /* Handle a recieved ping request for */ | ||
1161 | int handle_NATping(uint8_t * packet, uint32_t length, IP_Port source) | ||
1162 | { | ||
1163 | if(length <= crypto_box_PUBLICKEYBYTES * 2 + crypto_box_NONCEBYTES + 1 + ENCRYPTION_PADDING && | ||
1164 | length > MAX_DATA_SIZE + ENCRYPTION_PADDING) | ||
1165 | { | ||
1166 | return 1; | ||
1167 | } | ||
1168 | if(memcmp(packet + 1, self_public_key, crypto_box_PUBLICKEYBYTES) == 0)//check if request is for us. | ||
1169 | { | ||
1170 | uint8_t public_key[crypto_box_PUBLICKEYBYTES]; | ||
1171 | uint8_t data[MAX_DATA_SIZE]; | ||
1172 | int len = handle_request(public_key, data, packet, length); | ||
1173 | if(len != sizeof(uint64_t) + 1) | ||
1174 | { | ||
1175 | return 1; | ||
1176 | } | ||
1177 | uint64_t ping_id; | ||
1178 | memcpy(&ping_id, data + 1, sizeof(uint64_t)); | ||
1179 | |||
1180 | int friendnumber = friend_number(public_key); | ||
1181 | if(friendnumber == -1) | ||
1182 | { | ||
1183 | return 1; | ||
1184 | } | ||
1185 | |||
1186 | if(data[0] == 0) | ||
1187 | { | ||
1188 | send_NATping(public_key, ping_id, 1);/*1 is reply*/ | ||
1189 | return 0; | ||
1190 | } | ||
1191 | else if (data[0] == 1) | ||
1192 | { | ||
1193 | if(friends_list[friendnumber].NATping_id == ping_id) | ||
1194 | { | ||
1195 | friends_list[friendnumber].NATping_id = ((uint64_t)random_int() << 32) + random_int(); | ||
1196 | friends_list[friendnumber].hole_punching = 1; | ||
1197 | return 0; | ||
1198 | } | ||
1199 | } | ||
1200 | return 1; | ||
1201 | } | ||
1202 | else//if request is not for us, try routing it. | ||
1203 | { | ||
1204 | if(route_packet(packet + 1, packet, length) == length) | ||
1205 | { | ||
1206 | return 0; | ||
1207 | } | ||
1208 | } | ||
1209 | return 0; | ||
1210 | } | ||
1211 | |||
1212 | /*Get the most common ip in the ip_portlist | ||
1213 | Only return ip if it appears in list min_num or more | ||
1214 | len must not be bigger than MAX_FRIEND_CLIENTS | ||
1215 | return ip of 0 if failure */ | ||
1216 | static IP NAT_commonip(IP_Port * ip_portlist, uint16_t len, uint16_t min_num) | ||
1217 | { | ||
1218 | IP zero = {{0}}; | ||
1219 | if(len > MAX_FRIEND_CLIENTS) | ||
1220 | { | ||
1221 | return zero; | ||
1222 | } | ||
1223 | |||
1057 | uint32_t i, j; | 1224 | uint32_t i, j; |
1225 | uint16_t numbers[MAX_FRIEND_CLIENTS] = {0}; | ||
1226 | for(i = 0; i < len; ++i) | ||
1227 | { | ||
1228 | for(j = 0; j < len; ++j) | ||
1229 | { | ||
1230 | if(ip_portlist[i].ip.i == ip_portlist[j].ip.i) | ||
1231 | { | ||
1232 | ++numbers[i]; | ||
1233 | } | ||
1234 | } | ||
1235 | if(numbers[i] >= min_num) | ||
1236 | { | ||
1237 | return ip_portlist[i].ip; | ||
1238 | } | ||
1239 | } | ||
1240 | return zero; | ||
1241 | } | ||
1242 | |||
1243 | /*Return all the ports for one ip in a list | ||
1244 | portlist must be at least len long | ||
1245 | where len is the length of ip_portlist | ||
1246 | returns the number of ports and puts the list of ports in portlist*/ | ||
1247 | static uint16_t NAT_getports(uint16_t * portlist, IP_Port * ip_portlist, uint16_t len, IP ip) | ||
1248 | { | ||
1249 | uint32_t i; | ||
1250 | uint16_t num = 0; | ||
1251 | for(i = 0; i < len; ++i) | ||
1252 | { | ||
1253 | if(ip_portlist[i].ip.i == ip.i) | ||
1254 | { | ||
1255 | portlist[num] = ntohs(ip_portlist[i].port); | ||
1256 | ++num; | ||
1257 | } | ||
1258 | } | ||
1259 | return num; | ||
1260 | } | ||
1261 | |||
1262 | #define MAX_PUNCHING_PORTS 32 | ||
1263 | |||
1264 | static void punch_holes(IP ip, uint16_t * port_list, uint16_t numports, uint16_t friend_num) | ||
1265 | { | ||
1266 | if(numports > MAX_FRIEND_CLIENTS || numports == 0) | ||
1267 | { | ||
1268 | return; | ||
1269 | } | ||
1270 | uint32_t i; | ||
1271 | uint32_t top = friends_list[friend_num].punching_index + MAX_PUNCHING_PORTS; | ||
1272 | for(i = friends_list[friend_num].punching_index; i != top; i++) | ||
1273 | { | ||
1274 | /*TODO: improve port guessing algorithm*/ | ||
1275 | uint16_t port = port_list[(i/2) % numports] + (i/(2*numports))*((i % 2) ? -1 : 1); | ||
1276 | IP_Port pinging = {ip, htons(port)}; | ||
1277 | pingreq(pinging, friends_list[friend_num].client_id); | ||
1278 | } | ||
1279 | friends_list[friend_num].punching_index = i; | ||
1280 | } | ||
1281 | |||
1282 | /*Interval in seconds between punching attempts*/ | ||
1283 | #define PUNCH_INTERVAL 10 | ||
1284 | |||
1285 | static void doNAT() | ||
1286 | { | ||
1287 | uint32_t i; | ||
1058 | uint32_t temp_time = unix_time(); | 1288 | uint32_t temp_time = unix_time(); |
1059 | for(i = 0; i < num_friends; ++i) | 1289 | for(i = 0; i < num_friends; ++i) |
1060 | { | 1290 | { |
1061 | if(memcmp(friends_list[i].client_id, friend_id, CLIENT_ID_SIZE) == 0) /* Equal */ | 1291 | IP_Port ip_list[MAX_FRIEND_CLIENTS]; |
1292 | int num = friend_iplist(ip_list, i); | ||
1293 | /*If we are connected to friend or if friend is not online don't try to hole punch with him*/ | ||
1294 | if(num < MAX_FRIEND_CLIENTS/2) | ||
1062 | { | 1295 | { |
1063 | for(j = 0; j < MAX_FRIEND_CLIENTS; ++j) | 1296 | continue; |
1297 | } | ||
1298 | if(friends_list[i].hole_punching != 1) | ||
1299 | { | ||
1300 | if(friends_list[i].NATping_timestamp + PUNCH_INTERVAL < temp_time) | ||
1064 | { | 1301 | { |
1065 | if(friends_list[i].client_list[j].ret_ip_port.ip.i != 0 && | 1302 | send_NATping(friends_list[i].client_id, friends_list[i].NATping_id, 0); /*0 is request*/ |
1066 | friends_list[i].client_list[j].ret_timestamp + BAD_NODE_TIMEOUT > temp_time) | 1303 | friends_list[i].NATping_timestamp = temp_time; |
1067 | /*If ip is not zero and node is good */ | ||
1068 | { | ||
1069 | ip_portlist[num_ips] = friends_list[i].client_list[j].ret_ip_port; | ||
1070 | ++num_ips; | ||
1071 | } | ||
1072 | } | 1304 | } |
1073 | return num_ips; | ||
1074 | } | 1305 | } |
1306 | else if(friends_list[i].punching_timestamp + PUNCH_INTERVAL < temp_time) | ||
1307 | { | ||
1308 | IP ip = NAT_commonip(ip_list, num, MAX_FRIEND_CLIENTS/2); | ||
1309 | if(ip.i == 0) | ||
1310 | { | ||
1311 | continue; | ||
1312 | } | ||
1313 | uint16_t port_list[MAX_FRIEND_CLIENTS]; | ||
1314 | uint16_t numports = NAT_getports(port_list, ip_list, num, ip); | ||
1315 | punch_holes(ip, port_list, numports, i); | ||
1316 | |||
1317 | friends_list[i].punching_timestamp = temp_time; | ||
1318 | friends_list[i].hole_punching = 0; | ||
1319 | } | ||
1320 | } | ||
1321 | } | ||
1322 | |||
1323 | /*END OF NAT PUNCHING FUNCTIONS*/ | ||
1324 | |||
1325 | |||
1326 | int DHT_handlepacket(uint8_t * packet, uint32_t length, IP_Port source) | ||
1327 | { | ||
1328 | switch (packet[0]) { | ||
1329 | case 0: | ||
1330 | return handle_pingreq(packet, length, source); | ||
1331 | |||
1332 | case 1: | ||
1333 | return handle_pingres(packet, length, source); | ||
1334 | |||
1335 | case 2: | ||
1336 | return handle_getnodes(packet, length, source); | ||
1337 | |||
1338 | case 3: | ||
1339 | return handle_sendnodes(packet, length, source); | ||
1340 | |||
1341 | case 254: | ||
1342 | return handle_NATping(packet, length, source); | ||
1343 | |||
1344 | default: | ||
1345 | return 1; | ||
1075 | 1346 | ||
1076 | } | 1347 | } |
1348 | |||
1077 | return 0; | 1349 | return 0; |
1350 | |||
1078 | } | 1351 | } |
1079 | 1352 | ||
1353 | |||
1354 | void doDHT() | ||
1355 | { | ||
1356 | doClose(); | ||
1357 | doDHTFriends(); | ||
1358 | doNAT(); | ||
1359 | } | ||
1360 | |||
1361 | |||
1362 | |||
1080 | /* get the size of the DHT (for saving) */ | 1363 | /* get the size of the DHT (for saving) */ |
1081 | uint32_t DHT_size() | 1364 | uint32_t DHT_size() |
1082 | { | 1365 | { |