Rev 4736 | Rev 4747 | 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 )){ |
||
313 | case NET_SOCKET_RECEIVED: |
||
4726 | mejdrech | 314 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4589 | mejdrech | 315 | // find the socket |
4701 | mejdrech | 316 | socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call )); |
4578 | mejdrech | 317 | if( ! socket ){ |
318 | ERROR_CODE = ENOTSOCK; |
||
4726 | mejdrech | 319 | }else{ |
320 | fibril_mutex_lock( & socket->receive_lock ); |
||
321 | // push the number of received packet fragments |
||
322 | if( ! ERROR_OCCURRED( dyn_fifo_push( & socket->received, SOCKET_GET_DATA_FRAGMENTS( call ), SOCKET_MAX_RECEIVED_SIZE ))){ |
||
323 | // signal the received packet |
||
324 | fibril_condvar_signal( & socket->receive_signal ); |
||
325 | } |
||
326 | fibril_mutex_unlock( & socket->receive_lock ); |
||
4578 | mejdrech | 327 | } |
4726 | mejdrech | 328 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4578 | mejdrech | 329 | break; |
330 | case NET_SOCKET_ACCEPTED: |
||
4726 | mejdrech | 331 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4589 | mejdrech | 332 | // find the socket |
4701 | mejdrech | 333 | socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call )); |
4578 | mejdrech | 334 | if( ! socket ){ |
335 | ERROR_CODE = ENOTSOCK; |
||
4589 | mejdrech | 336 | }else{ |
4726 | mejdrech | 337 | // create a new scoket |
338 | new_socket = ( socket_ref ) malloc( sizeof( socket_t )); |
||
339 | if( ! new_socket ){ |
||
340 | ERROR_CODE = ENOMEM; |
||
4589 | mejdrech | 341 | }else{ |
4743 | mejdrech | 342 | bzero( new_socket, sizeof( * new_socket )); |
343 | socket_initialize( new_socket, SOCKET_GET_NEW_SOCKET_ID( call ), socket->phone, socket->service ); |
||
4726 | mejdrech | 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 | }else{ |
||
353 | // signal the accepted socket |
||
354 | fibril_condvar_signal( & socket->accept_signal ); |
||
355 | } |
||
356 | fibril_mutex_unlock( & socket->accept_lock ); |
||
357 | ERROR_CODE = EOK; |
||
358 | } |
||
4589 | mejdrech | 359 | } |
4578 | mejdrech | 360 | } |
4726 | mejdrech | 361 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4578 | mejdrech | 362 | break; |
4589 | mejdrech | 363 | case NET_SOCKET_DATA_FRAGMENT_SIZE: |
4726 | mejdrech | 364 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4589 | mejdrech | 365 | // find the socket |
4701 | mejdrech | 366 | socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call )); |
4578 | mejdrech | 367 | if( ! socket ){ |
368 | ERROR_CODE = ENOTSOCK; |
||
4726 | mejdrech | 369 | }else{ |
370 | fibril_rwlock_write_lock( & socket->sending_lock ); |
||
371 | // set the data fragment size |
||
372 | socket->data_fragment_size = SOCKET_GET_DATA_FRAGMENT_SIZE( call ); |
||
373 | fibril_rwlock_write_unlock( & socket->sending_lock ); |
||
374 | ERROR_CODE = EOK; |
||
4578 | mejdrech | 375 | } |
4726 | mejdrech | 376 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4578 | mejdrech | 377 | break; |
4589 | mejdrech | 378 | default: |
4578 | mejdrech | 379 | ERROR_CODE = ENOTSUP; |
380 | } |
||
4708 | mejdrech | 381 | ipc_answer_0( callid, ( ipcarg_t ) ERROR_CODE ); |
4578 | mejdrech | 382 | } |
383 | } |
||
384 | |||
385 | int socket( int domain, int type, int protocol ){ |
||
4589 | mejdrech | 386 | ERROR_DECLARE; |
387 | |||
4578 | mejdrech | 388 | socket_ref socket; |
389 | int phone; |
||
390 | int socket_id; |
||
391 | services_t service; |
||
392 | |||
4589 | mejdrech | 393 | // find the appropriate service |
4578 | mejdrech | 394 | switch( domain ){ |
395 | case PF_INET: |
||
396 | switch( type ){ |
||
397 | case SOCK_STREAM: |
||
398 | if( ! protocol ) protocol = IPPROTO_TCP; |
||
399 | switch( protocol ){ |
||
400 | case IPPROTO_TCP: |
||
401 | phone = socket_get_tcp_phone(); |
||
402 | service = SERVICE_TCP; |
||
403 | break; |
||
404 | default: |
||
405 | return EPROTONOSUPPORT; |
||
406 | } |
||
407 | break; |
||
408 | case SOCK_DGRAM: |
||
409 | if( ! protocol ) protocol = IPPROTO_UDP; |
||
410 | switch( protocol ){ |
||
411 | case IPPROTO_UDP: |
||
412 | phone = socket_get_udp_phone(); |
||
413 | service = SERVICE_UDP; |
||
414 | break; |
||
415 | default: |
||
416 | return EPROTONOSUPPORT; |
||
417 | } |
||
418 | break; |
||
419 | case SOCK_RAW: |
||
420 | default: |
||
421 | return ESOCKTNOSUPPORT; |
||
422 | } |
||
423 | break; |
||
424 | // TODO IPv6 |
||
425 | default: |
||
426 | return EPFNOSUPPORT; |
||
427 | } |
||
4589 | mejdrech | 428 | // create a new socket structure |
4578 | mejdrech | 429 | socket = ( socket_ref ) malloc( sizeof( socket_t )); |
430 | if( ! socket ) return ENOMEM; |
||
4736 | mejdrech | 431 | bzero( socket, sizeof( * socket )); |
4589 | mejdrech | 432 | // request a new socket |
4726 | mejdrech | 433 | 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 | 434 | free( socket ); |
435 | return ERROR_CODE; |
||
436 | } |
||
437 | // finish the new socket initialization |
||
438 | socket_initialize( socket, socket_id, phone, service ); |
||
439 | // store the new socket |
||
4726 | mejdrech | 440 | fibril_rwlock_write_lock( & socket_globals.lock ); |
4589 | mejdrech | 441 | ERROR_CODE = sockets_add( socket_get_sockets(), socket_id, socket ); |
4726 | mejdrech | 442 | fibril_rwlock_write_unlock( & socket_globals.lock ); |
4589 | mejdrech | 443 | if( ERROR_CODE < 0 ){ |
444 | dyn_fifo_destroy( & socket->received ); |
||
445 | dyn_fifo_destroy( & socket->accepted ); |
||
446 | free( socket ); |
||
4708 | mejdrech | 447 | async_msg_3( phone, NET_SOCKET_CLOSE, ( ipcarg_t ) socket_id, 0, service ); |
4589 | mejdrech | 448 | return ERROR_CODE; |
449 | } |
||
450 | |||
4578 | mejdrech | 451 | return socket_id; |
452 | } |
||
453 | |||
4708 | mejdrech | 454 | int socket_send_data( int socket_id, ipcarg_t message, ipcarg_t arg2, const void * data, size_t datalength ){ |
4578 | mejdrech | 455 | socket_ref socket; |
456 | aid_t message_id; |
||
457 | ipcarg_t result; |
||
458 | |||
459 | if( ! data ) return EBADMEM; |
||
460 | if( ! datalength ) return NO_DATA; |
||
4726 | mejdrech | 461 | |
462 | fibril_rwlock_read_lock( & socket_globals.lock ); |
||
4589 | mejdrech | 463 | // find the socket |
4578 | mejdrech | 464 | socket = sockets_find( socket_get_sockets(), socket_id ); |
4726 | mejdrech | 465 | if( ! socket ){ |
466 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
467 | return ENOTSOCK; |
||
468 | } |
||
4589 | mejdrech | 469 | // request the message |
4708 | mejdrech | 470 | message_id = async_send_3( socket->phone, message, ( ipcarg_t ) socket->socket_id, arg2, socket->service, NULL ); |
4589 | mejdrech | 471 | // send the address |
4578 | mejdrech | 472 | ipc_data_write_start( socket->phone, data, datalength ); |
4726 | mejdrech | 473 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4578 | mejdrech | 474 | async_wait_for( message_id, & result ); |
475 | return ( int ) result; |
||
476 | } |
||
477 | |||
478 | int bind( int socket_id, const struct sockaddr * my_addr, socklen_t addrlen ){ |
||
4708 | mejdrech | 479 | if( addrlen <= 0 ) return EINVAL; |
4589 | mejdrech | 480 | // send the address |
4708 | mejdrech | 481 | return socket_send_data( socket_id, NET_SOCKET_BIND, 0, my_addr, ( size_t ) addrlen ); |
4578 | mejdrech | 482 | } |
483 | |||
484 | int listen( int socket_id, int backlog ){ |
||
4726 | mejdrech | 485 | socket_ref socket; |
486 | int result; |
||
4578 | mejdrech | 487 | |
488 | if( backlog <= 0 ) return EINVAL; |
||
4726 | mejdrech | 489 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4589 | mejdrech | 490 | // find the socket |
4578 | mejdrech | 491 | socket = sockets_find( socket_get_sockets(), socket_id ); |
4726 | mejdrech | 492 | if( ! socket ){ |
493 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
494 | return ENOTSOCK; |
||
495 | } |
||
4589 | mejdrech | 496 | // request listen backlog change |
4726 | mejdrech | 497 | result = ( int ) async_req_3_0( socket->phone, NET_SOCKET_LISTEN, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) backlog, socket->service ); |
498 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
499 | return result; |
||
4578 | mejdrech | 500 | } |
501 | |||
502 | int accept( int socket_id, struct sockaddr * cliaddr, socklen_t * addrlen ){ |
||
503 | socket_ref socket; |
||
504 | aid_t message_id; |
||
4589 | mejdrech | 505 | int result; |
4743 | mejdrech | 506 | ipc_call_t answer; |
4578 | mejdrech | 507 | |
4701 | mejdrech | 508 | if(( ! cliaddr ) || ( ! addrlen )) return EBADMEM; |
4726 | mejdrech | 509 | |
510 | fibril_rwlock_read_lock( & socket_globals.lock ); |
||
4589 | mejdrech | 511 | // find the socket |
4578 | mejdrech | 512 | socket = sockets_find( socket_get_sockets(), socket_id ); |
4726 | mejdrech | 513 | if( ! socket ){ |
514 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
515 | return ENOTSOCK; |
||
516 | } |
||
4582 | mejdrech | 517 | fibril_mutex_lock( & socket->accept_lock ); |
4589 | mejdrech | 518 | // wait for an accepted socket |
4726 | mejdrech | 519 | ++ socket->blocked; |
4589 | mejdrech | 520 | while( dyn_fifo_value( & socket->accepted ) <= 0 ){ |
4726 | mejdrech | 521 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4582 | mejdrech | 522 | fibril_condvar_wait( & socket->accept_signal, & socket->accept_lock ); |
4726 | mejdrech | 523 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4578 | mejdrech | 524 | } |
4726 | mejdrech | 525 | -- socket->blocked; |
4589 | mejdrech | 526 | // request accept |
4743 | mejdrech | 527 | message_id = async_send_3( socket->phone, NET_SOCKET_ACCEPT, ( ipcarg_t ) socket->socket_id, 0, socket->service, & answer ); |
4589 | mejdrech | 528 | // read address |
4578 | mejdrech | 529 | ipc_data_read_start( socket->phone, cliaddr, * addrlen ); |
4726 | mejdrech | 530 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4589 | mejdrech | 531 | async_wait_for( message_id, ( ipcarg_t * ) & result ); |
4578 | mejdrech | 532 | if( result > 0 ){ |
4743 | mejdrech | 533 | // dequeue the accepted socket if successful |
4589 | mejdrech | 534 | dyn_fifo_pop( & socket->accepted ); |
4743 | mejdrech | 535 | // set address length |
536 | * addrlen = SOCKET_GET_ADDRESS_LENGTH( answer ); |
||
537 | }else if( result == ENOTSOCK ){ |
||
538 | // empty the queue if no accepted sockets |
||
539 | while( dyn_fifo_pop( & socket->accepted ) > 0 ); |
||
4578 | mejdrech | 540 | } |
4582 | mejdrech | 541 | fibril_mutex_unlock( & socket->accept_lock ); |
4589 | mejdrech | 542 | return result; |
4578 | mejdrech | 543 | } |
544 | |||
545 | int connect( int socket_id, const struct sockaddr * serv_addr, socklen_t addrlen ){ |
||
4736 | mejdrech | 546 | if( ! serv_addr ) return EDESTADDRREQ; |
547 | if( ! addrlen ) return EDESTADDRREQ; |
||
4589 | mejdrech | 548 | // send the address |
4578 | mejdrech | 549 | return socket_send_data( socket_id, NET_SOCKET_CONNECT, 0, serv_addr, addrlen ); |
550 | } |
||
551 | |||
552 | int closesocket( int socket_id ){ |
||
4589 | mejdrech | 553 | ERROR_DECLARE; |
554 | |||
4578 | mejdrech | 555 | socket_ref socket; |
556 | |||
4729 | mejdrech | 557 | fibril_rwlock_write_lock( & socket_globals.lock ); |
4578 | mejdrech | 558 | socket = sockets_find( socket_get_sockets(), socket_id ); |
4726 | mejdrech | 559 | if( ! socket ){ |
560 | fibril_rwlock_write_unlock( & socket_globals.lock ); |
||
561 | return ENOTSOCK; |
||
562 | } |
||
563 | if( socket->blocked ){ |
||
564 | fibril_rwlock_write_unlock( & socket_globals.lock ); |
||
565 | return EINPROGRESS; |
||
566 | } |
||
4589 | mejdrech | 567 | // request close |
4708 | mejdrech | 568 | ERROR_PROPAGATE(( int ) async_req_3_0( socket->phone, NET_SOCKET_CLOSE, ( ipcarg_t ) socket->socket_id, 0, socket->service )); |
4589 | mejdrech | 569 | // free the socket structure |
570 | socket_destroy( socket ); |
||
4726 | mejdrech | 571 | fibril_rwlock_write_unlock( & socket_globals.lock ); |
4589 | mejdrech | 572 | return EOK; |
4578 | mejdrech | 573 | } |
574 | |||
4589 | mejdrech | 575 | void socket_destroy( socket_ref socket ){ |
576 | int accepted_id; |
||
577 | |||
578 | // destroy all accepted sockets |
||
4701 | mejdrech | 579 | while(( accepted_id = dyn_fifo_pop( & socket->accepted )) >= 0 ){ |
4589 | mejdrech | 580 | socket_destroy( sockets_find( socket_get_sockets(), accepted_id )); |
581 | } |
||
582 | dyn_fifo_destroy( & socket->received ); |
||
583 | dyn_fifo_destroy( & socket->accepted ); |
||
584 | sockets_exclude( socket_get_sockets(), socket->socket_id ); |
||
585 | } |
||
586 | |||
4578 | mejdrech | 587 | int send( int socket_id, void * data, size_t datalength, int flags ){ |
4589 | mejdrech | 588 | // without the address |
589 | return sendto_core( NET_SOCKET_SEND, socket_id, data, datalength, flags, NULL, 0 ); |
||
4578 | mejdrech | 590 | } |
591 | |||
592 | int sendto( int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){ |
||
4736 | mejdrech | 593 | if( ! toaddr ) return EDESTADDRREQ; |
594 | if( ! addrlen ) return EDESTADDRREQ; |
||
4589 | mejdrech | 595 | // with the address |
596 | return sendto_core( NET_SOCKET_SENDTO, socket_id, data, datalength, flags, toaddr, addrlen ); |
||
4578 | mejdrech | 597 | } |
598 | |||
4708 | mejdrech | 599 | 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 | 600 | socket_ref socket; |
601 | aid_t message_id; |
||
602 | ipcarg_t result; |
||
4708 | mejdrech | 603 | size_t fragments; |
4578 | mejdrech | 604 | |
605 | if( ! data ) return EBADMEM; |
||
606 | if( ! datalength ) return NO_DATA; |
||
4726 | mejdrech | 607 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4589 | mejdrech | 608 | // find socket |
4578 | mejdrech | 609 | socket = sockets_find( socket_get_sockets(), socket_id ); |
4726 | mejdrech | 610 | if( ! socket ){ |
611 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
612 | return ENOTSOCK; |
||
613 | } |
||
614 | fibril_rwlock_read_lock( & socket->sending_lock ); |
||
4589 | mejdrech | 615 | // compute data fragment count |
616 | fragments = ( datalength + socket->header_size ) / socket->data_fragment_size; |
||
617 | if(( datalength + socket->header_size ) % socket->data_fragment_size ) ++ fragments; |
||
618 | // request send |
||
4726 | mejdrech | 619 | 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 | 620 | // send the address if given |
621 | if(( ! toaddr ) || ( ipc_data_write_start( socket->phone, toaddr, addrlen ) == EOK )){ |
||
622 | if( fragments == 1 ){ |
||
623 | // send all if only one fragment |
||
624 | ipc_data_write_start( socket->phone, data, datalength ); |
||
625 | }else{ |
||
626 | // send the first fragment |
||
627 | ipc_data_write_start( socket->phone, data, socket->data_fragment_size - socket->header_size ); |
||
4708 | mejdrech | 628 | data = (( const uint8_t * ) data ) + socket->data_fragment_size - socket->header_size; |
4589 | mejdrech | 629 | // send the middle fragments |
630 | while(( -- fragments ) > 1 ){ |
||
631 | ipc_data_write_start( socket->phone, data, socket->data_fragment_size ); |
||
4708 | mejdrech | 632 | data = (( const uint8_t * ) data ) + socket->data_fragment_size; |
4589 | mejdrech | 633 | } |
634 | // send the last fragment |
||
635 | ipc_data_write_start( socket->phone, data, ( datalength + socket->header_size ) % socket->data_fragment_size ); |
||
636 | } |
||
4578 | mejdrech | 637 | } |
4726 | mejdrech | 638 | fibril_rwlock_read_unlock( & socket->sending_lock ); |
639 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
4578 | mejdrech | 640 | async_wait_for( message_id, & result ); |
641 | return ( int ) result; |
||
642 | } |
||
643 | |||
4589 | mejdrech | 644 | int recv( int socket_id, void * data, size_t datalength, int flags ){ |
645 | // without the address |
||
646 | return recvfrom_core( NET_SOCKET_RECV, socket_id, data, datalength, flags, NULL, NULL ); |
||
647 | } |
||
648 | |||
4578 | mejdrech | 649 | int recvfrom( int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){ |
4589 | mejdrech | 650 | if( ! fromaddr ) return EBADMEM; |
651 | if( ! addrlen ) return NO_DATA; |
||
652 | // with the address |
||
653 | return recvfrom_core( NET_SOCKET_RECVFROM, socket_id, data, datalength, flags, fromaddr, addrlen ); |
||
654 | } |
||
655 | |||
4708 | mejdrech | 656 | int recvfrom_core( ipcarg_t message, int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){ |
4578 | mejdrech | 657 | socket_ref socket; |
658 | aid_t message_id; |
||
4589 | mejdrech | 659 | int result; |
4708 | mejdrech | 660 | size_t fragments; |
661 | size_t * lengths; |
||
662 | size_t index; |
||
4589 | mejdrech | 663 | ipc_call_t answer; |
4578 | mejdrech | 664 | |
665 | if( ! data ) return EBADMEM; |
||
666 | if( ! datalength ) return NO_DATA; |
||
4701 | mejdrech | 667 | if( fromaddr && ( ! addrlen )) return EINVAL; |
4726 | mejdrech | 668 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4589 | mejdrech | 669 | // find the socket |
4578 | mejdrech | 670 | socket = sockets_find( socket_get_sockets(), socket_id ); |
4726 | mejdrech | 671 | if( ! socket ){ |
672 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
673 | return ENOTSOCK; |
||
674 | } |
||
4582 | mejdrech | 675 | fibril_mutex_lock( & socket->receive_lock ); |
4589 | mejdrech | 676 | // wait for a received packet |
4726 | mejdrech | 677 | ++ socket->blocked; |
4708 | mejdrech | 678 | while(( result = dyn_fifo_value( & socket->received )) <= 0 ){ |
4726 | mejdrech | 679 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4582 | mejdrech | 680 | fibril_condvar_wait( & socket->receive_signal, & socket->receive_lock ); |
4726 | mejdrech | 681 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4578 | mejdrech | 682 | } |
4726 | mejdrech | 683 | -- socket->blocked; |
4708 | mejdrech | 684 | fragments = ( size_t ) result; |
4589 | mejdrech | 685 | // prepare lengths if more fragments |
686 | if( fragments > 1 ){ |
||
4708 | mejdrech | 687 | lengths = ( size_t * ) malloc( sizeof( size_t ) * fragments + sizeof( size_t )); |
4589 | mejdrech | 688 | if( ! lengths ){ |
689 | fibril_mutex_unlock( & socket->receive_lock ); |
||
4726 | mejdrech | 690 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4589 | mejdrech | 691 | return ENOMEM; |
692 | } |
||
4708 | mejdrech | 693 | // request packet data |
694 | message_id = async_send_4( socket->phone, message, ( ipcarg_t ) socket->socket_id, 0, socket->service, ( ipcarg_t ) flags, & answer ); |
||
695 | // read the address if desired |
||
696 | if(( ! fromaddr ) || ( ipc_data_read_start( socket->phone, fromaddr, * addrlen ) == EOK )){ |
||
4589 | mejdrech | 697 | // read the fragment lengths |
698 | if( ipc_data_read_start( socket->phone, lengths, sizeof( int ) * ( fragments + 1 )) == EOK ){ |
||
699 | if( lengths[ fragments ] <= datalength ){ |
||
4708 | mejdrech | 700 | // read all fragments if long enough |
4589 | mejdrech | 701 | for( index = 0; index < fragments; ++ index ){ |
702 | ipc_data_read_start( socket->phone, data, lengths[ index ] ); |
||
4708 | mejdrech | 703 | data = (( uint8_t * ) data ) + lengths[ index ]; |
4589 | mejdrech | 704 | } |
705 | } |
||
706 | } |
||
707 | } |
||
708 | free( lengths ); |
||
4708 | mejdrech | 709 | }else{ |
710 | // request packet data |
||
711 | message_id = async_send_4( socket->phone, message, ( ipcarg_t ) socket->socket_id, 0, socket->service, ( ipcarg_t ) flags, & answer ); |
||
712 | // read the address if desired |
||
713 | if(( ! fromaddr ) || ( ipc_data_read_start( socket->phone, fromaddr, * addrlen ) == EOK )){ |
||
714 | // read all if only one fragment |
||
715 | ipc_data_read_start( socket->phone, data, datalength ); |
||
716 | } |
||
4589 | mejdrech | 717 | } |
718 | async_wait_for( message_id, ( ipcarg_t * ) & result ); |
||
719 | // if successful |
||
720 | if( result == EOK ){ |
||
721 | // dequeue the received packet |
||
722 | dyn_fifo_pop( & socket->received ); |
||
723 | // return read data length |
||
4701 | mejdrech | 724 | result = SOCKET_GET_READ_DATA_LENGTH( answer ); |
4603 | mejdrech | 725 | // set address length |
4701 | mejdrech | 726 | if( fromaddr && addrlen ) * addrlen = SOCKET_GET_ADDRESS_LENGTH( answer ); |
4589 | mejdrech | 727 | } |
4582 | mejdrech | 728 | fibril_mutex_unlock( & socket->receive_lock ); |
4726 | mejdrech | 729 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4589 | mejdrech | 730 | return result; |
4578 | mejdrech | 731 | } |
732 | |||
733 | int getsockopt( int socket_id, int level, int optname, void * value, size_t * optlen ){ |
||
734 | socket_ref socket; |
||
735 | aid_t message_id; |
||
736 | ipcarg_t result; |
||
737 | |||
4589 | mejdrech | 738 | if( !( value && optlen )) return EBADMEM; |
739 | if( !( * optlen )) return NO_DATA; |
||
4726 | mejdrech | 740 | fibril_rwlock_read_lock( & socket_globals.lock ); |
4589 | mejdrech | 741 | // find the socket |
4578 | mejdrech | 742 | socket = sockets_find( socket_get_sockets(), socket_id ); |
4726 | mejdrech | 743 | if( ! socket ){ |
744 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
||
745 | return ENOTSOCK; |
||
746 | } |
||
4589 | mejdrech | 747 | // request option value |
4708 | mejdrech | 748 | message_id = async_send_3( socket->phone, NET_SOCKET_GETSOCKOPT, ( ipcarg_t ) socket->socket_id, ( ipcarg_t ) optname, socket->service, NULL ); |
4589 | mejdrech | 749 | // read the length |
750 | if( ipc_data_read_start( socket->phone, optlen, sizeof( * optlen )) == EOK ){ |
||
751 | // read the value |
||
752 | ipc_data_read_start( socket->phone, value, * optlen ); |
||
753 | } |
||
4726 | mejdrech | 754 | fibril_rwlock_read_unlock( & socket_globals.lock ); |
4578 | mejdrech | 755 | async_wait_for( message_id, & result ); |
756 | return ( int ) result; |
||
757 | } |
||
758 | |||
759 | int setsockopt( int socket_id, int level, int optname, const void * value, size_t optlen ){ |
||
4589 | mejdrech | 760 | // send the value |
4708 | mejdrech | 761 | return socket_send_data( socket_id, NET_SOCKET_SETSOCKOPT, ( ipcarg_t ) optname, value, optlen ); |
4589 | mejdrech | 762 | |
4578 | mejdrech | 763 | } |
764 | |||
765 | /** @} |
||
766 | */ |