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

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

gradually introduce async ports, initial phase

The initial phase is to reimplement the traditional async client connections as an untyped fallback port. This creates the possibility to introduce ports typed by interface type gradually in later changesets.

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