source: mainline/uspace/srv/net/tl/tcp/sock.c@ b0d82d1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b0d82d1 was b0d82d1, checked in by Jiri Svoboda <jiri@…>, 14 years ago

Implement socket close. Unlock mutexes on error paths.

  • Property mode set to 100644
File size: 16.1 KB
Line 
1/*
2 * Copyright (c) 2008 Lukas Mejdrech
3 * Copyright (c) 2011 Jiri Svoboda
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup tcp
31 * @{
32 */
33
34/**
35 * @file Socket provider
36 */
37
38#include <async.h>
39#include <errno.h>
40#include <io/log.h>
41#include <ipc/socket.h>
42#include <net/modules.h>
43#include <net/socket.h>
44
45#include "sock.h"
46#include "std.h"
47#include "tcp.h"
48#include "tcp_type.h"
49#include "ucall.h"
50
51#define FRAGMENT_SIZE 1024
52
53/** Free ports pool start. */
54#define TCP_FREE_PORTS_START 1025
55
56/** Free ports pool end. */
57#define TCP_FREE_PORTS_END 65535
58
59static int last_used_port = TCP_FREE_PORTS_START - 1;
60static socket_ports_t gsock;
61
62void tcp_sock_init(void)
63{
64 socket_ports_initialize(&gsock);
65}
66
67static void tcp_free_sock_data(socket_core_t *sock_core)
68{
69 tcp_sockdata_t *socket;
70
71 socket = (tcp_sockdata_t *)sock_core->specific_data;
72 (void)socket;
73}
74
75static void tcp_sock_notify_data(socket_core_t *sock_core)
76{
77 log_msg(LVL_DEBUG, "tcp_sock_notify_data(%d)", sock_core->socket_id);
78 async_exch_t *exch = async_exchange_begin(sock_core->sess);
79 async_msg_5(exch, NET_SOCKET_RECEIVED, (sysarg_t)sock_core->socket_id,
80 FRAGMENT_SIZE, 0, 0, 1);
81 async_exchange_end(exch);
82}
83
84static void tcp_sock_notify_aconn(socket_core_t *lsock_core)
85{
86 log_msg(LVL_DEBUG, "tcp_sock_notify_aconn(%d)", lsock_core->socket_id);
87 async_exch_t *exch = async_exchange_begin(lsock_core->sess);
88 async_msg_5(exch, NET_SOCKET_ACCEPTED, (sysarg_t)lsock_core->socket_id,
89 FRAGMENT_SIZE, 0, 0, 0);
90 async_exchange_end(exch);
91}
92
93static void tcp_sock_socket(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
94{
95 tcp_sockdata_t *sock;
96 int sock_id;
97 int rc;
98 ipc_call_t answer;
99
100 log_msg(LVL_DEBUG, "tcp_sock_socket()");
101 sock = calloc(sizeof(tcp_sockdata_t), 1);
102 if (sock == NULL) {
103 async_answer_0(callid, ENOMEM);
104 return;
105 }
106
107 sock->client = client;
108
109 sock_id = SOCKET_GET_SOCKET_ID(call);
110 rc = socket_create(&client->sockets, client->sess, sock, &sock_id);
111 if (rc != EOK) {
112 async_answer_0(callid, rc);
113 return;
114 }
115
116 refresh_answer(&answer, NULL);
117 SOCKET_SET_SOCKET_ID(answer, sock_id);
118
119 SOCKET_SET_DATA_FRAGMENT_SIZE(answer, FRAGMENT_SIZE);
120 SOCKET_SET_HEADER_SIZE(answer, sizeof(tcp_header_t));
121 answer_call(callid, EOK, &answer, 3);
122}
123
124static void tcp_sock_bind(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
125{
126 int rc;
127 struct sockaddr *addr;
128 size_t addr_len;
129 socket_core_t *sock_core;
130 tcp_sockdata_t *socket;
131
132 log_msg(LVL_DEBUG, "tcp_sock_bind()");
133 log_msg(LVL_DEBUG, " - async_data_write_accept");
134 rc = async_data_write_accept((void **) &addr, false, 0, 0, 0, &addr_len);
135 if (rc != EOK) {
136 async_answer_0(callid, rc);
137 return;
138 }
139
140 log_msg(LVL_DEBUG, " - call socket_bind");
141 rc = socket_bind(&client->sockets, &gsock, SOCKET_GET_SOCKET_ID(call),
142 addr, addr_len, TCP_FREE_PORTS_START, TCP_FREE_PORTS_END,
143 last_used_port);
144 if (rc != EOK) {
145 async_answer_0(callid, rc);
146 return;
147 }
148
149 log_msg(LVL_DEBUG, " - call socket_cores_find");
150 sock_core = socket_cores_find(&client->sockets, SOCKET_GET_SOCKET_ID(call));
151 if (sock_core != NULL) {
152 socket = (tcp_sockdata_t *)sock_core->specific_data;
153 /* XXX Anything to do? */
154 (void) socket;
155 }
156
157 log_msg(LVL_DEBUG, " - success");
158 async_answer_0(callid, EOK);
159}
160
161static void tcp_sock_listen(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
162{
163 int socket_id;
164 int backlog;
165 socket_core_t *sock_core;
166 tcp_sockdata_t *socket;
167
168 log_msg(LVL_DEBUG, "tcp_sock_listen()");
169
170 socket_id = SOCKET_GET_SOCKET_ID(call);
171 backlog = SOCKET_GET_BACKLOG(call);
172
173 if (backlog < 0) {
174 async_answer_0(callid, EINVAL);
175 return;
176 }
177
178 sock_core = socket_cores_find(&client->sockets, socket_id);
179 if (sock_core == NULL) {
180 async_answer_0(callid, ENOTSOCK);
181 return;
182 }
183
184 socket = (tcp_sockdata_t *)sock_core->specific_data;
185
186 /*
187 * XXX We do not do anything and defer action to accept().
188 * This is a slight difference in semantics, but most servers
189 * would just listen() and immediately accept() in a loop.
190 *
191 * The only difference is that there is a window between
192 * listen() and accept() or two accept()s where we refuse
193 * connections.
194 */
195 (void)backlog;
196 (void)socket;
197
198 async_answer_0(callid, EOK);
199 log_msg(LVL_DEBUG, "tcp_sock_listen(): notify data\n");
200 /* Push one accept notification to client's queue */
201 tcp_sock_notify_aconn(sock_core);
202}
203
204static void tcp_sock_connect(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
205{
206 int rc;
207 struct sockaddr_in *addr;
208 int socket_id;
209 size_t addr_len;
210 socket_core_t *sock_core;
211 tcp_sockdata_t *socket;
212 tcp_error_t trc;
213 tcp_sock_t fsocket;
214 uint16_t lport;
215
216 log_msg(LVL_DEBUG, "tcp_sock_connect()");
217
218 rc = async_data_write_accept((void **) &addr, false, 0, 0, 0, &addr_len);
219 if (rc != EOK || addr_len != sizeof(struct sockaddr_in)) {
220 async_answer_0(callid, rc);
221 return;
222 }
223
224 socket_id = SOCKET_GET_SOCKET_ID(call);
225
226 sock_core = socket_cores_find(&client->sockets, socket_id);
227 if (sock_core == NULL) {
228 async_answer_0(callid, ENOTSOCK);
229 return;
230 }
231
232 socket = (tcp_sockdata_t *)sock_core->specific_data;
233 if (sock_core->port <= 0) {
234 rc = socket_bind_free_port(&gsock, sock_core,
235 TCP_FREE_PORTS_START, TCP_FREE_PORTS_END,
236 last_used_port);
237 if (rc != EOK) {
238 async_answer_0(callid, rc);
239 return;
240 }
241
242 last_used_port = sock_core->port;
243 }
244
245 lport = sock_core->port;
246 fsocket.addr.ipv4 = uint32_t_be2host(addr->sin_addr.s_addr);
247 fsocket.port = uint16_t_be2host(addr->sin_port);
248
249 trc = tcp_uc_open(lport, &fsocket, ap_active, &socket->conn);
250 socket->conn->name = (char *)"C";
251
252 switch (trc) {
253 case TCP_EOK:
254 rc = EOK;
255 break;
256 case TCP_ERESET:
257 rc = ECONNREFUSED;
258 break;
259 default:
260 assert(false);
261 }
262
263 async_answer_0(callid, rc);
264
265 /* Push one fragment notification to client's queue */
266 tcp_sock_notify_data(sock_core);
267 log_msg(LVL_DEBUG, "tcp_sock_connect(): notify conn\n");
268}
269
270static void tcp_sock_accept(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
271{
272 ipc_call_t answer;
273 int socket_id;
274 int asock_id;
275 socket_core_t *sock_core;
276 socket_core_t *asock_core;
277 tcp_sockdata_t *socket;
278 tcp_sockdata_t *asocket;
279 tcp_error_t trc;
280 tcp_sock_t fsocket;
281 tcp_conn_t *conn;
282 int rc;
283
284 log_msg(LVL_DEBUG, "tcp_sock_accept()");
285
286 socket_id = SOCKET_GET_SOCKET_ID(call);
287 asock_id = SOCKET_GET_NEW_SOCKET_ID(call);
288
289 sock_core = socket_cores_find(&client->sockets, socket_id);
290 if (sock_core == NULL) {
291 async_answer_0(callid, ENOTSOCK);
292 return;
293 }
294
295 socket = (tcp_sockdata_t *)sock_core->specific_data;
296
297 log_msg(LVL_DEBUG, " - verify socket->conn");
298 if (socket->conn != NULL) {
299 async_answer_0(callid, EINVAL);
300 return;
301 }
302
303 log_msg(LVL_DEBUG, " - open connection");
304
305 fsocket.addr.ipv4 = 0x7f000001; /* XXX */
306 fsocket.port = 1025; /* XXX */
307
308 trc = tcp_uc_open(sock_core->port, &fsocket, ap_passive, &conn);
309 conn->name = (char *)"S";
310
311 log_msg(LVL_DEBUG, " - decode TCP return code");
312
313 switch (trc) {
314 case TCP_EOK:
315 rc = EOK;
316 break;
317 case TCP_ERESET:
318 rc = ECONNABORTED;
319 break;
320 default:
321 assert(false);
322 }
323
324 log_msg(LVL_DEBUG, " - check TCP return code");
325 if (rc != EOK) {
326 async_answer_0(callid, rc);
327 return;
328 }
329
330 log_msg(LVL_DEBUG, "tcp_sock_accept(): allocate asocket\n");
331 asocket = calloc(sizeof(tcp_sockdata_t), 1);
332 if (asocket == NULL) {
333 async_answer_0(callid, ENOMEM);
334 return;
335 }
336
337 asocket->client = client;
338 asocket->conn = conn;
339 log_msg(LVL_DEBUG, "tcp_sock_accept():create asocket\n");
340
341 rc = socket_create(&client->sockets, client->sess, asocket, &asock_id);
342 if (rc != EOK) {
343 async_answer_0(callid, rc);
344 return;
345 }
346 log_msg(LVL_DEBUG, "tcp_sock_accept(): find acore\n");
347
348 asock_core = socket_cores_find(&client->sockets, asock_id);
349 assert(asock_core != NULL);
350
351 refresh_answer(&answer, NULL);
352
353 SOCKET_SET_DATA_FRAGMENT_SIZE(answer, FRAGMENT_SIZE);
354 SOCKET_SET_SOCKET_ID(answer, asock_id);
355 SOCKET_SET_ADDRESS_LENGTH(answer, sizeof(struct sockaddr_in));
356
357 answer_call(callid, asock_core->socket_id, &answer, 3);
358
359 /* Push one accept notification to client's queue */
360 tcp_sock_notify_aconn(sock_core);
361
362 /* Push one fragment notification to client's queue */
363 tcp_sock_notify_data(asock_core);
364 log_msg(LVL_DEBUG, "tcp_sock_accept(): notify aconn\n");
365}
366
367static void tcp_sock_send(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
368{
369 int socket_id;
370 int fragments;
371 int index;
372 socket_core_t *sock_core;
373 tcp_sockdata_t *socket;
374 ipc_call_t answer;
375 ipc_callid_t wcallid;
376 size_t length;
377 uint8_t buffer[FRAGMENT_SIZE];
378 tcp_error_t trc;
379 int rc;
380
381 log_msg(LVL_DEBUG, "tcp_sock_send()");
382 socket_id = SOCKET_GET_SOCKET_ID(call);
383 fragments = SOCKET_GET_DATA_FRAGMENTS(call);
384 SOCKET_GET_FLAGS(call);
385
386 sock_core = socket_cores_find(&client->sockets, socket_id);
387 if (sock_core == NULL) {
388 async_answer_0(callid, ENOTSOCK);
389 return;
390 }
391
392 socket = (tcp_sockdata_t *)sock_core->specific_data;
393 if (socket->conn == NULL) {
394 async_answer_0(callid, ENOTCONN);
395 return;
396 }
397
398 for (index = 0; index < fragments; index++) {
399 if (!async_data_write_receive(&wcallid, &length)) {
400 async_answer_0(callid, EINVAL);
401 return;
402 }
403
404 if (length > FRAGMENT_SIZE)
405 length = FRAGMENT_SIZE;
406
407 rc = async_data_write_finalize(wcallid, buffer, length);
408 if (rc != EOK) {
409 async_answer_0(callid, rc);
410 return;
411 }
412
413 trc = tcp_uc_send(socket->conn, buffer, length, 0);
414
415 switch (trc) {
416 case TCP_EOK:
417 rc = EOK;
418 break;
419 case TCP_ENOTEXIST:
420 rc = ENOTCONN;
421 break;
422 case TCP_ECLOSING:
423 rc = ENOTCONN;
424 break;
425 default:
426 assert(false);
427 }
428
429 if (rc != EOK) {
430 async_answer_0(callid, rc);
431 return;
432 }
433 }
434
435 refresh_answer(&answer, NULL);
436 SOCKET_SET_DATA_FRAGMENT_SIZE(answer, FRAGMENT_SIZE);
437 answer_call(callid, EOK, &answer, 2);
438}
439
440static void tcp_sock_sendto(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
441{
442 log_msg(LVL_DEBUG, "tcp_sock_sendto()");
443 async_answer_0(callid, ENOTSUP);
444}
445
446static void tcp_sock_recvfrom(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
447{
448 int socket_id;
449 int flags;
450 size_t addr_length, length;
451 socket_core_t *sock_core;
452 tcp_sockdata_t *socket;
453 ipc_call_t answer;
454 ipc_callid_t rcallid;
455 uint8_t buffer[FRAGMENT_SIZE];
456 size_t data_len;
457 xflags_t xflags;
458 tcp_error_t trc;
459 struct sockaddr_in addr;
460 tcp_sock_t *rsock;
461 int rc;
462
463 log_msg(LVL_DEBUG, "%p: tcp_sock_recv[from]()", client);
464
465 socket_id = SOCKET_GET_SOCKET_ID(call);
466 flags = SOCKET_GET_FLAGS(call);
467
468 sock_core = socket_cores_find(&client->sockets, socket_id);
469 if (sock_core == NULL) {
470 async_answer_0(callid, ENOTSOCK);
471 return;
472 }
473
474 socket = (tcp_sockdata_t *)sock_core->specific_data;
475 if (socket->conn == NULL) {
476 async_answer_0(callid, ENOTCONN);
477 return;
478 }
479
480 (void)flags;
481
482 trc = tcp_uc_receive(socket->conn, buffer, FRAGMENT_SIZE, &data_len,
483 &xflags);
484 log_msg(LVL_DEBUG, "**** tcp_uc_receive done");
485
486 switch (trc) {
487 case TCP_EOK:
488 rc = EOK;
489 break;
490 case TCP_ENOTEXIST:
491 case TCP_ECLOSING:
492 rc = ENOTCONN;
493 break;
494 default:
495 assert(false);
496 }
497
498 log_msg(LVL_DEBUG, "**** tcp_uc_receive -> %d", rc);
499 if (rc != EOK) {
500 async_answer_0(callid, rc);
501 return;
502 }
503
504 if (IPC_GET_IMETHOD(call) == NET_SOCKET_RECVFROM) {
505 /* Fill addr */
506 rsock = &socket->conn->ident.foreign;
507 addr.sin_family = AF_INET;
508 addr.sin_addr.s_addr = host2uint32_t_be(rsock->addr.ipv4);
509 addr.sin_port = host2uint16_t_be(rsock->port);
510
511 log_msg(LVL_DEBUG, "addr read receive");
512 if (!async_data_read_receive(&rcallid, &addr_length)) {
513 async_answer_0(callid, EINVAL);
514 return;
515 }
516
517 if (addr_length > sizeof(addr))
518 addr_length = sizeof(addr);
519
520 log_msg(LVL_DEBUG, "addr read finalize");
521 rc = async_data_read_finalize(rcallid, &addr, addr_length);
522 if (rc != EOK) {
523 async_answer_0(callid, EINVAL);
524 return;
525 }
526 }
527
528 log_msg(LVL_DEBUG, "data read receive");
529 if (!async_data_read_receive(&rcallid, &length)) {
530 async_answer_0(callid, EINVAL);
531 return;
532 }
533
534 if (length > data_len)
535 length = data_len;
536
537 log_msg(LVL_DEBUG, "data read finalize");
538 rc = async_data_read_finalize(rcallid, buffer, length);
539
540 if (length < data_len && rc == EOK)
541 rc = EOVERFLOW;
542
543 SOCKET_SET_READ_DATA_LENGTH(answer, length);
544 answer_call(callid, EOK, &answer, 1);
545
546 /* Push one fragment notification to client's queue */
547 tcp_sock_notify_data(sock_core);
548}
549
550static void tcp_sock_close(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
551{
552 int socket_id;
553 socket_core_t *sock_core;
554 tcp_sockdata_t *socket;
555 tcp_error_t trc;
556 int rc;
557 uint8_t buffer[FRAGMENT_SIZE];
558 size_t data_len;
559 xflags_t xflags;
560
561 log_msg(LVL_DEBUG, "tcp_sock_close()");
562 socket_id = SOCKET_GET_SOCKET_ID(call);
563
564 sock_core = socket_cores_find(&client->sockets, socket_id);
565 if (sock_core == NULL) {
566 async_answer_0(callid, ENOTSOCK);
567 return;
568 }
569
570 socket = (tcp_sockdata_t *)sock_core->specific_data;
571 rc = tcp_uc_close(socket->conn);
572 if (rc != EOK) {
573 async_answer_0(callid, rc);
574 return;
575 }
576
577 /* Drain incoming data. This should really be done in the background. */
578 do {
579 trc = tcp_uc_receive(socket->conn, buffer, FRAGMENT_SIZE,
580 &data_len, &xflags);
581 } while (trc == TCP_EOK);
582
583 rc = socket_destroy(net_sess, socket_id, &client->sockets, &gsock,
584 tcp_free_sock_data);
585 if (rc != EOK) {
586 async_answer_0(callid, rc);
587 return;
588 }
589
590 async_answer_0(callid, EOK);
591}
592
593static void tcp_sock_getsockopt(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
594{
595 log_msg(LVL_DEBUG, "tcp_sock_getsockopt()");
596 async_answer_0(callid, ENOTSUP);
597}
598
599static void tcp_sock_setsockopt(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
600{
601 log_msg(LVL_DEBUG, "tcp_sock_setsockopt()");
602 async_answer_0(callid, ENOTSUP);
603}
604
605int tcp_sock_connection(async_sess_t *sess, ipc_callid_t iid, ipc_call_t icall)
606{
607 ipc_callid_t callid;
608 ipc_call_t call;
609 tcp_client_t client;
610
611 /* Accept the connection */
612 async_answer_0(iid, EOK);
613
614 client.sess = sess;
615 socket_cores_initialize(&client.sockets);
616
617 while (true) {
618 callid = async_get_call(&call);
619 if (!IPC_GET_IMETHOD(call))
620 break;
621
622 log_msg(LVL_DEBUG, "tcp_sock_connection: METHOD=%d\n",
623 (int)IPC_GET_IMETHOD(call));
624
625 switch (IPC_GET_IMETHOD(call)) {
626 case NET_SOCKET:
627 tcp_sock_socket(&client, callid, call);
628 break;
629 case NET_SOCKET_BIND:
630 tcp_sock_bind(&client, callid, call);
631 break;
632 case NET_SOCKET_LISTEN:
633 tcp_sock_listen(&client, callid, call);
634 break;
635 case NET_SOCKET_CONNECT:
636 tcp_sock_connect(&client, callid, call);
637 break;
638 case NET_SOCKET_ACCEPT:
639 tcp_sock_accept(&client, callid, call);
640 break;
641 case NET_SOCKET_SEND:
642 tcp_sock_send(&client, callid, call);
643 break;
644 case NET_SOCKET_SENDTO:
645 tcp_sock_sendto(&client, callid, call);
646 break;
647 case NET_SOCKET_RECV:
648 case NET_SOCKET_RECVFROM:
649 tcp_sock_recvfrom(&client, callid, call);
650 break;
651 case NET_SOCKET_CLOSE:
652 tcp_sock_close(&client, callid, call);
653 break;
654 case NET_SOCKET_GETSOCKOPT:
655 tcp_sock_getsockopt(&client, callid, call);
656 break;
657 case NET_SOCKET_SETSOCKOPT:
658 tcp_sock_setsockopt(&client, callid, call);
659 break;
660 default:
661 async_answer_0(callid, ENOTSUP);
662 break;
663 }
664 }
665
666 return EOK;
667}
668
669/**
670 * @}
671 */
Note: See TracBrowser for help on using the repository browser.