xref: /openbsd-src/lib/libcrypto/bio/b_sock.c (revision dcb03dac24b6a52fa9b433eceeaac7ff6f5182e3)
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