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/mxshadowsrv.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
270 lines (243 sloc)
8.12 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
#define _GNU_SOURCE | |
#include <sys/mman.h> | |
#include <sys/stat.h> | |
#include <fcntl.h> | |
#include <netinet/ip.h> | |
#include <arpa/inet.h> | |
#include <string.h> | |
#include <getopt.h> | |
#include <pthread.h> | |
#include "common.h" | |
static int statbuf_file_changed(struct stat *b1, struct stat *b2) { | |
if ( b1->st_dev == b2->st_dev | |
&& b1->st_ino == b2->st_ino | |
&& b1->st_size == b2->st_size | |
&& b1->st_mtim.tv_sec == b2->st_mtim.tv_sec | |
&& b1->st_mtim.tv_nsec == b2->st_mtim.tv_nsec ) | |
return 0; | |
else | |
return 1; | |
} | |
static char *map_shadow(char *filename, struct stat *statbufptr) { | |
int fd; | |
while (1) { | |
while (1) { | |
fd = open(filename, O_RDONLY); | |
if (fd != -1) | |
break; | |
if (errno != ENOENT) { | |
perror(filename); | |
exit(1); | |
} | |
sleep(1); | |
} | |
int status = fstat(fd, statbufptr); | |
if (status == -1) { perror(filename); exit(1); } | |
if (statbufptr->st_size > 0) | |
break; | |
fprintf(stderr, "%s is empty\n", filename); | |
close(fd); | |
sleep (1); | |
} | |
char *shadow_buf = mmap(NULL, statbufptr->st_size, PROT_READ, MAP_SHARED, fd, 0); | |
if (shadow_buf == MAP_FAILED) { perror(filename); exit(1); } | |
close(fd); | |
return shadow_buf; | |
} | |
static int shadow_is_changed(char *filename, struct stat *statbuf) { | |
struct stat statbuf2; | |
int status = stat(filename, &statbuf2); | |
if (status == -1) | |
return 1; | |
return statbuf_file_changed(statbuf, &statbuf2); | |
} | |
static void unmap_shadow(char *shadow_buf, struct stat *statbuf) { | |
int sts = munmap(shadow_buf, statbuf->st_size); | |
if (sts == -1) { perror("munmap"); exit(1); } | |
} | |
#define THREADS 8 | |
#define TIMEOUT 30000 // client timeout in msec | |
static pthread_mutex_t shadow_mutex = PTHREAD_MUTEX_INITIALIZER ; | |
static char *shadow_buf = NULL; // protected by shadow_mutex | |
static struct stat statbuf; // protected by shadow_mutex | |
static SSL_CTX *ssl_ctx; | |
static int listen_socket; | |
static char *filename; | |
#ifdef DEBUG_MAX_CONNECTS | |
static int debug_remaining_connects = DEBUG_MAX_CONNECTS; | |
#endif | |
static void validate_shadow(char **shadow_buf, char *filename, struct stat *statbuf) { | |
int status = pthread_mutex_lock(&shadow_mutex); | |
if (status != 0) { errno = status; perror("pthread_mutex_lock"); exit(1);} | |
if (*shadow_buf == NULL) | |
*shadow_buf = map_shadow(filename, statbuf); | |
while(shadow_is_changed(filename, statbuf)) { | |
unmap_shadow(*shadow_buf, statbuf); | |
*shadow_buf = map_shadow(filename, statbuf); | |
} | |
status = pthread_mutex_unlock(&shadow_mutex); | |
if (status != 0) { errno = status; perror("pthread_mutex_unlock"); exit(1);} | |
} | |
static int find_in_shadow(char *username, int username_len, char *shadow_buf, size_t shadow_len, char **lineptr) { | |
char *s = shadow_buf; | |
char *field0; | |
int field0_len; | |
START_OF_LINE: | |
if (s == &shadow_buf[shadow_len]) | |
return 0; | |
if (*s == '\n') { | |
s++; | |
goto START_OF_LINE; | |
} | |
field0 = s; | |
s++; | |
IN_FIELD0: | |
if (s == &shadow_buf[shadow_len]) | |
return 0; | |
if (*s == '\n') { | |
s++; | |
goto START_OF_LINE; | |
} | |
if (*s == ':') { | |
field0_len = s-field0; | |
s++; | |
} else { | |
s++; | |
goto IN_FIELD0; | |
} | |
if (*s == '\n') { | |
s++; | |
goto START_OF_LINE; | |
} | |
IN_REST: | |
if (s == &shadow_buf[shadow_len]) { | |
; | |
} else if (*s == '\n') { | |
; | |
} else { | |
s++; | |
goto IN_REST; | |
} | |
if ( field0_len == username_len | |
&& memcmp(field0, username, username_len)==0 ) { | |
*lineptr = field0; | |
return s-field0; | |
} | |
goto START_OF_LINE; | |
} | |
static void die_usage(char *argv0) { | |
fprintf(stderr, "usage: %s\n" | |
" --key-file filename\n" | |
" --cert-file filename\n" | |
" [ --address addr ] [-L addr ]\n" | |
" [ --port port] [-p port]\n" | |
" shadowfile\n", | |
argv0); | |
exit(1); | |
} | |
static void process_client(int socket) { | |
SSL *ssl _cleanup_(free_ssl) = SSL_new(ssl_ctx); | |
if (ssl == NULL) { psslerror("SSL_new"); return; } | |
SSL_set_fd(ssl, socket); | |
if (ssl_accept_with_timeout(ssl, socket, TIMEOUT) <= 0) { | |
return; | |
} | |
char buf[64]; | |
int len = ssl_read_with_timeout(ssl, socket, buf, sizeof(buf), TIMEOUT); | |
if (len <= 0 ) | |
return; | |
if (len == sizeof(buf)) { | |
fprintf(stderr, "identifier to long\n"); | |
SSL_shutdown(ssl); | |
return; | |
} | |
int status = pthread_mutex_lock(&shadow_mutex); | |
if (status != 0) { errno = status; perror("pthread_mutex_lock"); exit(1);} | |
char *line; | |
int line_len; | |
line_len = find_in_shadow(buf, len, shadow_buf, statbuf.st_size, &line); | |
status = pthread_mutex_unlock(&shadow_mutex); | |
if (status != 0) { errno = status; perror("pthread_mutex_unlock"); exit(1);} | |
if (line_len) | |
ssl_write_with_timeout(ssl, socket, line, line_len, TIMEOUT); | |
SSL_shutdown(ssl); | |
} | |
static void *client_thread(void *arg) { | |
while (1) { | |
#ifdef DEBUG_MAX_CONNECTS | |
if ( __atomic_fetch_sub(&debug_remaining_connects, 1, __ATOMIC_RELAXED) <= 0) | |
return NULL; | |
#endif | |
int _cleanup_(free_fd) socket = accept4(listen_socket, NULL, NULL, SOCK_NONBLOCK); | |
if (socket == -1 ) { perror("accept"); exit(1); } | |
validate_shadow(&shadow_buf, filename, &statbuf); | |
process_client(socket); | |
} | |
return NULL; | |
} | |
int main(int argc, char **argv) { | |
char *key_file = NULL; | |
char *cert_file = NULL; | |
struct in_addr listen_address = {.s_addr = INADDR_ANY}; | |
int listen_port = 872; | |
struct option options[] = { | |
{ "key-file", required_argument, 0, 1 }, | |
{ "cert-file", required_argument, 0, 2 }, | |
{ "address", required_argument, 0, 'L' }, | |
{ "port", required_argument, 0, 'p' }, | |
{ 0,0,0,0 } | |
}; | |
while (1) { | |
int c = getopt_long(argc, argv, "L:p:", options, NULL); | |
if (c == -1) | |
break; | |
switch (c) { | |
case 1: | |
key_file = optarg; | |
break; | |
case 2: | |
cert_file = optarg; | |
break; | |
case 'L': | |
if (inet_aton(optarg, &listen_address) == 0) | |
die_usage(argv[0]); | |
break; | |
case 'p': | |
listen_port=atoi(optarg); | |
break; | |
} | |
} | |
if (key_file == NULL || cert_file == NULL) | |
die_usage(argv[0]); | |
if (optind+1 != argc) | |
die_usage(argv[0]); | |
filename = argv[optind++]; | |
SSL_CTX *_ssl_ctx _cleanup_(free_ssl_ctx) = SSL_CTX_new(TLS_server_method()); | |
if (_ssl_ctx == NULL) { psslerror("SSL_CTX_new"); return 1; } | |
ssl_ctx = _ssl_ctx; | |
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) <= 0 ) { psslerror("SSL_CTX_use_PrivateKey_file"); return 1; } | |
if (SSL_CTX_use_certificate_file(ssl_ctx, cert_file, SSL_FILETYPE_PEM) <= 0) { psslerror("SSL_CTX_use_certificate_file"); return 1; } | |
listen_socket = socket(AF_INET, SOCK_STREAM, 0); | |
if (listen_socket == -1) { perror("socket"); return 1; } | |
static int true = 1; | |
int status = setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &true, sizeof(true)); | |
if (status == -1) { perror("setsockopt"); exit(1); } | |
struct sockaddr_in sockaddr; | |
bzero(&sockaddr, sizeof(sockaddr)); | |
sockaddr.sin_family =AF_INET, | |
sockaddr.sin_port = htons(listen_port), | |
sockaddr.sin_addr = listen_address; | |
status = bind(listen_socket, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); | |
if (status == -1) { perror("bind"); exit(1); } | |
status = listen(listen_socket, 40); | |
if (status == -1) { perror("listen"); exit(1); } | |
pthread_t thread[THREADS]; | |
for (int i=0; i<THREADS; i++) { | |
int status = pthread_create(&thread[i], NULL, client_thread, NULL); | |
if (status != 0) { errno = status; perror("pthread_create"); exit(1); } | |
} | |
for (int i=0; i<THREADS; i++) { | |
int status = pthread_join(thread[i], NULL); | |
if (status != 0) { errno = status; perror("pthread_join"); exit(1); } | |
} | |
} |