1 /* $OpenBSD: server-tcp.c,v 1.4 2021/07/06 11:50:34 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
usage(void)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
main(int argc,char * argv[])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(10);
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(10);
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
listen_socket(const char * host,const char * port)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
accept_socket(int s)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