receptor.c 6.35 KB
Newer Older
1
/*
Bernd Zeimetz's avatar
Bernd Zeimetz committed
2
 * Copyright 2013-2018 Fabian Groffen
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>

#include "relay.h"
Bernd Zeimetz's avatar
Bernd Zeimetz committed
31
#include "router.h"
32

Bernd Zeimetz's avatar
Bernd Zeimetz committed
33 34 35
#ifdef HAVE_SSL
#include <openssl/ssl.h>
#include <openssl/err.h>
36 37 38
#endif


Bernd Zeimetz's avatar
Bernd Zeimetz committed
39 40
static char
bindlistenip(listener *lsnr, unsigned int backlog)
41 42 43 44
{
	int sock;
	int optval;
	struct timeval tv;
Bernd Zeimetz's avatar
Bernd Zeimetz committed
45
	struct addrinfo *resw;
46
	char saddr[INET6_ADDRSTRLEN];
Bernd Zeimetz's avatar
Bernd Zeimetz committed
47
	int binderr = 0;
Bernd Zeimetz's avatar
Bernd Zeimetz committed
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
	int sockcur = 0;

#ifdef HAVE_SSL
	if (lsnr->transport == W_SSL) {
		/* create a auto-negotiate context */
		const SSL_METHOD *m = SSLv23_server_method();
		lsnr->ctx = SSL_CTX_new(m);
		if (lsnr->ctx == NULL) {
			char *err = ERR_error_string(ERR_get_error(), NULL);
			logerr("failed to create SSL context for listener on port %d: %s\n",
					lsnr->port, err);
			return 1;
		}

		/* load certificates */
		if (SSL_CTX_use_certificate_file(
					lsnr->ctx, lsnr->pemcert, SSL_FILETYPE_PEM) <= 0)
		{
			char *err = ERR_error_string(ERR_get_error(), NULL);
			SSL_CTX_free(lsnr->ctx);
			logerr("cannot load certificate from %s: %s\n", lsnr->pemcert, err);
			return 1;
		}
		if (SSL_CTX_use_PrivateKey_file(
					lsnr->ctx, lsnr->pemcert, SSL_FILETYPE_PEM) <= 0)
		{
			char *err = ERR_error_string(ERR_get_error(), NULL);
			SSL_CTX_free(lsnr->ctx);
			logerr("cannot load private key from %s: %s\n", lsnr->pemcert, err);
			return 1;
		}
		if (!SSL_CTX_check_private_key(lsnr->ctx)) {
			char *err = ERR_error_string(ERR_get_error(), NULL);
			SSL_CTX_free(lsnr->ctx);
			logerr("private key does not match public certificate in %s: %s\n",
					lsnr->pemcert, err);
			return 1;
		}
	}
#endif
88 89 90 91

	tv.tv_sec = 0;
	tv.tv_usec = 500 * 1000;

Bernd Zeimetz's avatar
Bernd Zeimetz committed
92 93 94 95 96 97
	for (resw = lsnr->saddrs; resw != NULL; resw = resw->ai_next) {
		if (resw->ai_family != PF_INET && resw->ai_family != PF_INET6)
			continue;
		if (resw->ai_protocol != IPPROTO_TCP &&
				resw->ai_protocol != IPPROTO_UDP)
			continue;
98

Bernd Zeimetz's avatar
Bernd Zeimetz committed
99 100 101 102 103 104 105 106 107 108 109 110
		saddr_ntop(resw, saddr);
		if (saddr[0] == '\0')
			snprintf(saddr, sizeof(saddr), "(unknown)");

		if ((sock = socket(resw->ai_family, resw->ai_socktype,
						resw->ai_protocol)) < 0)
		{
			if (errno == EAFNOSUPPORT &&
					(sockcur > 0 || resw->ai_next != NULL))
			{
				/* ignore "address family not supported by protocol" for
				 * systems with ipv6 disabled, issue #296 */
111
				continue;
Bernd Zeimetz's avatar
Bernd Zeimetz committed
112
			}
Bernd Zeimetz's avatar
Bernd Zeimetz committed
113 114 115 116 117 118 119 120 121
			logerr("failed to create socket for %s%s%s: %s\n",
					resw->ai_family == PF_INET ? "[" : "",
					saddr,
					resw->ai_family == PF_INET ? "]" : "",
					strerror(errno));
			binderr = 1;
			break;
		}
		lsnr->socks[sockcur++] = sock;
122

Bernd Zeimetz's avatar
Bernd Zeimetz committed
123 124 125 126 127 128 129 130 131
		(void) setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
		optval = 1;  /* allow takeover */
		(void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
				&optval, sizeof(optval));
		if (resw->ai_family == PF_INET6) {
			optval = 1;
			(void) setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
					&optval, sizeof(optval));
		}
132

Bernd Zeimetz's avatar
Bernd Zeimetz committed
133 134 135 136 137 138 139 140
		if (bind(sock, resw->ai_addr, resw->ai_addrlen) < 0) {
			logerr("failed to bind on %s%d %s port %u: %s\n",
					resw->ai_protocol == IPPROTO_TCP ? "tcp" : "udp",
					resw->ai_family == PF_INET6 ? 6 : 4,
					saddr, lsnr->port, strerror(errno));
			binderr = 1;
			break;
		}
141

Bernd Zeimetz's avatar
Bernd Zeimetz committed
142 143 144 145 146
		if (resw->ai_protocol == IPPROTO_TCP) {
			if (listen(sock, backlog) < 0) {
				logerr("failed to listen on tcp%d %s port %u: %s\n",
						resw->ai_family == PF_INET6 ? 6 : 4,
						saddr, lsnr->port, strerror(errno));
Bernd Zeimetz's avatar
Bernd Zeimetz committed
147 148 149
				binderr = 1;
				break;
			}
Bernd Zeimetz's avatar
Bernd Zeimetz committed
150 151 152 153 154
			logout("listening on tcp%d %s port %u\n",
					resw->ai_family == PF_INET6 ? 6 : 4, saddr, lsnr->port);
		} else {
			logout("listening on udp%d %s port %u\n",
					resw->ai_family == PF_INET6 ? 6 : 4, saddr, lsnr->port);
155 156
		}
	}
Bernd Zeimetz's avatar
Bernd Zeimetz committed
157 158 159 160 161 162 163
	if (binderr != 0) {
		/* close all opened sockets */
		for (--sockcur; sockcur >= 0; sockcur--)
			close(lsnr->socks[sockcur]);
		return 1;
	}
	lsnr->socks[sockcur] = -1;
164

Bernd Zeimetz's avatar
Bernd Zeimetz committed
165 166
	return 0;
}
167

Bernd Zeimetz's avatar
Bernd Zeimetz committed
168 169 170 171 172
static char
bindlistenunix(listener *lsnr, unsigned int backlog)
{
	struct sockaddr_un srvr;
	int sock;
173 174 175 176

#ifndef PF_LOCAL
# define PF_LOCAL PF_UNIX
#endif
Bernd Zeimetz's avatar
Bernd Zeimetz committed
177 178 179 180 181 182 183
	if ((sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
		logerr("failed to create socket for %s: %s\n",
				lsnr->ip, strerror(errno));
		return 1;
	}
	lsnr->socks[0] = sock;
	lsnr->socks[1] = -1;
184

Bernd Zeimetz's avatar
Bernd Zeimetz committed
185 186 187
	memset(&srvr, 0, sizeof(struct sockaddr_un));
	srvr.sun_family = PF_LOCAL;
	strncpy(srvr.sun_path, lsnr->ip, sizeof(srvr.sun_path) - 1);
188

Bernd Zeimetz's avatar
Bernd Zeimetz committed
189 190 191 192 193 194 195 196
	unlink(lsnr->ip);  /* avoid address already in use */
	if (bind(sock, (struct sockaddr *)&srvr,
				sizeof(struct sockaddr_un)) < 0)
	{
		logerr("failed to bind for %s: %s\n", lsnr->ip, strerror(errno));
		close(sock);
		return 1;
	}
197

Bernd Zeimetz's avatar
Bernd Zeimetz committed
198 199 200 201 202
	if (listen(sock, backlog) < 0) {
		logerr("failed to listen on %s: %s\n", lsnr->ip, strerror(errno));
		close(sock);
		return 1;
	}
203

Bernd Zeimetz's avatar
Bernd Zeimetz committed
204 205 206 207
	logout("listening on UNIX socket %s\n", lsnr->ip);

	return 0;
}
208

Bernd Zeimetz's avatar
Bernd Zeimetz committed
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
/**
 * Open up sockets associated with listener.  Returns 0 when opening up
 * the listener succeeded, 1 otherwise.
 */
char
bindlisten(listener *lsnr, unsigned int backlog)
{
	switch (lsnr->ctype) {
		case CON_TCP:
		case CON_UDP:
			return bindlistenip(lsnr, backlog);
		case CON_UNIX:
			return bindlistenunix(lsnr, backlog);
		default:
			logerr("unsupported listener type");
			return 1;
225
	}
Bernd Zeimetz's avatar
Bernd Zeimetz committed
226
}
227

Bernd Zeimetz's avatar
Bernd Zeimetz committed
228 229 230 231 232 233 234 235 236
static void
close_socks(listener *lsnr)
{
	int i;
	for (i = 0; lsnr->socks[i] != -1; i++)
		close(lsnr->socks[i]);
	logout("closed listener for %s %s:%u\n",
			lsnr->ctype == CON_UDP ? "udp" : "tcp",
			lsnr->ip == NULL ? "" : lsnr->ip, lsnr->port);
237 238
}

Bernd Zeimetz's avatar
Bernd Zeimetz committed
239 240
static void
destroy_usock(listener *lsnr)
241
{
Bernd Zeimetz's avatar
Bernd Zeimetz committed
242 243 244 245
	close(lsnr->socks[0]);
	unlink(lsnr->ip);
	logout("removed listener for %s\n", lsnr->ip);
}
246

Bernd Zeimetz's avatar
Bernd Zeimetz committed
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
void
shutdownclose(listener *lsnr)
{
	switch (lsnr->ctype) {
		case CON_TCP:
		case CON_UDP:
			close_socks(lsnr);
			break;
		case CON_UNIX:
			destroy_usock(lsnr);
			break;
		default:
			logerr("unsupported listener type");
			break;
	}
262
}