source: mainline/uspace/srv/net/socket/socket_core.c@ 91001e2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 91001e2 was ede63e4, checked in by Lukas Mejdrech <lukasmejdrech@…>, 15 years ago
  • socket identifier generation moved to libsocket, + data fragment size fix and enhancement, + [ICMP|TCP|UDP]_HEADER_SIZE definition, + int_map_update() function to alter item key
  • Property mode set to 100644
File size: 14.9 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 "../err.h"
41
42#include "../include/in.h"
43#include "../include/inet.h"
44
45#include "../include/socket_codes.h"
46#include "../include/socket_errno.h"
47
48#include "../structures/dynamic_fifo.h"
49#include "../structures/int_map.h"
50#include "../structures/packet/packet.h"
51#include "../structures/packet/packet_client.h"
52
53#include "../modules.h"
54
55#include "socket_core.h"
56
57/** Maximum number of random attempts to find a new socket identifier before switching to the sequence.
58 */
59#define SOCKET_ID_TRIES 100
60
61/** Bound port sockets.
62 */
63struct socket_port{
64 /** The bound sockets map.
65 */
66 socket_port_map_t map;
67 /** The bound sockets count.
68 */
69 int count;
70};
71
72/** Binds the socket to the port.
73 * The SOCKET_MAP_KEY_LISTENING key identifier is used.
74 * @param[in] global_sockets The global sockets to be updated.
75 * @param[in] socket The socket to be added.
76 * @param[in] port The port number to be bound to.
77 * @returns EOK on success.
78 * @returns ENOMEM if there is not enough memory left.
79 * @returns Other error codes as defined for the socket_ports_add() function.
80 */
81int socket_bind_insert( socket_ports_ref global_sockets, socket_core_ref socket, int port );
82
83/** Destroys the socket.
84 * If the socket is bound, the port is released.
85 * Releases all buffered packets, calls the release function and removes the socket from the local sockets.
86 * @param[in] packet_phone The packet server phone to release buffered packets.
87 * @param[in] socket The socket to be destroyed.
88 * @param[in,out] local_sockets The local sockets to be updated.
89 * @param[in,out] global_sockets The global sockets to be updated.
90 * @param[in] socket_release The client release callback function.
91 */
92void 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 ));
93
94/** Adds the socket to a socket port.
95 * @param[in,out] socket_port The socket port structure.
96 * @param[in] socket The socket to be added.
97 * @param[in] key The socket key identifier.
98 * @param[in] key_length The socket key length.
99 * @returns EOK on success.
100 * @returns ENOMEM if there is not enough memory left.
101 */
102int socket_port_add_core( socket_port_ref socket_port, socket_core_ref socket, const char * key, size_t key_length );
103
104/** Tries to find a new free socket identifier.
105 * @param[in] local_sockets The local sockets to be searched.
106 * @param[in] positive A value indicating whether a positive identifier is requested. A negative identifier is requested if set to false.
107 * @returns The new socket identifier.
108 * @returns ELIMIT if there is no socket identifier available.
109 */
110static int socket_generate_new_id( socket_cores_ref local_sockets, int positive );
111
112INT_MAP_IMPLEMENT( socket_cores, socket_core_t );
113
114GENERIC_CHAR_MAP_IMPLEMENT( socket_port_map, socket_core_ref );
115
116INT_MAP_IMPLEMENT( socket_ports, socket_port_t );
117
118void socket_cores_release( int packet_phone, socket_cores_ref local_sockets, socket_ports_ref global_sockets, void ( * socket_release )( socket_core_ref socket )){
119 if( socket_cores_is_valid( local_sockets )){
120 int index;
121
122 local_sockets->magic = 0;
123 for( index = 0; index < local_sockets->next; ++ index ){
124 if( socket_cores_item_is_valid( &( local_sockets->items[ index ] ))){
125 local_sockets->items[ index ].magic = 0;
126 if( local_sockets->items[ index ].value ){
127 socket_destroy_core( packet_phone, local_sockets->items[ index ].value, local_sockets, global_sockets, socket_release );
128 free( local_sockets->items[ index ].value );
129 local_sockets->items[ index ].value = NULL;
130 }
131 }
132 }
133 free( local_sockets->items );
134 }
135}
136
137void 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 )){
138 int packet_id;
139
140 // if bound
141 if( socket->port ){
142 // release the port
143 socket_port_release( global_sockets, socket );
144 }
145 // release all received packets
146 while(( packet_id = dyn_fifo_pop( & socket->received )) >= 0 ){
147 pq_release( packet_phone, packet_id );
148 }
149 dyn_fifo_destroy( & socket->received );
150 dyn_fifo_destroy( & socket->accepted );
151 if( socket_release ){
152 socket_release( socket );
153 }
154 socket_cores_exclude( local_sockets, socket->socket_id );
155}
156
157int 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 ){
158 socket_core_ref socket;
159 socket_port_ref socket_port;
160 struct sockaddr * address;
161 struct sockaddr_in * address_in;
162
163 if( addrlen < sizeof( struct sockaddr )) return EINVAL;
164 address = ( struct sockaddr * ) addr;
165 switch( address->sa_family ){
166 case AF_INET:
167 if( addrlen != sizeof( struct sockaddr_in )) return EINVAL;
168 address_in = ( struct sockaddr_in * ) addr;
169 // find the socket
170 socket = socket_cores_find( local_sockets, socket_id );
171 if( ! socket ) return ENOTSOCK;
172 // bind a free port?
173 if( address_in->sin_port <= 0 ){
174 return socket_bind_free_port( global_sockets, socket, free_ports_start, free_ports_end, last_used_port );
175 }
176 // try to find the port
177 socket_port = socket_ports_find( global_sockets, ntohs( address_in->sin_port ));
178 if( socket_port ){
179 // already used
180 return EADDRINUSE;
181 }
182 // if bound
183 if( socket->port ){
184 // release the port
185 socket_port_release( global_sockets, socket );
186 }
187 socket->port = -1;
188 return socket_bind_insert( global_sockets, socket, ntohs( address_in->sin_port ));
189 break;
190 // TODO IPv6
191 }
192 return EAFNOSUPPORT;
193}
194
195int 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 ){
196 int index;
197
198 // from the last used one
199 index = last_used_port;
200 do{
201 ++ index;
202 // til the range end
203 if( index >= free_ports_end ){
204 // start from the range beginning
205 index = free_ports_start - 1;
206 do{
207 ++ index;
208 // til the last used one
209 if( index >= last_used_port ){
210 // none found
211 return ENOTCONN;
212 }
213 }while( socket_ports_find( global_sockets, index ) != NULL );
214 // found, break immediately
215 break;
216 }
217 }while( socket_ports_find( global_sockets, index ) != NULL );
218 return socket_bind_insert( global_sockets, socket, index );
219}
220
221int socket_bind_insert( socket_ports_ref global_sockets, socket_core_ref socket, int port ){
222 ERROR_DECLARE;
223
224 socket_port_ref socket_port;
225
226 // create a wrapper
227 socket_port = malloc( sizeof( * socket_port ));
228 if( ! socket_port ) return ENOMEM;
229 socket_port->count = 0;
230 if( ERROR_OCCURRED( socket_port_map_initialize( & socket_port->map ))
231 || ERROR_OCCURRED( socket_port_add_core( socket_port, socket, SOCKET_MAP_KEY_LISTENING, 0 ))){
232 socket_port_map_destroy( & socket_port->map );
233 free( socket_port );
234 return ERROR_CODE;
235 }
236 // register the incomming port
237 ERROR_CODE = socket_ports_add( global_sockets, port, socket_port );
238 if( ERROR_CODE < 0 ){
239 socket_port_map_destroy( & socket_port->map );
240 free( socket_port );
241 return ERROR_CODE;
242 }
243 socket->port = port;
244 return EOK;
245}
246
247
248static int socket_generate_new_id( socket_cores_ref local_sockets, int positive ){
249 int socket_id;
250 int count;
251
252 count = 0;
253// socket_id = socket_globals.last_id;
254 do{
255 if( count < SOCKET_ID_TRIES ){
256 socket_id = rand() % INT_MAX;
257 ++ count;
258 }else if( count == SOCKET_ID_TRIES ){
259 socket_id = 1;
260 ++ count;
261 // only this branch for last_id
262 }else{
263 if( socket_id < INT_MAX ){
264 ++ socket_id;
265/* }else if( socket_globals.last_id ){
266* socket_globals.last_id = 0;
267* socket_id = 1;
268*/ }else{
269 return ELIMIT;
270 }
271 }
272 }while( socket_cores_find( local_sockets, (( positive ? 1 : -1 ) * socket_id )));
273// last_id = socket_id
274 return socket_id;
275}
276
277int socket_create( socket_cores_ref local_sockets, int app_phone, void * specific_data, int * socket_id ){
278 ERROR_DECLARE;
279
280 socket_core_ref socket;
281 int res;
282 int positive;
283
284 if( ! socket_id ) return EINVAL;
285 // store the socket
286 if( * socket_id <= 0 ){
287 positive = ( * socket_id == 0 );
288 * socket_id = socket_generate_new_id( local_sockets, positive );
289 if( * socket_id <= 0 ){
290 return * socket_id;
291 }
292 if( ! positive ){
293 * socket_id *= -1;
294 }
295 }else if( socket_cores_find( local_sockets, * socket_id )){
296 return EEXIST;
297 }
298 socket = ( socket_core_ref ) malloc( sizeof( * socket ));
299 if( ! socket ) return ENOMEM;
300 // initialize
301 socket->phone = app_phone;
302 socket->port = -1;
303 socket->key = NULL;
304 socket->key_length = 0;
305 socket->specific_data = specific_data;
306 if( ERROR_OCCURRED( dyn_fifo_initialize( & socket->received, SOCKET_INITIAL_RECEIVED_SIZE ))){
307 free( socket );
308 return ERROR_CODE;
309 }
310 if( ERROR_OCCURRED( dyn_fifo_initialize( & socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE ))){
311 dyn_fifo_destroy( & socket->received );
312 free( socket );
313 return ERROR_CODE;
314 }
315 socket->socket_id = * socket_id;
316 res = socket_cores_add( local_sockets, socket->socket_id, socket );
317 if( res < 0 ){
318 dyn_fifo_destroy( & socket->received );
319 dyn_fifo_destroy( & socket->accepted );
320 free( socket );
321 return res;
322 }
323 return EOK;
324}
325
326int 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 )){
327 socket_core_ref socket;
328 int accepted_id;
329
330 // find the socket
331 socket = socket_cores_find( local_sockets, socket_id );
332 if( ! socket ) return ENOTSOCK;
333 // destroy all accepted sockets
334 while(( accepted_id = dyn_fifo_pop( & socket->accepted )) >= 0 ){
335 socket_destroy( packet_phone, accepted_id, local_sockets, global_sockets, socket_release );
336 }
337 socket_destroy_core( packet_phone, socket, local_sockets, global_sockets, socket_release );
338 return EOK;
339}
340
341int socket_reply_packets( packet_t packet, size_t * length ){
342 ERROR_DECLARE;
343
344 packet_t next_packet;
345 size_t fragments;
346 size_t * lengths;
347 size_t index;
348
349 if( ! length ) return EBADMEM;
350 next_packet = pq_next( packet );
351 if( ! next_packet ){
352 // write all if only one fragment
353 ERROR_PROPAGATE( data_reply( packet_get_data( packet ), packet_get_data_length( packet )));
354 // store the total length
355 * length = packet_get_data_length( packet );
356 }else{
357 // count the packet fragments
358 fragments = 1;
359 next_packet = pq_next( packet );
360 while(( next_packet = pq_next( next_packet ))){
361 ++ fragments;
362 }
363 // compute and store the fragment lengths
364 lengths = ( size_t * ) malloc( sizeof( size_t ) * fragments + sizeof( size_t ));
365 if( ! lengths ) return ENOMEM;
366 lengths[ 0 ] = packet_get_data_length( packet );
367 lengths[ fragments ] = lengths[ 0 ];
368 next_packet = pq_next( packet );
369 for( index = 1; index < fragments; ++ index ){
370 lengths[ index ] = packet_get_data_length( next_packet );
371 lengths[ fragments ] += lengths[ index ];
372 next_packet = pq_next( packet );
373 }while( next_packet );
374 // write the fragment lengths
375 ERROR_PROPAGATE( data_reply( lengths, sizeof( int ) * ( fragments + 1 )));
376 next_packet = packet;
377 // write the fragments
378 for( index = 0; index < fragments; ++ index ){
379 ERROR_PROPAGATE( data_reply( packet_get_data( next_packet ), lengths[ index ] ));
380 next_packet = pq_next( next_packet );
381 }while( next_packet );
382 // store the total length
383 * length = lengths[ fragments ];
384 free( lengths );
385 }
386 return EOK;
387}
388
389socket_core_ref socket_port_find( socket_ports_ref global_sockets, int port, const char * key, size_t key_length ){
390 socket_port_ref socket_port;
391 socket_core_ref * socket_ref;
392
393 socket_port = socket_ports_find( global_sockets, port );
394 if( socket_port && ( socket_port->count > 0 )){
395 socket_ref = socket_port_map_find( & socket_port->map, key, key_length );
396 if( socket_ref ){
397 return * socket_ref;
398 }
399 }
400 return NULL;
401}
402
403void socket_port_release( socket_ports_ref global_sockets, socket_core_ref socket ){
404 socket_port_ref socket_port;
405 socket_core_ref * socket_ref;
406
407 if( socket->port ){
408 // find ports
409 socket_port = socket_ports_find( global_sockets, socket->port );
410 if( socket_port ){
411 // find the socket
412 socket_ref = socket_port_map_find( & socket_port->map, socket->key, socket->key_length );
413 if( socket_ref ){
414 -- socket_port->count;
415 // release if empty
416 if( socket_port->count <= 0 ){
417 // destroy the map
418 socket_port_map_destroy( & socket_port->map );
419 // release the port
420 socket_ports_exclude( global_sockets, socket->port );
421 }else{
422 // remove
423 socket_port_map_exclude( & socket_port->map, socket->key, socket->key_length );
424 }
425 }
426 }
427 socket->port = 0;
428 socket->key = NULL;
429 socket->key_length = 0;
430 }
431}
432
433int socket_port_add( socket_ports_ref global_sockets, int port, socket_core_ref socket, const char * key, size_t key_length ){
434 ERROR_DECLARE;
435
436 socket_port_ref socket_port;
437
438 // find ports
439 socket_port = socket_ports_find( global_sockets, port );
440 if( ! socket_port ) return ENOENT;
441 // add the socket
442 ERROR_PROPAGATE( socket_port_add_core( socket_port, socket, key, key_length ));
443 socket->port = port;
444 return EOK;
445}
446
447int socket_port_add_core( socket_port_ref socket_port, socket_core_ref socket, const char * key, size_t key_length ){
448 ERROR_DECLARE;
449
450 socket_core_ref * socket_ref;
451
452 // create a wrapper
453 socket_ref = malloc( sizeof( * socket_ref ));
454 if( ! socket_ref ) return ENOMEM;
455 * socket_ref = socket;
456 // add the wrapper
457 if( ERROR_OCCURRED( socket_port_map_add( & socket_port->map, key, key_length, socket_ref ))){
458 free( socket_ref );
459 return ERROR_CODE;
460 }
461 ++ socket_port->count;
462 socket->key = key;
463 socket->key_length = key_length;
464 return EOK;
465}
466
467/** @}
468 */
Note: See TracBrowser for help on using the repository browser.