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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 258d77e was 258d77e, checked in by Jiri Svoboda <jiri@…>, 8 years ago

After getting to the end of data (FIN), tcp_conn_recv[_wait] should return 0 bytes instead of an error.

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