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