source: mainline/uspace/lib/socket/generic/socket_core.c@ 402eda5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 402eda5 was 14f1db0, checked in by Martin Decky <martin@…>, 15 years ago

networking overhaul:

  • separation of conserns
  • removal of (almost all) overlaping symbols, libnetif is not needed anymore
  • again, it is possible to build the networking in multiple architecture configurations (however, currently only the bundling netif and nil layers is supported, more to come)
  • code style updates and fixes (still a huge amount of work to do)
  • Property mode set to 100644
File size: 14.1 KB
Line 
1/*
2 * Copyright (c) 2009 Lukas Mejdrech
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - 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 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup socket
30 * @{
31 */
32
33/** @file
34 * Socket common core implementation.
35 */
36
37#include <limits.h>
38#include <stdlib.h>
39
40#include <net_err.h>
41#include <in.h>
42#include <inet.h>
43#include <socket_codes.h>
44#include <socket_errno.h>
45#include <adt/dynamic_fifo.h>
46#include <adt/int_map.h>
47#include <packet/packet.h>
48#include <packet/packet_client.h>
49#include <net_modules.h>
50#include <socket_core.h>
51
52/** Maximum number of random attempts to find a new socket identifier before switching to the sequence.
53 */
54#define SOCKET_ID_TRIES 100
55
56/** Bound port sockets.
57 */
58struct socket_port{
59 /** The bound sockets map.
60 */
61 socket_port_map_t map;
62 /** The bound sockets count.
63 */
64 int count;
65};
66
67INT_MAP_IMPLEMENT(socket_cores, socket_core_t);
68
69GENERIC_CHAR_MAP_IMPLEMENT(socket_port_map, socket_core_ref);
70
71INT_MAP_IMPLEMENT(socket_ports, socket_port_t);
72
73/** Destroys the socket.
74 * If the socket is bound, the port is released.
75 * Releases all buffered packets, calls the release function and removes the socket from the local sockets.
76 * @param[in] packet_phone The packet server phone to release buffered packets.
77 * @param[in] socket The socket to be destroyed.
78 * @param[in,out] local_sockets The local sockets to be updated.
79 * @param[in,out] global_sockets The global sockets to be updated.
80 * @param[in] socket_release The client release callback function.
81 */
82static void socket_destroy_core(int packet_phone, socket_core_ref socket, socket_cores_ref local_sockets, socket_ports_ref global_sockets, void (*socket_release)(socket_core_ref socket)){
83 int packet_id;
84
85 // if bound
86 if(socket->port){
87 // release the port
88 socket_port_release(global_sockets, socket);
89 }
90 // release all received packets
91 while((packet_id = dyn_fifo_pop(&socket->received)) >= 0){
92 pq_release_local(packet_phone, packet_id);
93 }
94 dyn_fifo_destroy(&socket->received);
95 dyn_fifo_destroy(&socket->accepted);
96 if(socket_release){
97 socket_release(socket);
98 }
99 socket_cores_exclude(local_sockets, socket->socket_id);
100}
101
102void socket_cores_release(int packet_phone, socket_cores_ref local_sockets, socket_ports_ref global_sockets, void (*socket_release)(socket_core_ref socket)){
103 if(socket_cores_is_valid(local_sockets)){
104 int index;
105
106 local_sockets->magic = 0;
107 for(index = 0; index < local_sockets->next; ++ index){
108 if(socket_cores_item_is_valid(&(local_sockets->items[index]))){
109 local_sockets->items[index].magic = 0;
110 if(local_sockets->items[index].value){
111 socket_destroy_core(packet_phone, local_sockets->items[index].value, local_sockets, global_sockets, socket_release);
112 free(local_sockets->items[index].value);
113 local_sockets->items[index].value = NULL;
114 }
115 }
116 }
117 free(local_sockets->items);
118 }
119}
120
121/** Adds the socket to a socket port.
122 * @param[in,out] socket_port The socket port structure.
123 * @param[in] socket The socket to be added.
124 * @param[in] key The socket key identifier.
125 * @param[in] key_length The socket key length.
126 * @returns EOK on success.
127 * @returns ENOMEM if there is not enough memory left.
128 */
129static int socket_port_add_core(socket_port_ref socket_port, socket_core_ref socket, const char * key, size_t key_length){
130 ERROR_DECLARE;
131
132 socket_core_ref * socket_ref;
133
134 // create a wrapper
135 socket_ref = malloc(sizeof(*socket_ref));
136 if(! socket_ref){
137 return ENOMEM;
138 }
139 *socket_ref = socket;
140 // add the wrapper
141 if(ERROR_OCCURRED(socket_port_map_add(&socket_port->map, key, key_length, socket_ref))){
142 free(socket_ref);
143 return ERROR_CODE;
144 }
145 ++ socket_port->count;
146 socket->key = key;
147 socket->key_length = key_length;
148 return EOK;
149}
150
151/** Binds the socket to the port.
152 * The SOCKET_MAP_KEY_LISTENING key identifier is used.
153 * @param[in] global_sockets The global sockets to be updated.
154 * @param[in] socket The socket to be added.
155 * @param[in] port The port number to be bound to.
156 * @returns EOK on success.
157 * @returns ENOMEM if there is not enough memory left.
158 * @returns Other error codes as defined for the socket_ports_add() function.
159 */
160static int socket_bind_insert(socket_ports_ref global_sockets, socket_core_ref socket, int port){
161 ERROR_DECLARE;
162
163 socket_port_ref socket_port;
164
165 // create a wrapper
166 socket_port = malloc(sizeof(*socket_port));
167 if(! socket_port){
168 return ENOMEM;
169 }
170 socket_port->count = 0;
171 if(ERROR_OCCURRED(socket_port_map_initialize(&socket_port->map))
172 || ERROR_OCCURRED(socket_port_add_core(socket_port, socket, SOCKET_MAP_KEY_LISTENING, 0))){
173 socket_port_map_destroy(&socket_port->map);
174 free(socket_port);
175 return ERROR_CODE;
176 }
177 // register the incomming port
178 ERROR_CODE = socket_ports_add(global_sockets, port, socket_port);
179 if(ERROR_CODE < 0){
180 socket_port_map_destroy(&socket_port->map);
181 free(socket_port);
182 return ERROR_CODE;
183 }
184 socket->port = port;
185 return EOK;
186}
187
188int socket_bind(socket_cores_ref local_sockets, socket_ports_ref global_sockets, int socket_id, void * addr, size_t addrlen, int free_ports_start, int free_ports_end, int last_used_port){
189 socket_core_ref socket;
190 socket_port_ref socket_port;
191 struct sockaddr * address;
192 struct sockaddr_in * address_in;
193
194 if(addrlen < sizeof(struct sockaddr)){
195 return EINVAL;
196 }
197 address = (struct sockaddr *) addr;
198 switch(address->sa_family){
199 case AF_INET:
200 if(addrlen != sizeof(struct sockaddr_in)){
201 return EINVAL;
202 }
203 address_in = (struct sockaddr_in *) addr;
204 // find the socket
205 socket = socket_cores_find(local_sockets, socket_id);
206 if(! socket){
207 return ENOTSOCK;
208 }
209 // bind a free port?
210 if(address_in->sin_port <= 0){
211 return socket_bind_free_port(global_sockets, socket, free_ports_start, free_ports_end, last_used_port);
212 }
213 // try to find the port
214 socket_port = socket_ports_find(global_sockets, ntohs(address_in->sin_port));
215 if(socket_port){
216 // already used
217 return EADDRINUSE;
218 }
219 // if bound
220 if(socket->port){
221 // release the port
222 socket_port_release(global_sockets, socket);
223 }
224 socket->port = -1;
225 return socket_bind_insert(global_sockets, socket, ntohs(address_in->sin_port));
226 break;
227 // TODO IPv6
228 }
229 return EAFNOSUPPORT;
230}
231
232int socket_bind_free_port(socket_ports_ref global_sockets, socket_core_ref socket, int free_ports_start, int free_ports_end, int last_used_port){
233 int index;
234
235 // from the last used one
236 index = last_used_port;
237 do{
238 ++ index;
239 // til the range end
240 if(index >= free_ports_end){
241 // start from the range beginning
242 index = free_ports_start - 1;
243 do{
244 ++ index;
245 // til the last used one
246 if(index >= last_used_port){
247 // none found
248 return ENOTCONN;
249 }
250 }while(socket_ports_find(global_sockets, index) != NULL);
251 // found, break immediately
252 break;
253 }
254 }while(socket_ports_find(global_sockets, index) != NULL);
255 return socket_bind_insert(global_sockets, socket, index);
256}
257
258/** Tries to find a new free socket identifier.
259 * @param[in] local_sockets The local sockets to be searched.
260 * @param[in] positive A value indicating whether a positive identifier is requested. A negative identifier is requested if set to false.
261 * @returns The new socket identifier.
262 * @returns ELIMIT if there is no socket identifier available.
263 */
264static int socket_generate_new_id(socket_cores_ref local_sockets, int positive){
265 int socket_id;
266 int count;
267
268 count = 0;
269// socket_id = socket_globals.last_id;
270 do{
271 if(count < SOCKET_ID_TRIES){
272 socket_id = rand() % INT_MAX;
273 ++ count;
274 }else if(count == SOCKET_ID_TRIES){
275 socket_id = 1;
276 ++ count;
277 // only this branch for last_id
278 }else{
279 if(socket_id < INT_MAX){
280 ++ socket_id;
281/* }else if(socket_globals.last_id){
282* socket_globals.last_id = 0;
283* socket_id = 1;
284*/ }else{
285 return ELIMIT;
286 }
287 }
288 }while(socket_cores_find(local_sockets, ((positive ? 1 : -1) * socket_id)));
289// last_id = socket_id
290 return socket_id;
291}
292
293int socket_create(socket_cores_ref local_sockets, int app_phone, void * specific_data, int * socket_id){
294 ERROR_DECLARE;
295
296 socket_core_ref socket;
297 int res;
298 int positive;
299
300 if(! socket_id){
301 return EINVAL;
302 }
303 // store the socket
304 if(*socket_id <= 0){
305 positive = (*socket_id == 0);
306 *socket_id = socket_generate_new_id(local_sockets, positive);
307 if(*socket_id <= 0){
308 return * socket_id;
309 }
310 if(! positive){
311 *socket_id *= -1;
312 }
313 }else if(socket_cores_find(local_sockets, * socket_id)){
314 return EEXIST;
315 }
316 socket = (socket_core_ref) malloc(sizeof(*socket));
317 if(! socket){
318 return ENOMEM;
319 }
320 // initialize
321 socket->phone = app_phone;
322 socket->port = -1;
323 socket->key = NULL;
324 socket->key_length = 0;
325 socket->specific_data = specific_data;
326 if(ERROR_OCCURRED(dyn_fifo_initialize(&socket->received, SOCKET_INITIAL_RECEIVED_SIZE))){
327 free(socket);
328 return ERROR_CODE;
329 }
330 if(ERROR_OCCURRED(dyn_fifo_initialize(&socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE))){
331 dyn_fifo_destroy(&socket->received);
332 free(socket);
333 return ERROR_CODE;
334 }
335 socket->socket_id = * socket_id;
336 res = socket_cores_add(local_sockets, socket->socket_id, socket);
337 if(res < 0){
338 dyn_fifo_destroy(&socket->received);
339 dyn_fifo_destroy(&socket->accepted);
340 free(socket);
341 return res;
342 }
343 return EOK;
344}
345
346int socket_destroy(int packet_phone, int socket_id, socket_cores_ref local_sockets, socket_ports_ref global_sockets, void (*socket_release)(socket_core_ref socket)){
347 socket_core_ref socket;
348 int accepted_id;
349
350 // find the socket
351 socket = socket_cores_find(local_sockets, socket_id);
352 if(! socket){
353 return ENOTSOCK;
354 }
355 // destroy all accepted sockets
356 while((accepted_id = dyn_fifo_pop(&socket->accepted)) >= 0){
357 socket_destroy(packet_phone, accepted_id, local_sockets, global_sockets, socket_release);
358 }
359 socket_destroy_core(packet_phone, socket, local_sockets, global_sockets, socket_release);
360 return EOK;
361}
362
363int socket_reply_packets(packet_t packet, size_t * length){
364 ERROR_DECLARE;
365
366 packet_t next_packet;
367 size_t fragments;
368 size_t * lengths;
369 size_t index;
370
371 if(! length){
372 return EBADMEM;
373 }
374 next_packet = pq_next(packet);
375 if(! next_packet){
376 // write all if only one fragment
377 ERROR_PROPAGATE(data_reply(packet_get_data(packet), packet_get_data_length(packet)));
378 // store the total length
379 *length = packet_get_data_length(packet);
380 }else{
381 // count the packet fragments
382 fragments = 1;
383 next_packet = pq_next(packet);
384 while((next_packet = pq_next(next_packet))){
385 ++ fragments;
386 }
387 // compute and store the fragment lengths
388 lengths = (size_t *) malloc(sizeof(size_t) * fragments + sizeof(size_t));
389 if(! lengths){
390 return ENOMEM;
391 }
392 lengths[0] = packet_get_data_length(packet);
393 lengths[fragments] = lengths[0];
394 next_packet = pq_next(packet);
395 for(index = 1; index < fragments; ++ index){
396 lengths[index] = packet_get_data_length(next_packet);
397 lengths[fragments] += lengths[index];
398 next_packet = pq_next(packet);
399 }while(next_packet);
400 // write the fragment lengths
401 ERROR_PROPAGATE(data_reply(lengths, sizeof(int) * (fragments + 1)));
402 next_packet = packet;
403 // write the fragments
404 for(index = 0; index < fragments; ++ index){
405 ERROR_PROPAGATE(data_reply(packet_get_data(next_packet), lengths[index]));
406 next_packet = pq_next(next_packet);
407 }while(next_packet);
408 // store the total length
409 *length = lengths[fragments];
410 free(lengths);
411 }
412 return EOK;
413}
414
415socket_core_ref socket_port_find(socket_ports_ref global_sockets, int port, const char * key, size_t key_length){
416 socket_port_ref socket_port;
417 socket_core_ref * socket_ref;
418
419 socket_port = socket_ports_find(global_sockets, port);
420 if(socket_port && (socket_port->count > 0)){
421 socket_ref = socket_port_map_find(&socket_port->map, key, key_length);
422 if(socket_ref){
423 return * socket_ref;
424 }
425 }
426 return NULL;
427}
428
429void socket_port_release(socket_ports_ref global_sockets, socket_core_ref socket){
430 socket_port_ref socket_port;
431 socket_core_ref * socket_ref;
432
433 if(socket->port){
434 // find ports
435 socket_port = socket_ports_find(global_sockets, socket->port);
436 if(socket_port){
437 // find the socket
438 socket_ref = socket_port_map_find(&socket_port->map, socket->key, socket->key_length);
439 if(socket_ref){
440 -- socket_port->count;
441 // release if empty
442 if(socket_port->count <= 0){
443 // destroy the map
444 socket_port_map_destroy(&socket_port->map);
445 // release the port
446 socket_ports_exclude(global_sockets, socket->port);
447 }else{
448 // remove
449 socket_port_map_exclude(&socket_port->map, socket->key, socket->key_length);
450 }
451 }
452 }
453 socket->port = 0;
454 socket->key = NULL;
455 socket->key_length = 0;
456 }
457}
458
459int socket_port_add(socket_ports_ref global_sockets, int port, socket_core_ref socket, const char * key, size_t key_length){
460 ERROR_DECLARE;
461
462 socket_port_ref socket_port;
463
464 // find ports
465 socket_port = socket_ports_find(global_sockets, port);
466 if(! socket_port){
467 return ENOENT;
468 }
469 // add the socket
470 ERROR_PROPAGATE(socket_port_add_core(socket_port, socket, key, key_length));
471 socket->port = port;
472 return EOK;
473}
474
475/** @}
476 */
Note: See TracBrowser for help on using the repository browser.