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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since daecfed3 was 21580dd, checked in by Lukas Mejdrech <lukas@…>, 16 years ago

Merged with network branch svn://svn.helenos.org/HelenOS/branches/network revision 4759; ipc_share_* and ipc_data_* changed to async_*; client connection in module.c returns on IPC_M_PHONE_HUNGUP; * Qemu scripts renamed to net-qe.*; (the dp8390 does not respond)

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