xref: /openbsd/regress/usr.bin/nc/server-tcp.c (revision 905646f0)
1 /*	$OpenBSD: server-tcp.c,v 1.3 2020/01/21 22:47:39 bluhm Exp $	*/
2 
3 /*
4  * Copyright (c) 2020 Alexander Bluhm <bluhm@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 
22 #include <err.h>
23 #include <errno.h>
24 #include <netdb.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 
30 #include "util.h"
31 
32 void __dead usage(void);
33 int listen_socket(const char *, const char *);
34 int accept_socket(int);
35 
36 void __dead
37 usage(void)
38 {
39 	fprintf(stderr, "server-tcp [-r rcvmsg] [-s sndmsg] host port\n"
40 	"    -E         wait for EOF\n"
41 	"    -N         shutdown write\n"
42 	"    -r rcvmsg  receive from client and check message\n"
43 	"    -s sndmsg  send message to client\n");
44 	exit(2);
45 }
46 
47 int
48 main(int argc, char *argv[])
49 {
50 	const char *host, *port;
51 	struct task todo[100];
52 	size_t tlen = 0;
53 	int ch, s;
54 
55 	while ((ch = getopt(argc, argv, "ENr:s:")) != -1) {
56 		switch (ch) {
57 		case 'E':
58 		case 'N':
59 		case 'r':
60 		case 's':
61 			if (tlen >= sizeof(todo) / sizeof(todo[0]))
62 				errx(1, "too many tasks");
63 			task_enqueue(&todo[tlen], ch, optarg);
64 			tlen++;
65 			break;
66 		default:
67 			usage();
68 		}
69 	}
70 	argc -= optind;
71 	argv += optind;
72 
73 	if (argc == 2) {
74 		host = argv[0];
75 		port = argv[1];
76 	} else {
77 		usage();
78 	}
79 
80 	alarm_timeout();
81 	s = listen_socket(host, port);
82 	print_sockname(s);
83 
84 	switch (fork()) {
85 	case -1:
86 		err(1, "fork");
87 	case 0:
88 		/* child continues, set timer for new process */
89 		alarm_timeout();
90 		break;
91 	default:
92 		/* parent exits and test runs in parallel */
93 		_exit(0);
94 	}
95 
96 	s = accept_socket(s);
97 	task_run(s, todo, tlen);
98 	if (close(s) == -1)
99 		err(1, "close");
100 
101 	return 0;
102 }
103 
104 int
105 listen_socket(const char *host, const char *port)
106 {
107 	struct addrinfo hints, *res, *res0;
108 	int error;
109 	int save_errno;
110 	int s;
111 	const char *cause = NULL;
112 
113 	memset(&hints, 0, sizeof(hints));
114 	hints.ai_family = AF_UNSPEC;
115 	hints.ai_socktype = SOCK_STREAM;
116 	hints.ai_flags = AI_PASSIVE;
117 	error = getaddrinfo(host, port, &hints, &res0);
118 	if (error)
119 		errx(1, "%s", gai_strerror(error));
120 	for (res = res0; res; res = res->ai_next) {
121 		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
122 		if (s == -1) {
123 			cause = "socket";
124 			continue;
125 		}
126 		if (bind(s, res->ai_addr, res->ai_addrlen) == -1) {
127 			cause = "bind";
128 			save_errno = errno;
129 			close(s);
130 			s = -1;
131 			errno = save_errno;
132 			continue;
133 		}
134 		break;  /* okay we got one */
135 	}
136 	if (s == -1)
137 		err(1, "%s", cause);
138 	freeaddrinfo(res0);
139 
140 	if (listen(s, 5) == -1)
141 		err(1, "listen");
142 	return s;
143 }
144 
145 int
146 accept_socket(int s)
147 {
148 	struct sockaddr_storage ss;
149 	socklen_t slen;
150 	char host[NI_MAXHOST], port[NI_MAXSERV];
151 
152 	slen = sizeof(ss);
153 	s = accept(s, (struct sockaddr *)&ss, &slen);
154 	if (s == -1)
155 		err(1, "accept");
156 	if (getnameinfo((struct sockaddr *)&ss, ss.ss_len, host, sizeof(host),
157 	    port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV))
158 		errx(1, "getnameinfo");
159 	fprintf(stderr, "peer: %s %s\n", host, port);
160 
161 	return s;
162 }
163