source: mainline/uspace/lib/c/generic/inet/tcp.c@ b99f6e2

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

Accepting connections.

  • Property mode set to 100644
File size: 14.6 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 libc
30 * @{
31 */
32/** @file TCP API
33 */
34
35#include <errno.h>
36#include <fibril.h>
37#include <inet/endpoint.h>
38#include <inet/tcp.h>
39#include <ipc/services.h>
40#include <ipc/tcp.h>
41#include <stdlib.h>
42
43#include <stdio.h>
44
45static void tcp_cb_conn(ipc_callid_t, ipc_call_t *, void *);
46static int tcp_conn_fibril(void *);
47
48/** Incoming TCP connection info */
49typedef struct {
50 tcp_listener_t *lst;
51 tcp_conn_t *conn;
52} tcp_in_conn_t;
53
54static int tcp_callback_create(tcp_t *tcp)
55{
56 async_exch_t *exch = async_exchange_begin(tcp->sess);
57
58 printf("tcp_callback_create()\n");
59
60 aid_t req = async_send_0(exch, TCP_CALLBACK_CREATE, NULL);
61 int rc = async_connect_to_me(exch, 0, 0, 0, tcp_cb_conn, tcp);
62 async_exchange_end(exch);
63
64 if (rc != EOK)
65 return rc;
66
67 sysarg_t retval;
68 async_wait_for(req, &retval);
69
70 return retval;
71}
72
73int tcp_create(tcp_t **rtcp)
74{
75 tcp_t *tcp;
76 service_id_t tcp_svcid;
77 int rc;
78
79 printf("tcp_create()\n");
80
81 tcp = calloc(1, sizeof(tcp_t));
82 if (tcp == NULL) {
83 rc = ENOMEM;
84 goto error;
85 }
86
87 list_initialize(&tcp->conn);
88 list_initialize(&tcp->listener);
89
90 rc = loc_service_get_id(SERVICE_NAME_TCP, &tcp_svcid,
91 IPC_FLAG_BLOCKING);
92 if (rc != EOK) {
93 rc = EIO;
94 goto error;
95 }
96
97 tcp->sess = loc_service_connect(EXCHANGE_SERIALIZE, tcp_svcid,
98 IPC_FLAG_BLOCKING);
99 if (tcp->sess == NULL) {
100 rc = EIO;
101 goto error;
102 }
103
104 rc = tcp_callback_create(tcp);
105 if (rc != EOK) {
106 rc = EIO;
107 goto error;
108 }
109
110 *rtcp = tcp;
111 return EOK;
112error:
113 free(tcp);
114 return rc;
115}
116
117void tcp_destroy(tcp_t *tcp)
118{
119 if (tcp == NULL)
120 return;
121
122 async_hangup(tcp->sess);
123 free(tcp);
124}
125
126static int tcp_conn_new(tcp_t *tcp, sysarg_t id, tcp_cb_t *cb, void *arg,
127 tcp_conn_t **rconn)
128{
129 tcp_conn_t *conn;
130
131 printf("tcp_conn_new()\n");
132
133 conn = calloc(1, sizeof(tcp_conn_t));
134 if (conn == NULL)
135 return ENOMEM;
136
137 conn->data_avail = false;
138 fibril_mutex_initialize(&conn->lock);
139 fibril_condvar_initialize(&conn->cv);
140
141 conn->tcp = tcp;
142 conn->id = id;
143 conn->cb = cb;
144 conn->cb_arg = arg;
145
146 list_append(&conn->ltcp, &tcp->conn);
147 *rconn = conn;
148
149 return EOK;
150}
151
152int tcp_conn_create(tcp_t *tcp, inet_ep2_t *epp, tcp_cb_t *cb, void *arg,
153 tcp_conn_t **rconn)
154{
155 async_exch_t *exch;
156 ipc_call_t answer;
157 sysarg_t conn_id;
158
159 printf("tcp_conn_create()\n");
160
161 exch = async_exchange_begin(tcp->sess);
162 aid_t req = async_send_0(exch, TCP_CONN_CREATE, &answer);
163 sysarg_t rc = async_data_write_start(exch, (void *)epp,
164 sizeof(inet_ep2_t));
165 async_exchange_end(exch);
166
167 if (rc != EOK) {
168 sysarg_t rc_orig;
169 async_wait_for(req, &rc_orig);
170 if (rc_orig != EOK)
171 rc = rc_orig;
172 goto error;
173 }
174
175 async_wait_for(req, &rc);
176 if (rc != EOK)
177 goto error;
178
179 conn_id = IPC_GET_ARG1(answer);
180
181 rc = tcp_conn_new(tcp, conn_id, cb, arg, rconn);
182 if (rc != EOK)
183 return rc;
184
185 return EOK;
186error:
187 return (int) rc;
188}
189
190void tcp_conn_destroy(tcp_conn_t *conn)
191{
192 async_exch_t *exch;
193
194 printf("tcp_conn_destroy()\n");
195
196 if (conn == NULL)
197 return;
198
199 list_remove(&conn->ltcp);
200
201 exch = async_exchange_begin(conn->tcp->sess);
202 sysarg_t rc = async_req_1_0(exch, TCP_CONN_DESTROY, conn->id);
203 async_exchange_end(exch);
204
205 free(conn);
206 (void) rc;
207}
208
209static int tcp_conn_get(tcp_t *tcp, sysarg_t id, tcp_conn_t **rconn)
210{
211 list_foreach(tcp->conn, ltcp, tcp_conn_t, conn) {
212 if (conn->id == id) {
213 *rconn = conn;
214 return EOK;
215 }
216 }
217
218 return EINVAL;
219}
220
221void *tcp_conn_userptr(tcp_conn_t *conn)
222{
223 return conn->cb_arg;
224}
225
226int tcp_listener_create(tcp_t *tcp, inet_ep_t *ep, tcp_listen_cb_t *lcb,
227 void *larg, tcp_cb_t *cb, void *arg, tcp_listener_t **rlst)
228{
229 async_exch_t *exch;
230 tcp_listener_t *lst;
231 ipc_call_t answer;
232
233 printf("tcp_listener_create()\n");
234
235 lst = calloc(1, sizeof(tcp_listener_t));
236 if (lst == NULL)
237 return ENOMEM;
238
239 exch = async_exchange_begin(tcp->sess);
240 aid_t req = async_send_0(exch, TCP_LISTENER_CREATE, &answer);
241 sysarg_t rc = async_data_write_start(exch, (void *)ep,
242 sizeof(inet_ep_t));
243 async_exchange_end(exch);
244
245 if (rc != EOK) {
246 sysarg_t rc_orig;
247 async_wait_for(req, &rc_orig);
248 if (rc_orig != EOK)
249 rc = rc_orig;
250 goto error;
251 }
252
253 async_wait_for(req, &rc);
254 if (rc != EOK)
255 goto error;
256
257 lst->tcp = tcp;
258 lst->id = IPC_GET_ARG1(answer);
259 lst->lcb = lcb;
260 lst->lcb_arg = larg;
261 lst->cb = cb;
262 lst->cb_arg = arg;
263
264 list_append(&lst->ltcp, &tcp->listener);
265 *rlst = lst;
266
267 return EOK;
268error:
269 free(lst);
270 return (int) rc;
271}
272
273void tcp_listener_destroy(tcp_listener_t *lst)
274{
275 async_exch_t *exch;
276
277 printf("tcp_listener_destroy()\n");
278
279 if (lst == NULL)
280 return;
281
282 list_remove(&lst->ltcp);
283
284 exch = async_exchange_begin(lst->tcp->sess);
285 sysarg_t rc = async_req_1_0(exch, TCP_LISTENER_DESTROY, lst->id);
286 async_exchange_end(exch);
287
288 free(lst);
289 (void) rc;
290}
291
292static int tcp_listener_get(tcp_t *tcp, sysarg_t id, tcp_listener_t **rlst)
293{
294 list_foreach(tcp->listener, ltcp, tcp_listener_t, lst) {
295 if (lst->id == id) {
296 *rlst = lst;
297 return EOK;
298 }
299 }
300
301 return EINVAL;
302}
303
304void *tcp_listener_userptr(tcp_listener_t *lst)
305{
306 return lst->lcb_arg;
307}
308
309int tcp_conn_wait_connected(tcp_conn_t *conn)
310{
311 fibril_mutex_lock(&conn->lock);
312 while (!conn->connected && !conn->conn_failed && !conn->conn_reset)
313 fibril_condvar_wait(&conn->cv, &conn->lock);
314
315 if (conn->connected) {
316 fibril_mutex_unlock(&conn->lock);
317 return EOK;
318 } else {
319 assert(conn->conn_failed || conn->conn_reset);
320 fibril_mutex_unlock(&conn->lock);
321 return EIO;
322 }
323}
324
325int tcp_conn_send(tcp_conn_t *conn, const void *data, size_t bytes)
326{
327 async_exch_t *exch;
328 sysarg_t rc;
329
330 printf("tcp_conn_send()\n");
331
332 exch = async_exchange_begin(conn->tcp->sess);
333 aid_t req = async_send_1(exch, TCP_CONN_SEND, conn->id, NULL);
334
335 rc = async_data_write_start(exch, data, bytes);
336 if (rc != EOK) {
337 async_forget(req);
338 return rc;
339 }
340
341 async_exchange_end(exch);
342
343 if (rc != EOK) {
344 async_forget(req);
345 return rc;
346 }
347
348 async_wait_for(req, &rc);
349 return rc;
350}
351
352
353int tcp_conn_send_fin(tcp_conn_t *conn)
354{
355 async_exch_t *exch;
356
357 printf("tcp_conn_send_fin()\n");
358
359 exch = async_exchange_begin(conn->tcp->sess);
360 sysarg_t rc = async_req_1_0(exch, TCP_CONN_SEND_FIN, conn->id);
361 async_exchange_end(exch);
362
363 return rc;
364}
365
366int tcp_conn_push(tcp_conn_t *conn)
367{
368 async_exch_t *exch;
369
370 printf("tcp_conn_push()\n");
371
372 exch = async_exchange_begin(conn->tcp->sess);
373 sysarg_t rc = async_req_1_0(exch, TCP_CONN_PUSH, conn->id);
374 async_exchange_end(exch);
375
376 return rc;
377}
378
379int tcp_conn_reset(tcp_conn_t *conn)
380{
381 async_exch_t *exch;
382
383 printf("tcp_conn_reset()\n");
384
385 exch = async_exchange_begin(conn->tcp->sess);
386 sysarg_t rc = async_req_1_0(exch, TCP_CONN_RESET, conn->id);
387 async_exchange_end(exch);
388
389 return rc;
390}
391
392int tcp_conn_recv(tcp_conn_t *conn, void *buf, size_t bsize, size_t *nrecv)
393{
394 async_exch_t *exch;
395 ipc_call_t answer;
396
397 printf("tcp_conn_recv() bsize=%zu\n", bsize);
398
399 fibril_mutex_lock(&conn->lock);
400 if (!conn->data_avail) {
401 printf("returning EAGAIN\n");
402 fibril_mutex_unlock(&conn->lock);
403 return EAGAIN;
404 }
405
406 exch = async_exchange_begin(conn->tcp->sess);
407 aid_t req = async_send_1(exch, TCP_CONN_RECV, conn->id, &answer);
408 int rc = async_data_read_start(exch, buf, bsize);
409 async_exchange_end(exch);
410
411 if (rc != EOK) {
412 printf("got rc = %d\n", rc);
413 async_forget(req);
414 fibril_mutex_unlock(&conn->lock);
415 return rc;
416 }
417
418 sysarg_t retval;
419 async_wait_for(req, &retval);
420 if (retval != EOK) {
421 printf("got rc = %d\n", rc);
422 fibril_mutex_unlock(&conn->lock);
423 return retval;
424 }
425
426 *nrecv = IPC_GET_ARG1(answer);
427 fibril_mutex_unlock(&conn->lock);
428 return EOK;
429}
430
431int tcp_conn_recv_wait(tcp_conn_t *conn, void *buf, size_t bsize, size_t *nrecv)
432{
433 async_exch_t *exch;
434 ipc_call_t answer;
435
436 printf("tcp_conn_recv_wait() bsize=%zu\n", bsize);
437again:
438 fibril_mutex_lock(&conn->lock);
439 while (!conn->data_avail) {
440 printf("wait for data to be avail\n");
441 fibril_condvar_wait(&conn->cv, &conn->lock);
442 }
443
444 printf("tcp_conn_recv_wait - get data\n");
445 exch = async_exchange_begin(conn->tcp->sess);
446 aid_t req = async_send_1(exch, TCP_CONN_RECV_WAIT, conn->id, &answer);
447 int rc = async_data_read_start(exch, buf, bsize);
448 printf("tcp_conn_recv_wait - rc = %d\n", rc);
449 async_exchange_end(exch);
450
451 if (rc != EOK) {
452 printf("got rc=%d\n", rc);
453 async_forget(req);
454 if (rc == EAGAIN) {
455 conn->data_avail = false;
456 fibril_mutex_unlock(&conn->lock);
457 goto again;
458 }
459 fibril_mutex_unlock(&conn->lock);
460 return rc;
461 }
462
463 sysarg_t retval;
464 async_wait_for(req, &retval);
465 if (retval != EOK) {
466 printf("got retval != EOK\n");
467 if (rc == EAGAIN) {
468 printf("rc == EAGAIN\n");
469 conn->data_avail = false;
470 }
471 fibril_mutex_unlock(&conn->lock);
472 return retval;
473 }
474
475 *nrecv = IPC_GET_ARG1(answer);
476 fibril_mutex_unlock(&conn->lock);
477 printf("tcp_conn_recv_wait: nrecv=%zu\n", *nrecv);
478 printf("received: '");
479 size_t i;
480 for (i = 0; i < *nrecv; i++) {
481 putchar((char)((char *)buf)[i]);
482 }
483 printf("'\n");
484 return EOK;
485}
486
487static void tcp_ev_connected(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
488{
489 tcp_conn_t *conn;
490 sysarg_t conn_id;
491 int rc;
492
493 printf("tcp_ev_connected()\n");
494 conn_id = IPC_GET_ARG1(*icall);
495
496 rc = tcp_conn_get(tcp, conn_id, &conn);
497 if (rc != EOK) {
498 printf("conn ID %zu not found\n",
499 conn_id);
500 async_answer_0(iid, ENOENT);
501 return;
502 }
503
504 fibril_mutex_lock(&conn->lock);
505 conn->connected = true;
506 fibril_condvar_broadcast(&conn->cv);
507 fibril_mutex_unlock(&conn->lock);
508
509 async_answer_0(iid, EOK);
510}
511
512static void tcp_ev_conn_failed(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
513{
514 tcp_conn_t *conn;
515 sysarg_t conn_id;
516 int rc;
517
518 printf("tcp_ev_conn_failed()\n");
519 conn_id = IPC_GET_ARG1(*icall);
520
521 rc = tcp_conn_get(tcp, conn_id, &conn);
522 if (rc != EOK) {
523 printf("conn ID %zu not found\n",
524 conn_id);
525 async_answer_0(iid, ENOENT);
526 return;
527 }
528
529 fibril_mutex_lock(&conn->lock);
530 conn->conn_failed = true;
531 fibril_condvar_broadcast(&conn->cv);
532 fibril_mutex_unlock(&conn->lock);
533
534 async_answer_0(iid, EOK);
535}
536
537static void tcp_ev_conn_reset(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
538{
539 tcp_conn_t *conn;
540 sysarg_t conn_id;
541 int rc;
542
543 printf("tcp_ev_conn_reset()\n");
544 conn_id = IPC_GET_ARG1(*icall);
545
546 rc = tcp_conn_get(tcp, conn_id, &conn);
547 if (rc != EOK) {
548 printf("conn ID %zu not found\n",
549 conn_id);
550 async_answer_0(iid, ENOENT);
551 return;
552 }
553
554 fibril_mutex_lock(&conn->lock);
555 conn->conn_reset = true;
556 fibril_condvar_broadcast(&conn->cv);
557 fibril_mutex_unlock(&conn->lock);
558
559 async_answer_0(iid, EOK);
560}
561
562static void tcp_ev_data(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
563{
564 tcp_conn_t *conn;
565 sysarg_t conn_id;
566 int rc;
567
568 printf("tcp_ev_data()\n");
569 conn_id = IPC_GET_ARG1(*icall);
570
571 rc = tcp_conn_get(tcp, conn_id, &conn);
572 if (rc != EOK) {
573 printf("conn ID %zu not found\n",
574 conn_id);
575 async_answer_0(iid, ENOENT);
576 return;
577 }
578
579 conn->data_avail = true;
580 fibril_condvar_broadcast(&conn->cv);
581
582 async_answer_0(iid, EOK);
583}
584
585static void tcp_ev_urg_data(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
586{
587 printf("tcp_ev_urg_data()\n");
588 async_answer_0(iid, ENOTSUP);
589}
590
591static void tcp_ev_new_conn(tcp_t *tcp, ipc_callid_t iid, ipc_call_t *icall)
592{
593 tcp_listener_t *lst;
594 tcp_conn_t *conn;
595 sysarg_t lst_id;
596 sysarg_t conn_id;
597 fid_t fid;
598 tcp_in_conn_t *cinfo;
599 int rc;
600
601 printf("tcp_ev_new_conn()\n");
602 lst_id = IPC_GET_ARG1(*icall);
603 conn_id = IPC_GET_ARG2(*icall);
604
605 printf("new conn: lst_id=%zu conn_id=%zu\n", lst_id, conn_id);
606
607 rc = tcp_listener_get(tcp, lst_id, &lst);
608 if (rc != EOK) {
609 printf("listener ID %zu not found\n",
610 lst_id);
611 async_answer_0(iid, ENOENT);
612 return;
613 }
614
615 rc = tcp_conn_new(tcp, conn_id, lst->cb, lst->cb_arg, &conn);
616 if (rc != EOK) {
617 printf("Failed creating new incoming connection.\n");
618 async_answer_0(iid, ENOMEM);
619 return;
620 }
621
622 if (lst->lcb != NULL && lst->lcb->new_conn != NULL) {
623 printf("Creating connection fibril\n");
624 cinfo = calloc(1, sizeof(tcp_in_conn_t));
625 if (cinfo == NULL) {
626 printf("Failed creating new incoming connection info.\n");
627 async_answer_0(iid, ENOMEM);
628 return;
629 }
630
631 cinfo->lst = lst;
632 cinfo->conn = conn;
633
634 fid = fibril_create(tcp_conn_fibril, cinfo);
635 if (fid == 0) {
636 printf("Error creating connection fibril.\n");
637 async_answer_0(iid, ENOMEM);
638 }
639
640 fibril_add_ready(fid);
641 }
642
643 async_answer_0(iid, EOK);
644}
645
646static void tcp_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
647{
648 tcp_t *tcp = (tcp_t *)arg;
649
650 async_answer_0(iid, EOK);
651
652 printf("tcp_cb_conn()\n");
653
654 while (true) {
655 ipc_call_t call;
656 ipc_callid_t callid = async_get_call(&call);
657
658 printf("tcp_cb_conn() - msg %d\n",
659 (int)IPC_GET_IMETHOD(call));
660 if (!IPC_GET_IMETHOD(call)) {
661 /* TODO: Handle hangup */
662 return;
663 }
664
665 switch (IPC_GET_IMETHOD(call)) {
666 case TCP_EV_CONNECTED:
667 tcp_ev_connected(tcp, callid, &call);
668 break;
669 case TCP_EV_CONN_FAILED:
670 tcp_ev_conn_failed(tcp, callid, &call);
671 break;
672 case TCP_EV_CONN_RESET:
673 tcp_ev_conn_reset(tcp, callid, &call);
674 break;
675 case TCP_EV_DATA:
676 tcp_ev_data(tcp, callid, &call);
677 break;
678 case TCP_EV_URG_DATA:
679 tcp_ev_urg_data(tcp, callid, &call);
680 break;
681 case TCP_EV_NEW_CONN:
682 tcp_ev_new_conn(tcp, callid, &call);
683 break;
684 default:
685 async_answer_0(callid, ENOTSUP);
686 break;
687 }
688 }
689}
690
691/** Fibril for handling incoming TCP connection in background */
692static int tcp_conn_fibril(void *arg)
693{
694 tcp_in_conn_t *cinfo = (tcp_in_conn_t *)arg;
695
696 printf("tcp_conn_fibril: begin\n");
697 cinfo->lst->lcb->new_conn(cinfo->lst, cinfo->conn);
698 printf("tcp_conn_fibril: end\n");
699 tcp_conn_destroy(cinfo->conn);
700
701 return EOK;
702}
703
704/** @}
705 */
Note: See TracBrowser for help on using the repository browser.