source: mainline/uspace/lib/net/generic/socket_core.c@ c69d327

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

Move the common packet interface to libc.

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