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

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

TCP and UDP client code needs to make sure callback connection handler has terminated before freeing session-related data.

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