1 /* $OpenBSD: b_sock.c,v 1.71 2023/07/05 21:23:37 beck 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 LCRYPTO_ALIAS(BIO_get_host_ip); 66 67 int 68 BIO_get_port(const char *str, unsigned short *port_ptr) 69 { 70 struct addrinfo *res = NULL; 71 struct addrinfo hints = { 72 .ai_family = AF_UNSPEC, 73 .ai_socktype = SOCK_STREAM, 74 .ai_flags = AI_PASSIVE, 75 }; 76 int error; 77 78 if (str == NULL) { 79 BIOerror(BIO_R_NO_PORT_SPECIFIED); 80 return (0); 81 } 82 83 if ((error = getaddrinfo(NULL, str, &hints, &res)) != 0) { 84 BIOerror(BIO_R_INVALID_ARGUMENT); 85 ERR_asprintf_error_data("getaddrinfo: service='%s' : %s'", str, 86 gai_strerror(error)); 87 return (0); 88 } 89 *port_ptr = ntohs(((struct sockaddr_in *)(res->ai_addr))->sin_port); 90 freeaddrinfo(res); 91 return (1); 92 } 93 LCRYPTO_ALIAS(BIO_get_port); 94 95 int 96 BIO_sock_error(int sock) 97 { 98 socklen_t len; 99 int err; 100 101 len = sizeof(err); 102 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) != 0) 103 return (1); 104 return (err); 105 } 106 LCRYPTO_ALIAS(BIO_sock_error); 107 108 struct hostent * 109 BIO_gethostbyname(const char *name) 110 { 111 return gethostbyname(name); 112 } 113 LCRYPTO_ALIAS(BIO_gethostbyname); 114 115 int 116 BIO_socket_ioctl(int fd, long type, void *arg) 117 { 118 int ret; 119 120 ret = ioctl(fd, type, arg); 121 if (ret < 0) 122 SYSerror(errno); 123 return (ret); 124 } 125 LCRYPTO_ALIAS(BIO_socket_ioctl); 126 127 int 128 BIO_get_accept_socket(char *host, int bind_mode) 129 { 130 struct addrinfo hints = { 131 .ai_family = AF_INET, 132 .ai_socktype = SOCK_STREAM, 133 .ai_flags = AI_PASSIVE, 134 }; 135 struct addrinfo *res = NULL; 136 char *h, *p, *str = NULL; 137 int error, ret = 0, s = -1; 138 139 if (host == NULL) { 140 BIOerror(BIO_R_NO_PORT_SPECIFIED); 141 return (-1); 142 } 143 if ((str = strdup(host)) == NULL) { 144 BIOerror(ERR_R_MALLOC_FAILURE); 145 return (-1); 146 } 147 p = NULL; 148 h = str; 149 if ((p = strrchr(str, ':')) == NULL) { 150 /* A string without a colon is treated as a port. */ 151 p = str; 152 h = NULL; 153 } else { 154 *p++ = '\0'; 155 if (*p == '\0') { 156 BIOerror(BIO_R_NO_PORT_SPECIFIED); 157 goto err; 158 } 159 if (*h == '\0' || strcmp(h, "*") == 0) 160 h = NULL; 161 } 162 163 if ((error = getaddrinfo(h, p, &hints, &res)) != 0) { 164 BIOerror(BIO_R_BAD_HOSTNAME_LOOKUP); 165 ERR_asprintf_error_data("getaddrinfo: '%s:%s': %s'", h, p, 166 gai_strerror(error)); 167 goto err; 168 } 169 if (h == NULL) { 170 struct sockaddr_in *sin = (struct sockaddr_in *)res->ai_addr; 171 sin->sin_addr.s_addr = INADDR_ANY; 172 } 173 174 s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 175 if (s == -1) { 176 SYSerror(errno); 177 ERR_asprintf_error_data("host='%s'", host); 178 BIOerror(BIO_R_UNABLE_TO_CREATE_SOCKET); 179 goto err; 180 } 181 if (bind_mode == BIO_BIND_REUSEADDR) { 182 int i = 1; 183 184 ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); 185 bind_mode = BIO_BIND_NORMAL; 186 } 187 if (bind(s, res->ai_addr, res->ai_addrlen) == -1) { 188 SYSerror(errno); 189 ERR_asprintf_error_data("host='%s'", host); 190 BIOerror(BIO_R_UNABLE_TO_BIND_SOCKET); 191 goto err; 192 } 193 if (listen(s, SOMAXCONN) == -1) { 194 SYSerror(errno); 195 ERR_asprintf_error_data("host='%s'", host); 196 BIOerror(BIO_R_UNABLE_TO_LISTEN_SOCKET); 197 goto err; 198 } 199 ret = 1; 200 201 err: 202 free(str); 203 if (res != NULL) 204 freeaddrinfo(res); 205 if ((ret == 0) && (s != -1)) { 206 close(s); 207 s = -1; 208 } 209 return (s); 210 } 211 LCRYPTO_ALIAS(BIO_get_accept_socket); 212 213 int 214 BIO_accept(int sock, char **addr) 215 { 216 char h[NI_MAXHOST], s[NI_MAXSERV]; 217 struct sockaddr_in sin; 218 socklen_t sin_len = sizeof(sin); 219 int ret = -1; 220 221 if (addr == NULL) { 222 BIOerror(BIO_R_NULL_PARAMETER); 223 goto end; 224 } 225 ret = accept(sock, (struct sockaddr *)&sin, &sin_len); 226 if (ret == -1) { 227 if (BIO_sock_should_retry(ret)) 228 return -2; 229 SYSerror(errno); 230 BIOerror(BIO_R_ACCEPT_ERROR); 231 goto end; 232 } 233 /* XXX Crazy API. Can't be helped */ 234 if (*addr != NULL) { 235 free(*addr); 236 *addr = NULL; 237 } 238 239 if (sin.sin_family != AF_INET) 240 goto end; 241 242 if (getnameinfo((struct sockaddr *)&sin, sin_len, h, sizeof(h), 243 s, sizeof(s), NI_NUMERICHOST|NI_NUMERICSERV) != 0) 244 goto end; 245 246 if ((asprintf(addr, "%s:%s", h, s)) == -1) { 247 BIOerror(ERR_R_MALLOC_FAILURE); 248 *addr = NULL; 249 goto end; 250 } 251 end: 252 return (ret); 253 } 254 LCRYPTO_ALIAS(BIO_accept); 255 256 int 257 BIO_set_tcp_ndelay(int s, int on) 258 { 259 return (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == 0); 260 } 261 LCRYPTO_ALIAS(BIO_set_tcp_ndelay); 262