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

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

Fix recv[from]() for active connections, connect() was missing notif.

  • Property mode set to 100644
File size: 15.7 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
251 switch (trc) {
252 case TCP_EOK:
253 rc = EOK;
254 break;
255 case TCP_ERESET:
256 rc = ECONNREFUSED;
257 break;
258 default:
259 assert(false);
260 }
261
262 async_answer_0(callid, rc);
263
264 /* Push one fragment notification to client's queue */
265 tcp_sock_notify_data(sock_core);
266 log_msg(LVL_DEBUG, "tcp_sock_listen(): notify conn\n");
267}
268
269static void tcp_sock_accept(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
270{
271 ipc_call_t answer;
272 int socket_id;
273 int asock_id;
274 socket_core_t *sock_core;
275 socket_core_t *asock_core;
276 tcp_sockdata_t *socket;
277 tcp_sockdata_t *asocket;
278 tcp_error_t trc;
279 tcp_sock_t fsocket;
280 tcp_conn_t *conn;
281 int rc;
282
283 log_msg(LVL_DEBUG, "tcp_sock_accept()");
284
285 socket_id = SOCKET_GET_SOCKET_ID(call);
286 asock_id = SOCKET_GET_NEW_SOCKET_ID(call);
287
288 sock_core = socket_cores_find(&client->sockets, socket_id);
289 if (sock_core == NULL) {
290 async_answer_0(callid, ENOTSOCK);
291 return;
292 }
293
294 socket = (tcp_sockdata_t *)sock_core->specific_data;
295
296 log_msg(LVL_DEBUG, " - verify socket->conn");
297 if (socket->conn != NULL) {
298 async_answer_0(callid, EINVAL);
299 return;
300 }
301
302 log_msg(LVL_DEBUG, " - open connection");
303
304 fsocket.addr.ipv4 = 0x7f000001; /* XXX */
305 fsocket.port = 1025; /* XXX */
306
307 trc = tcp_uc_open(sock_core->port, &fsocket, ap_passive, &conn);
308
309 log_msg(LVL_DEBUG, " - decode TCP return code");
310
311 switch (trc) {
312 case TCP_EOK:
313 rc = EOK;
314 break;
315 case TCP_ERESET:
316 rc = ECONNABORTED;
317 break;
318 default:
319 assert(false);
320 }
321
322 log_msg(LVL_DEBUG, " - check TCP return code");
323 if (rc != EOK) {
324 async_answer_0(callid, rc);
325 return;
326 }
327
328 log_msg(LVL_DEBUG, "tcp_sock_accept(): allocate asocket\n");
329 asocket = calloc(sizeof(tcp_sockdata_t), 1);
330 if (asocket == NULL) {
331 async_answer_0(callid, ENOMEM);
332 return;
333 }
334
335 asocket->client = client;
336 asocket->conn = conn;
337 log_msg(LVL_DEBUG, "tcp_sock_accept():create asocket\n");
338
339 rc = socket_create(&client->sockets, client->sess, asocket, &asock_id);
340 if (rc != EOK) {
341 async_answer_0(callid, rc);
342 return;
343 }
344 log_msg(LVL_DEBUG, "tcp_sock_accept(): find acore\n");
345
346 asock_core = socket_cores_find(&client->sockets, asock_id);
347 assert(asock_core != NULL);
348
349 refresh_answer(&answer, NULL);
350
351 SOCKET_SET_DATA_FRAGMENT_SIZE(answer, FRAGMENT_SIZE);
352 SOCKET_SET_SOCKET_ID(answer, asock_id);
353 SOCKET_SET_ADDRESS_LENGTH(answer, sizeof(struct sockaddr_in));
354
355 answer_call(callid, asock_core->socket_id, &answer, 3);
356
357 /* Push one accept notification to client's queue */
358 tcp_sock_notify_aconn(sock_core);
359
360 /* Push one fragment notification to client's queue */
361 tcp_sock_notify_data(asock_core);
362 log_msg(LVL_DEBUG, "tcp_sock_listen(): notify aconn\n");
363}
364
365static void tcp_sock_send(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
366{
367 int socket_id;
368 int fragments;
369 int index;
370 socket_core_t *sock_core;
371 tcp_sockdata_t *socket;
372 ipc_call_t answer;
373 ipc_callid_t wcallid;
374 size_t length;
375 uint8_t buffer[FRAGMENT_SIZE];
376 tcp_error_t trc;
377 int rc;
378
379 log_msg(LVL_DEBUG, "tcp_sock_send()");
380 socket_id = SOCKET_GET_SOCKET_ID(call);
381 fragments = SOCKET_GET_DATA_FRAGMENTS(call);
382 SOCKET_GET_FLAGS(call);
383
384 sock_core = socket_cores_find(&client->sockets, socket_id);
385 if (sock_core == NULL) {
386 async_answer_0(callid, ENOTSOCK);
387 return;
388 }
389
390 socket = (tcp_sockdata_t *)sock_core->specific_data;
391 if (socket->conn == NULL) {
392 async_answer_0(callid, ENOTCONN);
393 return;
394 }
395
396 for (index = 0; index < fragments; index++) {
397 if (!async_data_write_receive(&wcallid, &length)) {
398 async_answer_0(callid, EINVAL);
399 return;
400 }
401
402 if (length > FRAGMENT_SIZE)
403 length = FRAGMENT_SIZE;
404
405 rc = async_data_write_finalize(wcallid, buffer, length);
406 if (rc != EOK) {
407 async_answer_0(callid, rc);
408 return;
409 }
410
411 trc = tcp_uc_send(socket->conn, buffer, length, 0);
412
413 switch (trc) {
414 case TCP_EOK:
415 rc = EOK;
416 break;
417 case TCP_ENOTEXIST:
418 rc = ENOTCONN;
419 break;
420 case TCP_ECLOSING:
421 rc = ENOTCONN;
422 break;
423 default:
424 assert(false);
425 }
426
427 if (rc != EOK) {
428 async_answer_0(callid, rc);
429 return;
430 }
431 }
432
433 refresh_answer(&answer, NULL);
434 SOCKET_SET_DATA_FRAGMENT_SIZE(answer, FRAGMENT_SIZE);
435 answer_call(callid, EOK, &answer, 2);
436}
437
438static void tcp_sock_sendto(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
439{
440 log_msg(LVL_DEBUG, "tcp_sock_sendto()");
441 async_answer_0(callid, ENOTSUP);
442}
443
444static void tcp_sock_recvfrom(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
445{
446 int socket_id;
447 int flags;
448 size_t addr_length, length;
449 socket_core_t *sock_core;
450 tcp_sockdata_t *socket;
451 ipc_call_t answer;
452 ipc_callid_t rcallid;
453 uint8_t buffer[FRAGMENT_SIZE];
454 size_t data_len;
455 xflags_t xflags;
456 tcp_error_t trc;
457 struct sockaddr_in addr;
458 tcp_sock_t *rsock;
459 int rc;
460
461 log_msg(LVL_DEBUG, "tcp_sock_recv[from]()");
462
463 socket_id = SOCKET_GET_SOCKET_ID(call);
464 flags = SOCKET_GET_FLAGS(call);
465
466 sock_core = socket_cores_find(&client->sockets, socket_id);
467 if (sock_core == NULL) {
468 async_answer_0(callid, ENOTSOCK);
469 return;
470 }
471
472 socket = (tcp_sockdata_t *)sock_core->specific_data;
473 if (socket->conn == NULL) {
474 async_answer_0(callid, ENOTCONN);
475 return;
476 }
477
478 (void)flags;
479
480 trc = tcp_uc_receive(socket->conn, buffer, FRAGMENT_SIZE, &data_len,
481 &xflags);
482 log_msg(LVL_DEBUG, "**** tcp_uc_receive done");
483
484 switch (trc) {
485 case TCP_EOK:
486 rc = EOK;
487 break;
488 case TCP_ENOTEXIST:
489 case TCP_ECLOSING:
490 rc = ENOTCONN;
491 break;
492 default:
493 assert(false);
494 }
495
496 log_msg(LVL_DEBUG, "**** tcp_uc_receive -> %d", rc);
497 if (rc != EOK) {
498 async_answer_0(callid, rc);
499 return;
500 }
501
502 if (IPC_GET_IMETHOD(call) == NET_SOCKET_RECVFROM) {
503 /* Fill addr */
504 rsock = &socket->conn->ident.foreign;
505 addr.sin_family = AF_INET;
506 addr.sin_addr.s_addr = host2uint32_t_be(rsock->addr.ipv4);
507 addr.sin_port = host2uint16_t_be(rsock->port);
508
509 log_msg(LVL_DEBUG, "addr read receive");
510 if (!async_data_read_receive(&rcallid, &addr_length)) {
511 async_answer_0(callid, EINVAL);
512 return;
513 }
514
515 if (addr_length > sizeof(addr))
516 addr_length = sizeof(addr);
517
518 log_msg(LVL_DEBUG, "addr read finalize");
519 rc = async_data_read_finalize(rcallid, &addr, addr_length);
520 if (rc != EOK) {
521 async_answer_0(callid, EINVAL);
522 return;
523 }
524 }
525
526 log_msg(LVL_DEBUG, "data read receive");
527 if (!async_data_read_receive(&rcallid, &length)) {
528 async_answer_0(callid, EINVAL);
529 return;
530 }
531
532 if (length > data_len)
533 length = data_len;
534
535 log_msg(LVL_DEBUG, "data read finalize");
536 rc = async_data_read_finalize(rcallid, buffer, length);
537
538 if (length < data_len && rc == EOK)
539 rc = EOVERFLOW;
540
541 SOCKET_SET_READ_DATA_LENGTH(answer, length);
542 answer_call(callid, EOK, &answer, 1);
543
544 /* Push one fragment notification to client's queue */
545 tcp_sock_notify_data(sock_core);
546}
547
548static void tcp_sock_close(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
549{
550 int socket_id;
551 socket_core_t *sock_core;
552 tcp_sockdata_t *socket;
553 int rc;
554
555 log_msg(LVL_DEBUG, "tcp_sock_close()");
556 socket_id = SOCKET_GET_SOCKET_ID(call);
557
558 sock_core = socket_cores_find(&client->sockets, socket_id);
559 if (sock_core == NULL) {
560 async_answer_0(callid, ENOTSOCK);
561 return;
562 }
563
564 socket = (tcp_sockdata_t *)sock_core->specific_data;
565 (void) socket;
566 /* XXX Close */
567
568 rc = socket_destroy(net_sess, socket_id, &client->sockets, &gsock,
569 tcp_free_sock_data);
570 if (rc != EOK) {
571 async_answer_0(callid, rc);
572 return;
573 }
574
575 async_answer_0(callid, EOK);
576}
577
578static void tcp_sock_getsockopt(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
579{
580 log_msg(LVL_DEBUG, "tcp_sock_getsockopt()");
581 async_answer_0(callid, ENOTSUP);
582}
583
584static void tcp_sock_setsockopt(tcp_client_t *client, ipc_callid_t callid, ipc_call_t call)
585{
586 log_msg(LVL_DEBUG, "tcp_sock_setsockopt()");
587 async_answer_0(callid, ENOTSUP);
588}
589
590int tcp_sock_connection(async_sess_t *sess, ipc_callid_t iid, ipc_call_t icall)
591{
592 ipc_callid_t callid;
593 ipc_call_t call;
594 tcp_client_t client;
595
596 /* Accept the connection */
597 async_answer_0(iid, EOK);
598
599 client.sess = sess;
600 socket_cores_initialize(&client.sockets);
601
602 while (true) {
603 callid = async_get_call(&call);
604 if (!IPC_GET_IMETHOD(call))
605 break;
606
607 log_msg(LVL_DEBUG, "tcp_sock_connection: METHOD=%d\n",
608 (int)IPC_GET_IMETHOD(call));
609
610 switch (IPC_GET_IMETHOD(call)) {
611 case NET_SOCKET:
612 tcp_sock_socket(&client, callid, call);
613 break;
614 case NET_SOCKET_BIND:
615 tcp_sock_bind(&client, callid, call);
616 break;
617 case NET_SOCKET_LISTEN:
618 tcp_sock_listen(&client, callid, call);
619 break;
620 case NET_SOCKET_CONNECT:
621 tcp_sock_connect(&client, callid, call);
622 break;
623 case NET_SOCKET_ACCEPT:
624 tcp_sock_accept(&client, callid, call);
625 break;
626 case NET_SOCKET_SEND:
627 tcp_sock_send(&client, callid, call);
628 break;
629 case NET_SOCKET_SENDTO:
630 tcp_sock_sendto(&client, callid, call);
631 break;
632 case NET_SOCKET_RECV:
633 case NET_SOCKET_RECVFROM:
634 tcp_sock_recvfrom(&client, callid, call);
635 break;
636 case NET_SOCKET_CLOSE:
637 tcp_sock_close(&client, callid, call);
638 break;
639 case NET_SOCKET_GETSOCKOPT:
640 tcp_sock_getsockopt(&client, callid, call);
641 break;
642 case NET_SOCKET_SETSOCKOPT:
643 tcp_sock_setsockopt(&client, callid, call);
644 break;
645 default:
646 async_answer_0(callid, ENOTSUP);
647 break;
648 }
649 }
650
651 return EOK;
652}
653
654/**
655 * @}
656 */
Note: See TracBrowser for help on using the repository browser.