GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/portmapping.c
Date: 2025-12-12 23:40:09
Exec Total Coverage
Lines: 133 221 60.2%
Functions: 7 8 87.5%
Branches: 59 103 57.3%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/tech/portmapping.h"
30 #include "gate/debugging.h"
31
32 typedef struct gate_portmapper_connection_class
33 {
34 gate_uint32_t connection_id;
35 gate_uint32_t service_id;
36
37 gate_channel_id_t source_id;
38 gate_bool_t source_read_closed;
39 gate_bool_t source_write_closed;
40 gate_bool_t source_error;
41
42 gate_bool_t destination_connected;
43 gate_channel_id_t destination_id;
44 gate_bool_t destination_read_closed;
45 gate_bool_t destination_write_closed;
46 gate_bool_t destination_error;
47 } gate_portmapper_connection_t;
48
49 typedef struct gate_portmapper_service_class
50 {
51 gate_uint32_t service_id;
52 gate_channel_id_t server;
53 char remote_address[128];
54 } gate_portmapper_service_t;
55
56
57 1 static gate_portmapper_service_t* resolve_service(gate_portmapper_t* mapper, gate_channel_id_t channel)
58 {
59 gate_map_iterator_t iter;
60
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 for (iter = gate_map_first(&mapper->services); gate_map_iterator_valid(iter); iter = gate_map_iterator_next(iter))
61 {
62 1 gate_portmapper_service_t* service = (gate_portmapper_service_t*)iter->mapping.value;
63
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 if (service && (service->server == channel))
64 {
65 1 return service;
66 }
67 }
68 return NULL; /* not found */
69 }
70
71 static gate_atomic_int_t connection_id_counter = 0;
72
73 1 static void gate_portmapper_remove_connection(gate_portmapper_t* mapper, gate_uint32_t connection_id)
74 {
75 1 gate_portmapper_connection_t* conn = (gate_portmapper_connection_t*)gate_map_get_value(&mapper->connections, &connection_id);
76
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (conn)
77 {
78 1 gate_socketqueue_close(mapper->queue, conn->source_id);
79 1 gate_map_remove(&mapper->channels, &conn->source_id);
80
81 1 gate_socketqueue_close(mapper->queue, conn->destination_id);
82 1 gate_map_remove(&mapper->channels, &conn->destination_id);
83
84 1 gate_map_remove(&mapper->connections, &connection_id);
85 }
86 1 }
87
88 9 static gate_bool_t gate_portmapper_remove_invalid_connection_valid(gate_portmapper_t* mapper, gate_portmapper_connection_t* conn)
89 {
90
2/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
9 if (conn->source_error || conn->destination_error ||
91
6/8
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
9 (conn->source_read_closed && conn->source_write_closed && conn->destination_read_closed && conn->destination_write_closed))
92 {
93 1 gate_portmapper_remove_connection(mapper, conn->connection_id);
94 1 return true; /* connection was removed */
95 }
96 8 return false;
97 }
98
99 24 static void gate_portmapper_queue_callback(void* callback_data, gate_uint32_t result_type, gate_channel_id_t channel_id,
100 gate_result_t result_code, char const* buffer, gate_size_t buffer_length, void* user_param)
101 {
102 gate_result_t result;
103 24 gate_portmapper_t* mapper = (gate_portmapper_t*)callback_data;
104 24 gate_uint32_t const* ptr_connection_id = (gate_uint32_t const*)gate_map_get_value(&mapper->channels, &channel_id);
105 24 gate_portmapper_connection_t* conn = NULL;
106
107
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 9 times.
24 if (!ptr_connection_id)
108 {
109
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 1 times.
15 if (result_type != GATE_DATAQUEUE_RESULT_OPENNEW)
110 {
111 14 GATE_DEBUG_TRACE("Portmapper queue failed to resolve connection ID from channel");
112 14 GATE_DEBUG_TRACE_VALUE(channel_id);
113 14 GATE_DEBUG_TRACE_VALUE(result_type);
114 14 GATE_DEBUG_TRACE_VALUE(result_code);
115
116 14 gate_socketqueue_close(mapper->queue, channel_id);
117 14 return;
118 }
119 }
120 else
121 {
122 9 conn = (gate_portmapper_connection_t*)gate_map_get_value(&mapper->connections, ptr_connection_id);
123
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (!conn)
124 {
125 /* internal error or data corruption -> close connection and channel */
126 GATE_DEBUG_TRACE("Portmapper queue failed to resolve connection from connection ID");
127 GATE_DEBUG_TRACE_VALUE(channel_id);
128 GATE_DEBUG_TRACE_VALUE(result_type);
129 GATE_DEBUG_TRACE_VALUE(result_code);
130
131 gate_map_remove(&mapper->channels, &channel_id);
132 gate_socketqueue_close(mapper->queue, channel_id);
133 return;
134 }
135 }
136
137
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (GATE_FAILED(result_code))
138 {
139 GATE_DEBUG_TRACE("Portmapper queue operation failed");
140 GATE_DEBUG_TRACE_VALUE(channel_id);
141 GATE_DEBUG_TRACE_VALUE(result_type);
142 GATE_DEBUG_TRACE_VALUE(result_code);
143 if (conn)
144 {
145 GATE_DEBUG_TRACE_VALUE(conn->connection_id);
146 gate_portmapper_remove_connection(mapper, conn->connection_id);
147 }
148 return;
149 }
150
151
5/7
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
10 switch (result_type)
152 {
153 1 case GATE_DATAQUEUE_RESULT_OPEN: /* socket connect */
154 {
155
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_DEBUG_ASSERT(conn != NULL);
156 1 conn->destination_connected = true;
157 1 result = gate_socketqueue_begin_read(mapper->queue, conn->source_id, 4096, 0);
158
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (GATE_SUCCEEDED(result))
159 {
160 1 result = gate_socketqueue_begin_read(mapper->queue, conn->destination_id, 4096, 0);
161 }
162
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
163 {
164 conn->destination_error = true;
165 }
166
167 1 gate_portmapper_remove_invalid_connection_valid(mapper, conn);
168 1 break;
169 }
170 1 case GATE_DATAQUEUE_RESULT_OPENNEW: /* socket accept */
171 {
172 /* channel ID of originating service is published in data buffer */
173 1 gate_portmapper_service_t* service = resolve_service(mapper, *(gate_channel_id_t const*)buffer);
174
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (service)
175 {
176 gate_string_t remote_addr;
177 gate_portmapper_connection_t new_conn;
178 1 gate_mem_clear(&new_conn, sizeof(new_conn));
179 1 new_conn.connection_id = gate_atomic_int_inc(&connection_id_counter);
180 1 new_conn.source_id = channel_id;
181 1 new_conn.source_read_closed = false;
182 1 new_conn.source_write_closed = false;
183 1 new_conn.destination_connected = false;
184 1 new_conn.destination_read_closed = false;
185 1 new_conn.destination_write_closed = false;
186 1 new_conn.service_id = service->service_id;
187 1 gate_string_create_static(&remote_addr, service->remote_address);
188 1 result = gate_socketqueue_open(mapper->queue, &remote_addr, GATE_SOCKETQUEUE_OPEN_CLIENT, mapper, &new_conn.destination_id);
189
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (GATE_SUCCEEDED(result))
190 {
191 1 gate_map_add(&mapper->connections, &new_conn.connection_id, &new_conn);
192 1 gate_map_add(&mapper->channels, &new_conn.source_id, &new_conn.connection_id);
193 1 gate_map_add(&mapper->channels, &new_conn.destination_id, &new_conn.connection_id);
194 }
195 else
196 {
197 GATE_DEBUG_TRACE("Portmapper failed to connect to remote client");
198 GATE_DEBUG_TRACE_VALUE(result);
199 gate_socketqueue_close(mapper->queue, new_conn.source_id);
200 }
201 }
202 else
203 {
204 /* close all unknown service channels */
205 GATE_DEBUG_TRACE("Portmapper failed to resolve ACCEPTING service from channel ID");
206 gate_socketqueue_close(mapper->queue, channel_id);
207 }
208 1 break;
209 }
210 2 case GATE_DATAQUEUE_RESULT_READ: /* socket receive completed*/
211 {
212
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_DEBUG_ASSERT(conn != NULL);
213
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (conn->source_id == channel_id)
214 {
215 /* receive from source -> send to destination */
216 1 result = gate_socketqueue_begin_write(mapper->queue, conn->destination_id, buffer, buffer_length, user_param);
217
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
218 {
219 GATE_DEBUG_TRACE("Portmapper failed to write to DESTINATION in SOURCE-READ event");
220 conn->destination_error = true;
221 }
222 }
223
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 else if (conn->destination_id == channel_id)
224 {
225 /* receive from destination -> send to source */
226 1 result = gate_socketqueue_begin_write(mapper->queue, conn->source_id, buffer, buffer_length, user_param);
227
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
228 {
229 GATE_DEBUG_TRACE("Portmapper failed to write to SOURCE in DESTINATION-READ event");
230 conn->source_error = true;
231 }
232 }
233 else
234 {
235 /* internal error or data corruption -> close connection and channel */
236 GATE_DEBUG_TRACE("Portmapper cannot select connection channel in READ event");
237 GATE_DEBUG_TRACE_VALUE(channel_id);
238 conn->source_error = true;
239 conn->destination_error = true;
240 gate_socketqueue_close(mapper->queue, channel_id);
241 }
242
243 2 gate_portmapper_remove_invalid_connection_valid(mapper, conn);
244 2 break;
245 }
246 2 case GATE_DATAQUEUE_RESULT_CLOSE: /* socket receive completed, length == 0*/
247 {
248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 GATE_DEBUG_ASSERT(conn != NULL);
249
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (conn->source_id == channel_id)
250 {
251 /* end of stream of source -> shutdown send of destination */
252 1 conn->source_read_closed = true;
253 1 result = gate_socketqueue_begin_write(mapper->queue, conn->destination_id, NULL, 0, user_param);
254
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
255 {
256 GATE_DEBUG_TRACE("Portmapper failed to shutdown DESTINATION in SOURCE-CLOSE event");
257 conn->destination_error = true;
258 }
259 }
260
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 else if (conn->destination_id == channel_id)
261 {
262 /* end of stream of destination -> shutdown send of source */
263 1 conn->destination_read_closed = true;
264 1 result = gate_socketqueue_begin_write(mapper->queue, conn->source_id, NULL, 0, user_param);
265
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
266 {
267 GATE_DEBUG_TRACE("Portmapper failed to shutdown SOURCE in DESTINATION-CLOSE event");
268 conn->source_error = true;
269 }
270 }
271 else
272 {
273 /* internal error or data corruption -> close connection and channel */
274 GATE_DEBUG_TRACE("Portmapper cannot select connection channel in CLOSE event");
275 GATE_DEBUG_TRACE_VALUE(channel_id);
276 conn->source_error = true;
277 conn->destination_error = true;
278 gate_socketqueue_close(mapper->queue, channel_id);
279 }
280
281 2 gate_portmapper_remove_invalid_connection_valid(mapper, conn);
282 2 break;
283 }
284 4 case GATE_DATAQUEUE_RESULT_WRITE: /* socket send completed */
285 {
286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_DEBUG_ASSERT(conn != NULL);
287
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (conn->source_id == channel_id)
288 {
289 /* write to source succeeded */
290
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (buffer_length == 0)
291 {
292 /* socket write shutdown received */
293 1 conn->source_write_closed = true;
294 }
295 else
296 {
297 /* initiate next read from destination */
298 1 result = gate_socketqueue_begin_read(mapper->queue, conn->destination_id, 4096, user_param);
299
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
300 {
301 GATE_DEBUG_TRACE("Portmapper failed to read from DESTINATION in SOURCE-WRITE event");
302 conn->destination_error = true;
303 }
304 }
305
306 }
307
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 else if (conn->destination_id == channel_id)
308 {
309 /* write to destination succeeded */
310
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (buffer_length == 0)
311 {
312 /* socket write shutdown received */
313 1 conn->destination_write_closed = true;
314 }
315 else
316 {
317 /* initiate next read from source */
318 1 result = gate_socketqueue_begin_read(mapper->queue, conn->source_id, 4096, user_param);
319
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
320 {
321 GATE_DEBUG_TRACE("Portmapper failed to read from SOURCE in DESTINATION-WRITE event");
322 conn->source_error = true;
323 }
324 }
325
326 }
327 else
328 {
329 /* internal error or data corruption -> close connection and channel */
330 GATE_DEBUG_TRACE("Portmapper cannot select connection channel in WRITE event");
331 GATE_DEBUG_TRACE_VALUE(channel_id);
332 conn->source_error = true;
333 conn->destination_error = true;
334 gate_socketqueue_close(mapper->queue, channel_id);
335 }
336
337 4 gate_portmapper_remove_invalid_connection_valid(mapper, conn);
338 4 break;
339 }
340 case GATE_DATAQUEUE_RESULT_ERROR:
341 {
342 GATE_DEBUG_ASSERT(conn != NULL);
343 break;
344 }
345 default:
346 {
347 /* unsupported result -> ignore */
348 break;
349 }
350 }
351 }
352
353
354 1 gate_result_t gate_portmapper_create(gate_portmapper_t* mapper)
355 {
356 1 gate_result_t ret = GATE_RESULT_OK;
357 1 gate_mem_clear(mapper, sizeof(gate_portmapper_t));
358
359 do
360 {
361
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_map_create(&mapper->services, &gate_compare_uint32, sizeof(gate_uint32_t), NULL, NULL, sizeof(gate_portmapper_service_t), NULL, NULL))
362 {
363 ret = GATE_RESULT_OUTOFMEMORY;
364 break;
365 }
366
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_map_create(&mapper->connections, &gate_compare_uint32, sizeof(gate_uint32_t), NULL, NULL, sizeof(gate_portmapper_connection_t), NULL, NULL))
367 {
368 gate_map_destroy(&mapper->services);
369 ret = GATE_RESULT_OUTOFMEMORY;
370 break;
371 }
372
373
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_map_create(&mapper->channels, &gate_compare_uintptr, sizeof(gate_channel_id_t), NULL, NULL, sizeof(gate_uint32_t), NULL, NULL))
374 {
375 gate_map_destroy(&mapper->services);
376 gate_map_destroy(&mapper->connections);
377 ret = GATE_RESULT_OUTOFMEMORY;
378 break;
379 }
380
381 1 ret = gate_socketqueue_create(&mapper->queue, 500);
382
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
383 {
384 gate_map_destroy(&mapper->services);
385 gate_map_destroy(&mapper->channels);
386 gate_map_destroy(&mapper->connections);
387 break;
388 }
389
390 1 ret = gate_socketqueue_set_callback(mapper->queue, &gate_portmapper_queue_callback, mapper);
391
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
392 {
393 gate_object_release(mapper->queue);
394 gate_map_destroy(&mapper->services);
395 gate_map_destroy(&mapper->channels);
396 gate_map_destroy(&mapper->connections);
397 break;
398 }
399
400 1 ret = gate_socketqueue_start(mapper->queue);
401
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
402 {
403 gate_object_release(mapper->queue);
404 gate_map_destroy(&mapper->services);
405 gate_map_destroy(&mapper->channels);
406 gate_map_destroy(&mapper->connections);
407 break;
408 }
409 } while (0);
410 1 return ret;
411 }
412
413 1 gate_result_t gate_portmapper_destroy(gate_portmapper_t* mapper)
414 {
415 1 gate_socketqueue_stop(mapper->queue);
416 1 gate_socketqueue_close_all(mapper->queue);
417 1 gate_object_release(mapper->queue);
418 1 gate_map_destroy(&mapper->channels);
419 1 gate_map_destroy(&mapper->connections);
420 1 gate_map_destroy(&mapper->services);
421 1 gate_mem_clear(mapper, sizeof(gate_portmapper_t));
422 1 return GATE_RESULT_OK;
423 }
424
425 static gate_atomic_int_t mapping_id_counter = 0;
426
427 1 gate_result_t gate_portmapper_add(gate_portmapper_t* mapper, gate_string_t const* source_address, gate_string_t const* dest_address, gate_uint32_t* mapping_id)
428 {
429 gate_result_t ret;
430 gate_portmapper_service_t service;
431
432 do
433 {
434 1 gate_mem_clear(&service, sizeof(service));
435 1 service.service_id = gate_atomic_int_inc(&mapping_id_counter);
436
437 1 gate_string_to_buffer(dest_address, service.remote_address, sizeof(service.remote_address));
438
439 1 ret = gate_socketqueue_open(mapper->queue, source_address, GATE_SOCKETQUEUE_OPEN_SERVER, mapper, &service.server);
440
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
441 {
442 break;
443 }
444
445
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!gate_map_add(&mapper->services, &service.service_id, &service))
446 {
447 gate_socketqueue_close(mapper->queue, service.server);
448 ret = GATE_RESULT_OUTOFMEMORY;
449 break;
450 }
451
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (mapping_id)
452 {
453 1 *mapping_id = service.service_id;
454 }
455
456 1 ret = GATE_RESULT_OK;
457 } while (0);
458
459 1 return ret;
460 }
461
462 gate_result_t gate_portmapper_remove(gate_portmapper_t* mapper, gate_uint32_t service_id)
463 {
464 gate_portmapper_service_t* service = (gate_portmapper_service_t*)gate_map_get_value(&mapper->services, &service_id);
465 if (service)
466 {
467 gate_socketqueue_close(mapper->queue, service->server);
468 gate_map_remove(&mapper->services, &service_id);
469 return GATE_RESULT_OK;
470 }
471 return GATE_RESULT_NOMATCH;
472 }
473
474