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

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

Add str_error() in numerous places.

  • 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 negative 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 negative 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 negative 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 negative 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 negative 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 negative 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
664 rc = tcp_cconn_get(client, conn_id, &cconn);
665 if (rc != EOK)
666 return rc;
667
668 rc = tcp_uc_send(cconn->conn, data, size, 0);
669 if (rc != EOK)
670 return rc;
671
672 return EOK;
673}
674
675/** Receive data from connection.
676 *
677 * Handle client request to receive data (with parameters unmarshalled).
678 *
679 * @param client TCP client
680 * @param conn_id Connection ID
681 * @param data Data buffer
682 * @param size Buffer size in bytes
683 * @param nrecv Place to store actual number of bytes received
684 *
685 * @return EOK on success or negative error code
686 */
687static int tcp_conn_recv_impl(tcp_client_t *client, sysarg_t conn_id,
688 void *data, size_t size, size_t *nrecv)
689{
690 tcp_cconn_t *cconn;
691 xflags_t xflags;
692 int rc;
693
694 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl()");
695
696 rc = tcp_cconn_get(client, conn_id, &cconn);
697 if (rc != EOK) {
698 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl() - conn not found");
699 return rc;
700 }
701
702 rc = tcp_uc_receive(cconn->conn, data, size, nrecv, &xflags);
703 if (rc != EOK) {
704 switch (rc) {
705 case TCP_EAGAIN:
706 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl() - EAGAIN");
707 return EAGAIN;
708 case TCP_ECLOSING:
709 *nrecv = 0;
710 return EOK;
711 default:
712 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl() - trc=%d", rc);
713 return EIO;
714 }
715 }
716
717 return EOK;
718}
719
720/** Create client callback session.
721 *
722 * Handle client request to create callback session.
723 *
724 * @param client TCP client
725 * @param iid Async request ID
726 * @param icall Async request data
727 */
728static void tcp_callback_create_srv(tcp_client_t *client, ipc_callid_t iid,
729 ipc_call_t *icall)
730{
731 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_callback_create_srv()");
732
733 async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
734 if (sess == NULL) {
735 async_answer_0(iid, ENOMEM);
736 return;
737 }
738
739 client->sess = sess;
740 async_answer_0(iid, EOK);
741}
742
743/** Create connection.
744 *
745 * Handle client request to create connection.
746 *
747 * @param client TCP client
748 * @param iid Async request ID
749 * @param icall Async request data
750 */
751static void tcp_conn_create_srv(tcp_client_t *client, ipc_callid_t iid,
752 ipc_call_t *icall)
753{
754 ipc_callid_t callid;
755 size_t size;
756 inet_ep2_t epp;
757 sysarg_t conn_id;
758 int rc;
759
760 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_create_srv()");
761
762 if (!async_data_write_receive(&callid, &size)) {
763 async_answer_0(callid, EREFUSED);
764 async_answer_0(iid, EREFUSED);
765 return;
766 }
767
768 if (size != sizeof(inet_ep2_t)) {
769 async_answer_0(callid, EINVAL);
770 async_answer_0(iid, EINVAL);
771 return;
772 }
773
774 rc = async_data_write_finalize(callid, &epp, size);
775 if (rc != EOK) {
776 async_answer_0(callid, rc);
777 async_answer_0(iid, rc);
778 return;
779 }
780
781 rc = tcp_conn_create_impl(client, &epp, &conn_id);
782 if (rc != EOK) {
783 async_answer_0(iid, rc);
784 return;
785 }
786
787 async_answer_1(iid, EOK, conn_id);
788}
789
790/** Destroy connection.
791 *
792 * Handle client request to destroy connection.
793 *
794 * @param client TCP client
795 * @param iid Async request ID
796 * @param icall Async request data
797 */
798static void tcp_conn_destroy_srv(tcp_client_t *client, ipc_callid_t iid,
799 ipc_call_t *icall)
800{
801 sysarg_t conn_id;
802 int rc;
803
804 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_destroy_srv()");
805
806 conn_id = IPC_GET_ARG1(*icall);
807 rc = tcp_conn_destroy_impl(client, conn_id);
808 async_answer_0(iid, rc);
809}
810
811/** Create listener.
812 *
813 * Handle client request to create listener.
814 *
815 * @param client TCP client
816 * @param iid Async request ID
817 * @param icall Async request data
818 */
819static void tcp_listener_create_srv(tcp_client_t *client, ipc_callid_t iid,
820 ipc_call_t *icall)
821{
822 ipc_callid_t callid;
823 size_t size;
824 inet_ep_t ep;
825 sysarg_t lst_id;
826 int rc;
827
828 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_listener_create_srv()");
829
830 if (!async_data_write_receive(&callid, &size)) {
831 async_answer_0(callid, EREFUSED);
832 async_answer_0(iid, EREFUSED);
833 return;
834 }
835
836 if (size != sizeof(inet_ep_t)) {
837 async_answer_0(callid, EINVAL);
838 async_answer_0(iid, EINVAL);
839 return;
840 }
841
842 rc = async_data_write_finalize(callid, &ep, size);
843 if (rc != EOK) {
844 async_answer_0(callid, rc);
845 async_answer_0(iid, rc);
846 return;
847 }
848
849 rc = tcp_listener_create_impl(client, &ep, &lst_id);
850 if (rc != EOK) {
851 async_answer_0(iid, rc);
852 return;
853 }
854
855 async_answer_1(iid, EOK, lst_id);
856}
857
858/** Destroy listener.
859 *
860 * Handle client request to destroy listener.
861 *
862 * @param client TCP client
863 * @param iid Async request ID
864 * @param icall Async request data
865 */
866static void tcp_listener_destroy_srv(tcp_client_t *client, ipc_callid_t iid,
867 ipc_call_t *icall)
868{
869 sysarg_t lst_id;
870 int 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(iid, rc);
877}
878
879/** Send FIN.
880 *
881 * Handle client request to send FIN.
882 *
883 * @param client TCP client
884 * @param iid Async request ID
885 * @param icall Async request data
886 */
887static void tcp_conn_send_fin_srv(tcp_client_t *client, ipc_callid_t iid,
888 ipc_call_t *icall)
889{
890 sysarg_t conn_id;
891 int rc;
892
893 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_send_fin_srv()");
894
895 conn_id = IPC_GET_ARG1(*icall);
896 rc = tcp_conn_send_fin_impl(client, conn_id);
897 async_answer_0(iid, rc);
898}
899
900/** Push connection.
901 *
902 * Handle client request to push connection.
903 *
904 * @param client TCP client
905 * @param iid Async request ID
906 * @param icall Async request data
907 */
908static void tcp_conn_push_srv(tcp_client_t *client, ipc_callid_t iid,
909 ipc_call_t *icall)
910{
911 sysarg_t conn_id;
912 int rc;
913
914 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_push_srv()");
915
916 conn_id = IPC_GET_ARG1(*icall);
917 rc = tcp_conn_push_impl(client, conn_id);
918 async_answer_0(iid, rc);
919}
920
921/** Reset connection.
922 *
923 * Handle client request to reset connection.
924 *
925 * @param client TCP client
926 * @param iid Async request ID
927 * @param icall Async request data
928 */
929static void tcp_conn_reset_srv(tcp_client_t *client, ipc_callid_t iid,
930 ipc_call_t *icall)
931{
932 sysarg_t conn_id;
933 int rc;
934
935 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_reset_srv()");
936
937 conn_id = IPC_GET_ARG1(*icall);
938 rc = tcp_conn_reset_impl(client, conn_id);
939 async_answer_0(iid, rc);
940}
941
942/** Send data via connection..
943 *
944 * Handle client request to send data via connection.
945 *
946 * @param client TCP client
947 * @param iid Async request ID
948 * @param icall Async request data
949 */
950static void tcp_conn_send_srv(tcp_client_t *client, ipc_callid_t iid,
951 ipc_call_t *icall)
952{
953 ipc_callid_t callid;
954 size_t size;
955 sysarg_t conn_id;
956 void *data;
957 int rc;
958
959 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_send_srv())");
960
961 /* Receive message data */
962
963 if (!async_data_write_receive(&callid, &size)) {
964 async_answer_0(callid, EREFUSED);
965 async_answer_0(iid, EREFUSED);
966 return;
967 }
968
969 if (size > MAX_MSG_SIZE) {
970 async_answer_0(callid, EINVAL);
971 async_answer_0(iid, EINVAL);
972 return;
973 }
974
975 data = malloc(size);
976 if (data == NULL) {
977 async_answer_0(callid, ENOMEM);
978 async_answer_0(iid, ENOMEM);
979 return;
980 }
981
982 rc = async_data_write_finalize(callid, data, size);
983 if (rc != EOK) {
984 async_answer_0(callid, rc);
985 async_answer_0(iid, rc);
986 free(data);
987 return;
988 }
989
990 conn_id = IPC_GET_ARG1(*icall);
991
992 rc = tcp_conn_send_impl(client, conn_id, data, size);
993 if (rc != EOK) {
994 async_answer_0(iid, rc);
995 free(data);
996 return;
997 }
998
999 async_answer_0(iid, EOK);
1000 free(data);
1001}
1002
1003/** Read received data from connection without blocking.
1004 *
1005 * Handle client request to read received data via connection without blocking.
1006 *
1007 * @param client TCP client
1008 * @param iid Async request ID
1009 * @param icall Async request data
1010 */
1011static void tcp_conn_recv_srv(tcp_client_t *client, ipc_callid_t iid,
1012 ipc_call_t *icall)
1013{
1014 ipc_callid_t callid;
1015 sysarg_t conn_id;
1016 size_t size, rsize;
1017 void *data;
1018 int rc;
1019
1020 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_srv()");
1021
1022 conn_id = IPC_GET_ARG1(*icall);
1023
1024 if (!async_data_read_receive(&callid, &size)) {
1025 async_answer_0(callid, EREFUSED);
1026 async_answer_0(iid, EREFUSED);
1027 return;
1028 }
1029
1030 size = min(size, 16384);
1031 data = malloc(size);
1032 if (data == NULL) {
1033 async_answer_0(callid, ENOMEM);
1034 async_answer_0(iid, ENOMEM);
1035 return;
1036 }
1037
1038 rc = tcp_conn_recv_impl(client, conn_id, data, size, &rsize);
1039 if (rc != EOK) {
1040 async_answer_0(callid, rc);
1041 async_answer_0(iid, rc);
1042 free(data);
1043 return;
1044 }
1045
1046 rc = async_data_read_finalize(callid, data, size);
1047 if (rc != EOK) {
1048 async_answer_0(iid, rc);
1049 free(data);
1050 return;
1051 }
1052
1053 async_answer_1(iid, EOK, rsize);
1054 free(data);
1055
1056 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_srv(): OK");
1057}
1058
1059/** Read received data from connection with blocking.
1060 *
1061 * Handle client request to read received data via connection with blocking.
1062 *
1063 * @param client TCP client
1064 * @param iid Async request ID
1065 * @param icall Async request data
1066 */
1067static void tcp_conn_recv_wait_srv(tcp_client_t *client, ipc_callid_t iid,
1068 ipc_call_t *icall)
1069{
1070 ipc_callid_t callid;
1071 sysarg_t conn_id;
1072 size_t size, rsize;
1073 void *data;
1074 int rc;
1075
1076 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv()");
1077
1078 conn_id = IPC_GET_ARG1(*icall);
1079
1080 if (!async_data_read_receive(&callid, &size)) {
1081 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - data_receive failed");
1082 async_answer_0(callid, EREFUSED);
1083 async_answer_0(iid, EREFUSED);
1084 return;
1085 }
1086
1087 size = min(size, 16384);
1088 data = malloc(size);
1089 if (data == NULL) {
1090 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - allocation failed");
1091 async_answer_0(callid, ENOMEM);
1092 async_answer_0(iid, ENOMEM);
1093 return;
1094 }
1095
1096 rc = tcp_conn_recv_impl(client, conn_id, data, size, &rsize);
1097 if (rc != EOK) {
1098 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - recv_impl failed rc=%s", str_error_name(rc));
1099 async_answer_0(callid, rc);
1100 async_answer_0(iid, rc);
1101 free(data);
1102 return;
1103 }
1104
1105 rc = async_data_read_finalize(callid, data, size);
1106 if (rc != EOK) {
1107 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - finalize failed");
1108 async_answer_0(iid, rc);
1109 free(data);
1110 return;
1111 }
1112
1113 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv(): rsize=%zu", size);
1114 async_answer_1(iid, EOK, rsize);
1115 free(data);
1116
1117 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv(): OK");
1118}
1119
1120/** Initialize TCP client structure.
1121 *
1122 * @param client TCP client
1123 */
1124static void tcp_client_init(tcp_client_t *client)
1125{
1126 memset(client, 0, sizeof(tcp_client_t));
1127 client->sess = NULL;
1128 list_initialize(&client->cconn);
1129 list_initialize(&client->clst);
1130}
1131
1132/** Finalize TCP client structure.
1133 *
1134 * @param client TCP client
1135 */
1136static void tcp_client_fini(tcp_client_t *client)
1137{
1138 tcp_cconn_t *cconn;
1139 unsigned long n;
1140
1141 n = list_count(&client->cconn);
1142 if (n != 0) {
1143 log_msg(LOG_DEFAULT, LVL_WARN, "Client with %lu active "
1144 "connections closed session", n);
1145
1146 while (!list_empty(&client->cconn)) {
1147 cconn = list_get_instance(list_first(&client->cconn),
1148 tcp_cconn_t, lclient);
1149 tcp_uc_close(cconn->conn);
1150 tcp_uc_delete(cconn->conn);
1151 tcp_cconn_destroy(cconn);
1152 }
1153 }
1154
1155 n = list_count(&client->clst);
1156 if (n != 0) {
1157 log_msg(LOG_DEFAULT, LVL_WARN, "Client with %lu active "
1158 "listeners closed session", n);
1159 /* XXX Destroy listeners */
1160 }
1161
1162 if (client->sess != NULL)
1163 async_hangup(client->sess);
1164}
1165
1166/** Handle TCP client connection.
1167 *
1168 * @param iid Connect call ID
1169 * @param icall Connect call data
1170 * @param arg Connection argument
1171 */
1172static void tcp_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
1173{
1174 tcp_client_t client;
1175
1176 /* Accept the connection */
1177 async_answer_0(iid, EOK);
1178
1179 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_client_conn() - client=%p",
1180 &client);
1181
1182 tcp_client_init(&client);
1183
1184 while (true) {
1185 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_client_conn: wait req");
1186 ipc_call_t call;
1187 ipc_callid_t callid = async_get_call(&call);
1188 sysarg_t method = IPC_GET_IMETHOD(call);
1189
1190 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_client_conn: method=%d",
1191 (int)method);
1192 if (!method) {
1193 /* The other side has hung up */
1194 async_answer_0(callid, EOK);
1195 break;
1196 }
1197
1198 switch (method) {
1199 case TCP_CALLBACK_CREATE:
1200 tcp_callback_create_srv(&client, callid, &call);
1201 break;
1202 case TCP_CONN_CREATE:
1203 tcp_conn_create_srv(&client, callid, &call);
1204 break;
1205 case TCP_CONN_DESTROY:
1206 tcp_conn_destroy_srv(&client, callid, &call);
1207 break;
1208 case TCP_LISTENER_CREATE:
1209 tcp_listener_create_srv(&client, callid, &call);
1210 break;
1211 case TCP_LISTENER_DESTROY:
1212 tcp_listener_destroy_srv(&client, callid, &call);
1213 break;
1214 case TCP_CONN_SEND_FIN:
1215 tcp_conn_send_fin_srv(&client, callid, &call);
1216 break;
1217 case TCP_CONN_PUSH:
1218 tcp_conn_push_srv(&client, callid, &call);
1219 break;
1220 case TCP_CONN_RESET:
1221 tcp_conn_reset_srv(&client, callid, &call);
1222 break;
1223 case TCP_CONN_SEND:
1224 tcp_conn_send_srv(&client, callid, &call);
1225 break;
1226 case TCP_CONN_RECV:
1227 tcp_conn_recv_srv(&client, callid, &call);
1228 break;
1229 case TCP_CONN_RECV_WAIT:
1230 tcp_conn_recv_wait_srv(&client, callid, &call);
1231 break;
1232 default:
1233 async_answer_0(callid, ENOTSUP);
1234 break;
1235 }
1236 }
1237
1238 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_client_conn TERMINATED");
1239 tcp_client_fini(&client);
1240}
1241
1242/** Initialize TCP service.
1243 *
1244 * @return EOK on success or negative error code.
1245 */
1246int tcp_service_init(void)
1247{
1248 int rc;
1249 service_id_t sid;
1250
1251 async_set_fallback_port_handler(tcp_client_conn, NULL);
1252
1253 rc = loc_server_register(NAME);
1254 if (rc != EOK) {
1255 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering server.");
1256 return EIO;
1257 }
1258
1259 rc = loc_service_register(SERVICE_NAME_TCP, &sid);
1260 if (rc != EOK) {
1261 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service.");
1262 return EIO;
1263 }
1264
1265 return EOK;
1266}
1267
1268/**
1269 * @}
1270 */
Note: See TracBrowser for help on using the repository browser.