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