source: mainline/uspace/srv/net/tcp/service.c@ 76f566d

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

Selected ccheck-proposed comment fixes.

  • Property mode set to 100644
File size: 29.8 KB
RevLine 
[779541b]1/*
2 * Copyright (c) 2015 Jiri Svoboda
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
[b10460a]29/** @addtogroup tcp
[779541b]30 * @{
31 */
32
33/**
34 * @file HelenOS service implementation
35 */
36
37#include <async.h>
38#include <errno.h>
[c1694b6b]39#include <str_error.h>
[779541b]40#include <inet/endpoint.h>
41#include <inet/inet.h>
42#include <io/log.h>
43#include <ipc/services.h>
44#include <ipc/tcp.h>
45#include <loc.h>
46#include <macros.h>
[b10460a]47#include <mem.h>
[779541b]48#include <stdlib.h>
49
50#include "conn.h"
51#include "service.h"
52#include "tcp_type.h"
53#include "ucall.h"
54
55#define NAME "tcp"
56
[b10460a]57/** Maximum amount of data transferred in one send call */
[779541b]58#define MAX_MSG_SIZE DATA_XFER_LIMIT
59
60static void tcp_ev_data(tcp_cconn_t *);
[1d4b815]61static void tcp_ev_connected(tcp_cconn_t *);
62static void tcp_ev_conn_failed(tcp_cconn_t *);
63static void tcp_ev_conn_reset(tcp_cconn_t *);
[b99f6e2]64static void tcp_ev_new_conn(tcp_clst_t *, tcp_cconn_t *);
[779541b]65
[1d4b815]66static void tcp_service_cstate_change(tcp_conn_t *, void *, tcp_cstate_t);
[779541b]67static void tcp_service_recv_data(tcp_conn_t *, void *);
[b99f6e2]68static void tcp_service_lst_cstate_change(tcp_conn_t *, void *, tcp_cstate_t);
69
[b7fd2a0]70static errno_t tcp_cconn_create(tcp_client_t *, tcp_conn_t *, tcp_cconn_t **);
[779541b]71
[b10460a]72/** Connection callbacks to tie us to lower layer */
[779541b]73static tcp_cb_t tcp_service_cb = {
74 .cstate_change = tcp_service_cstate_change,
75 .recv_data = tcp_service_recv_data
76};
77
[b10460a]78/** Sentinel connection callbacks to tie us to lower layer */
[b99f6e2]79static tcp_cb_t tcp_service_lst_cb = {
80 .cstate_change = tcp_service_lst_cstate_change,
81 .recv_data = NULL
82};
83
[b10460a]84/** Connection state has changed.
85 *
86 * @param conn Connection
87 * @param arg Argument (not used)
88 * @param old_state Previous connection state
89 */
[1d4b815]90static void tcp_service_cstate_change(tcp_conn_t *conn, void *arg,
91 tcp_cstate_t old_state)
[779541b]92{
[1d4b815]93 tcp_cstate_t nstate;
94 tcp_cconn_t *cconn;
95
96 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_service_cstate_change()");
97 nstate = conn->cstate;
98 cconn = tcp_uc_get_userptr(conn);
99
100 if ((old_state == st_syn_sent || old_state == st_syn_received) &&
101 (nstate == st_established)) {
102 /* Connection established */
103 tcp_ev_connected(cconn);
104 }
105
106 if (old_state != st_closed && nstate == st_closed && conn->reset) {
[9362cc2]107 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_service_cstate_change: "
108 "Connection reset");
[1d4b815]109 /* Connection reset */
110 tcp_ev_conn_reset(cconn);
[9362cc2]111 } else {
112 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_service_cstate_change: "
113 "old_state=%d nstate=%d conn->reset=%d",
114 old_state, nstate, conn->reset);
[1d4b815]115 }
116
117 /* XXX Failed to establish connection */
[1433ecda]118 if (0)
119 tcp_ev_conn_failed(cconn);
[779541b]120}
121
[b10460a]122/** Sentinel connection state has changed.
123 *
124 * @param conn Connection
125 * @param arg Argument (not used)
126 * @param old_state Previous connection state
127 */
[b99f6e2]128static void tcp_service_lst_cstate_change(tcp_conn_t *conn, void *arg,
129 tcp_cstate_t old_state)
130{
131 tcp_cstate_t nstate;
132 tcp_clst_t *clst;
133 tcp_cconn_t *cconn;
[2f19103]134 inet_ep2_t epp;
[b7fd2a0]135 errno_t rc;
[b99f6e2]136 tcp_error_t trc;
137
138 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_service_lst_cstate_change()");
139 nstate = conn->cstate;
140 clst = tcp_uc_get_userptr(conn);
141
142 if ((old_state == st_syn_sent || old_state == st_syn_received) &&
143 (nstate == st_established)) {
144 /* Connection established */
145 clst->conn = NULL;
146
147 rc = tcp_cconn_create(clst->client, conn, &cconn);
148 if (rc != EOK) {
149 /* XXX Could not create client connection */
150 return;
151 }
152
153 /* XXX Is there a race here (i.e. the connection is already active)? */
154 tcp_uc_set_cb(conn, &tcp_service_cb, cconn);
155
156 /* New incoming connection */
157 tcp_ev_new_conn(clst, cconn);
158 }
159
160 if (old_state != st_closed && nstate == st_closed && conn->reset) {
161 /* Connection reset */
162 /* XXX */
163 }
164
165 /* XXX Failed to establish connection */
166 if (0) {
167 /* XXX */
168 }
169
170 /* Replenish sentinel connection */
171
[2f19103]172 inet_ep2_init(&epp);
173 epp.local = clst->elocal;
174
175 trc = tcp_uc_open(&epp, ap_passive, tcp_open_nonblock,
[b99f6e2]176 &conn);
177 if (trc != TCP_EOK) {
178 /* XXX Could not replenish connection */
179 return;
180 }
181
[8d48c7e]182 conn->name = (char *) "s";
[b99f6e2]183 clst->conn = conn;
184
185 /* XXX Is there a race here (i.e. the connection is already active)? */
186 tcp_uc_set_cb(conn, &tcp_service_lst_cb, clst);
187}
188
[b10460a]189/** Received data became available on connection.
190 *
191 * @param conn Connection
192 * @param arg Client connection
193 */
[779541b]194static void tcp_service_recv_data(tcp_conn_t *conn, void *arg)
195{
196 tcp_cconn_t *cconn = (tcp_cconn_t *)arg;
197
198 tcp_ev_data(cconn);
199}
200
[b10460a]201/** Send 'data' event to client.
202 *
203 * @param cconn Client connection
204 */
[779541b]205static void tcp_ev_data(tcp_cconn_t *cconn)
206{
207 async_exch_t *exch;
208
209 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_ev_data()");
210
211 log_msg(LOG_DEFAULT, LVL_DEBUG, "client=%p\n", cconn->client);
212 log_msg(LOG_DEFAULT, LVL_DEBUG, "sess=%p\n", cconn->client->sess);
213
214 exch = async_exchange_begin(cconn->client->sess);
215 aid_t req = async_send_1(exch, TCP_EV_DATA, cconn->id, NULL);
216 async_exchange_end(exch);
217
[1d4b815]218 async_forget(req);
219}
220
[b10460a]221/** Send 'connected' event to client.
222 *
223 * @param cconn Client connection
224 */
[1d4b815]225static void tcp_ev_connected(tcp_cconn_t *cconn)
226{
227 async_exch_t *exch;
228
229 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_ev_connected()");
230
231 exch = async_exchange_begin(cconn->client->sess);
232 aid_t req = async_send_1(exch, TCP_EV_CONNECTED, cconn->id, NULL);
233 async_exchange_end(exch);
234
235 async_forget(req);
236}
237
[b10460a]238/** Send 'conn_failed' event to client.
239 *
240 * @param cconn Client connection
241 */
[1d4b815]242static void tcp_ev_conn_failed(tcp_cconn_t *cconn)
243{
244 async_exch_t *exch;
245
246 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_ev_conn_failed()");
247
248 exch = async_exchange_begin(cconn->client->sess);
249 aid_t req = async_send_1(exch, TCP_EV_CONN_FAILED, cconn->id, NULL);
250 async_exchange_end(exch);
251
252 async_forget(req);
253}
254
[b10460a]255/** Send 'conn_reset' event to client.
256 *
257 * @param cconn Client connection
258 */
[1d4b815]259static void tcp_ev_conn_reset(tcp_cconn_t *cconn)
260{
261 async_exch_t *exch;
262
263 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_ev_conn_reset()");
264
265 exch = async_exchange_begin(cconn->client->sess);
266 aid_t req = async_send_1(exch, TCP_EV_CONN_RESET, cconn->id, NULL);
267 async_exchange_end(exch);
268
[779541b]269 async_forget(req);
270}
271
[b10460a]272/** Send 'new_conn' event to client.
273 *
274 * @param clst Client listener that received the connection
275 * @param cconn New client connection
276 */
[b99f6e2]277static void tcp_ev_new_conn(tcp_clst_t *clst, tcp_cconn_t *cconn)
278{
279 async_exch_t *exch;
280
281 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_ev_new_conn()");
282
283 exch = async_exchange_begin(clst->client->sess);
284 aid_t req = async_send_2(exch, TCP_EV_NEW_CONN, clst->id, cconn->id,
285 NULL);
286 async_exchange_end(exch);
287
288 async_forget(req);
289}
290
[b10460a]291/** Create client connection.
292 *
293 * This effectively adds a connection into a client's namespace.
294 *
295 * @param client TCP client
296 * @param conn Connection
297 * @param rcconn Place to store pointer to new client connection
298 *
299 * @return EOK on success or ENOMEM if out of memory
300 */
[b7fd2a0]301static errno_t tcp_cconn_create(tcp_client_t *client, tcp_conn_t *conn,
[779541b]302 tcp_cconn_t **rcconn)
303{
304 tcp_cconn_t *cconn;
305 sysarg_t id;
306
307 cconn = calloc(1, sizeof(tcp_cconn_t));
308 if (cconn == NULL)
309 return ENOMEM;
310
311 /* Allocate new ID */
312 id = 0;
313 list_foreach (client->cconn, lclient, tcp_cconn_t, cconn) {
314 if (cconn->id >= id)
315 id = cconn->id + 1;
316 }
317
318 cconn->id = id;
319 cconn->client = client;
320 cconn->conn = conn;
321
322 list_append(&cconn->lclient, &client->cconn);
323 *rcconn = cconn;
324 return EOK;
325}
326
[b10460a]327/** Destroy client connection.
328 *
329 * @param cconn Client connection
330 */
[779541b]331static void tcp_cconn_destroy(tcp_cconn_t *cconn)
332{
333 list_remove(&cconn->lclient);
334 free(cconn);
335}
336
[b10460a]337/** Create client listener.
338 *
339 * Create client listener based on sentinel connection.
340 * XXX Implement actual listener in protocol core
341 *
342 * @param client TCP client
343 * @param conn Sentinel connection
344 * @param rclst Place to store pointer to new client listener
345 *
346 * @return EOK on success or ENOMEM if out of memory
347 */
[b7fd2a0]348static errno_t tcp_clistener_create(tcp_client_t *client, tcp_conn_t *conn,
[779541b]349 tcp_clst_t **rclst)
350{
351 tcp_clst_t *clst;
352 sysarg_t id;
353
354 clst = calloc(1, sizeof(tcp_clst_t));
355 if (clst == NULL)
356 return ENOMEM;
357
358 /* Allocate new ID */
359 id = 0;
360 list_foreach (client->clst, lclient, tcp_clst_t, clst) {
361 if (clst->id >= id)
362 id = clst->id + 1;
363 }
364
365 clst->id = id;
366 clst->client = client;
367 clst->conn = conn;
368
369 list_append(&clst->lclient, &client->clst);
370 *rclst = clst;
371 return EOK;
372}
373
[b10460a]374/** Destroy client listener.
375 *
376 * @param clst Client listener
377 */
[779541b]378static void tcp_clistener_destroy(tcp_clst_t *clst)
379{
380 list_remove(&clst->lclient);
381 free(clst);
382}
383
[b10460a]384/** Get client connection by ID.
385 *
386 * @param client Client
387 * @param id Client connection ID
388 * @param rcconn Place to store pointer to client connection
389 *
390 * @return EOK on success, ENOENT if no client connection with the given ID
391 * is found.
392 */
[b7fd2a0]393static errno_t tcp_cconn_get(tcp_client_t *client, sysarg_t id,
[779541b]394 tcp_cconn_t **rcconn)
395{
396 list_foreach (client->cconn, lclient, tcp_cconn_t, cconn) {
397 if (cconn->id == id) {
398 *rcconn = cconn;
399 return EOK;
400 }
401 }
402
403 return ENOENT;
404}
405
[b10460a]406/** Get client listener by ID.
407 *
408 * @param client Client
409 * @param id Client connection ID
410 * @param rclst Place to store pointer to client listener
411 *
412 * @return EOK on success, ENOENT if no client listener with the given ID
413 * is found.
414 */
[b7fd2a0]415static errno_t tcp_clistener_get(tcp_client_t *client, sysarg_t id,
[779541b]416 tcp_clst_t **rclst)
417{
418 list_foreach (client->clst, lclient, tcp_clst_t, clst) {
419 if (clst->id == id) {
420 *rclst = clst;
421 return EOK;
422 }
423 }
424
425 return ENOENT;
426}
427
[b10460a]428/** Create connection.
429 *
430 * Handle client request to create connection (with parameters unmarshalled).
431 *
432 * @param client TCP client
433 * @param epp Endpoint pair
434 * @param rconn_id Place to store ID of new connection
435 *
[cde999a]436 * @return EOK on success or an error code
[b10460a]437 */
[b7fd2a0]438static errno_t tcp_conn_create_impl(tcp_client_t *client, inet_ep2_t *epp,
[779541b]439 sysarg_t *rconn_id)
440{
441 tcp_conn_t *conn;
442 tcp_cconn_t *cconn;
[b7fd2a0]443 errno_t rc;
[779541b]444 tcp_error_t trc;
445 char *slocal;
446 char *sremote;
447
448 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_create_impl");
449
[2989c7e]450 inet_addr_format(&epp->local.addr, &slocal);
451 inet_addr_format(&epp->remote.addr, &sremote);
[779541b]452 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_create: local=%s remote=%s",
453 slocal, sremote);
[2f19103]454 free(slocal);
455 free(sremote);
[779541b]456
[2989c7e]457 trc = tcp_uc_open(epp, ap_active, tcp_open_nonblock, &conn);
[779541b]458 if (trc != TCP_EOK)
459 return EIO;
460
[8d48c7e]461 conn->name = (char *) "c";
462
[779541b]463 rc = tcp_cconn_create(client, conn, &cconn);
464 if (rc != EOK) {
465 assert(rc == ENOMEM);
466 tcp_conn_delete(conn);
467 return ENOMEM;
468 }
469
470 /* XXX Is there a race here (i.e. the connection is already active)? */
471 tcp_uc_set_cb(conn, &tcp_service_cb, cconn);
472
473 *rconn_id = cconn->id;
474 return EOK;
475}
476
[b10460a]477/** Destroy connection.
478 *
479 * Handle client request to destroy connection (with parameters unmarshalled).
480 *
481 * @param client TCP client
482 * @param conn_id Connection ID
483 * @return EOK on success, ENOENT if no such connection is found
484 */
[b7fd2a0]485static errno_t tcp_conn_destroy_impl(tcp_client_t *client, sysarg_t conn_id)
[779541b]486{
487 tcp_cconn_t *cconn;
[b7fd2a0]488 errno_t rc;
[779541b]489
490 rc = tcp_cconn_get(client, conn_id, &cconn);
491 if (rc != EOK) {
492 assert(rc == ENOENT);
493 return ENOENT;
494 }
495
496 tcp_uc_close(cconn->conn);
[bf7587b0]497 tcp_uc_delete(cconn->conn);
[779541b]498 tcp_cconn_destroy(cconn);
499 return EOK;
500}
501
[b10460a]502/** Create listener.
503 *
504 * Handle client request to create listener (with parameters unmarshalled).
505 *
506 * @param client TCP client
507 * @param ep Endpoint
508 * @param rlst_id Place to store ID of new listener
509 *
[cde999a]510 * @return EOK on success or an error code
[ae7d03c]511 */
[b7fd2a0]512static errno_t tcp_listener_create_impl(tcp_client_t *client, inet_ep_t *ep,
[779541b]513 sysarg_t *rlst_id)
514{
515 tcp_conn_t *conn;
516 tcp_clst_t *clst;
[2f19103]517 inet_ep2_t epp;
[b7fd2a0]518 errno_t rc;
[779541b]519 tcp_error_t trc;
520
521 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_listener_create_impl");
522
[2f19103]523 inet_ep2_init(&epp);
524 epp.local.addr = ep->addr;
525 epp.local.port = ep->port;
[779541b]526
[2f19103]527 trc = tcp_uc_open(&epp, ap_passive, tcp_open_nonblock, &conn);
[779541b]528 if (trc != TCP_EOK)
529 return EIO;
530
[8d48c7e]531 conn->name = (char *) "s";
532
[779541b]533 rc = tcp_clistener_create(client, conn, &clst);
534 if (rc != EOK) {
535 assert(rc == ENOMEM);
536 tcp_conn_delete(conn);
537 return ENOMEM;
538 }
539
[2f19103]540 clst->elocal = epp.local;
[b99f6e2]541
542 /* XXX Is there a race here (i.e. the connection is already active)? */
543 tcp_uc_set_cb(conn, &tcp_service_lst_cb, clst);
[779541b]544
545 *rlst_id = clst->id;
546 return EOK;
547}
548
[b10460a]549/** Destroy listener.
550 *
551 * Handle client request to destroy listener (with parameters unmarshalled).
552 *
553 * @param client TCP client
554 * @param lst_id Listener ID
555 *
556 * @return EOK on success, ENOENT if no such listener is found
557 */
[b7fd2a0]558static errno_t tcp_listener_destroy_impl(tcp_client_t *client, sysarg_t lst_id)
[779541b]559{
560 tcp_clst_t *clst;
[b7fd2a0]561 errno_t rc;
[779541b]562
563 rc = tcp_clistener_get(client, lst_id, &clst);
564 if (rc != EOK) {
565 assert(rc == ENOENT);
566 return ENOENT;
567 }
568
[3fafe5e0]569#if 0
570 tcp_uc_close(cconn->conn);
571#endif
[779541b]572 tcp_clistener_destroy(clst);
573 return EOK;
574}
575
[b10460a]576/** Send FIN.
577 *
578 * Handle client request to send FIN (with parameters unmarshalled).
579 *
580 * @param client TCP client
581 * @param conn_id Connection ID
582 *
[cde999a]583 * @return EOK on success or an error code
[b10460a]584 */
[b7fd2a0]585static errno_t tcp_conn_send_fin_impl(tcp_client_t *client, sysarg_t conn_id)
[779541b]586{
587 tcp_cconn_t *cconn;
[b7fd2a0]588 errno_t rc;
[779541b]589
590 rc = tcp_cconn_get(client, conn_id, &cconn);
591 if (rc != EOK) {
592 assert(rc == ENOENT);
593 return ENOENT;
594 }
595
596 (void) cconn;
597 /* XXX TODO */
598 return EOK;
599}
600
[b10460a]601/** Push connection.
602 *
603 * Handle client request to push connection (with parameters unmarshalled).
604 *
605 * @param client TCP client
606 * @param conn_id Connection ID
607 *
[cde999a]608 * @return EOK on success or an error code
[b10460a]609 */
[b7fd2a0]610static errno_t tcp_conn_push_impl(tcp_client_t *client, sysarg_t conn_id)
[779541b]611{
612 tcp_cconn_t *cconn;
[b7fd2a0]613 errno_t rc;
[779541b]614
615 rc = tcp_cconn_get(client, conn_id, &cconn);
616 if (rc != EOK) {
617 assert(rc == ENOENT);
618 return ENOENT;
619 }
620
621 (void) cconn;
622 /* XXX TODO */
623 return EOK;
624}
625
[b10460a]626/** Reset connection.
627 *
628 * Handle client request to reset connection (with parameters unmarshalled).
629 *
630 * @param client TCP client
631 * @param conn_id Connection ID
632 *
[cde999a]633 * @return EOK on success or an error code
[b10460a]634 */
[b7fd2a0]635static errno_t tcp_conn_reset_impl(tcp_client_t *client, sysarg_t conn_id)
[779541b]636{
637 tcp_cconn_t *cconn;
[b7fd2a0]638 errno_t rc;
[779541b]639
640 rc = tcp_cconn_get(client, conn_id, &cconn);
641 if (rc != EOK) {
642 assert(rc == ENOENT);
643 return ENOENT;
644 }
645
646 tcp_uc_abort(cconn->conn);
647 return EOK;
648}
649
[b10460a]650/** Send data over connection..
651 *
652 * Handle client request to send data (with parameters unmarshalled).
653 *
654 * @param client TCP client
655 * @param conn_id Connection ID
656 * @param data Data buffer
657 * @param size Data size in bytes
658 *
[cde999a]659 * @return EOK on success or an error code
[b10460a]660 */
[b7fd2a0]661static errno_t tcp_conn_send_impl(tcp_client_t *client, sysarg_t conn_id,
[779541b]662 void *data, size_t size)
663{
664 tcp_cconn_t *cconn;
[b7fd2a0]665 errno_t rc;
[7ce8f88]666 tcp_error_t trc;
[779541b]667
668 rc = tcp_cconn_get(client, conn_id, &cconn);
669 if (rc != EOK)
670 return rc;
671
[7ce8f88]672 trc = tcp_uc_send(cconn->conn, data, size, 0);
673 if (trc != TCP_EOK)
674 return EIO;
[779541b]675
676 return EOK;
677}
678
[b10460a]679/** Receive data from connection.
680 *
681 * Handle client request to receive data (with parameters unmarshalled).
682 *
683 * @param client TCP client
684 * @param conn_id Connection ID
685 * @param data Data buffer
686 * @param size Buffer size in bytes
687 * @param nrecv Place to store actual number of bytes received
688 *
[cde999a]689 * @return EOK on success or an error code
[b10460a]690 */
[b7fd2a0]691static errno_t tcp_conn_recv_impl(tcp_client_t *client, sysarg_t conn_id,
[779541b]692 void *data, size_t size, size_t *nrecv)
693{
694 tcp_cconn_t *cconn;
695 xflags_t xflags;
[b7fd2a0]696 errno_t rc;
[7ce8f88]697 tcp_error_t trc;
[779541b]698
699 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl()");
700
701 rc = tcp_cconn_get(client, conn_id, &cconn);
702 if (rc != EOK) {
703 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl() - conn not found");
704 return rc;
705 }
706
[7ce8f88]707 trc = tcp_uc_receive(cconn->conn, data, size, nrecv, &xflags);
708 if (trc != TCP_EOK) {
709 switch (trc) {
[779541b]710 case TCP_EAGAIN:
711 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl() - EAGAIN");
712 return EAGAIN;
[258d77e]713 case TCP_ECLOSING:
714 *nrecv = 0;
715 return EOK;
[779541b]716 default:
[7ce8f88]717 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl() - trc=%d", trc);
[779541b]718 return EIO;
719 }
720 }
721
722 return EOK;
723}
724
[b10460a]725/** Create client callback session.
726 *
727 * Handle client request to create callback session.
728 *
[a46e56b]729 * @param client TCP client
730 * @param icall_handle Async request call handle
731 * @param icall Async request data
[b10460a]732 */
[a46e56b]733static void tcp_callback_create_srv(tcp_client_t *client,
734 cap_call_handle_t icall_handle, ipc_call_t *icall)
[779541b]735{
736 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_callback_create_srv()");
737
738 async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
739 if (sess == NULL) {
[a46e56b]740 async_answer_0(icall_handle, ENOMEM);
[779541b]741 return;
742 }
743
744 client->sess = sess;
[a46e56b]745 async_answer_0(icall_handle, EOK);
[779541b]746}
747
[b10460a]748/** Create connection.
749 *
750 * Handle client request to create connection.
751 *
[a46e56b]752 * @param client TCP client
753 * @param icall_handle Async request call handle
754 * @param icall Async request data
[b10460a]755 */
[a46e56b]756static void tcp_conn_create_srv(tcp_client_t *client,
757 cap_call_handle_t icall_handle, ipc_call_t *icall)
[779541b]758{
[a46e56b]759 cap_call_handle_t chandle;
[779541b]760 size_t size;
761 inet_ep2_t epp;
762 sysarg_t conn_id;
[b7fd2a0]763 errno_t rc;
[779541b]764
765 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_create_srv()");
766
[a46e56b]767 if (!async_data_write_receive(&chandle, &size)) {
768 async_answer_0(chandle, EREFUSED);
769 async_answer_0(icall_handle, EREFUSED);
[779541b]770 return;
771 }
772
773 if (size != sizeof(inet_ep2_t)) {
[a46e56b]774 async_answer_0(chandle, EINVAL);
775 async_answer_0(icall_handle, EINVAL);
[779541b]776 return;
777 }
778
[a46e56b]779 rc = async_data_write_finalize(chandle, &epp, size);
[779541b]780 if (rc != EOK) {
[a46e56b]781 async_answer_0(chandle, rc);
782 async_answer_0(icall_handle, rc);
[779541b]783 return;
784 }
785
786 rc = tcp_conn_create_impl(client, &epp, &conn_id);
787 if (rc != EOK) {
[a46e56b]788 async_answer_0(icall_handle, rc);
[779541b]789 return;
790 }
791
[a46e56b]792 async_answer_1(icall_handle, EOK, conn_id);
[779541b]793}
794
[b10460a]795/** Destroy connection.
796 *
797 * Handle client request to destroy connection.
798 *
[a46e56b]799 * @param client TCP client
[b752a31]800 * @param icall_handle Async request call handle
[a46e56b]801 * @param icall Async request data
[b10460a]802 */
[a46e56b]803static void tcp_conn_destroy_srv(tcp_client_t *client,
804 cap_call_handle_t icall_handle, ipc_call_t *icall)
[779541b]805{
806 sysarg_t conn_id;
[b7fd2a0]807 errno_t rc;
[779541b]808
809 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_destroy_srv()");
810
811 conn_id = IPC_GET_ARG1(*icall);
812 rc = tcp_conn_destroy_impl(client, conn_id);
[a46e56b]813 async_answer_0(icall_handle, rc);
[779541b]814}
815
[b10460a]816/** Create listener.
817 *
818 * Handle client request to create listener.
819 *
[a46e56b]820 * @param client TCP client
[b752a31]821 * @param icall_handle Async request call handle
[a46e56b]822 * @param icall Async request data
[b10460a]823 */
[a46e56b]824static void tcp_listener_create_srv(tcp_client_t *client,
825 cap_call_handle_t icall_handle, ipc_call_t *icall)
[779541b]826{
[a46e56b]827 cap_call_handle_t chandle;
[779541b]828 size_t size;
829 inet_ep_t ep;
830 sysarg_t lst_id;
[b7fd2a0]831 errno_t rc;
[779541b]832
833 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_listener_create_srv()");
834
[a46e56b]835 if (!async_data_write_receive(&chandle, &size)) {
836 async_answer_0(chandle, EREFUSED);
837 async_answer_0(icall_handle, EREFUSED);
[779541b]838 return;
839 }
840
841 if (size != sizeof(inet_ep_t)) {
[a46e56b]842 async_answer_0(chandle, EINVAL);
843 async_answer_0(icall_handle, EINVAL);
[779541b]844 return;
845 }
846
[a46e56b]847 rc = async_data_write_finalize(chandle, &ep, size);
[779541b]848 if (rc != EOK) {
[a46e56b]849 async_answer_0(chandle, rc);
850 async_answer_0(icall_handle, rc);
[779541b]851 return;
852 }
853
854 rc = tcp_listener_create_impl(client, &ep, &lst_id);
855 if (rc != EOK) {
[a46e56b]856 async_answer_0(icall_handle, rc);
[779541b]857 return;
858 }
859
[a46e56b]860 async_answer_1(icall_handle, EOK, lst_id);
[779541b]861}
862
[b10460a]863/** Destroy listener.
864 *
865 * Handle client request to destroy listener.
866 *
[a46e56b]867 * @param client TCP client
868 * @param icall_handle Async request call handle
869 * @param icall Async request data
[b10460a]870 */
[a46e56b]871static void tcp_listener_destroy_srv(tcp_client_t *client,
872 cap_call_handle_t icall_handle, ipc_call_t *icall)
[779541b]873{
874 sysarg_t lst_id;
[b7fd2a0]875 errno_t rc;
[779541b]876
877 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_listener_destroy_srv()");
878
879 lst_id = IPC_GET_ARG1(*icall);
880 rc = tcp_listener_destroy_impl(client, lst_id);
[a46e56b]881 async_answer_0(icall_handle, rc);
[779541b]882}
883
[b10460a]884/** Send FIN.
885 *
886 * Handle client request to send FIN.
887 *
[a46e56b]888 * @param client TCP client
[b752a31]889 * @param icall_handle Async request call handle
[a46e56b]890 * @param icall Async request data
[b10460a]891 */
[a46e56b]892static void tcp_conn_send_fin_srv(tcp_client_t *client,
893 cap_call_handle_t icall_handle, ipc_call_t *icall)
[779541b]894{
895 sysarg_t conn_id;
[b7fd2a0]896 errno_t rc;
[779541b]897
898 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_send_fin_srv()");
899
900 conn_id = IPC_GET_ARG1(*icall);
901 rc = tcp_conn_send_fin_impl(client, conn_id);
[a46e56b]902 async_answer_0(icall_handle, rc);
[779541b]903}
904
[b10460a]905/** Push connection.
906 *
907 * Handle client request to push connection.
908 *
[a46e56b]909 * @param client TCP client
910 * @param icall_handle Async request call handle
911 * @param icall Async request data
[b10460a]912 */
[a46e56b]913static void tcp_conn_push_srv(tcp_client_t *client,
914 cap_call_handle_t icall_handle, ipc_call_t *icall)
[779541b]915{
916 sysarg_t conn_id;
[b7fd2a0]917 errno_t rc;
[779541b]918
919 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_push_srv()");
920
921 conn_id = IPC_GET_ARG1(*icall);
922 rc = tcp_conn_push_impl(client, conn_id);
[a46e56b]923 async_answer_0(icall_handle, rc);
[779541b]924}
925
[b10460a]926/** Reset connection.
927 *
928 * Handle client request to reset connection.
929 *
[a46e56b]930 * @param client TCP client
931 * @param icall_handle Async request call handle
932 * @param icall Async request data
[b10460a]933 */
[a46e56b]934static void tcp_conn_reset_srv(tcp_client_t *client,
935 cap_call_handle_t icall_handle, ipc_call_t *icall)
[779541b]936{
937 sysarg_t conn_id;
[b7fd2a0]938 errno_t rc;
[779541b]939
940 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_reset_srv()");
941
942 conn_id = IPC_GET_ARG1(*icall);
943 rc = tcp_conn_reset_impl(client, conn_id);
[a46e56b]944 async_answer_0(icall_handle, rc);
[779541b]945}
946
[b10460a]947/** Send data via connection..
948 *
949 * Handle client request to send data via connection.
950 *
[a46e56b]951 * @param client TCP client
952 * @param icall_handle Async request call handle
953 * @param icall Async request data
[b10460a]954 */
[a46e56b]955static void tcp_conn_send_srv(tcp_client_t *client,
956 cap_call_handle_t icall_handle, ipc_call_t *icall)
[779541b]957{
[a46e56b]958 cap_call_handle_t chandle;
[779541b]959 size_t size;
960 sysarg_t conn_id;
961 void *data;
[b7fd2a0]962 errno_t rc;
[779541b]963
964 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_send_srv())");
965
966 /* Receive message data */
967
[a46e56b]968 if (!async_data_write_receive(&chandle, &size)) {
969 async_answer_0(chandle, EREFUSED);
970 async_answer_0(icall_handle, EREFUSED);
[779541b]971 return;
972 }
973
974 if (size > MAX_MSG_SIZE) {
[a46e56b]975 async_answer_0(chandle, EINVAL);
976 async_answer_0(icall_handle, EINVAL);
[779541b]977 return;
978 }
979
980 data = malloc(size);
981 if (data == NULL) {
[a46e56b]982 async_answer_0(chandle, ENOMEM);
983 async_answer_0(icall_handle, ENOMEM);
[4f29118]984 return;
[779541b]985 }
986
[a46e56b]987 rc = async_data_write_finalize(chandle, data, size);
[779541b]988 if (rc != EOK) {
[a46e56b]989 async_answer_0(chandle, rc);
990 async_answer_0(icall_handle, rc);
[779541b]991 free(data);
992 return;
993 }
994
995 conn_id = IPC_GET_ARG1(*icall);
996
997 rc = tcp_conn_send_impl(client, conn_id, data, size);
998 if (rc != EOK) {
[a46e56b]999 async_answer_0(icall_handle, rc);
[779541b]1000 free(data);
1001 return;
1002 }
1003
[a46e56b]1004 async_answer_0(icall_handle, EOK);
[779541b]1005 free(data);
1006}
1007
[b10460a]1008/** Read received data from connection without blocking.
1009 *
1010 * Handle client request to read received data via connection without blocking.
1011 *
[a46e56b]1012 * @param client TCP client
1013 * @param icall_handle Async request call handle
1014 * @param icall Async request data
[b10460a]1015 */
[a46e56b]1016static void tcp_conn_recv_srv(tcp_client_t *client,
1017 cap_call_handle_t icall_handle, ipc_call_t *icall)
[779541b]1018{
[a46e56b]1019 cap_call_handle_t chandle;
[779541b]1020 sysarg_t conn_id;
1021 size_t size, rsize;
1022 void *data;
[b7fd2a0]1023 errno_t rc;
[779541b]1024
1025 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_srv()");
1026
1027 conn_id = IPC_GET_ARG1(*icall);
1028
[a46e56b]1029 if (!async_data_read_receive(&chandle, &size)) {
1030 async_answer_0(chandle, EREFUSED);
1031 async_answer_0(icall_handle, EREFUSED);
[779541b]1032 return;
1033 }
1034
[204ba47]1035 size = min(size, 16384);
[779541b]1036 data = malloc(size);
1037 if (data == NULL) {
[a46e56b]1038 async_answer_0(chandle, ENOMEM);
1039 async_answer_0(icall_handle, ENOMEM);
[779541b]1040 return;
1041 }
1042
1043 rc = tcp_conn_recv_impl(client, conn_id, data, size, &rsize);
1044 if (rc != EOK) {
[a46e56b]1045 async_answer_0(chandle, rc);
1046 async_answer_0(icall_handle, rc);
[779541b]1047 free(data);
1048 return;
1049 }
1050
[a46e56b]1051 rc = async_data_read_finalize(chandle, data, size);
[779541b]1052 if (rc != EOK) {
[a46e56b]1053 async_answer_0(icall_handle, rc);
[779541b]1054 free(data);
1055 return;
1056 }
1057
[a46e56b]1058 async_answer_1(icall_handle, EOK, rsize);
[779541b]1059 free(data);
1060
1061 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_srv(): OK");
1062}
1063
[b10460a]1064/** Read received data from connection with blocking.
1065 *
1066 * Handle client request to read received data via connection with blocking.
1067 *
[a46e56b]1068 * @param client TCP client
1069 * @param icall_handle Async request call handle
1070 * @param icall Async request data
[b10460a]1071 */
[a46e56b]1072static void tcp_conn_recv_wait_srv(tcp_client_t *client,
1073 cap_call_handle_t icall_handle, ipc_call_t *icall)
[779541b]1074{
[a46e56b]1075 cap_call_handle_t chandle;
[779541b]1076 sysarg_t conn_id;
1077 size_t size, rsize;
1078 void *data;
[b7fd2a0]1079 errno_t rc;
[779541b]1080
1081 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv()");
1082
1083 conn_id = IPC_GET_ARG1(*icall);
1084
[a46e56b]1085 if (!async_data_read_receive(&chandle, &size)) {
[779541b]1086 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - data_receive failed");
[a46e56b]1087 async_answer_0(chandle, EREFUSED);
1088 async_answer_0(icall_handle, EREFUSED);
[779541b]1089 return;
1090 }
1091
1092 size = min(size, 16384);
1093 data = malloc(size);
1094 if (data == NULL) {
1095 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - allocation failed");
[a46e56b]1096 async_answer_0(chandle, ENOMEM);
1097 async_answer_0(icall_handle, ENOMEM);
[779541b]1098 return;
1099 }
1100
1101 rc = tcp_conn_recv_impl(client, conn_id, data, size, &rsize);
1102 if (rc != EOK) {
[c1694b6b]1103 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - recv_impl failed rc=%s", str_error_name(rc));
[a46e56b]1104 async_answer_0(chandle, rc);
1105 async_answer_0(icall_handle, rc);
[779541b]1106 free(data);
1107 return;
1108 }
1109
[a46e56b]1110 rc = async_data_read_finalize(chandle, data, size);
[779541b]1111 if (rc != EOK) {
1112 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - finalize failed");
[a46e56b]1113 async_answer_0(icall_handle, rc);
[779541b]1114 free(data);
1115 return;
1116 }
1117
1118 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv(): rsize=%zu", size);
[a46e56b]1119 async_answer_1(icall_handle, EOK, rsize);
[779541b]1120 free(data);
1121
1122 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv(): OK");
1123}
1124
[b10460a]1125/** Initialize TCP client structure.
1126 *
1127 * @param client TCP client
1128 */
[309469de]1129static void tcp_client_init(tcp_client_t *client)
1130{
1131 memset(client, 0, sizeof(tcp_client_t));
1132 client->sess = NULL;
1133 list_initialize(&client->cconn);
1134 list_initialize(&client->clst);
1135}
1136
[b10460a]1137/** Finalize TCP client structure.
1138 *
1139 * @param client TCP client
1140 */
[309469de]1141static void tcp_client_fini(tcp_client_t *client)
1142{
1143 tcp_cconn_t *cconn;
[be12474]1144 unsigned long n;
[309469de]1145
1146 n = list_count(&client->cconn);
1147 if (n != 0) {
[1d03e86]1148 log_msg(LOG_DEFAULT, LVL_WARN, "Client with %lu active "
[309469de]1149 "connections closed session", n);
1150
1151 while (!list_empty(&client->cconn)) {
1152 cconn = list_get_instance(list_first(&client->cconn),
1153 tcp_cconn_t, lclient);
1154 tcp_uc_close(cconn->conn);
[8499160]1155 tcp_uc_delete(cconn->conn);
[309469de]1156 tcp_cconn_destroy(cconn);
1157 }
1158 }
1159
1160 n = list_count(&client->clst);
1161 if (n != 0) {
[1d03e86]1162 log_msg(LOG_DEFAULT, LVL_WARN, "Client with %lu active "
[309469de]1163 "listeners closed session", n);
1164 /* XXX Destroy listeners */
1165 }
[1f2b07a]1166
1167 if (client->sess != NULL)
1168 async_hangup(client->sess);
[309469de]1169}
1170
[b10460a]1171/** Handle TCP client connection.
1172 *
[a46e56b]1173 * @param icall_handle Connect call handle
1174 * @param icall Connect call data
1175 * @param arg Connection argument
[b10460a]1176 */
[a46e56b]1177static void tcp_client_conn(cap_call_handle_t icall_handle, ipc_call_t *icall,
1178 void *arg)
[779541b]1179{
1180 tcp_client_t client;
1181
1182 /* Accept the connection */
[a46e56b]1183 async_answer_0(icall_handle, EOK);
[779541b]1184
1185 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_client_conn() - client=%p",
1186 &client);
1187
[309469de]1188 tcp_client_init(&client);
[779541b]1189
1190 while (true) {
1191 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_client_conn: wait req");
1192 ipc_call_t call;
[a46e56b]1193 cap_call_handle_t chandle = async_get_call(&call);
[779541b]1194 sysarg_t method = IPC_GET_IMETHOD(call);
1195
1196 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_client_conn: method=%d",
1197 (int)method);
1198 if (!method) {
1199 /* The other side has hung up */
[a46e56b]1200 async_answer_0(chandle, EOK);
[309469de]1201 break;
[779541b]1202 }
1203
1204 switch (method) {
1205 case TCP_CALLBACK_CREATE:
[a46e56b]1206 tcp_callback_create_srv(&client, chandle, &call);
[779541b]1207 break;
1208 case TCP_CONN_CREATE:
[a46e56b]1209 tcp_conn_create_srv(&client, chandle, &call);
[779541b]1210 break;
1211 case TCP_CONN_DESTROY:
[a46e56b]1212 tcp_conn_destroy_srv(&client, chandle, &call);
[779541b]1213 break;
1214 case TCP_LISTENER_CREATE:
[a46e56b]1215 tcp_listener_create_srv(&client, chandle, &call);
[779541b]1216 break;
1217 case TCP_LISTENER_DESTROY:
[a46e56b]1218 tcp_listener_destroy_srv(&client, chandle, &call);
[779541b]1219 break;
1220 case TCP_CONN_SEND_FIN:
[a46e56b]1221 tcp_conn_send_fin_srv(&client, chandle, &call);
[779541b]1222 break;
1223 case TCP_CONN_PUSH:
[a46e56b]1224 tcp_conn_push_srv(&client, chandle, &call);
[779541b]1225 break;
1226 case TCP_CONN_RESET:
[a46e56b]1227 tcp_conn_reset_srv(&client, chandle, &call);
[779541b]1228 break;
1229 case TCP_CONN_SEND:
[a46e56b]1230 tcp_conn_send_srv(&client, chandle, &call);
[779541b]1231 break;
1232 case TCP_CONN_RECV:
[a46e56b]1233 tcp_conn_recv_srv(&client, chandle, &call);
[779541b]1234 break;
1235 case TCP_CONN_RECV_WAIT:
[a46e56b]1236 tcp_conn_recv_wait_srv(&client, chandle, &call);
[779541b]1237 break;
1238 default:
[a46e56b]1239 async_answer_0(chandle, ENOTSUP);
[779541b]1240 break;
1241 }
1242 }
[309469de]1243
1244 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_client_conn TERMINATED");
1245 tcp_client_fini(&client);
[779541b]1246}
1247
[b10460a]1248/** Initialize TCP service.
1249 *
[cde999a]1250 * @return EOK on success or an error code.
[b10460a]1251 */
[b7fd2a0]1252errno_t tcp_service_init(void)
[779541b]1253{
[b7fd2a0]1254 errno_t rc;
[779541b]1255 service_id_t sid;
1256
[b688fd8]1257 async_set_fallback_port_handler(tcp_client_conn, NULL);
[779541b]1258
1259 rc = loc_server_register(NAME);
1260 if (rc != EOK) {
1261 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering server.");
1262 return EIO;
1263 }
1264
1265 rc = loc_service_register(SERVICE_NAME_TCP, &sid);
1266 if (rc != EOK) {
1267 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service.");
1268 return EIO;
1269 }
1270
1271 return EOK;
1272}
1273
1274/**
1275 * @}
1276 */
Note: See TracBrowser for help on using the repository browser.