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

Last change on this file since cb20b05 was ca48672, checked in by Jiri Svoboda <jiri@…>, 8 days ago

loc_service_register() needs to take a port ID argument.

  • Property mode set to 100644
File size: 28.3 KB
Line 
1/*
2 * Copyright (c) 2025 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_accept_0(icall);
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 loc_srv_t *srv;
1245
1246 async_set_fallback_port_handler(tcp_client_conn, NULL);
1247
1248 rc = loc_server_register(NAME, &srv);
1249 if (rc != EOK) {
1250 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering server.");
1251 return EIO;
1252 }
1253
1254 rc = loc_service_register(srv, SERVICE_NAME_TCP, fallback_port_id,
1255 &sid);
1256 if (rc != EOK) {
1257 loc_server_unregister(srv);
1258 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service.");
1259 return EIO;
1260 }
1261
1262 return EOK;
1263}
1264
1265/**
1266 * @}
1267 */
Note: See TracBrowser for help on using the repository browser.