Skip to content
Permalink
ecb8bc7a95
Switch branches/tags

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?
Go to file
 
 
Cannot retrieve contributors at this time
270 lines (243 sloc) 8.12 KB
#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); }
}
}