1 /* $OpenBSD: b_sock.c,v 1.70 2022/12/22 20:13:45 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2017 Bob Beck <beck@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/ioctl.h> 19 #include <sys/socket.h> 20 #include <string.h> 21 22 #include <arpa/inet.h> 23 #include <netinet/in.h> 24 #include <netinet/tcp.h> 25 26 #include <errno.h> 27 #include <limits.h> 28 #include <netdb.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 33 #include <openssl/bio.h> 34 #include <openssl/buffer.h> 35 #include <openssl/err.h> 36 37 int 38 BIO_get_host_ip(const char *str, unsigned char *ip) 39 { 40 struct addrinfo *res = NULL; 41 struct addrinfo hints = { 42 .ai_family = AF_INET, 43 .ai_socktype = SOCK_STREAM, 44 .ai_flags = AI_PASSIVE, 45 }; 46 uint32_t *iap = (in_addr_t *)ip; 47 int error; 48 49 if (str == NULL) { 50 BIOerror(BIO_R_BAD_HOSTNAME_LOOKUP); 51 ERR_asprintf_error_data("NULL host provided"); 52 return (0); 53 } 54 55 if ((error = getaddrinfo(str, NULL, &hints, &res)) != 0) { 56 BIOerror(BIO_R_BAD_HOSTNAME_LOOKUP); 57 ERR_asprintf_error_data("getaddrinfo: host='%s' : %s'", str, 58 gai_strerror(error)); 59 return (0); 60 } 61 *iap = (uint32_t)(((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr); 62 freeaddrinfo(res); 63 return (1); 64 } 65 66 int 67 BIO_get_port(const char *str, unsigned short *port_ptr) 68 { 69 struct addrinfo *res = NULL; 70 struct addrinfo hints = { 71 .ai_family = AF_UNSPEC, 72 .ai_socktype = SOCK_STREAM, 73 .ai_flags = AI_PASSIVE, 74 }; 75 int error; 76 77 if (str == NULL) { 78 BIOerror(BIO_R_NO_PORT_SPECIFIED); 79 return (0); 80 } 81 82 if ((error = getaddrinfo(NULL, str, &hints, &res)) != 0) { 83 BIOerror(BIO_R_INVALID_ARGUMENT); 84 ERR_asprintf_error_data("getaddrinfo: service='%s' : %s'", str, 85 gai_strerror(error)); 86 return (0); 87 } 88 *port_ptr = ntohs(((struct sockaddr_in *)(res->ai_addr))->sin_port); 89 freeaddrinfo(res); 90 return (1); 91 } 92 93 int 94 BIO_sock_error(int sock) 95 { 96 socklen_t len; 97 int err; 98 99 len = sizeof(err); 100 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) != 0) 101 return (1); 102 return (err); 103 } 104 105 struct hostent * 106 BIO_gethostbyname(const char *name) 107 { 108 return gethostbyname(name); 109 } 110 111 int 112 BIO_socket_ioctl(int fd, long type, void *arg) 113 { 114 int ret; 115 116 ret = ioctl(fd, type, arg); 117 if (ret < 0) 118 SYSerror(errno); 119 return (ret); 120 } 121 122 int 123 BIO_get_accept_socket(char *host, int bind_mode) 124 { 125 struct addrinfo hints = { 126 .ai_family = AF_INET, 127 .ai_socktype = SOCK_STREAM, 128 .ai_flags = AI_PASSIVE, 129 }; 130 struct addrinfo *res = NULL; 131 char *h, *p, *str = NULL; 132 int error, ret = 0, s = -1; 133 134 if (host == NULL) { 135 BIOerror(BIO_R_NO_PORT_SPECIFIED); 136 return (-1); 137 } 138 if ((str = strdup(host)) == NULL) { 139 BIOerror(ERR_R_MALLOC_FAILURE); 140 return (-1); 141 } 142 p = NULL; 143 h = str; 144 if ((p = strrchr(str, ':')) == NULL) { 145 /* A string without a colon is treated as a port. */ 146 p = str; 147 h = NULL; 148 } else { 149 *p++ = '\0'; 150 if (*p == '\0') { 151 BIOerror(BIO_R_NO_PORT_SPECIFIED); 152 goto err; 153 } 154 if (*h == '\0' || strcmp(h, "*") == 0) 155 h = NULL; 156 } 157 158 if ((error = getaddrinfo(h, p, &hints, &res)) != 0) { 159 BIOerror(BIO_R_BAD_HOSTNAME_LOOKUP); 160 ERR_asprintf_error_data("getaddrinfo: '%s:%s': %s'", h, p, 161 gai_strerror(error)); 162 goto err; 163 } 164 if (h == NULL) { 165 struct sockaddr_in *sin = (struct sockaddr_in *)res->ai_addr; 166 sin->sin_addr.s_addr = INADDR_ANY; 167 } 168 169 s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 170 if (s == -1) { 171 SYSerror(errno); 172 ERR_asprintf_error_data("host='%s'", host); 173 BIOerror(BIO_R_UNABLE_TO_CREATE_SOCKET); 174 goto err; 175 } 176 if (bind_mode == BIO_BIND_REUSEADDR) { 177 int i = 1; 178 179 ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); 180 bind_mode = BIO_BIND_NORMAL; 181 } 182 if (bind(s, res->ai_addr, res->ai_addrlen) == -1) { 183 SYSerror(errno); 184 ERR_asprintf_error_data("host='%s'", host); 185 BIOerror(BIO_R_UNABLE_TO_BIND_SOCKET); 186 goto err; 187 } 188 if (listen(s, SOMAXCONN) == -1) { 189 SYSerror(errno); 190 ERR_asprintf_error_data("host='%s'", host); 191 BIOerror(BIO_R_UNABLE_TO_LISTEN_SOCKET); 192 goto err; 193 } 194 ret = 1; 195 196 err: 197 free(str); 198 if (res != NULL) 199 freeaddrinfo(res); 200 if ((ret == 0) && (s != -1)) { 201 close(s); 202 s = -1; 203 } 204 return (s); 205 } 206 207 int 208 BIO_accept(int sock, char **addr) 209 { 210 char h[NI_MAXHOST], s[NI_MAXSERV]; 211 struct sockaddr_in sin; 212 socklen_t sin_len = sizeof(sin); 213 int ret = -1; 214 215 if (addr == NULL) { 216 BIOerror(BIO_R_NULL_PARAMETER); 217 goto end; 218 } 219 ret = accept(sock, (struct sockaddr *)&sin, &sin_len); 220 if (ret == -1) { 221 if (BIO_sock_should_retry(ret)) 222 return -2; 223 SYSerror(errno); 224 BIOerror(BIO_R_ACCEPT_ERROR); 225 goto end; 226 } 227 /* XXX Crazy API. Can't be helped */ 228 if (*addr != NULL) { 229 free(*addr); 230 *addr = NULL; 231 } 232 233 if (sin.sin_family != AF_INET) 234 goto end; 235 236 if (getnameinfo((struct sockaddr *)&sin, sin_len, h, sizeof(h), 237 s, sizeof(s), NI_NUMERICHOST|NI_NUMERICSERV) != 0) 238 goto end; 239 240 if ((asprintf(addr, "%s:%s", h, s)) == -1) { 241 BIOerror(ERR_R_MALLOC_FAILURE); 242 *addr = NULL; 243 goto end; 244 } 245 end: 246 return (ret); 247 } 248 249 int 250 BIO_set_tcp_ndelay(int s, int on) 251 { 252 return (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == 0); 253 } 254