GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/portmapping.c
Date: 2025-09-22 22:57:09
Exec Total Coverage
Lines: 133 221 60.2%
Functions: 7 8 87.5%
Branches: 55 93 59.1%

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_portmapper_service_t* service;
60 gate_map_iterator_t iter;
61
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))
62 {
63 1 service = (gate_portmapper_service_t*)iter->mapping.value;
64
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))
65 {
66 1 return service;
67 }
68 }
69 ✗ return NULL; /* not found */
70 }
71
72 static gate_atomic_int_t connection_id_counter = 0;
73
74 1 static void gate_portmapper_remove_connection(gate_portmapper_t* mapper, gate_uint32_t connection_id)
75 {
76 1 gate_portmapper_connection_t* conn = (gate_portmapper_connection_t*)gate_map_get_value(&mapper->connections, &connection_id);
77
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (conn)
78 {
79 1 gate_socketqueue_close(mapper->queue, conn->source_id);
80 1 gate_map_remove(&mapper->channels, &conn->source_id);
81
82 1 gate_socketqueue_close(mapper->queue, conn->destination_id);
83 1 gate_map_remove(&mapper->channels, &conn->destination_id);
84
85 1 gate_map_remove(&mapper->connections, &connection_id);
86 }
87 1 }
88
89 9 static gate_bool_t gate_portmapper_remove_invalid_connection_valid(gate_portmapper_t* mapper, gate_portmapper_connection_t* conn)
90 {
91
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 ||
92
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))
93 {
94 1 gate_portmapper_remove_connection(mapper, conn->connection_id);
95 1 return true; /* connection was removed */
96 }
97 8 return false;
98 }
99
100 23 static void gate_portmapper_queue_callback(void* callback_data, gate_uint32_t result_type, gate_channel_id_t channel_id,
101 gate_result_t result_code, char const* buffer, gate_size_t buffer_length, void* user_param)
102 {
103 gate_result_t result;
104 23 gate_portmapper_t* mapper = (gate_portmapper_t*)callback_data;
105 23 gate_uint32_t const* ptr_connection_id = (gate_uint32_t const*)gate_map_get_value(&mapper->channels, &channel_id);
106 23 gate_portmapper_connection_t* conn = NULL;
107
108
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 9 times.
23 if (!ptr_connection_id)
109 {
110
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1 times.
14 if (result_type != GATE_DATAQUEUE_RESULT_OPENNEW)
111 {
112 13 GATE_DEBUG_TRACE("Portmapper queue failed to resolve connection ID from channel");
113 13 GATE_DEBUG_TRACE_VALUE(channel_id);
114 13 GATE_DEBUG_TRACE_VALUE(result_type);
115 13 GATE_DEBUG_TRACE_VALUE(result_code);
116
117 13 gate_socketqueue_close(mapper->queue, channel_id);
118 13 return;
119 }
120 }
121 else
122 {
123 9 conn = (gate_portmapper_connection_t*)gate_map_get_value(&mapper->connections, ptr_connection_id);
124
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (!conn)
125 {
126 /* internal error or data corruption -> close connection and channel */
127 ✗ GATE_DEBUG_TRACE("Portmapper queue failed to resolve connection from connection ID");
128 ✗ GATE_DEBUG_TRACE_VALUE(channel_id);
129 ✗ GATE_DEBUG_TRACE_VALUE(result_type);
130 ✗ GATE_DEBUG_TRACE_VALUE(result_code);
131
132 ✗ gate_map_remove(&mapper->channels, &channel_id);
133 ✗ gate_socketqueue_close(mapper->queue, channel_id);
134 ✗ return;
135 }
136 }
137
138
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (GATE_FAILED(result_code))
139 {
140 ✗ GATE_DEBUG_TRACE("Portmapper queue operation failed");
141 ✗ GATE_DEBUG_TRACE_VALUE(channel_id);
142 ✗ GATE_DEBUG_TRACE_VALUE(result_type);
143 ✗ GATE_DEBUG_TRACE_VALUE(result_code);
144 ✗ if (conn)
145 {
146 ✗ GATE_DEBUG_TRACE_VALUE(conn->connection_id);
147 ✗ gate_portmapper_remove_connection(mapper, conn->connection_id);
148 }
149 ✗ return;
150 }
151
152
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)
153 {
154 1 case GATE_DATAQUEUE_RESULT_OPEN: /* socket connect */
155 {
156 1 GATE_DEBUG_ASSERT(conn != NULL);
157 1 conn->destination_connected = true;
158 1 result = gate_socketqueue_begin_read(mapper->queue, conn->source_id, 4096, 0);
159
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (GATE_SUCCEEDED(result))
160 {
161 1 result = gate_socketqueue_begin_read(mapper->queue, conn->destination_id, 4096, 0);
162 }
163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
164 {
165 ✗ conn->destination_error = true;
166 }
167
168 1 gate_portmapper_remove_invalid_connection_valid(mapper, conn);
169 1 break;
170 }
171 1 case GATE_DATAQUEUE_RESULT_OPENNEW: /* socket accept */
172 {
173 /* channel ID of originating service is published in data buffer */
174 1 gate_portmapper_service_t* service = resolve_service(mapper, *(gate_channel_id_t const*)buffer);
175
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (service)
176 {
177 gate_string_t remote_addr;
178 gate_portmapper_connection_t new_conn;
179 1 gate_mem_clear(&new_conn, sizeof(new_conn));
180 1 new_conn.connection_id = gate_atomic_int_inc(&connection_id_counter);
181 1 new_conn.source_id = channel_id;
182 1 new_conn.source_read_closed = false;
183 1 new_conn.source_write_closed = false;
184 1 new_conn.destination_connected = false;
185 1 new_conn.destination_read_closed = false;
186 1 new_conn.destination_write_closed = false;
187 1 new_conn.service_id = service->service_id;
188 1 gate_string_create_static(&remote_addr, service->remote_address);
189 1 result = gate_socketqueue_open(mapper->queue, &remote_addr, GATE_SOCKETQUEUE_OPEN_CLIENT, mapper, &new_conn.destination_id);
190
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (GATE_SUCCEEDED(result))
191 {
192 1 gate_map_add(&mapper->connections, &new_conn.connection_id, &new_conn);
193 1 gate_map_add(&mapper->channels, &new_conn.source_id, &new_conn.connection_id);
194 1 gate_map_add(&mapper->channels, &new_conn.destination_id, &new_conn.connection_id);
195 }
196 else
197 {
198 ✗ GATE_DEBUG_TRACE("Portmapper failed to connect to remote client");
199 ✗ GATE_DEBUG_TRACE_VALUE(result);
200 ✗ gate_socketqueue_close(mapper->queue, new_conn.source_id);
201 }
202 }
203 else
204 {
205 /* close all unknown service channels */
206 ✗ GATE_DEBUG_TRACE("Portmapper failed to resolve ACCEPTING service from channel ID");
207 ✗ gate_socketqueue_close(mapper->queue, channel_id);
208 }
209 1 break;
210 }
211 2 case GATE_DATAQUEUE_RESULT_READ: /* socket receive completed*/
212 {
213 2 GATE_DEBUG_ASSERT(conn != NULL);
214
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (conn->source_id == channel_id)
215 {
216 /* receive from source -> send to destination */
217 1 result = gate_socketqueue_begin_write(mapper->queue, conn->destination_id, buffer, buffer_length, user_param);
218
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
219 {
220 ✗ GATE_DEBUG_TRACE("Portmapper failed to write to DESTINATION in SOURCE-READ event");
221 ✗ conn->destination_error = true;
222 }
223 }
224
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 else if (conn->destination_id == channel_id)
225 {
226 /* receive from destination -> send to source */
227 1 result = gate_socketqueue_begin_write(mapper->queue, conn->source_id, buffer, buffer_length, user_param);
228
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
229 {
230 ✗ GATE_DEBUG_TRACE("Portmapper failed to write to SOURCE in DESTINATION-READ event");
231 ✗ conn->source_error = true;
232 }
233 }
234 else
235 {
236 /* internal error or data corruption -> close connection and channel */
237 ✗ GATE_DEBUG_TRACE("Portmapper cannot select connection channel in READ event");
238 ✗ GATE_DEBUG_TRACE_VALUE(channel_id);
239 ✗ conn->source_error = true;
240 ✗ conn->destination_error = true;
241 ✗ gate_socketqueue_close(mapper->queue, channel_id);
242 }
243
244 2 gate_portmapper_remove_invalid_connection_valid(mapper, conn);
245 2 break;
246 }
247 2 case GATE_DATAQUEUE_RESULT_CLOSE: /* socket receive completed, length == 0*/
248 {
249 2 GATE_DEBUG_ASSERT(conn != NULL);
250
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (conn->source_id == channel_id)
251 {
252 /* end of stream of source -> shutdown send of destination */
253 1 conn->source_read_closed = true;
254 1 result = gate_socketqueue_begin_write(mapper->queue, conn->destination_id, NULL, 0, user_param);
255
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
256 {
257 ✗ GATE_DEBUG_TRACE("Portmapper failed to shutdown DESTINATION in SOURCE-CLOSE event");
258 ✗ conn->destination_error = true;
259 }
260 }
261
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 else if (conn->destination_id == channel_id)
262 {
263 /* end of stream of destination -> shutdown send of source */
264 1 conn->destination_read_closed = true;
265 1 result = gate_socketqueue_begin_write(mapper->queue, conn->source_id, NULL, 0, user_param);
266
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
267 {
268 ✗ GATE_DEBUG_TRACE("Portmapper failed to shutdown SOURCE in DESTINATION-CLOSE event");
269 ✗ conn->source_error = true;
270 }
271 }
272 else
273 {
274 /* internal error or data corruption -> close connection and channel */
275 ✗ GATE_DEBUG_TRACE("Portmapper cannot select connection channel in CLOSE event");
276 ✗ GATE_DEBUG_TRACE_VALUE(channel_id);
277 ✗ conn->source_error = true;
278 ✗ conn->destination_error = true;
279 ✗ gate_socketqueue_close(mapper->queue, channel_id);
280 }
281
282 2 gate_portmapper_remove_invalid_connection_valid(mapper, conn);
283 2 break;
284 }
285 4 case GATE_DATAQUEUE_RESULT_WRITE: /* socket send completed */
286 {
287 4 GATE_DEBUG_ASSERT(conn != NULL);
288
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (conn->source_id == channel_id)
289 {
290 /* write to source succeeded */
291
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (buffer_length == 0)
292 {
293 /* socket write shutdown received */
294 1 conn->source_write_closed = true;
295 }
296 else
297 {
298 /* initiate next read from destination */
299 1 result = gate_socketqueue_begin_read(mapper->queue, conn->destination_id, 4096, user_param);
300
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
301 {
302 ✗ GATE_DEBUG_TRACE("Portmapper failed to read from DESTINATION in SOURCE-WRITE event");
303 ✗ conn->destination_error = true;
304 }
305 }
306
307 }
308
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 else if (conn->destination_id == channel_id)
309 {
310 /* write to destination succeeded */
311
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (buffer_length == 0)
312 {
313 /* socket write shutdown received */
314 1 conn->destination_write_closed = true;
315 }
316 else
317 {
318 /* initiate next read from source */
319 1 result = gate_socketqueue_begin_read(mapper->queue, conn->source_id, 4096, user_param);
320
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
321 {
322 ✗ GATE_DEBUG_TRACE("Portmapper failed to read from SOURCE in DESTINATION-WRITE event");
323 ✗ conn->source_error = true;
324 }
325 }
326
327 }
328 else
329 {
330 /* internal error or data corruption -> close connection and channel */
331 ✗ GATE_DEBUG_TRACE("Portmapper cannot select connection channel in WRITE event");
332 ✗ GATE_DEBUG_TRACE_VALUE(channel_id);
333 ✗ conn->source_error = true;
334 ✗ conn->destination_error = true;
335 ✗ gate_socketqueue_close(mapper->queue, channel_id);
336 }
337
338 4 gate_portmapper_remove_invalid_connection_valid(mapper, conn);
339 4 break;
340 }
341 ✗ case GATE_DATAQUEUE_RESULT_ERROR:
342 {
343 ✗ GATE_DEBUG_ASSERT(conn != NULL);
344 ✗ break;
345 }
346 ✗ default:
347 {
348 /* unsupported result -> ignore */
349 ✗ break;
350 }
351 }
352 }
353
354
355 1 gate_result_t gate_portmapper_create(gate_portmapper_t* mapper)
356 {
357 1 gate_result_t ret = GATE_RESULT_OK;
358 1 gate_mem_clear(mapper, sizeof(gate_portmapper_t));
359
360 do
361 {
362
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))
363 {
364 ✗ ret = GATE_RESULT_OUTOFMEMORY;
365 ✗ break;
366 }
367
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))
368 {
369 ✗ gate_map_destroy(&mapper->services);
370 ✗ ret = GATE_RESULT_OUTOFMEMORY;
371 ✗ break;
372 }
373
374
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))
375 {
376 ✗ gate_map_destroy(&mapper->services);
377 ✗ gate_map_destroy(&mapper->connections);
378 ✗ ret = GATE_RESULT_OUTOFMEMORY;
379 ✗ break;
380 }
381
382 1 ret = gate_socketqueue_create(&mapper->queue, 500);
383
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
384 {
385 ✗ gate_map_destroy(&mapper->services);
386 ✗ gate_map_destroy(&mapper->channels);
387 ✗ gate_map_destroy(&mapper->connections);
388 ✗ break;
389 }
390
391 1 ret = gate_socketqueue_set_callback(mapper->queue, &gate_portmapper_queue_callback, mapper);
392
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
393 {
394 ✗ gate_object_release(mapper->queue);
395 ✗ gate_map_destroy(&mapper->services);
396 ✗ gate_map_destroy(&mapper->channels);
397 ✗ gate_map_destroy(&mapper->connections);
398 ✗ break;
399 }
400
401 1 ret = gate_socketqueue_start(mapper->queue);
402
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
403 {
404 ✗ gate_object_release(mapper->queue);
405 ✗ gate_map_destroy(&mapper->services);
406 ✗ gate_map_destroy(&mapper->channels);
407 ✗ gate_map_destroy(&mapper->connections);
408 ✗ break;
409 }
410 } while (0);
411 1 return ret;
412 }
413
414 1 gate_result_t gate_portmapper_destroy(gate_portmapper_t* mapper)
415 {
416 1 gate_socketqueue_stop(mapper->queue);
417 1 gate_socketqueue_close_all(mapper->queue);
418 1 gate_object_release(mapper->queue);
419 1 gate_map_destroy(&mapper->channels);
420 1 gate_map_destroy(&mapper->connections);
421 1 gate_map_destroy(&mapper->services);
422 1 gate_mem_clear(mapper, sizeof(gate_portmapper_t));
423 1 return GATE_RESULT_OK;
424 }
425
426 static gate_atomic_int_t mapping_id_counter = 0;
427
428 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)
429 {
430 gate_result_t ret;
431 gate_portmapper_service_t service;
432
433 do
434 {
435 1 gate_mem_clear(&service, sizeof(service));
436 1 service.service_id = gate_atomic_int_inc(&mapping_id_counter);
437
438 1 gate_string_to_buffer(dest_address, service.remote_address, sizeof(service.remote_address));
439
440 1 ret = gate_socketqueue_open(mapper->queue, source_address, GATE_SOCKETQUEUE_OPEN_SERVER, mapper, &service.server);
441
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
442 {
443 ✗ break;
444 }
445
446
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!gate_map_add(&mapper->services, &service.service_id, &service))
447 {
448 ✗ gate_socketqueue_close(mapper->queue, service.server);
449 ✗ ret = GATE_RESULT_OUTOFMEMORY;
450 ✗ break;
451 }
452
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (mapping_id)
453 {
454 1 *mapping_id = service.service_id;
455 }
456
457 1 ret = GATE_RESULT_OK;
458 } while (0);
459
460 1 return ret;
461 }
462
463 ✗ gate_result_t gate_portmapper_remove(gate_portmapper_t* mapper, gate_uint32_t service_id)
464 {
465 ✗ gate_portmapper_service_t* service = (gate_portmapper_service_t*)gate_map_get_value(&mapper->services, &service_id);
466 ✗ if (service)
467 {
468 ✗ gate_socketqueue_close(mapper->queue, service->server);
469 ✗ gate_map_remove(&mapper->services, &service_id);
470 ✗ return GATE_RESULT_OK;
471 }
472 ✗ return GATE_RESULT_NOMATCH;
473 }
474
475