Rev 4603 | Rev 4708 | 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. |
||
84 | */ |
||
4578 | mejdrech | 85 | struct socket{ |
4701 | mejdrech | 86 | /** Socket identifier. |
87 | */ |
||
4578 | mejdrech | 88 | int socket_id; |
4701 | mejdrech | 89 | /** Parent module phone. |
90 | */ |
||
4578 | mejdrech | 91 | int phone; |
4701 | mejdrech | 92 | /** Parent module service. |
93 | */ |
||
4578 | mejdrech | 94 | services_t service; |
4701 | mejdrech | 95 | /** Underlying protocol header size. |
96 | * Sending and receiving optimalization. |
||
97 | */ |
||
4589 | mejdrech | 98 | int header_size; |
4701 | mejdrech | 99 | /** Packet data fragment size. |
100 | * Sending and receiving optimalization. |
||
101 | */ |
||
4589 | mejdrech | 102 | int data_fragment_size; |
4701 | mejdrech | 103 | /** Received packets queue. |
104 | */ |
||
4589 | mejdrech | 105 | dyn_fifo_t received; |
4701 | mejdrech | 106 | /** Received packets safety lock. |
107 | */ |
||
4582 | mejdrech | 108 | fibril_mutex_t receive_lock; |
4701 | mejdrech | 109 | /** Received packets signaling. |
110 | */ |
||
4582 | mejdrech | 111 | fibril_condvar_t receive_signal; |
4701 | mejdrech | 112 | /** Waiting sockets queue. |
113 | */ |
||
4589 | mejdrech | 114 | dyn_fifo_t accepted; |
4701 | mejdrech | 115 | /** Waiting sockets safety lock. |
116 | */ |
||
4582 | mejdrech | 117 | fibril_mutex_t accept_lock; |
4701 | mejdrech | 118 | /** Waiting sockets signaling. |
119 | */ |
||
4582 | mejdrech | 120 | fibril_condvar_t accept_signal; |
4578 | mejdrech | 121 | }; |
122 | |||
4701 | mejdrech | 123 | /** Sockets map. |
124 | * Maps socket identifiers to the socket specific data. |
||
125 | * @see int_map.h |
||
126 | */ |
||
4578 | mejdrech | 127 | INT_MAP_DECLARE( sockets, socket_t ); |
128 | |||
4701 | mejdrech | 129 | /** Socket client library global data. |
130 | */ |
||
4578 | mejdrech | 131 | static struct{ |
4701 | mejdrech | 132 | /** TCP module phone. |
133 | */ |
||
4578 | mejdrech | 134 | int tcp_phone; |
4701 | mejdrech | 135 | /** UDP module phone. |
136 | */ |
||
4578 | mejdrech | 137 | int udp_phone; |
4701 | mejdrech | 138 | /** Active sockets. |
139 | */ |
||
4578 | mejdrech | 140 | sockets_ref sockets; |
141 | } socket_globals = { -1, -1, NULL }; |
||
142 | |||
143 | INT_MAP_IMPLEMENT( sockets, socket_t ); |
||
144 | |||
4701 | mejdrech | 145 | /** Returns the TCP module phone. |
146 | * Connects to the TCP module if necessary. |
||
147 | * @returns The TCP module phone. |
||
148 | */ |
||
4578 | mejdrech | 149 | static int socket_get_tcp_phone(); |
4701 | mejdrech | 150 | |
151 | /** Returns the UDP module phone. |
||
152 | * Connects to the UDP module if necessary. |
||
153 | * @returns The UDP module phone. |
||
154 | */ |
||
4578 | mejdrech | 155 | static int socket_get_tcp_phone(); |
4701 | mejdrech | 156 | |
157 | /** Returns the active sockets. |
||
158 | * @returns The active sockets. |
||
159 | */ |
||
4578 | mejdrech | 160 | static sockets_ref socket_get_sockets(); |
4701 | mejdrech | 161 | |
162 | /** Default thread for new connections. |
||
163 | * @param iid The initial message identifier. Input parameter. |
||
164 | * @param icall The initial message call structure. Input parameter. |
||
165 | */ |
||
4578 | mejdrech | 166 | void socket_connection( ipc_callid_t iid, ipc_call_t * icall ); |
4701 | mejdrech | 167 | |
168 | /** Sends message to the socket parent module with specified data. |
||
169 | * @param socket_id Socket identifier. Input parameter. |
||
170 | * @param message The action message. Input parameter. |
||
171 | * @param arg2 The second message parameter. Input parameter. |
||
172 | * @param data The data to be sent. Input parameter. |
||
173 | * @param datalength The data length. Input parameter. |
||
174 | * @returns EOK on success. |
||
175 | * @returns ENOTSOCK if the socket is not found. |
||
176 | * @returns EBADMEM if the data parameter is NULL. |
||
177 | * @returns NO_DATA if the datalength parameter is zero (0). |
||
178 | * @returns Other error codes as defined for the spcific message. |
||
179 | */ |
||
4578 | mejdrech | 180 | int socket_send_data( int socket_id, int message, ipcarg_t arg2, const void * data, size_t datalength ); |
4701 | mejdrech | 181 | |
182 | /** Initializes a new socket specific data. |
||
183 | * @param socket The socket to be initialized. Input/output parameter. |
||
184 | * @param socket_id The new socket identifier. Input parameter. |
||
185 | * @param phone The parent module phone. Input parameter. |
||
186 | * @param service The parent module service. Input parameter. |
||
187 | */ |
||
4578 | mejdrech | 188 | void socket_initialize( socket_ref socket, int socket_id, int phone, services_t service ); |
4701 | mejdrech | 189 | |
190 | /** Clears and destroys the socket. |
||
191 | * @param socket The socket to be destroyed. Input parameter. |
||
192 | */ |
||
4589 | mejdrech | 193 | void socket_destroy( socket_ref socket ); |
4701 | mejdrech | 194 | |
195 | /** Receives data via the socket. |
||
196 | * @param message The action message. Input parameter. |
||
197 | * @param socket_id Socket identifier. Input parameter. |
||
198 | * @param data The data buffer to be filled. Output parameter. |
||
199 | * @param datalength The data length. Input parameter. |
||
200 | * @param flags Various receive flags. Input parameter. |
||
201 | * @param fromaddr The source address. May be NULL for connected sockets. Output parameter. |
||
202 | * @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. |
||
203 | * @returns EOK on success. |
||
204 | * @returns ENOTSOCK if the socket is not found. |
||
205 | * @returns EBADMEM if the data parameter is NULL. |
||
206 | * @returns NO_DATA if the datalength or addrlen parameter is zero (0). |
||
207 | * @returns Other error codes as defined for the spcific message. |
||
208 | */ |
||
4589 | mejdrech | 209 | int recvfrom_core( int message, int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ); |
4701 | mejdrech | 210 | |
211 | /** Sends data via the socket to the remote address. |
||
212 | * Binds the socket to a free port if not already connected/bound. |
||
213 | * @param message The action message. Input parameter. |
||
214 | * @param socket_id Socket identifier. Input parameter. |
||
215 | * @param data The data to be sent. Input parameter. |
||
216 | * @param datalength The data length. Input parameter. |
||
217 | * @param flags Various send flags. Input parameter. |
||
218 | * @param toaddr The destination address. May be NULL for connected sockets. Input parameter. |
||
219 | * @param addrlen The address length. Used only if toaddr is not NULL. Input parameter. |
||
220 | * @returns EOK on success. |
||
221 | * @returns ENOTSOCK if the socket is not found. |
||
222 | * @returns EBADMEM if the data or toaddr parameter is NULL. |
||
223 | * @returns NO_DATA if the datalength or the addrlen parameter is zero (0). |
||
224 | * @returns Other error codes as defined for the NET_SOCKET_SENDTO message. |
||
225 | */ |
||
4589 | mejdrech | 226 | int sendto_core( int message, int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ); |
4578 | mejdrech | 227 | |
228 | static int socket_get_tcp_phone(){ |
||
229 | if( socket_globals.tcp_phone < 0 ){ |
||
230 | socket_globals.tcp_phone = bind_service( SERVICE_TCP, 0, 0, SERVICE_TCP, socket_connection ); |
||
231 | } |
||
232 | return socket_globals.tcp_phone; |
||
233 | } |
||
234 | |||
235 | static int socket_get_udp_phone(){ |
||
236 | if( socket_globals.udp_phone < 0 ){ |
||
237 | socket_globals.udp_phone = bind_service( SERVICE_UDP, 0, 0, SERVICE_UDP, socket_connection ); |
||
238 | } |
||
239 | return socket_globals.udp_phone; |
||
240 | } |
||
241 | |||
242 | static sockets_ref socket_get_sockets(){ |
||
243 | if( ! socket_globals.sockets ){ |
||
244 | socket_globals.sockets = ( sockets_ref ) malloc( sizeof( sockets_t )); |
||
245 | if( ! socket_globals.sockets ) return NULL; |
||
246 | if( sockets_initialize( socket_globals.sockets ) != EOK ){ |
||
247 | free( socket_globals.sockets ); |
||
248 | socket_globals.sockets = NULL; |
||
249 | } |
||
250 | } |
||
251 | return socket_globals.sockets; |
||
252 | } |
||
253 | |||
254 | void socket_initialize( socket_ref socket, int socket_id, int phone, services_t service ){ |
||
255 | socket->socket_id = socket_id; |
||
256 | socket->phone = phone; |
||
257 | socket->service = service; |
||
4589 | mejdrech | 258 | dyn_fifo_initialize( & socket->received, SOCKET_INITIAL_RECEIVED_SIZE ); |
259 | dyn_fifo_initialize( & socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE ); |
||
4582 | mejdrech | 260 | fibril_mutex_initialize( & socket->receive_lock ); |
261 | fibril_condvar_initialize( & socket->receive_signal ); |
||
262 | fibril_mutex_initialize( & socket->accept_lock ); |
||
263 | fibril_condvar_initialize( & socket->accept_signal ); |
||
4578 | mejdrech | 264 | } |
265 | |||
266 | void socket_connection( ipc_callid_t iid, ipc_call_t * icall ){ |
||
267 | ERROR_DECLARE; |
||
268 | |||
269 | ipc_callid_t callid; |
||
270 | ipc_call_t call; |
||
271 | socket_ref socket; |
||
272 | socket_ref new_socket; |
||
273 | |||
274 | while( true ){ |
||
275 | |||
276 | callid = async_get_call( & call ); |
||
277 | switch( IPC_GET_METHOD( call )){ |
||
278 | case NET_SOCKET_RECEIVED: |
||
4589 | mejdrech | 279 | // find the socket |
4701 | mejdrech | 280 | socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call )); |
4578 | mejdrech | 281 | if( ! socket ){ |
282 | ERROR_CODE = ENOTSOCK; |
||
283 | break; |
||
284 | } |
||
4582 | mejdrech | 285 | fibril_mutex_lock( & socket->receive_lock ); |
4589 | mejdrech | 286 | // push the number of received packet fragments |
4701 | mejdrech | 287 | if( ! ERROR_OCCURRED( dyn_fifo_push( & socket->received, SOCKET_GET_DATA_FRAGMENTS( call ), SOCKET_MAX_RECEIVED_SIZE ))){ |
4589 | mejdrech | 288 | // signal the received packet |
289 | fibril_condvar_signal( & socket->receive_signal ); |
||
290 | } |
||
4582 | mejdrech | 291 | fibril_mutex_unlock( & socket->receive_lock ); |
4578 | mejdrech | 292 | break; |
293 | case NET_SOCKET_ACCEPTED: |
||
4589 | mejdrech | 294 | // find the socket |
4701 | mejdrech | 295 | socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call )); |
4578 | mejdrech | 296 | if( ! socket ){ |
297 | ERROR_CODE = ENOTSOCK; |
||
298 | break; |
||
299 | } |
||
4589 | mejdrech | 300 | // create a new scoket |
4578 | mejdrech | 301 | new_socket = ( socket_ref ) malloc( sizeof( socket_t )); |
302 | if( ! new_socket ){ |
||
303 | ERROR_CODE = ENOMEM; |
||
304 | break; |
||
305 | } |
||
4701 | mejdrech | 306 | socket_initialize( new_socket, SOCKET_GET_SOCKET_ID( call ), socket->phone, socket->service ); |
4589 | mejdrech | 307 | ERROR_CODE = sockets_add( socket_get_sockets(), new_socket->socket_id, new_socket ); |
308 | if( ERROR_CODE < 0 ){ |
||
309 | free( new_socket ); |
||
310 | }else{ |
||
311 | // push the new socket identifier |
||
312 | fibril_mutex_lock( & socket->accept_lock ); |
||
313 | if( ERROR_OCCURRED( dyn_fifo_push( & socket->accepted, new_socket->socket_id, SOCKET_MAX_ACCEPTED_SIZE ))){ |
||
314 | sockets_exclude( socket_get_sockets(), new_socket->socket_id ); |
||
315 | free( new_socket ); |
||
316 | }else{ |
||
317 | // signal the accepted socket |
||
318 | fibril_condvar_signal( & socket->accept_signal ); |
||
319 | } |
||
320 | fibril_mutex_unlock( & socket->accept_lock ); |
||
321 | ERROR_CODE = EOK; |
||
4578 | mejdrech | 322 | } |
323 | break; |
||
4589 | mejdrech | 324 | case NET_SOCKET_DATA_FRAGMENT_SIZE: |
325 | // find the socket |
||
4701 | mejdrech | 326 | socket = sockets_find( socket_get_sockets(), SOCKET_GET_SOCKET_ID( call )); |
4578 | mejdrech | 327 | if( ! socket ){ |
328 | ERROR_CODE = ENOTSOCK; |
||
329 | break; |
||
330 | } |
||
4589 | mejdrech | 331 | // set the data fragment size |
4701 | mejdrech | 332 | socket->data_fragment_size = SOCKET_GET_DATA_FRAGMENT_SIZE( call ); |
4578 | mejdrech | 333 | ERROR_CODE = EOK; |
334 | break; |
||
4589 | mejdrech | 335 | default: |
4578 | mejdrech | 336 | ERROR_CODE = ENOTSUP; |
337 | } |
||
338 | ipc_answer_0( callid, ERROR_CODE ); |
||
339 | } |
||
340 | } |
||
341 | |||
342 | int socket( int domain, int type, int protocol ){ |
||
4589 | mejdrech | 343 | ERROR_DECLARE; |
344 | |||
4578 | mejdrech | 345 | socket_ref socket; |
346 | int phone; |
||
347 | int socket_id; |
||
348 | services_t service; |
||
349 | |||
4589 | mejdrech | 350 | // find the appropriate service |
4578 | mejdrech | 351 | switch( domain ){ |
352 | case PF_INET: |
||
353 | switch( type ){ |
||
354 | case SOCK_STREAM: |
||
355 | if( ! protocol ) protocol = IPPROTO_TCP; |
||
356 | switch( protocol ){ |
||
357 | case IPPROTO_TCP: |
||
358 | phone = socket_get_tcp_phone(); |
||
359 | service = SERVICE_TCP; |
||
360 | break; |
||
361 | default: |
||
362 | return EPROTONOSUPPORT; |
||
363 | } |
||
364 | break; |
||
365 | case SOCK_DGRAM: |
||
366 | if( ! protocol ) protocol = IPPROTO_UDP; |
||
367 | switch( protocol ){ |
||
368 | case IPPROTO_UDP: |
||
369 | phone = socket_get_udp_phone(); |
||
370 | service = SERVICE_UDP; |
||
371 | break; |
||
372 | default: |
||
373 | return EPROTONOSUPPORT; |
||
374 | } |
||
375 | break; |
||
376 | case SOCK_RAW: |
||
377 | default: |
||
378 | return ESOCKTNOSUPPORT; |
||
379 | } |
||
380 | break; |
||
381 | // TODO IPv6 |
||
382 | default: |
||
383 | return EPFNOSUPPORT; |
||
384 | } |
||
4589 | mejdrech | 385 | // create a new socket structure |
4578 | mejdrech | 386 | socket = ( socket_ref ) malloc( sizeof( socket_t )); |
387 | if( ! socket ) return ENOMEM; |
||
4589 | mejdrech | 388 | if( ERROR_OCCURRED( dyn_fifo_initialize( & socket->received, SOCKET_INITIAL_RECEIVED_SIZE ))){ |
4578 | mejdrech | 389 | free( socket ); |
4589 | mejdrech | 390 | return ERROR_CODE; |
4578 | mejdrech | 391 | } |
4589 | mejdrech | 392 | if( ERROR_OCCURRED( dyn_fifo_initialize( & socket->accepted, SOCKET_INITIAL_ACCEPTED_SIZE ))){ |
393 | dyn_fifo_destroy( & socket->received ); |
||
394 | free( socket ); |
||
395 | return ERROR_CODE; |
||
396 | } |
||
397 | // request a new socket |
||
398 | if( ERROR_OCCURRED( async_req_3_3( phone, NET_SOCKET, 0, 0, service, ( ipcarg_t * ) & socket_id, ( ipcarg_t * ) & socket->header_size, ( ipcarg_t * ) & socket->data_fragment_size ))){ |
||
399 | dyn_fifo_destroy( & socket->received ); |
||
400 | dyn_fifo_destroy( & socket->accepted ); |
||
401 | free( socket ); |
||
402 | return ERROR_CODE; |
||
403 | } |
||
404 | // finish the new socket initialization |
||
405 | socket_initialize( socket, socket_id, phone, service ); |
||
406 | // store the new socket |
||
407 | ERROR_CODE = sockets_add( socket_get_sockets(), socket_id, socket ); |
||
408 | if( ERROR_CODE < 0 ){ |
||
409 | dyn_fifo_destroy( & socket->received ); |
||
410 | dyn_fifo_destroy( & socket->accepted ); |
||
411 | free( socket ); |
||
412 | async_msg_3( phone, NET_SOCKET_CLOSE, socket_id, 0, service ); |
||
413 | return ERROR_CODE; |
||
414 | } |
||
415 | |||
4578 | mejdrech | 416 | return socket_id; |
417 | } |
||
418 | |||
419 | int socket_send_data( int socket_id, int message, ipcarg_t arg2, const void * data, size_t datalength ){ |
||
420 | socket_ref socket; |
||
421 | aid_t message_id; |
||
422 | ipcarg_t result; |
||
423 | |||
424 | if( ! data ) return EBADMEM; |
||
425 | if( ! datalength ) return NO_DATA; |
||
4589 | mejdrech | 426 | // find the socket |
4578 | mejdrech | 427 | socket = sockets_find( socket_get_sockets(), socket_id ); |
428 | if( ! socket ) return ENOTSOCK; |
||
4589 | mejdrech | 429 | // request the message |
4578 | mejdrech | 430 | message_id = async_send_3( socket->phone, message, socket->socket_id, arg2, socket->service, NULL ); |
4589 | mejdrech | 431 | // send the address |
4578 | mejdrech | 432 | ipc_data_write_start( socket->phone, data, datalength ); |
433 | async_wait_for( message_id, & result ); |
||
434 | return ( int ) result; |
||
435 | } |
||
436 | |||
437 | int bind( int socket_id, const struct sockaddr * my_addr, socklen_t addrlen ){ |
||
4589 | mejdrech | 438 | // send the address |
4578 | mejdrech | 439 | return socket_send_data( socket_id, NET_SOCKET_BIND, 0, my_addr, addrlen ); |
440 | } |
||
441 | |||
442 | int listen( int socket_id, int backlog ){ |
||
443 | socket_ref socket; |
||
444 | |||
445 | if( backlog <= 0 ) return EINVAL; |
||
4589 | mejdrech | 446 | // find the socket |
4578 | mejdrech | 447 | socket = sockets_find( socket_get_sockets(), socket_id ); |
448 | if( ! socket ) return ENOTSOCK; |
||
4589 | mejdrech | 449 | // request listen backlog change |
4578 | mejdrech | 450 | return async_req_3_0( socket->phone, NET_SOCKET_LISTEN, socket->socket_id, backlog, socket->service ); |
451 | } |
||
452 | |||
453 | int accept( int socket_id, struct sockaddr * cliaddr, socklen_t * addrlen ){ |
||
454 | socket_ref socket; |
||
455 | aid_t message_id; |
||
4589 | mejdrech | 456 | int result; |
4578 | mejdrech | 457 | |
4701 | mejdrech | 458 | if(( ! cliaddr ) || ( ! addrlen )) return EBADMEM; |
4589 | mejdrech | 459 | // find the socket |
4578 | mejdrech | 460 | socket = sockets_find( socket_get_sockets(), socket_id ); |
461 | if( ! socket ) return ENOTSOCK; |
||
4582 | mejdrech | 462 | fibril_mutex_lock( & socket->accept_lock ); |
4589 | mejdrech | 463 | // wait for an accepted socket |
464 | while( dyn_fifo_value( & socket->accepted ) <= 0 ){ |
||
4582 | mejdrech | 465 | fibril_condvar_wait( & socket->accept_signal, & socket->accept_lock ); |
4578 | mejdrech | 466 | } |
4589 | mejdrech | 467 | // request accept |
468 | message_id = async_send_3( socket->phone, NET_SOCKET_ACCEPT, socket->socket_id, dyn_fifo_value( & socket->accepted ), socket->service, NULL ); |
||
469 | // read address |
||
4578 | mejdrech | 470 | ipc_data_read_start( socket->phone, cliaddr, * addrlen ); |
4589 | mejdrech | 471 | async_wait_for( message_id, ( ipcarg_t * ) & result ); |
4578 | mejdrech | 472 | if( result > 0 ){ |
4589 | mejdrech | 473 | // dequeue the accepted apcket if successful |
474 | dyn_fifo_pop( & socket->accepted ); |
||
4578 | mejdrech | 475 | } |
4582 | mejdrech | 476 | fibril_mutex_unlock( & socket->accept_lock ); |
4589 | mejdrech | 477 | return result; |
4578 | mejdrech | 478 | } |
479 | |||
480 | int connect( int socket_id, const struct sockaddr * serv_addr, socklen_t addrlen ){ |
||
4589 | mejdrech | 481 | // send the address |
4578 | mejdrech | 482 | return socket_send_data( socket_id, NET_SOCKET_CONNECT, 0, serv_addr, addrlen ); |
483 | } |
||
484 | |||
485 | int closesocket( int socket_id ){ |
||
4589 | mejdrech | 486 | ERROR_DECLARE; |
487 | |||
4578 | mejdrech | 488 | socket_ref socket; |
489 | |||
490 | socket = sockets_find( socket_get_sockets(), socket_id ); |
||
491 | if( ! socket ) return ENOTSOCK; |
||
4589 | mejdrech | 492 | // request close |
493 | ERROR_PROPAGATE( async_req_3_0( socket->phone, NET_SOCKET_CLOSE, socket->socket_id, 0, socket->service )); |
||
494 | // free the socket structure |
||
495 | socket_destroy( socket ); |
||
496 | return EOK; |
||
4578 | mejdrech | 497 | } |
498 | |||
4589 | mejdrech | 499 | void socket_destroy( socket_ref socket ){ |
500 | int accepted_id; |
||
501 | |||
502 | // destroy all accepted sockets |
||
4701 | mejdrech | 503 | while(( accepted_id = dyn_fifo_pop( & socket->accepted )) >= 0 ){ |
4589 | mejdrech | 504 | socket_destroy( sockets_find( socket_get_sockets(), accepted_id )); |
505 | } |
||
506 | dyn_fifo_destroy( & socket->received ); |
||
507 | dyn_fifo_destroy( & socket->accepted ); |
||
508 | sockets_exclude( socket_get_sockets(), socket->socket_id ); |
||
509 | } |
||
510 | |||
4578 | mejdrech | 511 | int send( int socket_id, void * data, size_t datalength, int flags ){ |
4589 | mejdrech | 512 | // without the address |
513 | return sendto_core( NET_SOCKET_SEND, socket_id, data, datalength, flags, NULL, 0 ); |
||
4578 | mejdrech | 514 | } |
515 | |||
516 | int sendto( int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){ |
||
517 | if( ! toaddr ) return EBADMEM; |
||
518 | if( ! addrlen ) return NO_DATA; |
||
4589 | mejdrech | 519 | // with the address |
520 | return sendto_core( NET_SOCKET_SENDTO, socket_id, data, datalength, flags, toaddr, addrlen ); |
||
4578 | mejdrech | 521 | } |
522 | |||
4589 | mejdrech | 523 | int sendto_core( int message, int socket_id, const void * data, size_t datalength, int flags, const struct sockaddr * toaddr, socklen_t addrlen ){ |
4578 | mejdrech | 524 | socket_ref socket; |
525 | aid_t message_id; |
||
526 | ipcarg_t result; |
||
4589 | mejdrech | 527 | int fragments; |
4578 | mejdrech | 528 | |
529 | if( ! data ) return EBADMEM; |
||
530 | if( ! datalength ) return NO_DATA; |
||
4589 | mejdrech | 531 | // find socket |
4578 | mejdrech | 532 | socket = sockets_find( socket_get_sockets(), socket_id ); |
533 | if( ! socket ) return ENOTSOCK; |
||
4589 | mejdrech | 534 | // compute data fragment count |
535 | fragments = ( datalength + socket->header_size ) / socket->data_fragment_size; |
||
536 | if(( datalength + socket->header_size ) % socket->data_fragment_size ) ++ fragments; |
||
537 | // request send |
||
538 | message_id = async_send_4( socket->phone, message, socket->socket_id, fragments, socket->service, flags, NULL ); |
||
539 | // send the address if given |
||
540 | if(( ! toaddr ) || ( ipc_data_write_start( socket->phone, toaddr, addrlen ) == EOK )){ |
||
541 | if( fragments == 1 ){ |
||
542 | // send all if only one fragment |
||
543 | ipc_data_write_start( socket->phone, data, datalength ); |
||
544 | }else{ |
||
545 | // send the first fragment |
||
546 | ipc_data_write_start( socket->phone, data, socket->data_fragment_size - socket->header_size ); |
||
547 | data += socket->data_fragment_size - socket->header_size; |
||
548 | // send the middle fragments |
||
549 | while(( -- fragments ) > 1 ){ |
||
550 | ipc_data_write_start( socket->phone, data, socket->data_fragment_size ); |
||
551 | data += socket->data_fragment_size; |
||
552 | } |
||
553 | // send the last fragment |
||
554 | ipc_data_write_start( socket->phone, data, ( datalength + socket->header_size ) % socket->data_fragment_size ); |
||
555 | } |
||
4578 | mejdrech | 556 | } |
557 | async_wait_for( message_id, & result ); |
||
558 | return ( int ) result; |
||
559 | } |
||
560 | |||
4589 | mejdrech | 561 | int recv( int socket_id, void * data, size_t datalength, int flags ){ |
562 | // without the address |
||
563 | return recvfrom_core( NET_SOCKET_RECV, socket_id, data, datalength, flags, NULL, NULL ); |
||
564 | } |
||
565 | |||
4578 | mejdrech | 566 | int recvfrom( int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){ |
4589 | mejdrech | 567 | if( ! fromaddr ) return EBADMEM; |
568 | if( ! addrlen ) return NO_DATA; |
||
569 | // with the address |
||
570 | return recvfrom_core( NET_SOCKET_RECVFROM, socket_id, data, datalength, flags, fromaddr, addrlen ); |
||
571 | } |
||
572 | |||
573 | int recvfrom_core( int message, int socket_id, void * data, size_t datalength, int flags, struct sockaddr * fromaddr, socklen_t * addrlen ){ |
||
4578 | mejdrech | 574 | socket_ref socket; |
575 | aid_t message_id; |
||
4589 | mejdrech | 576 | int result; |
577 | int fragments; |
||
578 | int * lengths; |
||
579 | int index; |
||
580 | ipc_call_t answer; |
||
4578 | mejdrech | 581 | |
582 | if( ! data ) return EBADMEM; |
||
583 | if( ! datalength ) return NO_DATA; |
||
4701 | mejdrech | 584 | if( fromaddr && ( ! addrlen )) return EINVAL; |
4589 | mejdrech | 585 | // find the socket |
4578 | mejdrech | 586 | socket = sockets_find( socket_get_sockets(), socket_id ); |
587 | if( ! socket ) return ENOTSOCK; |
||
4582 | mejdrech | 588 | fibril_mutex_lock( & socket->receive_lock ); |
4589 | mejdrech | 589 | // wait for a received packet |
590 | while(( fragments = dyn_fifo_value( & socket->received )) <= 0 ){ |
||
4582 | mejdrech | 591 | fibril_condvar_wait( & socket->receive_signal, & socket->receive_lock ); |
4578 | mejdrech | 592 | } |
4589 | mejdrech | 593 | // prepare lengths if more fragments |
594 | if( fragments > 1 ){ |
||
595 | lengths = ( int * ) malloc( sizeof( int ) * ( fragments + 1 )); |
||
596 | if( ! lengths ){ |
||
597 | fibril_mutex_unlock( & socket->receive_lock ); |
||
598 | return ENOMEM; |
||
599 | } |
||
4578 | mejdrech | 600 | } |
4589 | mejdrech | 601 | // request packet data |
602 | message_id = async_send_4( socket->phone, message, socket->socket_id, 0, socket->service, flags, & answer ); |
||
603 | // read the address if desired |
||
604 | if(( ! fromaddr ) || ( ipc_data_read_start( socket->phone, fromaddr, * addrlen ) == EOK )){ |
||
605 | if( fragments == 1 ){ |
||
606 | // read all if only one fragment |
||
607 | ipc_data_read_start( socket->phone, data, datalength ); |
||
608 | }else{ |
||
609 | // read the fragment lengths |
||
610 | if( ipc_data_read_start( socket->phone, lengths, sizeof( int ) * ( fragments + 1 )) == EOK ){ |
||
611 | if( lengths[ fragments ] <= datalength ){ |
||
612 | // read all fragments if long enough |
||
613 | for( index = 0; index < fragments; ++ index ){ |
||
614 | ipc_data_read_start( socket->phone, data, lengths[ index ] ); |
||
615 | data += lengths[ index ]; |
||
616 | } |
||
617 | } |
||
618 | } |
||
619 | free( lengths ); |
||
620 | } |
||
621 | }else if( fragments > 1 ){ |
||
622 | free( lengths ); |
||
623 | } |
||
624 | async_wait_for( message_id, ( ipcarg_t * ) & result ); |
||
625 | // if successful |
||
626 | if( result == EOK ){ |
||
627 | // dequeue the received packet |
||
628 | dyn_fifo_pop( & socket->received ); |
||
629 | // return read data length |
||
4701 | mejdrech | 630 | result = SOCKET_GET_READ_DATA_LENGTH( answer ); |
4603 | mejdrech | 631 | // set address length |
4701 | mejdrech | 632 | if( fromaddr && addrlen ) * addrlen = SOCKET_GET_ADDRESS_LENGTH( answer ); |
4589 | mejdrech | 633 | } |
4582 | mejdrech | 634 | fibril_mutex_unlock( & socket->receive_lock ); |
4589 | mejdrech | 635 | return result; |
4578 | mejdrech | 636 | } |
637 | |||
638 | int getsockopt( int socket_id, int level, int optname, void * value, size_t * optlen ){ |
||
639 | socket_ref socket; |
||
640 | aid_t message_id; |
||
641 | ipcarg_t result; |
||
642 | |||
4589 | mejdrech | 643 | if( !( value && optlen )) return EBADMEM; |
644 | if( !( * optlen )) return NO_DATA; |
||
645 | // find the socket |
||
4578 | mejdrech | 646 | socket = sockets_find( socket_get_sockets(), socket_id ); |
647 | if( ! socket ) return ENOTSOCK; |
||
4589 | mejdrech | 648 | // request option value |
4578 | mejdrech | 649 | message_id = async_send_3( socket->phone, NET_SOCKET_GETSOCKOPT, socket->socket_id, optname, socket->service, NULL ); |
4589 | mejdrech | 650 | // read the length |
651 | if( ipc_data_read_start( socket->phone, optlen, sizeof( * optlen )) == EOK ){ |
||
652 | // read the value |
||
653 | ipc_data_read_start( socket->phone, value, * optlen ); |
||
654 | } |
||
4578 | mejdrech | 655 | async_wait_for( message_id, & result ); |
656 | return ( int ) result; |
||
657 | } |
||
658 | |||
659 | int setsockopt( int socket_id, int level, int optname, const void * value, size_t optlen ){ |
||
4589 | mejdrech | 660 | // send the value |
4578 | mejdrech | 661 | return socket_send_data( socket_id, NET_SOCKET_SETSOCKOPT, optname, value, optlen ); |
4589 | mejdrech | 662 | |
4578 | mejdrech | 663 | } |
664 | |||
665 | /** @} |
||
666 | */ |