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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since cde999a was cde999a, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Fix comments to stop referring to error codes as negative.

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