/work/toxcore/friend_requests.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: GPL-3.0-or-later |
2 | | * Copyright © 2016-2025 The TokTok team. |
3 | | * Copyright © 2013 Tox project. |
4 | | */ |
5 | | |
6 | | /** |
7 | | * Handle friend requests. |
8 | | */ |
9 | | #include "friend_requests.h" |
10 | | |
11 | | #include <string.h> |
12 | | |
13 | | #include "attributes.h" |
14 | | #include "ccompat.h" |
15 | | #include "crypto_core.h" |
16 | | #include "friend_connection.h" |
17 | | #include "mem.h" |
18 | | #include "network.h" |
19 | | #include "onion.h" |
20 | | #include "onion_announce.h" |
21 | | #include "onion_client.h" |
22 | | |
23 | | static_assert(ONION_CLIENT_MAX_DATA_SIZE <= MAX_DATA_REQUEST_SIZE, "ONION_CLIENT_MAX_DATA_SIZE is too big"); |
24 | | static_assert(MAX_DATA_REQUEST_SIZE <= ONION_MAX_DATA_SIZE, "MAX_DATA_REQUEST_SIZE is too big"); |
25 | | static_assert(SIZE_IPPORT <= ONION_SEND_BASE, "IP_Port does not fit in the onion packet"); |
26 | | |
27 | | /** |
28 | | * NOTE: The following is just a temporary fix for the multiple friend requests received at the same time problem. |
29 | | * TODO(irungentoo): Make this better (This will most likely tie in with the way we will handle spam). |
30 | | */ |
31 | 9.96k | #define MAX_RECEIVED_STORED 32 |
32 | | |
33 | | struct Received_Requests { |
34 | | uint8_t requests[MAX_RECEIVED_STORED][CRYPTO_PUBLIC_KEY_SIZE]; |
35 | | uint16_t requests_index; |
36 | | }; |
37 | | |
38 | | struct Friend_Requests { |
39 | | const Memory *mem; |
40 | | |
41 | | uint32_t nospam; |
42 | | fr_friend_request_cb *handle_friendrequest; |
43 | | uint8_t handle_friendrequest_isset; |
44 | | void *handle_friendrequest_object; |
45 | | |
46 | | filter_function_cb *filter_function; |
47 | | void *filter_function_userdata; |
48 | | |
49 | | struct Received_Requests received; |
50 | | }; |
51 | | |
52 | | /** Set and get the nospam variable used to prevent one type of friend request spam. */ |
53 | | void set_nospam(Friend_Requests *fr, uint32_t num) |
54 | 3.31k | { |
55 | 3.31k | fr->nospam = num; |
56 | 3.31k | } |
57 | | |
58 | | uint32_t get_nospam(const Friend_Requests *fr) |
59 | 1.22k | { |
60 | 1.22k | return fr->nospam; |
61 | 1.22k | } |
62 | | |
63 | | /** Set the function that will be executed when a friend request for us is received. */ |
64 | | void callback_friendrequest(Friend_Requests *fr, fr_friend_request_cb *function, void *object) |
65 | 2.76k | { |
66 | 2.76k | fr->handle_friendrequest = function; |
67 | 2.76k | fr->handle_friendrequest_isset = 1; |
68 | 2.76k | fr->handle_friendrequest_object = object; |
69 | 2.76k | } |
70 | | |
71 | | /** @brief Set the function used to check if a friend request should be displayed to the user or not. |
72 | | * It must return 0 if the request is ok (anything else if it is bad). |
73 | | */ |
74 | | void set_filter_function(Friend_Requests *fr, filter_function_cb *function, void *userdata) |
75 | 2.76k | { |
76 | 2.76k | fr->filter_function = function; |
77 | 2.76k | fr->filter_function_userdata = userdata; |
78 | 2.76k | } |
79 | | |
80 | | /** Add to list of received friend requests. */ |
81 | | static void addto_receivedlist(Friend_Requests *_Nonnull fr, const uint8_t *_Nonnull real_pk) |
82 | 141 | { |
83 | 141 | if (fr->received.requests_index >= MAX_RECEIVED_STORED) { |
84 | 0 | fr->received.requests_index = 0; |
85 | 0 | } |
86 | | |
87 | 141 | pk_copy(fr->received.requests[fr->received.requests_index], real_pk); |
88 | 141 | ++fr->received.requests_index; |
89 | 141 | } |
90 | | |
91 | | /** @brief Check if a friend request was already received. |
92 | | * |
93 | | * @retval false if it did not. |
94 | | * @retval true if it did. |
95 | | */ |
96 | | static bool request_received(const Friend_Requests *_Nonnull fr, const uint8_t *_Nonnull real_pk) |
97 | 266 | { |
98 | 5.83k | for (uint32_t i = 0; i < MAX_RECEIVED_STORED; ++i) { |
99 | 5.69k | if (pk_equal(fr->received.requests[i], real_pk)) { |
100 | 125 | return true; |
101 | 125 | } |
102 | 5.69k | } |
103 | | |
104 | 141 | return false; |
105 | 266 | } |
106 | | |
107 | | /** @brief Remove real_pk from received_requests list. |
108 | | * |
109 | | * @retval 0 if it removed it successfully. |
110 | | * @retval -1 if it didn't find it. |
111 | | */ |
112 | | int remove_request_received(Friend_Requests *_Nonnull fr, const uint8_t *_Nonnull real_pk) |
113 | 121 | { |
114 | 3.99k | for (uint32_t i = 0; i < MAX_RECEIVED_STORED; ++i) { |
115 | 3.87k | if (pk_equal(fr->received.requests[i], real_pk)) { |
116 | 0 | crypto_memzero(fr->received.requests[i], CRYPTO_PUBLIC_KEY_SIZE); |
117 | 0 | return 0; |
118 | 0 | } |
119 | 3.87k | } |
120 | | |
121 | 121 | return -1; |
122 | 121 | } |
123 | | |
124 | | static int friendreq_handlepacket(void *_Nonnull object, const uint8_t *_Nonnull source_pubkey, const uint8_t *_Nonnull data, uint16_t length, void *_Nonnull userdata) |
125 | 266 | { |
126 | 266 | Friend_Requests *const fr = (Friend_Requests *)object; |
127 | | |
128 | 266 | if (length <= 1 + sizeof(fr->nospam) || length > ONION_CLIENT_MAX_DATA_SIZE) { |
129 | 0 | return 1; |
130 | 0 | } |
131 | | |
132 | 266 | ++data; |
133 | 266 | --length; |
134 | | |
135 | 266 | if (fr->handle_friendrequest_isset == 0) { |
136 | 0 | return 1; |
137 | 0 | } |
138 | | |
139 | 266 | if (request_received(fr, source_pubkey)) { |
140 | 125 | return 1; |
141 | 125 | } |
142 | | |
143 | 141 | if (memcmp(data, &fr->nospam, sizeof(fr->nospam)) != 0) { |
144 | 0 | return 1; |
145 | 0 | } |
146 | | |
147 | 141 | if (fr->filter_function != nullptr) { |
148 | 141 | if (fr->filter_function(fr->filter_function_userdata, source_pubkey) != 0) { |
149 | 0 | return 1; |
150 | 0 | } |
151 | 141 | } |
152 | | |
153 | 141 | addto_receivedlist(fr, source_pubkey); |
154 | | |
155 | 141 | const uint16_t message_len = length - sizeof(fr->nospam); |
156 | 141 | VLA(uint8_t, message, message_len + 1); |
157 | 141 | memcpy(message, data + sizeof(fr->nospam), message_len); |
158 | 141 | message[message_len] = 0; /* Be sure the message is null terminated. TODO(iphydf): But why? */ |
159 | | |
160 | 141 | fr->handle_friendrequest(fr->handle_friendrequest_object, source_pubkey, message, message_len, userdata); |
161 | 141 | return 0; |
162 | 141 | } |
163 | | |
164 | | void friendreq_init(Friend_Requests *fr, Friend_Connections *fr_c) |
165 | 2.76k | { |
166 | 2.76k | set_friend_request_callback(fr_c, &friendreq_handlepacket, fr); |
167 | 2.76k | } |
168 | | |
169 | | Friend_Requests *friendreq_new(const Memory *mem) |
170 | 3.16k | { |
171 | 3.16k | Friend_Requests *fr = (Friend_Requests *)mem_alloc(mem, sizeof(Friend_Requests)); |
172 | | |
173 | 3.16k | if (fr == nullptr) { |
174 | 10 | return nullptr; |
175 | 10 | } |
176 | | |
177 | 3.15k | fr->mem = mem; |
178 | | |
179 | 3.15k | return fr; |
180 | 3.16k | } |
181 | | |
182 | | void friendreq_kill(Friend_Requests *fr) |
183 | 2.27k | { |
184 | 2.27k | if (fr == nullptr) { |
185 | 0 | return; |
186 | 0 | } |
187 | | |
188 | 2.27k | mem_delete(fr->mem, fr); |
189 | 2.27k | } |