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