source: mainline/uspace/srv/net/tcp/service.c@ 94ab1fee

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 94ab1fee was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

  • Property mode set to 100644
File size: 28.2 KB
Line 
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
29/** @addtogroup tcp
30 * @{
31 */
32
33/**
34 * @file HelenOS service implementation
35 */
36
37#include <async.h>
38#include <errno.h>
39#include <str_error.h>
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>
47#include <mem.h>
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
57/** Maximum amount of data transferred in one send call */
58#define MAX_MSG_SIZE DATA_XFER_LIMIT
59
60static void tcp_ev_data(tcp_cconn_t *);
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 *);
64static void tcp_ev_new_conn(tcp_clst_t *, tcp_cconn_t *);
65
66static void tcp_service_cstate_change(tcp_conn_t *, void *, tcp_cstate_t);
67static void tcp_service_recv_data(tcp_conn_t *, void *);
68static void tcp_service_lst_cstate_change(tcp_conn_t *, void *, tcp_cstate_t);
69
70static errno_t tcp_cconn_create(tcp_client_t *, tcp_conn_t *, tcp_cconn_t **);
71
72/** Connection callbacks to tie us to lower layer */
73static tcp_cb_t tcp_service_cb = {
74 .cstate_change = tcp_service_cstate_change,
75 .recv_data = tcp_service_recv_data
76};
77
78/** Sentinel connection callbacks to tie us to lower layer */
79static tcp_cb_t tcp_service_lst_cb = {
80 .cstate_change = tcp_service_lst_cstate_change,
81 .recv_data = NULL
82};
83
84/** Connection state has changed.
85 *
86 * @param conn Connection
87 * @param arg Argument (not used)
88 * @param old_state Previous connection state
89 */
90static void tcp_service_cstate_change(tcp_conn_t *conn, void *arg,
91 tcp_cstate_t old_state)
92{
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) {
107 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_service_cstate_change: "
108 "Connection reset");
109 /* Connection reset */
110 tcp_ev_conn_reset(cconn);
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);
115 }
116
117 /* XXX Failed to establish connection */
118 if (0)
119 tcp_ev_conn_failed(cconn);
120}
121
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 */
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;
134 inet_ep2_t epp;
135 errno_t rc;
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
172 inet_ep2_init(&epp);
173 epp.local = clst->elocal;
174
175 trc = tcp_uc_open(&epp, ap_passive, tcp_open_nonblock,
176 &conn);
177 if (trc != TCP_EOK) {
178 /* XXX Could not replenish connection */
179 return;
180 }
181
182 conn->name = (char *) "s";
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
189/** Received data became available on connection.
190 *
191 * @param conn Connection
192 * @param arg Client connection
193 */
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
201/** Send 'data' event to client.
202 *
203 * @param cconn Client connection
204 */
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
218 async_forget(req);
219}
220
221/** Send 'connected' event to client.
222 *
223 * @param cconn Client connection
224 */
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
238/** Send 'conn_failed' event to client.
239 *
240 * @param cconn Client connection
241 */
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
255/** Send 'conn_reset' event to client.
256 *
257 * @param cconn Client connection
258 */
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
269 async_forget(req);
270}
271
272/** Send 'new_conn' event to client.
273 *
274 * @param clst Client listener that received the connection
275 * @param cconn New client connection
276 */
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
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 */
301static errno_t tcp_cconn_create(tcp_client_t *client, tcp_conn_t *conn,
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
327/** Destroy client connection.
328 *
329 * @param cconn Client connection
330 */
331static void tcp_cconn_destroy(tcp_cconn_t *cconn)
332{
333 list_remove(&cconn->lclient);
334 free(cconn);
335}
336
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 */
348static errno_t tcp_clistener_create(tcp_client_t *client, tcp_conn_t *conn,
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
374/** Destroy client listener.
375 *
376 * @param clst Client listener
377 */
378static void tcp_clistener_destroy(tcp_clst_t *clst)
379{
380 list_remove(&clst->lclient);
381 free(clst);
382}
383
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 */
393static errno_t tcp_cconn_get(tcp_client_t *client, sysarg_t id,
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
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 */
415static errno_t tcp_clistener_get(tcp_client_t *client, sysarg_t id,
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
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 *
436 * @return EOK on success or an error code
437 */
438static errno_t tcp_conn_create_impl(tcp_client_t *client, inet_ep2_t *epp,
439 sysarg_t *rconn_id)
440{
441 tcp_conn_t *conn;
442 tcp_cconn_t *cconn;
443 errno_t rc;
444 tcp_error_t trc;
445 char *slocal;
446 char *sremote;
447
448 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_create_impl");
449
450 inet_addr_format(&epp->local.addr, &slocal);
451 inet_addr_format(&epp->remote.addr, &sremote);
452 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_create: local=%s remote=%s",
453 slocal, sremote);
454 free(slocal);
455 free(sremote);
456
457 trc = tcp_uc_open(epp, ap_active, tcp_open_nonblock, &conn);
458 if (trc != TCP_EOK)
459 return EIO;
460
461 conn->name = (char *) "c";
462
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
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 */
485static errno_t tcp_conn_destroy_impl(tcp_client_t *client, sysarg_t conn_id)
486{
487 tcp_cconn_t *cconn;
488 errno_t rc;
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);
497 tcp_uc_delete(cconn->conn);
498 tcp_cconn_destroy(cconn);
499 return EOK;
500}
501
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 *
510 * @return EOK on success or an error code
511 */
512static errno_t tcp_listener_create_impl(tcp_client_t *client, inet_ep_t *ep,
513 sysarg_t *rlst_id)
514{
515 tcp_conn_t *conn;
516 tcp_clst_t *clst;
517 inet_ep2_t epp;
518 errno_t rc;
519 tcp_error_t trc;
520
521 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_listener_create_impl");
522
523 inet_ep2_init(&epp);
524 epp.local.addr = ep->addr;
525 epp.local.port = ep->port;
526
527 trc = tcp_uc_open(&epp, ap_passive, tcp_open_nonblock, &conn);
528 if (trc != TCP_EOK)
529 return EIO;
530
531 conn->name = (char *) "s";
532
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
540 clst->elocal = epp.local;
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);
544
545 *rlst_id = clst->id;
546 return EOK;
547}
548
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 */
558static errno_t tcp_listener_destroy_impl(tcp_client_t *client, sysarg_t lst_id)
559{
560 tcp_clst_t *clst;
561 errno_t rc;
562
563 rc = tcp_clistener_get(client, lst_id, &clst);
564 if (rc != EOK) {
565 assert(rc == ENOENT);
566 return ENOENT;
567 }
568
569#if 0
570 tcp_uc_close(cconn->conn);
571#endif
572 tcp_clistener_destroy(clst);
573 return EOK;
574}
575
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 *
583 * @return EOK on success or an error code
584 */
585static errno_t tcp_conn_send_fin_impl(tcp_client_t *client, sysarg_t conn_id)
586{
587 tcp_cconn_t *cconn;
588 errno_t rc;
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
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 *
608 * @return EOK on success or an error code
609 */
610static errno_t tcp_conn_push_impl(tcp_client_t *client, sysarg_t conn_id)
611{
612 tcp_cconn_t *cconn;
613 errno_t rc;
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
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 *
633 * @return EOK on success or an error code
634 */
635static errno_t tcp_conn_reset_impl(tcp_client_t *client, sysarg_t conn_id)
636{
637 tcp_cconn_t *cconn;
638 errno_t rc;
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
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 *
659 * @return EOK on success or an error code
660 */
661static errno_t tcp_conn_send_impl(tcp_client_t *client, sysarg_t conn_id,
662 void *data, size_t size)
663{
664 tcp_cconn_t *cconn;
665 errno_t rc;
666 tcp_error_t trc;
667
668 rc = tcp_cconn_get(client, conn_id, &cconn);
669 if (rc != EOK)
670 return rc;
671
672 trc = tcp_uc_send(cconn->conn, data, size, 0);
673 if (trc != TCP_EOK)
674 return EIO;
675
676 return EOK;
677}
678
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 *
689 * @return EOK on success or an error code
690 */
691static errno_t tcp_conn_recv_impl(tcp_client_t *client, sysarg_t conn_id,
692 void *data, size_t size, size_t *nrecv)
693{
694 tcp_cconn_t *cconn;
695 xflags_t xflags;
696 errno_t rc;
697 tcp_error_t trc;
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
707 trc = tcp_uc_receive(cconn->conn, data, size, nrecv, &xflags);
708 if (trc != TCP_EOK) {
709 switch (trc) {
710 case TCP_EAGAIN:
711 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl() - EAGAIN");
712 return EAGAIN;
713 case TCP_ECLOSING:
714 *nrecv = 0;
715 return EOK;
716 default:
717 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl() - trc=%d", trc);
718 return EIO;
719 }
720 }
721
722 return EOK;
723}
724
725/** Create client callback session.
726 *
727 * Handle client request to create callback session.
728 *
729 * @param client TCP client
730 * @param icall Async request data
731 *
732 */
733static void tcp_callback_create_srv(tcp_client_t *client, ipc_call_t *icall)
734{
735 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_callback_create_srv()");
736
737 async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
738 if (sess == NULL) {
739 async_answer_0(icall, ENOMEM);
740 return;
741 }
742
743 client->sess = sess;
744 async_answer_0(icall, EOK);
745}
746
747/** Create connection.
748 *
749 * Handle client request to create connection.
750 *
751 * @param client TCP client
752 * @param icall Async request data
753 *
754 */
755static void tcp_conn_create_srv(tcp_client_t *client, ipc_call_t *icall)
756{
757 ipc_call_t call;
758 size_t size;
759 inet_ep2_t epp;
760 sysarg_t conn_id;
761 errno_t rc;
762
763 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_create_srv()");
764
765 if (!async_data_write_receive(&call, &size)) {
766 async_answer_0(&call, EREFUSED);
767 async_answer_0(icall, EREFUSED);
768 return;
769 }
770
771 if (size != sizeof(inet_ep2_t)) {
772 async_answer_0(&call, EINVAL);
773 async_answer_0(icall, EINVAL);
774 return;
775 }
776
777 rc = async_data_write_finalize(&call, &epp, size);
778 if (rc != EOK) {
779 async_answer_0(&call, rc);
780 async_answer_0(icall, rc);
781 return;
782 }
783
784 rc = tcp_conn_create_impl(client, &epp, &conn_id);
785 if (rc != EOK) {
786 async_answer_0(icall, rc);
787 return;
788 }
789
790 async_answer_1(icall, EOK, conn_id);
791}
792
793/** Destroy connection.
794 *
795 * Handle client request to destroy connection.
796 *
797 * @param client TCP client
798 * @param icall Async request data
799 *
800 */
801static void tcp_conn_destroy_srv(tcp_client_t *client, ipc_call_t *icall)
802{
803 sysarg_t conn_id;
804 errno_t rc;
805
806 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_destroy_srv()");
807
808 conn_id = IPC_GET_ARG1(*icall);
809 rc = tcp_conn_destroy_impl(client, conn_id);
810 async_answer_0(icall, rc);
811}
812
813/** Create listener.
814 *
815 * Handle client request to create listener.
816 *
817 * @param client TCP client
818 * @param icall Async request data
819 *
820 */
821static void tcp_listener_create_srv(tcp_client_t *client, ipc_call_t *icall)
822{
823 ipc_call_t call;
824 size_t size;
825 inet_ep_t ep;
826 sysarg_t lst_id;
827 errno_t rc;
828
829 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_listener_create_srv()");
830
831 if (!async_data_write_receive(&call, &size)) {
832 async_answer_0(&call, EREFUSED);
833 async_answer_0(icall, EREFUSED);
834 return;
835 }
836
837 if (size != sizeof(inet_ep_t)) {
838 async_answer_0(&call, EINVAL);
839 async_answer_0(icall, EINVAL);
840 return;
841 }
842
843 rc = async_data_write_finalize(&call, &ep, size);
844 if (rc != EOK) {
845 async_answer_0(&call, rc);
846 async_answer_0(icall, rc);
847 return;
848 }
849
850 rc = tcp_listener_create_impl(client, &ep, &lst_id);
851 if (rc != EOK) {
852 async_answer_0(icall, rc);
853 return;
854 }
855
856 async_answer_1(icall, EOK, lst_id);
857}
858
859/** Destroy listener.
860 *
861 * Handle client request to destroy listener.
862 *
863 * @param client TCP client
864 * @param icall Async request data
865 *
866 */
867static void tcp_listener_destroy_srv(tcp_client_t *client, ipc_call_t *icall)
868{
869 sysarg_t lst_id;
870 errno_t rc;
871
872 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_listener_destroy_srv()");
873
874 lst_id = IPC_GET_ARG1(*icall);
875 rc = tcp_listener_destroy_impl(client, lst_id);
876 async_answer_0(icall, rc);
877}
878
879/** Send FIN.
880 *
881 * Handle client request to send FIN.
882 *
883 * @param client TCP client
884 * @param icall Async request data
885 *
886 */
887static void tcp_conn_send_fin_srv(tcp_client_t *client, ipc_call_t *icall)
888{
889 sysarg_t conn_id;
890 errno_t rc;
891
892 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_send_fin_srv()");
893
894 conn_id = IPC_GET_ARG1(*icall);
895 rc = tcp_conn_send_fin_impl(client, conn_id);
896 async_answer_0(icall, rc);
897}
898
899/** Push connection.
900 *
901 * Handle client request to push connection.
902 *
903 * @param client TCP client
904 * @param icall Async request data
905 *
906 */
907static void tcp_conn_push_srv(tcp_client_t *client, ipc_call_t *icall)
908{
909 sysarg_t conn_id;
910 errno_t rc;
911
912 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_push_srv()");
913
914 conn_id = IPC_GET_ARG1(*icall);
915 rc = tcp_conn_push_impl(client, conn_id);
916 async_answer_0(icall, rc);
917}
918
919/** Reset connection.
920 *
921 * Handle client request to reset connection.
922 *
923 * @param client TCP client
924 * @param icall Async request data
925 *
926 */
927static void tcp_conn_reset_srv(tcp_client_t *client, ipc_call_t *icall)
928{
929 sysarg_t conn_id;
930 errno_t rc;
931
932 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_reset_srv()");
933
934 conn_id = IPC_GET_ARG1(*icall);
935 rc = tcp_conn_reset_impl(client, conn_id);
936 async_answer_0(icall, rc);
937}
938
939/** Send data via connection..
940 *
941 * Handle client request to send data via connection.
942 *
943 * @param client TCP client
944 * @param icall Async request data
945 *
946 */
947static void tcp_conn_send_srv(tcp_client_t *client, ipc_call_t *icall)
948{
949 ipc_call_t call;
950 size_t size;
951 sysarg_t conn_id;
952 void *data;
953 errno_t rc;
954
955 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_send_srv())");
956
957 /* Receive message data */
958
959 if (!async_data_write_receive(&call, &size)) {
960 async_answer_0(&call, EREFUSED);
961 async_answer_0(icall, EREFUSED);
962 return;
963 }
964
965 if (size > MAX_MSG_SIZE) {
966 async_answer_0(&call, EINVAL);
967 async_answer_0(icall, EINVAL);
968 return;
969 }
970
971 data = malloc(size);
972 if (data == NULL) {
973 async_answer_0(&call, ENOMEM);
974 async_answer_0(icall, ENOMEM);
975 return;
976 }
977
978 rc = async_data_write_finalize(&call, data, size);
979 if (rc != EOK) {
980 async_answer_0(&call, rc);
981 async_answer_0(icall, rc);
982 free(data);
983 return;
984 }
985
986 conn_id = IPC_GET_ARG1(*icall);
987
988 rc = tcp_conn_send_impl(client, conn_id, data, size);
989 if (rc != EOK) {
990 async_answer_0(icall, rc);
991 free(data);
992 return;
993 }
994
995 async_answer_0(icall, EOK);
996 free(data);
997}
998
999/** Read received data from connection without blocking.
1000 *
1001 * Handle client request to read received data via connection without blocking.
1002 *
1003 * @param client TCP client
1004 * @param icall Async request data
1005 *
1006 */
1007static void tcp_conn_recv_srv(tcp_client_t *client, ipc_call_t *icall)
1008{
1009 ipc_call_t call;
1010 sysarg_t conn_id;
1011 size_t size, rsize;
1012 void *data;
1013 errno_t rc;
1014
1015 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_srv()");
1016
1017 conn_id = IPC_GET_ARG1(*icall);
1018
1019 if (!async_data_read_receive(&call, &size)) {
1020 async_answer_0(&call, EREFUSED);
1021 async_answer_0(icall, EREFUSED);
1022 return;
1023 }
1024
1025 size = min(size, 16384);
1026 data = malloc(size);
1027 if (data == NULL) {
1028 async_answer_0(&call, ENOMEM);
1029 async_answer_0(icall, ENOMEM);
1030 return;
1031 }
1032
1033 rc = tcp_conn_recv_impl(client, conn_id, data, size, &rsize);
1034 if (rc != EOK) {
1035 async_answer_0(&call, rc);
1036 async_answer_0(icall, rc);
1037 free(data);
1038 return;
1039 }
1040
1041 rc = async_data_read_finalize(&call, data, size);
1042 if (rc != EOK) {
1043 async_answer_0(icall, rc);
1044 free(data);
1045 return;
1046 }
1047
1048 async_answer_1(icall, EOK, rsize);
1049 free(data);
1050
1051 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_srv(): OK");
1052}
1053
1054/** Read received data from connection with blocking.
1055 *
1056 * Handle client request to read received data via connection with blocking.
1057 *
1058 * @param client TCP client
1059 * @param icall Async request data
1060 *
1061 */
1062static void tcp_conn_recv_wait_srv(tcp_client_t *client, ipc_call_t *icall)
1063{
1064 ipc_call_t call;
1065 sysarg_t conn_id;
1066 size_t size, rsize;
1067 void *data;
1068 errno_t rc;
1069
1070 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv()");
1071
1072 conn_id = IPC_GET_ARG1(*icall);
1073
1074 if (!async_data_read_receive(&call, &size)) {
1075 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - data_receive failed");
1076 async_answer_0(&call, EREFUSED);
1077 async_answer_0(icall, EREFUSED);
1078 return;
1079 }
1080
1081 size = min(size, 16384);
1082 data = malloc(size);
1083 if (data == NULL) {
1084 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - allocation failed");
1085 async_answer_0(&call, ENOMEM);
1086 async_answer_0(icall, ENOMEM);
1087 return;
1088 }
1089
1090 rc = tcp_conn_recv_impl(client, conn_id, data, size, &rsize);
1091 if (rc != EOK) {
1092 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - recv_impl failed rc=%s", str_error_name(rc));
1093 async_answer_0(&call, rc);
1094 async_answer_0(icall, rc);
1095 free(data);
1096 return;
1097 }
1098
1099 rc = async_data_read_finalize(&call, data, size);
1100 if (rc != EOK) {
1101 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - finalize failed");
1102 async_answer_0(icall, rc);
1103 free(data);
1104 return;
1105 }
1106
1107 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv(): rsize=%zu", size);
1108 async_answer_1(icall, EOK, rsize);
1109 free(data);
1110
1111 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv(): OK");
1112}
1113
1114/** Initialize TCP client structure.
1115 *
1116 * @param client TCP client
1117 */
1118static void tcp_client_init(tcp_client_t *client)
1119{
1120 memset(client, 0, sizeof(tcp_client_t));
1121 client->sess = NULL;
1122 list_initialize(&client->cconn);
1123 list_initialize(&client->clst);
1124}
1125
1126/** Finalize TCP client structure.
1127 *
1128 * @param client TCP client
1129 */
1130static void tcp_client_fini(tcp_client_t *client)
1131{
1132 tcp_cconn_t *cconn;
1133 unsigned long n;
1134
1135 n = list_count(&client->cconn);
1136 if (n != 0) {
1137 log_msg(LOG_DEFAULT, LVL_WARN, "Client with %lu active "
1138 "connections closed session", n);
1139
1140 while (!list_empty(&client->cconn)) {
1141 cconn = list_get_instance(list_first(&client->cconn),
1142 tcp_cconn_t, lclient);
1143 tcp_uc_close(cconn->conn);
1144 tcp_uc_delete(cconn->conn);
1145 tcp_cconn_destroy(cconn);
1146 }
1147 }
1148
1149 n = list_count(&client->clst);
1150 if (n != 0) {
1151 log_msg(LOG_DEFAULT, LVL_WARN, "Client with %lu active "
1152 "listeners closed session", n);
1153 /* XXX Destroy listeners */
1154 }
1155
1156 if (client->sess != NULL)
1157 async_hangup(client->sess);
1158}
1159
1160/** Handle TCP client connection.
1161 *
1162 * @param icall Connect call data
1163 * @param arg Connection argument
1164 *
1165 */
1166static void tcp_client_conn(ipc_call_t *icall, void *arg)
1167{
1168 tcp_client_t client;
1169
1170 /* Accept the connection */
1171 async_answer_0(icall, EOK);
1172
1173 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_client_conn() - client=%p",
1174 &client);
1175
1176 tcp_client_init(&client);
1177
1178 while (true) {
1179 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_client_conn: wait req");
1180 ipc_call_t call;
1181 async_get_call(&call);
1182 sysarg_t method = IPC_GET_IMETHOD(call);
1183
1184 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_client_conn: method=%d",
1185 (int)method);
1186 if (!method) {
1187 /* The other side has hung up */
1188 async_answer_0(&call, EOK);
1189 break;
1190 }
1191
1192 switch (method) {
1193 case TCP_CALLBACK_CREATE:
1194 tcp_callback_create_srv(&client, &call);
1195 break;
1196 case TCP_CONN_CREATE:
1197 tcp_conn_create_srv(&client, &call);
1198 break;
1199 case TCP_CONN_DESTROY:
1200 tcp_conn_destroy_srv(&client, &call);
1201 break;
1202 case TCP_LISTENER_CREATE:
1203 tcp_listener_create_srv(&client, &call);
1204 break;
1205 case TCP_LISTENER_DESTROY:
1206 tcp_listener_destroy_srv(&client, &call);
1207 break;
1208 case TCP_CONN_SEND_FIN:
1209 tcp_conn_send_fin_srv(&client, &call);
1210 break;
1211 case TCP_CONN_PUSH:
1212 tcp_conn_push_srv(&client, &call);
1213 break;
1214 case TCP_CONN_RESET:
1215 tcp_conn_reset_srv(&client, &call);
1216 break;
1217 case TCP_CONN_SEND:
1218 tcp_conn_send_srv(&client, &call);
1219 break;
1220 case TCP_CONN_RECV:
1221 tcp_conn_recv_srv(&client, &call);
1222 break;
1223 case TCP_CONN_RECV_WAIT:
1224 tcp_conn_recv_wait_srv(&client, &call);
1225 break;
1226 default:
1227 async_answer_0(&call, ENOTSUP);
1228 break;
1229 }
1230 }
1231
1232 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_client_conn TERMINATED");
1233 tcp_client_fini(&client);
1234}
1235
1236/** Initialize TCP service.
1237 *
1238 * @return EOK on success or an error code.
1239 */
1240errno_t tcp_service_init(void)
1241{
1242 errno_t rc;
1243 service_id_t sid;
1244
1245 async_set_fallback_port_handler(tcp_client_conn, NULL);
1246
1247 rc = loc_server_register(NAME);
1248 if (rc != EOK) {
1249 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering server.");
1250 return EIO;
1251 }
1252
1253 rc = loc_service_register(SERVICE_NAME_TCP, &sid);
1254 if (rc != EOK) {
1255 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service.");
1256 return EIO;
1257 }
1258
1259 return EOK;
1260}
1261
1262/**
1263 * @}
1264 */
Note: See TracBrowser for help on using the repository browser.