1 /*
2 * Copyright (C) 2011-2013 Michael Tuexen
3 * Copyright (C) 2018-2018 Felix Weinrank
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #ifdef _WIN32
33 #define _CRT_SECURE_NO_WARNINGS
34 #endif
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <stdarg.h>
39 #include <sys/types.h>
40 #ifndef _WIN32
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
44 #include <errno.h>
45 #include <pthread.h>
46 #include <unistd.h>
47 #else
48 #include <winsock2.h>
49 #include <ws2tcpip.h>
50 #endif
51 #include <usrsctp.h>
52 #include "programs_helper.h"
53
54 #define MAX_PACKET_SIZE (1<<16)
55 #define LINE_LENGTH (1<<20)
56 #define DISCARD_PPID 39
57 /* #define DUMP_PKTS_TO_FILE */
58
59 #ifdef _WIN32
60 static DWORD WINAPI
61 #else
62 static void *
63 #endif
handle_packets(void * arg)64 handle_packets(void *arg)
65 {
66 #ifdef _WIN32
67 SOCKET *fdp;
68 #else
69 int *fdp;
70 #endif
71 char *dump_buf;
72 ssize_t length;
73 char buf[MAX_PACKET_SIZE];
74
75 #ifdef _WIN32
76 fdp = (SOCKET *)arg;
77 #else
78 fdp = (int *)arg;
79 #endif
80 for (;;) {
81 #if defined(__NetBSD__)
82 pthread_testcancel();
83 #endif
84 length = recv(*fdp, buf, MAX_PACKET_SIZE, 0);
85 if (length > 0) {
86 if ((dump_buf = usrsctp_dumppacket(buf, (size_t)length, SCTP_DUMP_INBOUND)) != NULL) {
87 /* fprintf(stderr, "%s", dump_buf); */
88 usrsctp_freedumpbuffer(dump_buf);
89 }
90 usrsctp_conninput(fdp, buf, (size_t)length, 0);
91 }
92 }
93 #ifdef _WIN32
94 return 0;
95 #else
96 return (NULL);
97 #endif
98 }
99
100 static int
conn_output(void * addr,void * buf,size_t length,uint8_t tos,uint8_t set_df)101 conn_output(void *addr, void *buf, size_t length, uint8_t tos, uint8_t set_df)
102 {
103 char *dump_buf;
104 #ifdef _WIN32
105 SOCKET *fdp;
106 #else
107 int *fdp;
108 #endif
109
110 #ifdef _WIN32
111 fdp = (SOCKET *)addr;
112 #else
113 fdp = (int *)addr;
114 #endif
115 if ((dump_buf = usrsctp_dumppacket(buf, length, SCTP_DUMP_OUTBOUND)) != NULL) {
116 /* fprintf(stderr, "%s", dump_buf); */
117 usrsctp_freedumpbuffer(dump_buf);
118 }
119
120 #ifdef DUMP_PKTS_TO_FILE
121 FILE *fp;
122 char fname[128];
123 static int pktnum = 0;
124 if (snprintf(fname, sizeof(fname), "pkt-%d", pktnum++) < 0) {
125 fname[0] = '\0';
126 }
127 fp = fopen(fname, "wb");
128 fwrite((char *)buf + 12, 1, length - 12, fp);
129 fclose(fp);
130 #endif
131
132 #ifdef _WIN32
133 if (send(*fdp, buf, (int)length, 0) == SOCKET_ERROR) {
134 return (WSAGetLastError());
135 #else
136 if (send(*fdp, buf, length, 0) < 0) {
137 return (errno);
138 #endif
139 } else {
140 return (0);
141 }
142 }
143
144 static void
145 handle_upcall(struct socket *sock, void *data, int flgs)
146 {
147 char *buf;
148 int events;
149
150 buf = malloc(MAX_PACKET_SIZE);
151
152 while ((events = usrsctp_get_events(sock)) && (events & SCTP_EVENT_READ)) {
153 struct sctp_recvv_rn rn;
154 ssize_t n;
155 union sctp_sockstore addr;
156 int flags = 0;
157 socklen_t len = (socklen_t)sizeof(addr);
158 unsigned int infotype = 0;
159 socklen_t infolen = sizeof(struct sctp_recvv_rn);
160 memset(&rn, 0, sizeof(struct sctp_recvv_rn));
161 n = usrsctp_recvv(sock, buf, MAX_PACKET_SIZE, (struct sockaddr *) &addr, &len, (void *)&rn, &infolen, &infotype, &flags);
162 if (n < 0) {
163 perror("usrsctp_recvv");
164 break;
165 } else if (n > 0) {
166 if (flags & MSG_NOTIFICATION) {
167 printf("Notification of length %d received.\n", (int)n);
168 } else {
169 printf("Message of length %d received via %p:%u on stream %u with SSN %u and TSN %u, PPID %u, context %u, flags %x.\n",
170 (int)n,
171 addr.sconn.sconn_addr,
172 ntohs(addr.sconn.sconn_port),
173 rn.recvv_rcvinfo.rcv_sid,
174 rn.recvv_rcvinfo.rcv_ssn,
175 rn.recvv_rcvinfo.rcv_tsn,
176 ntohl(rn.recvv_rcvinfo.rcv_ppid),
177 rn.recvv_rcvinfo.rcv_context,
178 flags);
179 }
180 } else {
181 usrsctp_deregister_address(data);
182 usrsctp_close(sock);
183 break;
184 }
185 }
186 free(buf);
187
188 return;
189 }
190
191 #if 0
192 static void
193 print_addresses(struct socket *sock)
194 {
195 int i, n;
196 struct sockaddr *addrs, *addr;
197
198 n = usrsctp_getladdrs(sock, 0, &addrs);
199 addr = addrs;
200 for (i = 0; i < n; i++) {
201 switch (addr->sa_family) {
202 case AF_INET:
203 {
204 struct sockaddr_in *sin;
205 char buf[INET_ADDRSTRLEN];
206 const char *name;
207
208 sin = (struct sockaddr_in *)addr;
209 name = inet_ntop(AF_INET, &sin->sin_addr, buf, INET_ADDRSTRLEN);
210 printf("%s:%d", name, ntohs(sin->sin_port));
211 break;
212 }
213 case AF_INET6:
214 {
215 struct sockaddr_in6 *sin6;
216 char buf[INET6_ADDRSTRLEN];
217 const char *name;
218
219 sin6 = (struct sockaddr_in6 *)addr;
220 name = inet_ntop(AF_INET6, &sin6->sin6_addr, buf, INET6_ADDRSTRLEN);
221 printf("%s:%d", name, ntohs(sin6->sin6_port));
222 break;
223 }
224 case AF_CONN:
225 {
226 struct sockaddr_conn *sconn;
227
228 sconn = (struct sockaddr_conn *)addr;
229 printf("%p:%d", sconn->sconn_addr, ntohs(sconn->sconn_port));
230 break;
231 }
232 default:
233 printf("Unknown family: %d", addr->sa_family);
234 break;
235 }
236 addr = (struct sockaddr *)((caddr_t)addr + addr->sa_len);
237 if (i != n - 1) {
238 printf(",");
239 }
240 }
241 if (n > 0) {
242 usrsctp_freeladdrs(addrs);
243 }
244 printf("<->");
245 n = usrsctp_getpaddrs(sock, 0, &addrs);
246 addr = addrs;
247 for (i = 0; i < n; i++) {
248 switch (addr->sa_family) {
249 case AF_INET:
250 {
251 struct sockaddr_in *sin;
252 char buf[INET_ADDRSTRLEN];
253 const char *name;
254
255 sin = (struct sockaddr_in *)addr;
256 name = inet_ntop(AF_INET, &sin->sin_addr, buf, INET_ADDRSTRLEN);
257 printf("%s:%d", name, ntohs(sin->sin_port));
258 break;
259 }
260 case AF_INET6:
261 {
262 struct sockaddr_in6 *sin6;
263 char buf[INET6_ADDRSTRLEN];
264 const char *name;
265
266 sin6 = (struct sockaddr_in6 *)addr;
267 name = inet_ntop(AF_INET6, &sin6->sin6_addr, buf, INET6_ADDRSTRLEN);
268 printf("%s:%d", name, ntohs(sin6->sin6_port));
269 break;
270 }
271 case AF_CONN:
272 {
273 struct sockaddr_conn *sconn;
274
275 sconn = (struct sockaddr_conn *)addr;
276 printf("%p:%d", sconn->sconn_addr, ntohs(sconn->sconn_port));
277 break;
278 }
279 default:
280 printf("Unknown family: %d", addr->sa_family);
281 break;
282 }
283 addr = (struct sockaddr *)((caddr_t)addr + addr->sa_len);
284 if (i != n - 1) {
285 printf(",");
286 }
287 }
288 if (n > 0) {
289 usrsctp_freepaddrs(addrs);
290 }
291 printf("\n");
292 }
293 #endif
294
295 int
296 main(int argc, char *argv[])
297 {
298 struct sockaddr_in sin_s, sin_c;
299 struct sockaddr_conn sconn;
300 #ifdef _WIN32
301 SOCKET fd_c, fd_s;
302 #else
303 int fd_c, fd_s, rc;
304 #endif
305 struct socket *s_c, *s_s, *s_l;
306 #ifdef _WIN32
307 HANDLE tid_c, tid_s;
308 #else
309 pthread_t tid_c, tid_s;
310 #endif
311 int cur_buf_size, snd_buf_size, rcv_buf_size, on;
312 socklen_t opt_len;
313 struct sctp_sndinfo sndinfo;
314 char *line;
315 #ifdef _WIN32
316 WSADATA wsaData;
317 #endif
318 uint16_t client_port = 9900;
319 uint16_t server_port = 9901;
320
321 if (argc == 3) {
322 client_port = atoi(argv[1]);
323 server_port = atoi(argv[2]);
324 }
325
326 #ifdef _WIN32
327 if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
328 fprintf(stderr, "WSAStartup failed\n");
329 exit (EXIT_FAILURE);
330 }
331 #endif
332 usrsctp_init(0, conn_output, debug_printf_stack);
333 /* set up a connected UDP socket */
334 #ifdef _WIN32
335 if ((fd_c = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) {
336 fprintf(stderr, "socket() failed with error: %d\n", WSAGetLastError());
337 exit(EXIT_FAILURE);
338 }
339 if ((fd_s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) {
340 fprintf(stderr, "socket() failed with error: %d\n", WSAGetLastError());
341 exit(EXIT_FAILURE);
342 }
343 #else
344 if ((fd_c = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
345 perror("socket");
346 exit(EXIT_FAILURE);
347 }
348 if ((fd_s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
349 perror("socket");
350 exit(EXIT_FAILURE);
351 }
352 #endif
353 memset(&sin_c, 0, sizeof(struct sockaddr_in));
354 sin_c.sin_family = AF_INET;
355 #ifdef HAVE_SIN_LEN
356 sin_c.sin_len = sizeof(struct sockaddr_in);
357 #endif
358 sin_c.sin_port = htons(client_port);
359 sin_c.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
360 memset(&sin_s, 0, sizeof(struct sockaddr_in));
361 sin_s.sin_family = AF_INET;
362 #ifdef HAVE_SIN_LEN
363 sin_s.sin_len = sizeof(struct sockaddr_in);
364 #endif
365 sin_s.sin_port = htons(server_port);
366 sin_s.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
367 #ifdef _WIN32
368 if (bind(fd_c, (struct sockaddr *)&sin_c, sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
369 fprintf(stderr, "bind() failed with error: %d\n", WSAGetLastError());
370 exit(EXIT_FAILURE);
371 }
372 if (bind(fd_s, (struct sockaddr *)&sin_s, sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
373 fprintf(stderr, "bind() failed with error: %d\n", WSAGetLastError());
374 exit(EXIT_FAILURE);
375 }
376 #else
377 if (bind(fd_c, (struct sockaddr *)&sin_c, sizeof(struct sockaddr_in)) < 0) {
378 perror("bind");
379 exit(EXIT_FAILURE);
380 }
381 if (bind(fd_s, (struct sockaddr *)&sin_s, sizeof(struct sockaddr_in)) < 0) {
382 perror("bind");
383 exit(EXIT_FAILURE);
384 }
385 #endif
386 #ifdef _WIN32
387 if (connect(fd_c, (struct sockaddr *)&sin_s, sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
388 fprintf(stderr, "connect() failed with error: %d\n", WSAGetLastError());
389 exit(EXIT_FAILURE);
390 }
391 if (connect(fd_s, (struct sockaddr *)&sin_c, sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
392 fprintf(stderr, "connect() failed with error: %d\n", WSAGetLastError());
393 exit(EXIT_FAILURE);
394 }
395 #else
396 if (connect(fd_c, (struct sockaddr *)&sin_s, sizeof(struct sockaddr_in)) < 0) {
397 perror("connect");
398 exit(EXIT_FAILURE);
399 }
400 if (connect(fd_s, (struct sockaddr *)&sin_c, sizeof(struct sockaddr_in)) < 0) {
401 perror("connect");
402 exit(EXIT_FAILURE);
403 }
404 #endif
405 #ifdef _WIN32
406 if ((tid_c = CreateThread(NULL, 0, &handle_packets, (void *)&fd_c, 0, NULL)) == NULL) {
407 fprintf(stderr, "CreateThread() failed with error: %d\n", GetLastError());
408 exit(EXIT_FAILURE);
409 }
410 if ((tid_s = CreateThread(NULL, 0, &handle_packets, (void *)&fd_s, 0, NULL)) == NULL) {
411 fprintf(stderr, "CreateThread() failed with error: %d\n", GetLastError());
412 exit(EXIT_FAILURE);
413 }
414 #else
415 if ((rc = pthread_create(&tid_c, NULL, &handle_packets, (void *)&fd_c)) != 0) {
416 fprintf(stderr, "pthread_create tid_c: %s\n", strerror(rc));
417 exit(EXIT_FAILURE);
418 }
419
420 if ((rc = pthread_create(&tid_s, NULL, &handle_packets, (void *)&fd_s)) != 0) {
421 fprintf(stderr, "pthread_create tid_s: %s\n", strerror(rc));
422 exit(EXIT_FAILURE);
423 };
424 #endif
425 #ifdef SCTP_DEBUG
426 usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_NONE);
427 #endif
428 usrsctp_sysctl_set_sctp_ecn_enable(0);
429 usrsctp_register_address((void *)&fd_c);
430 usrsctp_register_address((void *)&fd_s);
431
432 if ((s_c = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, NULL, NULL, 0, NULL)) == NULL) {
433 perror("usrsctp_socket");
434 exit(EXIT_FAILURE);
435 }
436 usrsctp_set_upcall(s_c, handle_upcall, &fd_c);
437
438 opt_len = (socklen_t)sizeof(int);
439 cur_buf_size = 0;
440 if (usrsctp_getsockopt(s_c, SOL_SOCKET, SO_SNDBUF, &cur_buf_size, &opt_len) < 0) {
441 perror("usrsctp_getsockopt");
442 exit(EXIT_FAILURE);
443 }
444 printf("Change send socket buffer size from %d ", cur_buf_size);
445 snd_buf_size = 1<<22; /* 4 MB */
446 if (usrsctp_setsockopt(s_c, SOL_SOCKET, SO_SNDBUF, &snd_buf_size, sizeof(int)) < 0) {
447 perror("usrsctp_setsockopt");
448 exit(EXIT_FAILURE);
449 }
450 opt_len = (socklen_t)sizeof(int);
451 cur_buf_size = 0;
452 if (usrsctp_getsockopt(s_c, SOL_SOCKET, SO_SNDBUF, &cur_buf_size, &opt_len) < 0) {
453 perror("usrsctp_getsockopt");
454 exit(EXIT_FAILURE);
455 }
456 printf("to %d.\n", cur_buf_size);
457 if ((s_l = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, NULL, NULL, 0, NULL)) == NULL) {
458 perror("usrsctp_socket");
459 exit(EXIT_FAILURE);
460 }
461
462 opt_len = (socklen_t)sizeof(int);
463 cur_buf_size = 0;
464 if (usrsctp_getsockopt(s_l, SOL_SOCKET, SO_RCVBUF, &cur_buf_size, &opt_len) < 0) {
465 perror("usrsctp_getsockopt");
466 exit(EXIT_FAILURE);
467 }
468 printf("Change receive socket buffer size from %d ", cur_buf_size);
469 rcv_buf_size = 1<<16; /* 64 KB */
470 if (usrsctp_setsockopt(s_l, SOL_SOCKET, SO_RCVBUF, &rcv_buf_size, sizeof(int)) < 0) {
471 perror("usrsctp_setsockopt");
472 exit(EXIT_FAILURE);
473 }
474 opt_len = (socklen_t)sizeof(int);
475 cur_buf_size = 0;
476 if (usrsctp_getsockopt(s_l, SOL_SOCKET, SO_RCVBUF, &cur_buf_size, &opt_len) < 0) {
477 perror("usrsctp_getsockopt");
478 exit(EXIT_FAILURE);
479 }
480 printf("to %d.\n", cur_buf_size);
481
482 on = 1;
483 if (usrsctp_setsockopt(s_l, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on, sizeof(int)) < 0) {
484 perror("usrsctp_setsockopt");
485 exit(EXIT_FAILURE);
486 }
487 /* Bind the client side. */
488 memset(&sconn, 0, sizeof(struct sockaddr_conn));
489 sconn.sconn_family = AF_CONN;
490 #ifdef HAVE_SCONN_LEN
491 sconn.sconn_len = sizeof(struct sockaddr_conn);
492 #endif
493 sconn.sconn_port = htons(5001);
494 sconn.sconn_addr = &fd_c;
495 if (usrsctp_bind(s_c, (struct sockaddr *)&sconn, sizeof(struct sockaddr_conn)) < 0) {
496 perror("usrsctp_bind");
497 exit(EXIT_FAILURE);
498 }
499 /* Bind the server side. */
500 memset(&sconn, 0, sizeof(struct sockaddr_conn));
501 sconn.sconn_family = AF_CONN;
502 #ifdef HAVE_SCONN_LEN
503 sconn.sconn_len = sizeof(struct sockaddr_conn);
504 #endif
505 sconn.sconn_port = htons(5001);
506 sconn.sconn_addr = &fd_s;
507 if (usrsctp_bind(s_l, (struct sockaddr *)&sconn, sizeof(struct sockaddr_conn)) < 0) {
508 perror("usrsctp_bind");
509 exit(EXIT_FAILURE);
510 }
511 /* Make server side passive... */
512 if (usrsctp_listen(s_l, 1) < 0) {
513 perror("usrsctp_listen");
514 exit(EXIT_FAILURE);
515 }
516 /* Initiate the handshake */
517 memset(&sconn, 0, sizeof(struct sockaddr_conn));
518 sconn.sconn_family = AF_CONN;
519 #ifdef HAVE_SCONN_LEN
520 sconn.sconn_len = sizeof(struct sockaddr_conn);
521 #endif
522 sconn.sconn_port = htons(5001);
523 sconn.sconn_addr = &fd_c;
524 if (usrsctp_connect(s_c, (struct sockaddr *)&sconn, sizeof(struct sockaddr_conn)) < 0) {
525 perror("usrsctp_connect");
526 exit(EXIT_FAILURE);
527 }
528
529 if ((s_s = usrsctp_accept(s_l, NULL, NULL)) == NULL) {
530 perror("usrsctp_accept");
531 exit(EXIT_FAILURE);
532 }
533
534 usrsctp_set_upcall(s_s, handle_upcall, &fd_s);
535
536 usrsctp_close(s_l);
537 if ((line = malloc(LINE_LENGTH)) == NULL) {
538 exit(EXIT_FAILURE);
539 }
540 memset(line, 'A', LINE_LENGTH);
541 sndinfo.snd_sid = 1;
542 sndinfo.snd_flags = SCTP_UNORDERED;
543 sndinfo.snd_ppid = htonl(DISCARD_PPID);
544 sndinfo.snd_context = 0;
545 sndinfo.snd_assoc_id = 0;
546 /* Send a 1 MB ordered message */
547 if (usrsctp_sendv(s_c, line, LINE_LENGTH, NULL, 0, (void *)&sndinfo,
548 (socklen_t)sizeof(struct sctp_sndinfo), SCTP_SENDV_SNDINFO, 0) < 0) {
549 perror("usrsctp_sendv");
550 exit(EXIT_FAILURE);
551 }
552 /* Send a 1 MB ordered message */
553 if (usrsctp_sendv(s_c, line, LINE_LENGTH, NULL, 0, (void *)&sndinfo,
554 (socklen_t)sizeof(struct sctp_sndinfo), SCTP_SENDV_SNDINFO, 0) < 0) {
555 perror("usrsctp_sendv");
556 exit(EXIT_FAILURE);
557 }
558 free(line);
559 usrsctp_shutdown(s_c, SHUT_WR);
560
561 while (usrsctp_finish() != 0) {
562 #ifdef _WIN32
563 Sleep(1000);
564 #else
565 sleep(1);
566 #endif
567 }
568 #ifdef _WIN32
569 TerminateThread(tid_c, 0);
570 WaitForSingleObject(tid_c, INFINITE);
571 TerminateThread(tid_s, 0);
572 WaitForSingleObject(tid_s, INFINITE);
573 if (closesocket(fd_c) == SOCKET_ERROR) {
574 fprintf(stderr, "closesocket() failed with error: %d\n", WSAGetLastError());
575 exit(EXIT_FAILURE);
576 }
577 if (closesocket(fd_s) == SOCKET_ERROR) {
578 fprintf(stderr, "closesocket() failed with error: %d\n", WSAGetLastError());
579 exit(EXIT_FAILURE);
580 }
581 WSACleanup();
582 #else
583 pthread_cancel(tid_c);
584 pthread_join(tid_c, NULL);
585 pthread_cancel(tid_s);
586 pthread_join(tid_s, NULL);
587 if (close(fd_c) < 0) {
588 perror("close");
589 exit(EXIT_FAILURE);
590 }
591 if (close(fd_s) < 0) {
592 perror("close");
593 exit(EXIT_FAILURE);
594 }
595 #endif
596 return (0);
597 }
598