Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
mxshadow/common.h
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
191 lines (176 sloc)
6.06 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _COMMON_H | |
#define _COMMON_H | |
#include <openssl/ssl.h> | |
#include <openssl/err.h> | |
#include <poll.h> | |
#include <unistd.h> | |
#define _cleanup_(x) __attribute__((cleanup(x))) | |
#ifndef COMMON_LOG | |
#include <stdio.h> | |
#define COMMON_LOG(prio, msg, ...) (fprintf(stderr, msg "\n", ## __VA_ARGS__ )) | |
#endif | |
static void __attribute__((unused)) free_ssl_ctx(SSL_CTX **ctxp) { | |
if (*ctxp) { | |
SSL_CTX_free(*ctxp); | |
} | |
} | |
static void __attribute__((unused)) free_ssl(SSL **sslp) { | |
if (*sslp) { | |
SSL_free(*sslp); | |
} | |
} | |
static void __attribute__((unused)) free_fd(int *fdp) { | |
if (*fdp != -1) { | |
close(*fdp); | |
} | |
} | |
static void __attribute__((unused)) free_file(FILE **filep) { | |
if (*filep != NULL) { | |
fclose(*filep); | |
} | |
} | |
static void __attribute__((unused)) free_string(char **ptr) { | |
if (*ptr != NULL) { | |
free(*ptr); | |
} | |
} | |
static void __attribute__((unused)) psslerror(char *str) { | |
if (str != NULL && strcmp(str, "") != 0) | |
COMMON_LOG(LOG_ERR, "%s:", str); | |
unsigned long ssl_err; | |
while ((ssl_err = ERR_get_error())) { | |
COMMON_LOG(LOG_ERR, "ssl error: %lud:%s:%s:%s", | |
ssl_err, | |
ERR_lib_error_string(ssl_err), | |
ERR_func_error_string(ssl_err), | |
ERR_reason_error_string(ssl_err)); | |
} | |
} | |
static int wait_rd_with_timeout(int fd, int timeout) { | |
struct pollfd pollfd = { .fd = fd, .events = POLLIN }; | |
int status = poll(&pollfd, 1, timeout); | |
if (status == -1) | |
return -1; | |
if (status == 0) { | |
errno = ETIMEDOUT; | |
return -1; | |
} | |
return 0; | |
} | |
static char *ssl_err(int e) { | |
switch(e) { | |
case SSL_ERROR_NONE: return("SSL_ERROR_NONE"); | |
case SSL_ERROR_ZERO_RETURN: return("SSL_ERROR_ZERO_RETURN"); | |
case SSL_ERROR_WANT_READ: return("SSL_ERROR_WANT_READ"); | |
case SSL_ERROR_WANT_WRITE: return("SSL_ERROR_WANT_WRITE"); | |
case SSL_ERROR_WANT_CONNECT: return("SSL_ERROR_WANT_CONNECT"); | |
case SSL_ERROR_WANT_ACCEPT: return("SSL_ERROR_WANT_ACCEPT"); | |
case SSL_ERROR_WANT_X509_LOOKUP: return("SSL_ERROR_WANT_X509_LOOKUP"); | |
case SSL_ERROR_WANT_ASYNC: return("SSL_ERROR_WANT_ASYNC"); | |
case SSL_ERROR_WANT_ASYNC_JOB: return("SSL_ERROR_WANT_ASYNC_JOB"); | |
case SSL_ERROR_WANT_CLIENT_HELLO_CB: return("SSL_ERROR_WANT_CLIENT_HELLO_CB"); | |
case SSL_ERROR_SYSCALL: return("SSL_ERROR_SYSCALL"); | |
case SSL_ERROR_SSL: return("SSL_ERROR_SSL"); | |
} | |
return"?"; | |
} | |
static int __attribute__((unused)) ssl_write_with_timeout(SSL *ssl, int fd, char *data, size_t datalen, int timeout) { | |
while (1) { | |
int status = SSL_write(ssl, data, datalen); | |
if (status > 0) { | |
if (status == datalen) | |
return 0; | |
COMMON_LOG(LOG_ERR, "%s: unexpected partial write. requested %lud bytes, returned: %d", __func__, datalen, status); | |
errno = EPIPE; | |
return -1; | |
} | |
int ssl_error = SSL_get_error(ssl, status); | |
switch (ssl_error) { | |
case SSL_ERROR_WANT_READ: | |
status = wait_rd_with_timeout(fd, timeout); | |
if (status == -1) { | |
COMMON_LOG(LOG_ERR, "%s: %m", __func__); | |
return -1; | |
} | |
continue; | |
case SSL_ERROR_SYSCALL: | |
COMMON_LOG(LOG_ERR, "%s: %m", __func__); | |
return -1; | |
case SSL_ERROR_SSL: | |
psslerror(""); | |
errno = EPROTO; | |
return -1; | |
default: | |
COMMON_LOG(LOG_ERR, "%s: %s unimplemented", __func__, ssl_err(ssl_error)); | |
errno = ENOSYS; | |
return -1; | |
} | |
} | |
} | |
static int __attribute__((unused)) ssl_read_with_timeout(SSL *ssl, int fd, void *buf, size_t num, int timeout){ | |
errno = 0; /* see commit message */ | |
while (1) { | |
int status = SSL_read(ssl, buf, num); | |
if (status > 0) | |
return status; | |
int ssl_error = SSL_get_error(ssl, status); | |
switch (ssl_error) { | |
case SSL_ERROR_WANT_READ: | |
status = wait_rd_with_timeout(fd, timeout); | |
if (status == -1) { | |
COMMON_LOG(LOG_ERR, "%s: %m", __func__); | |
return -1; | |
} | |
continue; | |
case SSL_ERROR_SYSCALL: | |
if (errno == 0) { | |
COMMON_LOG(LOG_ERR, "%s: unexpected EOF from peer", __func__); | |
errno = ECONNABORTED; | |
return -1; | |
} | |
COMMON_LOG(LOG_ERR, "%s: %m", __func__); | |
return -1; | |
case SSL_ERROR_ZERO_RETURN: | |
return 0; | |
default: | |
COMMON_LOG(LOG_ERR, "%s: %s unimplemented", __func__, ssl_err(ssl_error)); | |
errno = ENOSYS; | |
return -1; | |
} | |
} | |
} | |
static int __attribute__((unused)) ssl_accept_with_timeout(SSL *ssl, int fd, int timeout) { | |
errno = 0; /* see commit message */ | |
while (1) { | |
int status = SSL_accept(ssl); | |
if (status == 1) | |
return 1; | |
int ssl_error = SSL_get_error(ssl, status); | |
switch (ssl_error) { | |
case SSL_ERROR_WANT_READ: | |
status = wait_rd_with_timeout(fd, timeout); | |
if (status == -1) { | |
COMMON_LOG(LOG_ERR, "%s: %m", __func__); | |
return -1; | |
} | |
continue; | |
case SSL_ERROR_SYSCALL: | |
if (errno == 0) { | |
COMMON_LOG(LOG_ERR, "%s: unexpected EOF from peer", __func__); | |
errno = ECONNABORTED; | |
return -1; | |
} | |
COMMON_LOG(LOG_ERR, "%s: %m", __func__); | |
return -1; | |
case SSL_ERROR_SSL: | |
psslerror(""); | |
errno = EPROTO; | |
return -1; | |
default: | |
COMMON_LOG(LOG_ERR, "%s: %s unimplemented", __func__, ssl_err(ssl_error)); | |
errno = ENOSYS; | |
return -1; | |
} | |
} | |
} | |
#endif /* _COMMON_H */ |