GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/portmapping.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 0 221 0.0%
Functions: 0 8 0.0%
Branches: 0 93 0.0%

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 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 for (iter = gate_map_first(&mapper->services); gate_map_iterator_valid(iter); iter = gate_map_iterator_next(iter))
62 {
63 service = (gate_portmapper_service_t*)iter->mapping.value;
64 if (service && (service->server == channel))
65 {
66 return service;
67 }
68 }
69 return NULL; /* not found */
70 }
71
72 static gate_atomic_int_t connection_id_counter = 0;
73
74 static void gate_portmapper_remove_connection(gate_portmapper_t* mapper, gate_uint32_t connection_id)
75 {
76 gate_portmapper_connection_t* conn = (gate_portmapper_connection_t*)gate_map_get_value(&mapper->connections, &connection_id);
77 if (conn)
78 {
79 gate_socketqueue_close(mapper->queue, conn->source_id);
80 gate_map_remove(&mapper->channels, &conn->source_id);
81
82 gate_socketqueue_close(mapper->queue, conn->destination_id);
83 gate_map_remove(&mapper->channels, &conn->destination_id);
84
85 gate_map_remove(&mapper->connections, &connection_id);
86 }
87 }
88
89 static gate_bool_t gate_portmapper_remove_invalid_connection_valid(gate_portmapper_t* mapper, gate_portmapper_connection_t* conn)
90 {
91 if (conn->source_error || conn->destination_error ||
92 (conn->source_read_closed && conn->source_write_closed && conn->destination_read_closed && conn->destination_write_closed))
93 {
94 gate_portmapper_remove_connection(mapper, conn->connection_id);
95 return true; /* connection was removed */
96 }
97 return false;
98 }
99
100 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 gate_portmapper_t* mapper = (gate_portmapper_t*)callback_data;
105 gate_uint32_t const* ptr_connection_id = (gate_uint32_t const*)gate_map_get_value(&mapper->channels, &channel_id);
106 gate_portmapper_connection_t* conn = NULL;
107
108 if (!ptr_connection_id)
109 {
110 if (result_type != GATE_DATAQUEUE_RESULT_OPENNEW)
111 {
112 GATE_DEBUG_TRACE("Portmapper queue failed to resolve connection ID from channel");
113 GATE_DEBUG_TRACE_VALUE(channel_id);
114 GATE_DEBUG_TRACE_VALUE(result_type);
115 GATE_DEBUG_TRACE_VALUE(result_code);
116
117 gate_socketqueue_close(mapper->queue, channel_id);
118 return;
119 }
120 }
121 else
122 {
123 conn = (gate_portmapper_connection_t*)gate_map_get_value(&mapper->connections, ptr_connection_id);
124 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 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 switch (result_type)
153 {
154 case GATE_DATAQUEUE_RESULT_OPEN: /* socket connect */
155 {
156 GATE_DEBUG_ASSERT(conn != NULL);
157 conn->destination_connected = true;
158 result = gate_socketqueue_begin_read(mapper->queue, conn->source_id, 4096, 0);
159 if (GATE_SUCCEEDED(result))
160 {
161 result = gate_socketqueue_begin_read(mapper->queue, conn->destination_id, 4096, 0);
162 }
163 if (GATE_FAILED(result))
164 {
165 conn->destination_error = true;
166 }
167
168 gate_portmapper_remove_invalid_connection_valid(mapper, conn);
169 break;
170 }
171 case GATE_DATAQUEUE_RESULT_OPENNEW: /* socket accept */
172 {
173 /* channel ID of originating service is published in data buffer */
174 gate_portmapper_service_t* service = resolve_service(mapper, *(gate_channel_id_t const*)buffer);
175 if (service)
176 {
177 gate_string_t remote_addr;
178 gate_portmapper_connection_t new_conn;
179 gate_mem_clear(&new_conn, sizeof(new_conn));
180 new_conn.connection_id = gate_atomic_int_inc(&connection_id_counter);
181 new_conn.source_id = channel_id;
182 new_conn.source_read_closed = false;
183 new_conn.source_write_closed = false;
184 new_conn.destination_connected = false;
185 new_conn.destination_read_closed = false;
186 new_conn.destination_write_closed = false;
187 new_conn.service_id = service->service_id;
188 gate_string_create_static(&remote_addr, service->remote_address);
189 result = gate_socketqueue_open(mapper->queue, &remote_addr, GATE_SOCKETQUEUE_OPEN_CLIENT, mapper, &new_conn.destination_id);
190 if (GATE_SUCCEEDED(result))
191 {
192 gate_map_add(&mapper->connections, &new_conn.connection_id, &new_conn);
193 gate_map_add(&mapper->channels, &new_conn.source_id, &new_conn.connection_id);
194 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 break;
210 }
211 case GATE_DATAQUEUE_RESULT_READ: /* socket receive completed*/
212 {
213 GATE_DEBUG_ASSERT(conn != NULL);
214 if (conn->source_id == channel_id)
215 {
216 /* receive from source -> send to destination */
217 result = gate_socketqueue_begin_write(mapper->queue, conn->destination_id, buffer, buffer_length, user_param);
218 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 else if (conn->destination_id == channel_id)
225 {
226 /* receive from destination -> send to source */
227 result = gate_socketqueue_begin_write(mapper->queue, conn->source_id, buffer, buffer_length, user_param);
228 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 gate_portmapper_remove_invalid_connection_valid(mapper, conn);
245 break;
246 }
247 case GATE_DATAQUEUE_RESULT_CLOSE: /* socket receive completed, length == 0*/
248 {
249 GATE_DEBUG_ASSERT(conn != NULL);
250 if (conn->source_id == channel_id)
251 {
252 /* end of stream of source -> shutdown send of destination */
253 conn->source_read_closed = true;
254 result = gate_socketqueue_begin_write(mapper->queue, conn->destination_id, NULL, 0, user_param);
255 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 else if (conn->destination_id == channel_id)
262 {
263 /* end of stream of destination -> shutdown send of source */
264 conn->destination_read_closed = true;
265 result = gate_socketqueue_begin_write(mapper->queue, conn->source_id, NULL, 0, user_param);
266 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 gate_portmapper_remove_invalid_connection_valid(mapper, conn);
283 break;
284 }
285 case GATE_DATAQUEUE_RESULT_WRITE: /* socket send completed */
286 {
287 GATE_DEBUG_ASSERT(conn != NULL);
288 if (conn->source_id == channel_id)
289 {
290 /* write to source succeeded */
291 if (buffer_length == 0)
292 {
293 /* socket write shutdown received */
294 conn->source_write_closed = true;
295 }
296 else
297 {
298 /* initiate next read from destination */
299 result = gate_socketqueue_begin_read(mapper->queue, conn->destination_id, 4096, user_param);
300 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 else if (conn->destination_id == channel_id)
309 {
310 /* write to destination succeeded */
311 if (buffer_length == 0)
312 {
313 /* socket write shutdown received */
314 conn->destination_write_closed = true;
315 }
316 else
317 {
318 /* initiate next read from source */
319 result = gate_socketqueue_begin_read(mapper->queue, conn->source_id, 4096, user_param);
320 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 gate_portmapper_remove_invalid_connection_valid(mapper, conn);
339 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 gate_result_t gate_portmapper_create(gate_portmapper_t* mapper)
356 {
357 gate_result_t ret = GATE_RESULT_OK;
358 gate_mem_clear(mapper, sizeof(gate_portmapper_t));
359
360 do
361 {
362 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 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 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 ret = gate_socketqueue_create(&mapper->queue, 500);
383 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 ret = gate_socketqueue_set_callback(mapper->queue, &gate_portmapper_queue_callback, mapper);
392 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 ret = gate_socketqueue_start(mapper->queue);
402 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 return ret;
412 }
413
414 gate_result_t gate_portmapper_destroy(gate_portmapper_t* mapper)
415 {
416 gate_socketqueue_stop(mapper->queue);
417 gate_socketqueue_close_all(mapper->queue);
418 gate_object_release(mapper->queue);
419 gate_map_destroy(&mapper->channels);
420 gate_map_destroy(&mapper->connections);
421 gate_map_destroy(&mapper->services);
422 gate_mem_clear(mapper, sizeof(gate_portmapper_t));
423 return GATE_RESULT_OK;
424 }
425
426 static gate_atomic_int_t mapping_id_counter = 0;
427
428 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 gate_mem_clear(&service, sizeof(service));
436 service.service_id = gate_atomic_int_inc(&mapping_id_counter);
437
438 gate_string_to_buffer(dest_address, service.remote_address, sizeof(service.remote_address));
439
440 ret = gate_socketqueue_open(mapper->queue, source_address, GATE_SOCKETQUEUE_OPEN_SERVER, mapper, &service.server);
441 if (GATE_FAILED(ret))
442 {
443 break;
444 }
445
446 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 if (mapping_id)
453 {
454 *mapping_id = service.service_id;
455 }
456
457 ret = GATE_RESULT_OK;
458 } while (0);
459
460 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