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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c7a8442 was c7a8442, checked in by Jakub Jermar <jakub@…>, 15 years ago

Move net_modules.[ch] to the standard library. Note that this functionality is
not directly related to networking so the next step regarding these two files
would be to somehow merge its functionality with what we already have in lib c.

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