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
BIO_get_host_ip(const char * str,unsigned char * ip)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
BIO_get_port(const char * str,unsigned short * port_ptr)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
BIO_sock_error(int sock)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 *
BIO_gethostbyname(const char * name)109 BIO_gethostbyname(const char *name)
110 {
111 return gethostbyname(name);
112 }
113 LCRYPTO_ALIAS(BIO_gethostbyname);
114
115 int
BIO_socket_ioctl(int fd,long type,void * arg)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
BIO_get_accept_socket(char * host,int bind_mode)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
BIO_accept(int sock,char ** addr)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
BIO_set_tcp_ndelay(int s,int on)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