~martin-decky/helenos/rcu

« back to all changes in this revision

Viewing changes to uspace/srv/net/socket/socket_core.c

  • Committer: Pavel Rimsky
  • Date: 2010-02-20 20:54:53 UTC
  • mfrom: (292 head)
  • mto: This revision was merged to the branch mainline in revision 296.
  • Revision ID: pavel@pavel-laptop-20100220205453-70sim280j709dgp3
Synchronize with head.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 */
 
63
struct 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
 */
 
81
int     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
 */
 
92
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 ));
 
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
 */
 
102
int     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
 */
 
110
static int      socket_generate_new_id( socket_cores_ref local_sockets, int positive );
 
111
 
 
112
INT_MAP_IMPLEMENT( socket_cores, socket_core_t );
 
113
 
 
114
GENERIC_CHAR_MAP_IMPLEMENT( socket_port_map, socket_core_ref );
 
115
 
 
116
INT_MAP_IMPLEMENT( socket_ports, socket_port_t );
 
117
 
 
118
void 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
 
 
137
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 )){
 
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
 
 
157
int 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
 
 
195
int 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
 
 
221
int 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
 
 
248
static 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
 
 
277
int 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
 
 
326
int 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
 
 
341
int 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
 
 
389
socket_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
 
 
403
void 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
 
 
433
int 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
 
 
447
int 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
 */