1 /* $OpenBSD: b_sock.c,v 1.69 2018/02/07 00:52:05 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 /* A string without a colon is treated as a port. */ 138 p = str; 139 h = NULL; 140 } else { 141 *p++ = '\0'; 142 if (*p == '\0') { 143 BIOerror(BIO_R_NO_PORT_SPECIFIED); 144 goto err; 145 } 146 if (*h == '\0' || strcmp(h, "*") == 0) 147 h = NULL; 148 } 149 150 if ((error = getaddrinfo(h, p, &hints, &res)) != 0) { 151 ERR_asprintf_error_data("getaddrinfo: '%s:%s': %s'", h, p, 152 gai_strerror(error)); 153 goto err; 154 } 155 if (h == NULL) { 156 struct sockaddr_in *sin = (struct sockaddr_in *)res->ai_addr; 157 sin->sin_addr.s_addr = INADDR_ANY; 158 } 159 160 s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 161 if (s == -1) { 162 SYSerror(errno); 163 ERR_asprintf_error_data("host='%s'", host); 164 BIOerror(BIO_R_UNABLE_TO_CREATE_SOCKET); 165 goto err; 166 } 167 if (bind_mode == BIO_BIND_REUSEADDR) { 168 int i = 1; 169 170 ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); 171 bind_mode = BIO_BIND_NORMAL; 172 } 173 if (bind(s, res->ai_addr, res->ai_addrlen) == -1) { 174 SYSerror(errno); 175 ERR_asprintf_error_data("host='%s'", host); 176 BIOerror(BIO_R_UNABLE_TO_BIND_SOCKET); 177 goto err; 178 } 179 if (listen(s, SOMAXCONN) == -1) { 180 SYSerror(errno); 181 ERR_asprintf_error_data("host='%s'", host); 182 BIOerror(BIO_R_UNABLE_TO_LISTEN_SOCKET); 183 goto err; 184 } 185 ret = 1; 186 187 err: 188 free(str); 189 if (res != NULL) 190 freeaddrinfo(res); 191 if ((ret == 0) && (s != -1)) { 192 close(s); 193 s = -1; 194 } 195 return (s); 196 } 197 198 int 199 BIO_accept(int sock, char **addr) 200 { 201 char h[NI_MAXHOST], s[NI_MAXSERV]; 202 struct sockaddr_in sin; 203 socklen_t sin_len = sizeof(sin); 204 int ret = -1; 205 206 if (addr == NULL) 207 goto end; 208 209 ret = accept(sock, (struct sockaddr *)&sin, &sin_len); 210 if (ret == -1) { 211 if (BIO_sock_should_retry(ret)) 212 return -2; 213 SYSerror(errno); 214 BIOerror(BIO_R_ACCEPT_ERROR); 215 goto end; 216 } 217 /* XXX Crazy API. Can't be helped */ 218 if (*addr != NULL) { 219 free(*addr); 220 *addr = NULL; 221 } 222 223 if (sin.sin_family != AF_INET) 224 goto end; 225 226 if (getnameinfo((struct sockaddr *)&sin, sin_len, h, sizeof(h), 227 s, sizeof(s), NI_NUMERICHOST|NI_NUMERICSERV) != 0) 228 goto end; 229 230 if ((asprintf(addr, "%s:%s", h, s)) == -1) { 231 BIOerror(ERR_R_MALLOC_FAILURE); 232 *addr = NULL; 233 goto end; 234 } 235 end: 236 return (ret); 237 } 238 239 int 240 BIO_set_tcp_ndelay(int s, int on) 241 { 242 return (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == 0); 243 } 244