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

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

Move socket_codes.h from libsocket to libc.

Remove stuff that was probably inappropriatelly taken from a Linux kernel
header.

Remove unneeded AF and PF codes.

Start assigning AF and PF codes sequentially. (These are not standardized.)

  • 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
39#include <stdint.h>
40#include <stdlib.h>
41#include <errno.h>
42#include <err.h>
43
44#include <in.h>
45#include <inet.h>
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.