Rev 4708 | Rev 4729 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4578 | mejdrech | 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 application program interface (API) implementation. |
||
35 | * @see socket.h for more information. |
||
36 | * This is a part of the network application library. |
||
37 | */ |
||
38 | |||
39 | #include <assert.h> |
||
40 | #include <async.h> |
||
4582 | mejdrech | 41 | #include <fibril_sync.h> |
4578 | mejdrech | 42 | |
43 | #include <ipc/services.h> |
||
44 | |||
45 | #include "../err.h" |
||
46 | #include "../modules.h" |
||
47 | |||
48 | #include "../include/in.h" |
||
49 | #include "../include/socket.h" |
||
50 | #include "../include/socket_errno.h" |
||
51 | |||
4589 | mejdrech | 52 | #include "../structures/dynamic_fifo.h" |
4578 | mejdrech | 53 | #include "../structures/int_map.h" |
54 | |||
55 | #include "socket_messages.h" |
||
56 | |||
4701 | mejdrech | 57 | /** Initial received packet queue size. |
58 | */ |
||
4589 | mejdrech | 59 | #define SOCKET_INITIAL_RECEIVED_SIZE 4 |
4701 | mejdrech | 60 | |
61 | /** Maximum received packet queue size. |
||
62 | */ |
||
4589 | mejdrech | 63 | #define SOCKET_MAX_RECEIVED_SIZE 64 |
64 | |||
4701 | mejdrech | 65 | /** Initial waiting sockets queue size. |
66 | */ |
||
4589 | mejdrech | 67 | #define SOCKET_INITIAL_ACCEPTED_SIZE 1 |
4701 | mejdrech | 68 | |
69 | /** Maximum waiting sockets queue size. |
||
70 | */ |
||
4589 | mejdrech | 71 | #define SOCKET_MAX_ACCEPTED_SIZE 64 |
72 | |||
4701 | mejdrech | 73 | /** Type definition of the socket specific data. |
74 | * @see socket |
||
75 | */ |
||
4578 | mejdrech | 76 | typedef struct socket socket_t; |
77 | |||
4701 | mejdrech | 78 | /** Type definition of the socket specific data pointer. |
79 | * @see socket |
||
80 | */ |
||
81 | typedef socket_t * socket_ref; |
||
82 | |||
83 | /** Socket specific data. |
||
4726 | mejdrech | 84 | * Each socket lock locks only its structure part and any number of them may be locked simultaneously. |
4701 | mejdrech | 85 | */ |
4578 | mejdrech | 86 | struct socket{ |
4701 | mejdrech | 87 | /** Socket identifier. |
88 | */ |
||
4578 | mejdrech | 89 | int socket_id; |
4701 | mejdrech | 90 | /** Parent module phone. |
91 | */ |
||
4578 | mejdrech | 92 | int phone; |
4701 | mejdrech | 93 | /** Parent module service. |
94 | */ |
||
4578 | mejdrech | 95 | services_t service; |
4701 | mejdrech | 96 | /** Underlying protocol header size. |
97 | * Sending and receiving optimalization. |
||
98 | */ |
||
4708 | mejdrech | 99 | size_t header_size; |
4701 | mejdrech | 100 | /** Packet data fragment size. |
4726 | mejdrech | 101 | * Sending optimalization. |
4701 | mejdrech | 102 | */ |
4708 | mejdrech | 103 | size_t data_fragment_size; |
4726 | mejdrech | 104 | /** Sending safety lock. |
105 | * Locks the header_size and data_fragment_size attributes. |
||
106 | */ |
||
107 | fibril_rwlock_t sending_lock; |
||
4701 | mejdrech | 108 | /** Received packets queue. |
109 | */ |
||
4589 | mejdrech | 110 | dyn_fifo_t received; |
4701 | mejdrech | 111 | /** Received packets safety lock. |
4726 | mejdrech | 112 | * Used for receiving and receive notifications. |
113 | * Locks the received attribute. |
||
4701 | mejdrech | 114 | */ |
4582 | mejdrech | 115 | fibril_mutex_t receive_lock; |
4701 | mejdrech | 116 | /** Received packets signaling. |
4726 | mejdrech | 117 | * Signaled upon receive notification. |
4701 | mejdrech | 118 | */ |
4582 | mejdrech | 119 | fibril_condvar_t receive_signal; |
4701 | mejdrech | 120 | /** Waiting sockets queue. |
121 | */ |
||
4589 | mejdrech | 122 | dyn_fifo_t accepted; |
4701 | mejdrech | 123 | /** Waiting sockets safety lock. |
4726 | mejdrech | 124 | * Used for accepting and accept notifications. |
125 | * Locks the accepted attribute. |
||
4701 | mejdrech | 126 | */ |
4582 | mejdrech | 127 | fibril_mutex_t accept_lock; |
4701 | mejdrech | 128 | /** Waiting sockets signaling. |
4726 | mejdrech | 129 | * Signaled upon accept notification. |
4701 | mejdrech | 130 | */ |
4582 | mejdrech | 131 | fibril_condvar_t accept_signal; |
4726 | mejdrech | 132 | /** The number of blocked functions called. |
133 | * Used while waiting for the received packets or accepted sockets. |
||
134 | */ |
||
135 | int blocked; |
||
4578 | mejdrech | 136 | }; |
137 | |||
4701 | mejdrech | 138 | /** Sockets map. |
139 | * Maps socket identifiers to the socket specific data. |
||
140 | * @see int_map.h |
||
141 | */ |
||
4578 | mejdrech | 142 | INT_MAP_DECLARE( sockets, socket_t ); |
143 | |||
4701 | mejdrech | 144 | /** Socket client library global data. |
145 | */ |
||
4726 | mejdrech | 146 | static struct socket_client_globals { |
4701 | mejdrech | 147 | /** TCP module phone. |
148 | */ |
||
4578 | mejdrech | 149 | int tcp_phone; |
4701 | mejdrech | 150 | /** UDP module phone. |
151 | */ |
||
4578 | mejdrech | 152 | int udp_phone; |
4701 | mejdrech | 153 | /** Active sockets. |
154 | */ |
||
4578 | mejdrech | 155 | sockets_ref sockets; |
4726 | mejdrech | 156 | /** Safety lock. |
157 | * Write lock is used only for adding or removing sockets. |
||
158 | * When locked for writing, no other socket locks need to be locked. |
||
159 | * When locked for reading, any other socket locks may be locked. |
||
160 | * No socket lock may be locked if this lock is unlocked. |
||
161 | */ |
||
162 | fibril_rwlock_t lock; |
||
163 | } socket_globals = { |
||
164 | .tcp_phone = -1, |
||
165 | .udp_phone = -1, |
||
166 | .sockets = NULL, |
||
167 | .lock = { |
||
168 | .readers = 0, |
||
169 | .writers = 0, |
||
170 | .waiters = { |
||
171 | .prev = & socket_globals.lock.waiters, |
||
172 | .next = & socket_globals.lock.waiters |
||
173 | } |
||
174 | } |
||
175 | }; |
||
4578 | mejdrech | 176 | |
177 | INT_MAP_IMPLEMENT( sockets, socket_t ); |
||
178 | |||
4701 | mejdrech | 179 | /** Returns the TCP module phone. |
180 | * Connects to the TCP module if necessary. |
||
181 | * @returns The TCP module phone. |
||
182 | */ |
||
4708 | mejdrech | 183 | static int socket_get_tcp_phone( void ); |
4701 | mejdrech | 184 | |
185 | /** Returns the UDP module phone. |
||
186 | * Connects to the UDP module if necessary. |
||
187 | * @returns The UDP module phone. |
||
188 | */ |
||
4708 | mejdrech | 189 | static int socket_get_udp_phone( void ); |
4701 | mejdrech | 190 | |
191 | /** Returns the active sockets. |
||
192 | * @returns The active sockets. |
||
193 | */ |
||
4708 | mejdrech | 194 | static sockets_ref socket_get_sockets( void ); |
4701 | mejdrech | 195 | |
196 | /** Default thread for new connections. |
||
197 | * @param iid The initial message identifier. Input parameter. |
||
198 | * @param icall The initial message call structure. Input parameter. |
||
199 | */ |
||
4578 | mejdrech | 200 | void socket_connection( ipc_callid_t iid, ipc_call_t * icall ); |
4701 | mejdrech | 201 | |
202 | /** Sends message to the socket parent module with specified data. |
||
203 | * @param socket_id Socket identifier. Input parameter. |
||
204 | * @param message The action message. Input parameter. |
||
205 | * @param arg2 The second message parameter. Input parameter. |
||
206 | * @param data The data to be sent. Input parameter. |
||
207 | * @param datalength The data length. Input parameter. |
||
208 | * @returns EOK on success. |
||
209 | * @returns ENOTSOCK if the socket is not found. |
||
210 | * @returns EBADMEM if the data parameter is NULL. |
||
211 | * @returns NO_DATA if the datalength parameter is zero (0). |
||
212 | * @returns Other error codes as defined for the spcific message. |
||
213 | */ |
||
4708 | mejdrech | 214 | int socket_send_data( int socket_id, ipcarg_t message, ipcarg_t arg2, const void * data, size_t datalength ); |
4701 | mejdrech | 215 | |
216 | /** Initializes a new socket specific data. |
||
217 | * @param socket The socket to be initialized. Input/output parameter. |
||
218 | * @param socket_id The new socket identifier. Input parameter. |
||
219 | * @param phone The parent module phone. Input parameter. |
||
220 | * @param service The parent module service. Input parameter. |
||
221 | */ |
||
4578 | mejdrech | 222 | void socket_initialize( socket_ref socket, int socket_id, int phone, services_t service ); |
4701 | mejdrech | 223 | |
224 | /** Clears and destroys the socket. |
||
225 | * @param socket The socket to be destroyed. Input parameter. |
||
226 | */ |
||
4589 | mejdrech | 227 | void socket_destroy( socket_ref socket ); |
4701 | mejdrech | 228 | |
229 | /** Receives data via the socket. |
||
230 | * @param message The action message. Input parameter. |
||
231 | * @param socket_id Socket identifier. Input parameter. |
||
232 | * @param data The data buffer to be filled. Output parameter. |
||
233 | * @param datalength The data length. Input parameter. |
||
234 | * @param flags Various receive flags. Input parameter. |
||
235 | * @param fromaddr The source address. May be NULL for connected sockets. Output parameter. |
||
236 | * @param addrlen The address length. The maximum address length is read. The actual address length is set. Used only if fromaddr is not NULL. Input/output parameter. |
||
237 | * @returns EOK on success. |
||
238 | * @returns ENOTSOCK if the socket is not found. |
||
239 | * @returns EBADMEM if the data parameter is NULL. |
||
240 | * @returns NO_DATA if the datalength or addrlen parameter is zero (0). |
||
241 | * @returns Other error codes as defined for the spcific message. |
||
242 | */ |
||
4708 | mejdrech | 243 | int recvfrom_core( ipcarg_t message, int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ); |
4701 | mejdrech | 244 | |
245 | /** Sends data via the socket to the remote address. |
||
246 | * Binds the socket to a free port if not already connected/bound. |
||
247 | * @param message The action message. Input parameter. |
||
248 | * @param socket_id Socket identifier. Input parameter. |
||
249 | * @param data The data to be sent. Input parameter. |
||
250 | * @param datalength The data length. Input parameter. |
||
251 | * @param flags Various send flags. Input parameter. |
||
252 | * @param toaddr The destination address. May be NULL for connected sockets. Input parameter. |
||
253 | * @param addrlen The address length. Used only if toaddr is not NULL. Input parameter. |
||
254 | * @returns EOK on success. |
||
255 | * @returns ENOTSOCK if the socket is not found. |
||
256 | * @returns EBADMEM if the data or toaddr parameter is NULL. |
||
257 | * @returns NO_DATA if the datalength or the addrlen parameter is zero (0). |
||
258 | * @returns Other error codes as defined for the NET_SOCKET_SENDTO message. |
||
259 | */ |
||
4708 | mejdrech | 260 | int sendto_core( ipcarg_t message, int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ); |
4578 | mejdrech | 261 | |
4708 | mejdrech | 262 | static int socket_get_tcp_phone( void ){ |
4578 | mejdrech | 263 | if( socket_globals.tcp_phone < 0 ){ |
264 | socket_globals.tcp_phone = bind_service( SERVICE_TCP, 0, 0, SERVICE_TCP, socket_connection ); |
||
265 | } |
||
266 | return socket_globals.tcp_phone; |
||
267 | } |
||
268 | |||
4708 | mejdrech | 269 | static int socket_get_udp_phone( void ){ |
4578 | mejdrech | 270 | if( socket_globals.udp_phone < 0 ){ |
271 | socket_globals.udp_phone = bind_service( SERVICE_UDP, 0, 0, SERVICE_UDP, socket_connection ); |
||
272 | } |
||
273 | return socket_globals.udp_phone; |
||
274 | } |
||
275 | |||
4708 | mejdrech | 276 | static sockets_ref socket_get_sockets( void ){ |
4578 | mejdrech | 277 | if( ! socket_globals.sockets ){ |
278 | socket_globals.sockets = ( sockets_ref ) malloc( sizeof( sockets_t )); |
||
279 | if( ! socket_globals.sockets ) return NULL; |
||
280 | if( sockets_initialize( socket_globals.sockets ) != EOK ){ |
||
281 | free( socket_globals.sockets ); |
||
282 | socket_globals.sockets = NULL; |
||
283 | } |
||
284 | } |
||
285 | return socket_globals.sockets; |
||
286 | } |
||
287 | |||
288 | void socket_initialize( socket_ref socket, int socket_id, int phone, services_t service ){ |
||
289 | socket->socket_id = socket_id; |
||
290 | socket->phone = phone; |
||
291 | socket->service = service; |
||
4589 | mejdrech | 292 | dyn_fifo_initialize( & socket->received, SOCKET_INITIAL_RECEIVED_SIZE ); |
293 | dyn_fifo_initialize( & socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE ); |
||
4582 | mejdrech | 294 | fibril_mutex_initialize( & socket->receive_lock ); |
295 | fibril_condvar_initialize( & socket->receive_signal ); |
||
296 | fibril_mutex_initialize( & socket->accept_lock ); |
||
297 | fibril_condvar_initialize( & socket->accept_signal ); |
||
4726 | mejdrech | 298 | fibril_rwlock_initialize( & socket->sending_lock ); |
4578 | mejdrech | 299 | } |
300 | |||
301 | void socket_connection( ipc_callid_t iid, ipc_call_t * icall ){ |
||
302 | ERROR_DECLARE; |
||
303 | |||
304 | ipc_callid_t callid; |
||
305 | ipc_call_t call; |
||
306 | socket_ref socket; |
||
307 | socket_ref new_socket; |
||
308 | |||
309 | while( true ){ |
||
310 | |||
311 | callid = async_get_call( & call ); |
||
312 | switch( IPC_GET_METHOD( call )){ |
||
4726 | mejdrech | 313 | // TODO remember the data_fragment_size |
4578 | mejdrech | 314 | case NET_SOCKET_RECEIVED: |
4726 | mejdrech | 315 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4589 | mejdrech | 316 | // find the socket |
4701 | mejdrech | 317 | socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call )); |
4578 | mejdrech | 318 | if( ! socket ){ |
319 | ERROR_CODE = ENOTSOCK; |
||
4726 | mejdrech | 320 | }else{ |
321 | fibril_mutex_lock( & socket->receive_lock ); |
||
322 | // push the number of received packet fragments |
||
323 | if( ! ERROR_OCCURRED( dyn_fifo_push( & socket->received, SOCKET_GET_DATA_FRAGMENTS( call ), SOCKET_MAX_RECEIVED_SIZE ))){ |
||
324 | // signal the received packet |
||
325 | fibril_condvar_signal( & socket->receive_signal ); |
||
326 | } |
||
327 | fibril_mutex_unlock( & socket->receive_lock ); |
||
4578 | mejdrech | 328 | } |
4726 | mejdrech | 329 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4578 | mejdrech | 330 | break; |
331 | case NET_SOCKET_ACCEPTED: |
||
4726 | mejdrech | 332 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4589 | mejdrech | 333 | // find the socket |
4701 | mejdrech | 334 | socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call )); |
4578 | mejdrech | 335 | if( ! socket ){ |
336 | ERROR_CODE = ENOTSOCK; |
||
4589 | mejdrech | 337 | }else{ |
4726 | mejdrech | 338 | // create a new scoket |
339 | new_socket = ( socket_ref ) malloc( sizeof( socket_t )); |
||
340 | if( ! new_socket ){ |
||
341 | ERROR_CODE = ENOMEM; |
||
4589 | mejdrech | 342 | }else{ |
4726 | mejdrech | 343 | socket_initialize( new_socket, SOCKET_GET_SOCKET_ID( call ), socket->phone, socket->service ); |
344 | ERROR_CODE = sockets_add( socket_get_sockets(), new_socket->socket_id, new_socket ); |
||
345 | if( ERROR_CODE < 0 ){ |
||
346 | free( new_socket ); |
||
347 | }else{ |
||
348 | // push the new socket identifier |
||
349 | fibril_mutex_lock( & socket->accept_lock ); |
||
350 | if( ERROR_OCCURRED( dyn_fifo_push( & socket->accepted, new_socket->socket_id, SOCKET_MAX_ACCEPTED_SIZE ))){ |
||
351 | sockets_exclude( socket_get_sockets(), new_socket->socket_id ); |
||
352 | free( new_socket ); |
||
353 | }else{ |
||
354 | // signal the accepted socket |
||
355 | fibril_condvar_signal( & socket->accept_signal ); |
||
356 | } |
||
357 | fibril_mutex_unlock( & socket->accept_lock ); |
||
358 | ERROR_CODE = EOK; |
||
359 | } |
||
4589 | mejdrech | 360 | } |
4578 | mejdrech | 361 | } |
4726 | mejdrech | 362 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4578 | mejdrech | 363 | break; |
4726 | mejdrech | 364 | // TODO obsolete? |
4589 | mejdrech | 365 | case NET_SOCKET_DATA_FRAGMENT_SIZE: |
4726 | mejdrech | 366 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4589 | mejdrech | 367 | // find the socket |
4701 | mejdrech | 368 | socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call )); |
4578 | mejdrech | 369 | if( ! socket ){ |
370 | ERROR_CODE = ENOTSOCK; |
||
4726 | mejdrech | 371 | }else{ |
372 | fibril_rwlock_write_lock( & socket->sending_lock ); |
||
373 | // set the data fragment size |
||
374 | socket->data_fragment_size = SOCKET_GET_DATA_FRAGMENT_SIZE( call ); |
||
375 | fibril_rwlock_write_unlock( & socket->sending_lock ); |
||
376 | ERROR_CODE = EOK; |
||
4578 | mejdrech | 377 | } |
4726 | mejdrech | 378 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4578 | mejdrech | 379 | break; |
4589 | mejdrech | 380 | default: |
4578 | mejdrech | 381 | ERROR_CODE = ENOTSUP; |
382 | } |
||
4708 | mejdrech | 383 | ipc_answer_0( callid, ( ipcarg_t ) ERROR_CODE ); |
4578 | mejdrech | 384 | } |
385 | } |
||
386 | |||
387 | int socket( int domain, int type, int protocol ){ |
||
4589 | mejdrech | 388 | ERROR_DECLARE; |
389 | |||
4578 | mejdrech | 390 | socket_ref socket; |
391 | int phone; |
||
392 | int socket_id; |
||
393 | services_t service; |
||
394 | |||
4589 | mejdrech | 395 | // find the appropriate service |
4578 | mejdrech | 396 | switch( domain ){ |
397 | case PF_INET: |
||
398 | switch( type ){ |
||
399 | case SOCK_STREAM: |
||
400 | if( ! protocol ) protocol = IPPROTO_TCP; |
||
401 | switch( protocol ){ |
||
402 | case IPPROTO_TCP: |
||
403 | phone = socket_get_tcp_phone(); |
||
404 | service = SERVICE_TCP; |
||
405 | break; |
||
406 | default: |
||
407 | return EPROTONOSUPPORT; |
||
408 | } |
||
409 | break; |
||
410 | case SOCK_DGRAM: |
||
411 | if( ! protocol ) protocol = IPPROTO_UDP; |
||
412 | switch( protocol ){ |
||
413 | case IPPROTO_UDP: |
||
414 | phone = socket_get_udp_phone(); |
||
415 | service = SERVICE_UDP; |
||
416 | break; |
||
417 | default: |
||
418 | return EPROTONOSUPPORT; |
||
419 | } |
||
420 | break; |
||
421 | case SOCK_RAW: |
||
422 | default: |
||
423 | return ESOCKTNOSUPPORT; |
||
424 | } |
||
425 | break; |
||
426 | // TODO IPv6 |
||
427 | default: |
||
428 | return EPFNOSUPPORT; |
||
429 | } |
||
4589 | mejdrech | 430 | // create a new socket structure |
4578 | mejdrech | 431 | socket = ( socket_ref ) malloc( sizeof( socket_t )); |
432 | if( ! socket ) return ENOMEM; |
||
4589 | mejdrech | 433 | if( ERROR_OCCURRED( dyn_fifo_initialize( & socket->received, SOCKET_INITIAL_RECEIVED_SIZE ))){ |
4578 | mejdrech | 434 | free( socket ); |
4589 | mejdrech | 435 | return ERROR_CODE; |
4578 | mejdrech | 436 | } |
4589 | mejdrech | 437 | if( ERROR_OCCURRED( dyn_fifo_initialize( & socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE ))){ |
438 | dyn_fifo_destroy( & socket->received ); |
||
439 | free( socket ); |
||
440 | return ERROR_CODE; |
||
441 | } |
||
442 | // request a new socket |
||
4726 | mejdrech | 443 | if( ERROR_OCCURRED(( int ) async_req_3_3( phone, NET_SOCKET, 0, 0, service, ( ipcarg_t * ) & socket_id, ( ipcarg_t * ) & socket->data_fragment_size, ( ipcarg_t * ) & socket->header_size ))){ |
4589 | mejdrech | 444 | dyn_fifo_destroy( & socket->received ); |
445 | dyn_fifo_destroy( & socket->accepted ); |
||
446 | free( socket ); |
||
447 | return ERROR_CODE; |
||
448 | } |
||
449 | // finish the new socket initialization |
||
450 | socket_initialize( socket, socket_id, phone, service ); |
||
451 | // store the new socket |
||
4726 | mejdrech | 452 | fibril_rwlock_write_lock( & socket_globals.lock ); |
4589 | mejdrech | 453 | ERROR_CODE = sockets_add( socket_get_sockets(), socket_id, socket ); |
4726 | mejdrech | 454 | fibril_rwlock_write_unlock( & socket_globals.lock ); |
4589 | mejdrech | 455 | if( ERROR_CODE < 0 ){ |
456 | dyn_fifo_destroy( & socket->received ); |
||
457 | dyn_fifo_destroy( & socket->accepted ); |
||
458 | free( socket ); |
||
4708 | mejdrech | 459 | async_msg_3( phone, NET_SOCKET_CLOSE, ( ipcarg_t ) socket_id, 0, service ); |
4589 | mejdrech | 460 | return ERROR_CODE; |
461 | } |
||
462 | |||
4578 | mejdrech | 463 | return socket_id; |
464 | } |
||
465 | |||
4708 | mejdrech | 466 | int socket_send_data( int socket_id, ipcarg_t message, ipcarg_t arg2, const void * data, size_t datalength ){ |
4578 | mejdrech | 467 | socket_ref socket; |
468 | aid_t message_id; |
||
469 | ipcarg_t result; |
||
470 | |||
471 | if( ! data ) return EBADMEM; |
||
472 | if( ! datalength ) return NO_DATA; |
||
4726 | mejdrech | 473 | |
474 | fibril_rwlock_read_lock( & socket_globals.lock ); |
||
4589 | mejdrech | 475 | // find the socket |
4578 | mejdrech | 476 | socket = sockets_find( socket_get_sockets(), socket_id ); |
4726 | mejdrech | 477 | if( ! socket ){ |
478 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
479 | return ENOTSOCK; |
||
480 | } |
||
4589 | mejdrech | 481 | // request the message |
4708 | mejdrech | 482 | message_id = async_send_3( socket->phone, message, ( ipcarg_t ) socket->socket_id, arg2, socket->service, NULL ); |
4589 | mejdrech | 483 | // send the address |
4578 | mejdrech | 484 | ipc_data_write_start( socket->phone, data, datalength ); |
4726 | mejdrech | 485 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4578 | mejdrech | 486 | async_wait_for( message_id, & result ); |
487 | return ( int ) result; |
||
488 | } |
||
489 | |||
490 | int bind( int socket_id, const struct sockaddr * my_addr, socklen_t addrlen ){ |
||
4708 | mejdrech | 491 | if( addrlen <= 0 ) return EINVAL; |
4589 | mejdrech | 492 | // send the address |
4708 | mejdrech | 493 | return socket_send_data( socket_id, NET_SOCKET_BIND, 0, my_addr, ( size_t ) addrlen ); |
4578 | mejdrech | 494 | } |
495 | |||
496 | int listen( int socket_id, int backlog ){ |
||
4726 | mejdrech | 497 | socket_ref socket; |
498 | int result; |
||
4578 | mejdrech | 499 | |
500 | if( backlog <= 0 ) return EINVAL; |
||
4726 | mejdrech | 501 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4589 | mejdrech | 502 | // find the socket |
4578 | mejdrech | 503 | socket = sockets_find( socket_get_sockets(), socket_id ); |
4726 | mejdrech | 504 | if( ! socket ){ |
505 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
506 | return ENOTSOCK; |
||
507 | } |
||
4589 | mejdrech | 508 | // request listen backlog change |
4726 | mejdrech | 509 | result = ( int ) async_req_3_0( socket->phone, NET_SOCKET_LISTEN, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) backlog, socket->service ); |
510 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
511 | return result; |
||
4578 | mejdrech | 512 | } |
513 | |||
514 | int accept( int socket_id, struct sockaddr * cliaddr, socklen_t * addrlen ){ |
||
515 | socket_ref socket; |
||
516 | aid_t message_id; |
||
4589 | mejdrech | 517 | int result; |
4578 | mejdrech | 518 | |
4701 | mejdrech | 519 | if(( ! cliaddr ) || ( ! addrlen )) return EBADMEM; |
4726 | mejdrech | 520 | |
521 | fibril_rwlock_read_lock( & socket_globals.lock ); |
||
4589 | mejdrech | 522 | // find the socket |
4578 | mejdrech | 523 | socket = sockets_find( socket_get_sockets(), socket_id ); |
4726 | mejdrech | 524 | if( ! socket ){ |
525 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
526 | return ENOTSOCK; |
||
527 | } |
||
4582 | mejdrech | 528 | fibril_mutex_lock( & socket->accept_lock ); |
4589 | mejdrech | 529 | // wait for an accepted socket |
4726 | mejdrech | 530 | ++ socket->blocked; |
4589 | mejdrech | 531 | while( dyn_fifo_value( & socket->accepted ) <= 0 ){ |
4726 | mejdrech | 532 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4582 | mejdrech | 533 | fibril_condvar_wait( & socket->accept_signal, & socket->accept_lock ); |
4726 | mejdrech | 534 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4578 | mejdrech | 535 | } |
4726 | mejdrech | 536 | -- socket->blocked; |
4589 | mejdrech | 537 | // request accept |
4708 | mejdrech | 538 | message_id = async_send_3( socket->phone, NET_SOCKET_ACCEPT, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) dyn_fifo_value( & socket->accepted ), socket->service, NULL ); |
4589 | mejdrech | 539 | // read address |
4578 | mejdrech | 540 | ipc_data_read_start( socket->phone, cliaddr, * addrlen ); |
4726 | mejdrech | 541 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4589 | mejdrech | 542 | async_wait_for( message_id, ( ipcarg_t * ) & result ); |
4578 | mejdrech | 543 | if( result > 0 ){ |
4589 | mejdrech | 544 | // dequeue the accepted apcket if successful |
545 | dyn_fifo_pop( & socket->accepted ); |
||
4578 | mejdrech | 546 | } |
4582 | mejdrech | 547 | fibril_mutex_unlock( & socket->accept_lock ); |
4589 | mejdrech | 548 | return result; |
4578 | mejdrech | 549 | } |
550 | |||
551 | int connect( int socket_id, const struct sockaddr * serv_addr, socklen_t addrlen ){ |
||
4589 | mejdrech | 552 | // send the address |
4578 | mejdrech | 553 | return socket_send_data( socket_id, NET_SOCKET_CONNECT, 0, serv_addr, addrlen ); |
554 | } |
||
555 | |||
556 | int closesocket( int socket_id ){ |
||
4589 | mejdrech | 557 | ERROR_DECLARE; |
558 | |||
4578 | mejdrech | 559 | socket_ref socket; |
560 | |||
4726 | mejdrech | 561 | fibril_rwlock_write_unlock( & socket_globals.lock ); |
4578 | mejdrech | 562 | socket = sockets_find( socket_get_sockets(), socket_id ); |
4726 | mejdrech | 563 | if( ! socket ){ |
564 | fibril_rwlock_write_unlock( & socket_globals.lock ); |
||
565 | return ENOTSOCK; |
||
566 | } |
||
567 | if( socket->blocked ){ |
||
568 | fibril_rwlock_write_unlock( & socket_globals.lock ); |
||
569 | return EINPROGRESS; |
||
570 | } |
||
4589 | mejdrech | 571 | // request close |
4708 | mejdrech | 572 | ERROR_PROPAGATE(( int ) async_req_3_0( socket->phone, NET_SOCKET_CLOSE, ( ipcarg_t ) socket->socket_id, 0, socket->service )); |
4589 | mejdrech | 573 | // free the socket structure |
574 | socket_destroy( socket ); |
||
4726 | mejdrech | 575 | fibril_rwlock_write_unlock( & socket_globals.lock ); |
4589 | mejdrech | 576 | return EOK; |
4578 | mejdrech | 577 | } |
578 | |||
4589 | mejdrech | 579 | void socket_destroy( socket_ref socket ){ |
580 | int accepted_id; |
||
581 | |||
582 | // destroy all accepted sockets |
||
4701 | mejdrech | 583 | while(( accepted_id = dyn_fifo_pop( & socket->accepted )) >= 0 ){ |
4589 | mejdrech | 584 | socket_destroy( sockets_find( socket_get_sockets(), accepted_id )); |
585 | } |
||
586 | dyn_fifo_destroy( & socket->received ); |
||
587 | dyn_fifo_destroy( & socket->accepted ); |
||
588 | sockets_exclude( socket_get_sockets(), socket->socket_id ); |
||
589 | } |
||
590 | |||
4578 | mejdrech | 591 | int send( int socket_id, void * data, size_t datalength, int flags ){ |
4589 | mejdrech | 592 | // without the address |
593 | return sendto_core( NET_SOCKET_SEND, socket_id, data, datalength, flags, NULL, 0 ); |
||
4578 | mejdrech | 594 | } |
595 | |||
596 | int sendto( int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){ |
||
597 | if( ! toaddr ) return EBADMEM; |
||
598 | if( ! addrlen ) return NO_DATA; |
||
4589 | mejdrech | 599 | // with the address |
600 | return sendto_core( NET_SOCKET_SENDTO, socket_id, data, datalength, flags, toaddr, addrlen ); |
||
4578 | mejdrech | 601 | } |
602 | |||
4708 | mejdrech | 603 | int sendto_core( ipcarg_t message, int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){ |
4578 | mejdrech | 604 | socket_ref socket; |
605 | aid_t message_id; |
||
606 | ipcarg_t result; |
||
4708 | mejdrech | 607 | size_t fragments; |
4578 | mejdrech | 608 | |
609 | if( ! data ) return EBADMEM; |
||
610 | if( ! datalength ) return NO_DATA; |
||
4726 | mejdrech | 611 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4589 | mejdrech | 612 | // find socket |
4578 | mejdrech | 613 | socket = sockets_find( socket_get_sockets(), socket_id ); |
4726 | mejdrech | 614 | if( ! socket ){ |
615 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
616 | return ENOTSOCK; |
||
617 | } |
||
618 | fibril_rwlock_read_lock( & socket->sending_lock ); |
||
4589 | mejdrech | 619 | // compute data fragment count |
620 | fragments = ( datalength + socket->header_size ) / socket->data_fragment_size; |
||
621 | if(( datalength + socket->header_size ) % socket->data_fragment_size ) ++ fragments; |
||
622 | // request send |
||
4726 | mejdrech | 623 | message_id = async_send_5( socket->phone, message, ( ipcarg_t ) socket->socket_id, socket->data_fragment_size, socket->service, ( ipcarg_t ) flags, fragments, NULL ); |
4589 | mejdrech | 624 | // send the address if given |
625 | if(( ! toaddr ) || ( ipc_data_write_start( socket->phone, toaddr, addrlen ) == EOK )){ |
||
626 | if( fragments == 1 ){ |
||
627 | // send all if only one fragment |
||
628 | ipc_data_write_start( socket->phone, data, datalength ); |
||
629 | }else{ |
||
630 | // send the first fragment |
||
631 | ipc_data_write_start( socket->phone, data, socket->data_fragment_size - socket->header_size ); |
||
4708 | mejdrech | 632 | data = (( const uint8_t * ) data ) + socket->data_fragment_size - socket->header_size; |
4589 | mejdrech | 633 | // send the middle fragments |
634 | while(( -- fragments ) > 1 ){ |
||
635 | ipc_data_write_start( socket->phone, data, socket->data_fragment_size ); |
||
4708 | mejdrech | 636 | data = (( const uint8_t * ) data ) + socket->data_fragment_size; |
4589 | mejdrech | 637 | } |
638 | // send the last fragment |
||
639 | ipc_data_write_start( socket->phone, data, ( datalength + socket->header_size ) % socket->data_fragment_size ); |
||
640 | } |
||
4578 | mejdrech | 641 | } |
4726 | mejdrech | 642 | fibril_rwlock_read_unlock( & socket->sending_lock ); |
643 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
4578 | mejdrech | 644 | async_wait_for( message_id, & result ); |
645 | return ( int ) result; |
||
646 | } |
||
647 | |||
4589 | mejdrech | 648 | int recv( int socket_id, void * data, size_t datalength, int flags ){ |
649 | // without the address |
||
650 | return recvfrom_core( NET_SOCKET_RECV, socket_id, data, datalength, flags, NULL, NULL ); |
||
651 | } |
||
652 | |||
4578 | mejdrech | 653 | int recvfrom( int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){ |
4589 | mejdrech | 654 | if( ! fromaddr ) return EBADMEM; |
655 | if( ! addrlen ) return NO_DATA; |
||
656 | // with the address |
||
657 | return recvfrom_core( NET_SOCKET_RECVFROM, socket_id, data, datalength, flags, fromaddr, addrlen ); |
||
658 | } |
||
659 | |||
4708 | mejdrech | 660 | int recvfrom_core( ipcarg_t message, int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){ |
4578 | mejdrech | 661 | socket_ref socket; |
662 | aid_t message_id; |
||
4589 | mejdrech | 663 | int result; |
4708 | mejdrech | 664 | size_t fragments; |
665 | size_t * lengths; |
||
666 | size_t index; |
||
4589 | mejdrech | 667 | ipc_call_t answer; |
4578 | mejdrech | 668 | |
669 | if( ! data ) return EBADMEM; |
||
670 | if( ! datalength ) return NO_DATA; |
||
4701 | mejdrech | 671 | if( fromaddr && ( ! addrlen )) return EINVAL; |
4726 | mejdrech | 672 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4589 | mejdrech | 673 | // find the socket |
4578 | mejdrech | 674 | socket = sockets_find( socket_get_sockets(), socket_id ); |
4726 | mejdrech | 675 | if( ! socket ){ |
676 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
677 | return ENOTSOCK; |
||
678 | } |
||
4582 | mejdrech | 679 | fibril_mutex_lock( & socket->receive_lock ); |
4589 | mejdrech | 680 | // wait for a received packet |
4726 | mejdrech | 681 | ++ socket->blocked; |
4708 | mejdrech | 682 | while(( result = dyn_fifo_value( & socket->received )) <= 0 ){ |
4726 | mejdrech | 683 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4582 | mejdrech | 684 | fibril_condvar_wait( & socket->receive_signal, & socket->receive_lock ); |
4726 | mejdrech | 685 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4578 | mejdrech | 686 | } |
4726 | mejdrech | 687 | -- socket->blocked; |
4708 | mejdrech | 688 | fragments = ( size_t ) result; |
4589 | mejdrech | 689 | // prepare lengths if more fragments |
690 | if( fragments > 1 ){ |
||
4708 | mejdrech | 691 | lengths = ( size_t * ) malloc( sizeof( size_t ) * fragments + sizeof( size_t )); |
4589 | mejdrech | 692 | if( ! lengths ){ |
693 | fibril_mutex_unlock( & socket->receive_lock ); |
||
4726 | mejdrech | 694 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4589 | mejdrech | 695 | return ENOMEM; |
696 | } |
||
4708 | mejdrech | 697 | // request packet data |
698 | message_id = async_send_4( socket->phone, message, ( ipcarg_t ) socket->socket_id, 0, socket->service, ( ipcarg_t ) flags, & answer ); |
||
699 | // read the address if desired |
||
700 | if(( ! fromaddr ) || ( ipc_data_read_start( socket->phone, fromaddr, * addrlen ) == EOK )){ |
||
4589 | mejdrech | 701 | // read the fragment lengths |
702 | if( ipc_data_read_start( socket->phone, lengths, sizeof( int ) * ( fragments + 1 )) == EOK ){ |
||
703 | if( lengths[ fragments ] <= datalength ){ |
||
4708 | mejdrech | 704 | // read all fragments if long enough |
4589 | mejdrech | 705 | for( index = 0; index < fragments; ++ index ){ |
706 | ipc_data_read_start( socket->phone, data, lengths[ index ] ); |
||
4708 | mejdrech | 707 | data = (( uint8_t * ) data ) + lengths[ index ]; |
4589 | mejdrech | 708 | } |
709 | } |
||
710 | } |
||
711 | } |
||
712 | free( lengths ); |
||
4708 | mejdrech | 713 | }else{ |
714 | // request packet data |
||
715 | message_id = async_send_4( socket->phone, message, ( ipcarg_t ) socket->socket_id, 0, socket->service, ( ipcarg_t ) flags, & answer ); |
||
716 | // read the address if desired |
||
717 | if(( ! fromaddr ) || ( ipc_data_read_start( socket->phone, fromaddr, * addrlen ) == EOK )){ |
||
718 | // read all if only one fragment |
||
719 | ipc_data_read_start( socket->phone, data, datalength ); |
||
720 | } |
||
4589 | mejdrech | 721 | } |
722 | async_wait_for( message_id, ( ipcarg_t * ) & result ); |
||
723 | // if successful |
||
724 | if( result == EOK ){ |
||
725 | // dequeue the received packet |
||
726 | dyn_fifo_pop( & socket->received ); |
||
727 | // return read data length |
||
4701 | mejdrech | 728 | result = SOCKET_GET_READ_DATA_LENGTH( answer ); |
4603 | mejdrech | 729 | // set address length |
4701 | mejdrech | 730 | if( fromaddr && addrlen ) * addrlen = SOCKET_GET_ADDRESS_LENGTH( answer ); |
4589 | mejdrech | 731 | } |
4582 | mejdrech | 732 | fibril_mutex_unlock( & socket->receive_lock ); |
4726 | mejdrech | 733 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4589 | mejdrech | 734 | return result; |
4578 | mejdrech | 735 | } |
736 | |||
737 | int getsockopt( int socket_id, int level, int optname, void * value, size_t * optlen ){ |
||
738 | socket_ref socket; |
||
739 | aid_t message_id; |
||
740 | ipcarg_t result; |
||
741 | |||
4589 | mejdrech | 742 | if( !( value && optlen )) return EBADMEM; |
743 | if( !( * optlen )) return NO_DATA; |
||
4726 | mejdrech | 744 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4589 | mejdrech | 745 | // find the socket |
4578 | mejdrech | 746 | socket = sockets_find( socket_get_sockets(), socket_id ); |
4726 | mejdrech | 747 | if( ! socket ){ |
748 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
749 | return ENOTSOCK; |
||
750 | } |
||
4589 | mejdrech | 751 | // request option value |
4708 | mejdrech | 752 | message_id = async_send_3( socket->phone, NET_SOCKET_GETSOCKOPT, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) optname, socket->service, NULL ); |
4589 | mejdrech | 753 | // read the length |
754 | if( ipc_data_read_start( socket->phone, optlen, sizeof( * optlen )) == EOK ){ |
||
755 | // read the value |
||
756 | ipc_data_read_start( socket->phone, value, * optlen ); |
||
757 | } |
||
4726 | mejdrech | 758 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4578 | mejdrech | 759 | async_wait_for( message_id, & result ); |
760 | return ( int ) result; |
||
761 | } |
||
762 | |||
763 | int setsockopt( int socket_id, int level, int optname, const void * value, size_t optlen ){ |
||
4589 | mejdrech | 764 | // send the value |
4708 | mejdrech | 765 | return socket_send_data( socket_id, NET_SOCKET_SETSOCKOPT, ( ipcarg_t ) optname, value, optlen ); |
4589 | mejdrech | 766 | |
4578 | mejdrech | 767 | } |
768 | |||
769 | /** @} |
||
770 | */ |