xref: /dragonfly/crypto/libressl/crypto/bio/b_sock.c (revision 3d33658b)
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