#include #include #include #include #include #include #include #include #include #include #include #include #include #include "dir.h" #include "vlink.h" #include "ht.h" #include "acl.h" #include "sig.h" #include "server.h" static SRVConfig srv_config; static int proc_forked = 0; static pthread_t rcvr_thd[MAX_THREADS]; static pthread_mutex_t dir_sync_mutex; static pthread_cond_t dir_sync_cond; static int dir_sync_res = 0; static FILE *log_file = NULL; int log_level; static const char *log_level_str[] = { "ERROR", "INFO", "DEBUG" }; SRVConfig *srv_get_config(void) { return &srv_config; } static void handle_err(ECPConnection *conn, unsigned char mtype, int err) { LOG(LOG_ERR, "handle_err: ctype:0x%.2x mtype:0x%.2x err:%d\n", conn->type, mtype, err); } static ssize_t conn_auth(ECPSocket *sock, ECPConnection *parent, unsigned char ctype, ecp_ecdh_public_t *public, unsigned char *msg, size_t msg_size) { switch (ctype) { case CTYPE_DIR: { if (public == NULL) return ECP_ERR_AUTH; if (!acl_inlist(public)) return ECP_ERR_AUTH; return 0; } case ECP_CTYPE_VLINK: { if (public == NULL) return ECP_ERR_AUTH; if ((parent == NULL) && !acl_inlist(public)) return ECP_ERR_AUTH; return 0; } case ECP_CTYPE_DIR: case ECP_CTYPE_VCONN: return 0; default: return ECP_ERR_AUTH; } } static ECPConnection *conn_new(ECPSocket *sock, ECPConnection *parent, unsigned char ctype) { ECPConnection *conn = NULL; switch (ctype) { case CTYPE_DIR: { if (!(srv_config.roles & ECP_ROLE_DIR)) return NULL; conn = malloc(sizeof(ECPConnection)); if (conn) { ecp_conn_init(conn, sock, ctype); ecp_conn_set_flags(conn, ECP_CONN_FLAG_VBOX); } break; } case ECP_CTYPE_DIR: { if (!(srv_config.roles & ECP_ROLE_DIR)) return NULL; conn = malloc(sizeof(ECPConnection)); if (conn) ecp_conn_init(conn, sock, ctype); break; } case ECP_CTYPE_VCONN: { ECPVConnInb *_conn; if (!(srv_config.roles & ECP_ROLE_VCONN)) return NULL; _conn = malloc(sizeof(ECPVConnInb)); if (_conn) { ecp_vconn_init_inb(_conn, sock); conn = &_conn->b; } break; } case ECP_CTYPE_VLINK: { if (!(srv_config.roles & ECP_ROLE_VCONN)) return NULL; conn = malloc(sizeof(ECPConnection)); if (conn) ecp_vlink_init(conn, sock); break; } } if (conn) ecp_conn_set_flags(conn, ECP_CONN_FLAG_GC); return conn; } static void conn_free(ECPConnection *conn) { free(conn); } static int core_logger(const char *format, ...) { va_list ap; va_start(ap, format); log_vprintf(LOG_ERR, format, ap); va_end(ap); return 0; } int ecp_init(ECPContext *ctx, ECPConnHandler *vconn_handler, ECPConnHandler *vlink_handler) { int rv; rv = ecp_ctx_init(ctx, conn_auth, conn_new, conn_free, handle_err, core_logger); if (rv) return rv; rv = ecp_vconn_handler_init(ctx, vconn_handler); if (rv) return rv; rv = ecp_vlink_handler_init(ctx, vlink_handler); if (rv) return rv; return ECP_OK; } void log_vprintf(int level, const char *format, va_list ap) { FILE *file; if (level > log_level) return; if (level >= (sizeof(log_level_str) / sizeof(char *))) return; file = srv_config.log_file ? srv_config.log_file : stderr; if (proc_forked) { time_t t; char t_buf[26]; char s_buf[256]; int rv; t = time(NULL); ctime_r(&t, t_buf); /* terminate string before newline */ t_buf[24] = '\0'; rv = vsnprintf(s_buf, sizeof(s_buf), format, ap); /* always print newline at the and of string */ if (rv <= 0) return; if (rv >= (sizeof(s_buf) - 1)) { s_buf[sizeof(s_buf) - 2] = '\n'; } else if (s_buf[rv - 1] != '\n') { s_buf[rv] = '\n'; s_buf[rv + 1] = '\0'; } fprintf(file, "%s [%s]: %s", t_buf, log_level_str[level], s_buf); } else { vfprintf(file, format, ap); } fflush(file); } void log_printf(int level, const char *format, ...) { va_list ap; if (level > log_level) return; va_start(ap, format); log_vprintf(level, format, ap); va_end(ap); } static void fail(const char *format, ...) { va_list ap; va_start(ap, format); log_vprintf(LOG_ERR, format, ap); va_end(ap); exit(1); } static void usage(char *arg) { fprintf(stderr, "Usage: %s [options] [ ] [ ]\n", arg); fprintf(stderr, "Options:\n"); fprintf(stderr, "\t-d\n"); fprintf(stderr, "\t\tdetach\n"); fprintf(stderr, "\t-t \n"); fprintf(stderr, "\t\tstart receiver threads (maximum: %d)\n", MAX_THREADS); fprintf(stderr, "\t-l \n"); fprintf(stderr, "\t\tset log level: 0 - error, 1 - info, 2 - debug\n"); fprintf(stderr, "\t-f \n"); fprintf(stderr, "\t\twrite log to \n"); fprintf(stderr, "\t-u \n"); fprintf(stderr, "\t\trun as user \n"); fprintf(stderr, "\t-g \n"); fprintf(stderr, "\t\trun as group \n"); exit(1); } static void daemonize(void) { pid_t pid; int null_rd, null_wr; int rv; pid = fork(); if (pid < 0) fail("fork failed\n"); if (pid > 0) exit(0); proc_forked = 1; pid = setsid(); if (pid < 0) fail("setsid failed\n"); /* second fork */ pid = fork(); if (pid < 0) fail("fork2 failed\n"); if (pid > 0) exit(0); /* redirect /dev/null to stdin */ null_rd = open("/dev/null", O_RDONLY); if (null_rd < 0) fail("open (r) /dev/null failed\n"); rv = dup2(null_rd, STDIN_FILENO); if (rv < 0) fail("dup2 (stdin) failed\n"); close(null_rd); /* redirect stdout and stderr to srv_config.log_file or /dev/null */ if (srv_config.log_file) { null_wr = fileno(srv_config.log_file); } else { null_wr = open("/dev/null", O_WRONLY); if (null_wr < 0) fail("open (w) /dev/null failed\n"); } rv = dup2(null_wr, STDOUT_FILENO); if (rv < 0) fail("dup2 (stdout) failed\n"); rv = dup2(null_wr, STDERR_FILENO); if (rv < 0) fail("dup2 (stderr) failed\n"); if (srv_config.log_file == NULL) close(null_wr); } static void *_ecp_receiver(void *arg) { ecp_receiver((ECPSocket *)arg); return NULL; } static int start_receivers(ECPSocket *sock) { int i, rv; for (i=0; icount; i++) { if (list->items[i].roles & ECP_ROLE_DIR) dir_cnt++; } rv = dir_init_dir_cnt(dir_cnt, list->serial); if (rv) fail("Bad timing\n"); for (i=0; icount; i++) { dir_process_item(&list->items[i], sock, NULL); } ecp_dir_list_destroy(list); pthread_mutex_lock(&dir_sync_mutex); dir_sync_res = 1; pthread_cond_signal(&dir_sync_cond); pthread_mutex_unlock(&dir_sync_mutex); } int main(int argc, char *argv[]) { char *endptr; char *init_switch; ecp_ecdh_public_t remote_pub; ecp_tr_addr_t remote_addr; ECPContext ctx; ECPSocket sock; ECPConnection dir_conn; ECPConnHandler vconn_handler; ECPConnHandler vlink_handler; ECPConnHandler dir_handler; int _argi, _argc, remote, fd; int rv; memset(&srv_config, 0, sizeof(srv_config)); if (argc < 1) fail("Bad argument count\n"); _argc = argc - 1; _argi = 1; srv_config.rcvr_thd_num = 1; while (_argc && (argv[_argi][0] == '-')) { switch (argv[_argi][1]) { case 'd': { _argi++; _argc--; srv_config.detach = 1; break; } case 't': { _argi++; _argc--; if (_argc == 0) usage(argv[0]); srv_config.rcvr_thd_num = (unsigned int)strtol(argv[_argi], &endptr, 10); if (endptr[0] != '\0') fail("Bad number of receiver threads: %s\n", argv[_argi]); if (srv_config.rcvr_thd_num > MAX_THREADS) fail ("Maximum number of threads allowed: %d\n", MAX_THREADS); _argi++; _argc--; break; } case 'l': { _argi++; _argc--; if (_argc == 0) usage(argv[0]); log_level = (uint8_t)strtol(argv[_argi], &endptr, 10); if (endptr[0] != '\0') fail("Bad log level: %s\n", argv[_argi]); if (log_level > LOG_MAX_LEVEL) fail("Maximum log level allowed: %d\n", LOG_MAX_LEVEL); _argi++; _argc--; break; } case 'f': { _argi++; _argc--; if (_argc == 0) usage(argv[0]); srv_config.log_fn = strdup(argv[_argi]); _argi++; _argc--; break; } case 'u': { _argi++; _argc--; if (_argc == 0) usage(argv[0]); srv_config.uid = (uid_t)strtol(argv[_argi], &endptr, 10); if (endptr[0] != '\0') fail("Bad uid: %s\n", argv[_argi]); _argi++; _argc--; break; } case 'g': { _argi++; _argc--; if (_argc == 0) usage(argv[0]); srv_config.gid = (gid_t)strtol(argv[_argi], &endptr, 10); if (endptr[0] != '\0') fail("Bad gid: %s\n", argv[_argi]); _argi++; _argc--; break; } default: usage(argv[0]); } } if (_argc < 4) usage(argv[0]); srv_config.region = (uint8_t)strtol(argv[_argi], &endptr, 16); if (endptr[0] != '\0') fail("Bad region: %s\n", argv[_argi]); if (srv_config.region >= MAX_REGION) fail("Bad region: %d\n", srv_config.region); _argi++; _argc--; srv_config.roles = (uint8_t)strtol(argv[_argi], &endptr, 16); if (endptr[0] != '\0') fail("Bad roles: %s\n", argv[_argi]); _argi++; _argc--; fd = open(argv[_argi], O_RDONLY); if (fd < 0) fail("Unable to open key file: %s\n", argv[_argi]); rv = ecp_util_read_key(fd, &srv_config.key_perma.public, &srv_config.key_perma.private, NULL); close(fd); if (rv) fail("Bad key file: %s\n", argv[_argi]); srv_config.key_perma.valid = 1; _argi++; _argc--; rv = ecp_addr_init(&srv_config.my_addr, argv[_argi]); if (rv) fail("Bad local address: %s\n", argv[_argi]); _argi++; _argc--; rv = acl_init(); if (rv) fail("acl_init err:%d\n", rv); if (srv_config.roles & ECP_ROLE_DIR) { if (_argc < 2) usage(argv[0]); srv_config.acl_fn_dir = strdup(argv[_argi]); _argi++; _argc--; srv_config.acl_fn = strdup(argv[_argi]); _argi++; _argc--; rv = acl_load(); if (rv) fail("ACL load failed\n"); } remote = 0; if (_argc == 2) { remote = 1; fd = open(argv[_argi], O_RDONLY); if (fd < 0) fail("Unable to open public key file: %s\n", argv[_argi]); rv = ecp_util_read_key(fd, &remote_pub, NULL, NULL); close(fd); if (rv) fail("Bad public key file: %s\n", argv[_argi]); _argi++; _argc--; rv = ecp_addr_init(&remote_addr, argv[_argi]); if (rv) fail("Bad remote address: %s\n", argv[_argi]); _argi++; _argc--; } else if (_argc) { usage(argv[0]); } umask(077); /* rw for owner only */ if (srv_config.log_fn) { srv_config.log_file = fopen(srv_config.log_fn, "a"); if (srv_config.log_file == NULL) fail("Unable to open log file: %s\n", srv_config.log_fn); } if (srv_config.detach) daemonize(); rv = sig_init(); if (rv) fail("sig_init err:%d\n", rv); rv = sig_start_handler(&sock); if (rv) fail("sig_start_handler err:%d\n", rv); rv = ecp_init(&ctx, &vconn_handler, &vlink_handler); if (rv) fail("ecp_init err:%d\n", rv); rv = ecp_sock_create(&sock, &ctx, &srv_config.key_perma); if (rv) fail("ecp_sock_create err:%d\n", rv); rv = ecp_vconn_sock_create(&sock); if (rv) fail("ecp_vconn_sock_create err:%d\n", rv); rv = ecp_sock_open(&sock, &srv_config.my_addr); if (rv) fail("ecp_sock_open err:%d\n", rv); if (srv_config.uid || srv_config.gid) { if (srv_config.gid) { rv = setgid(srv_config.gid); if (rv) fail("Unable to set group id\n"); } if (srv_config.uid) { rv = setuid(srv_config.uid); if (rv) fail("Unable to set user id\n"); } } rv = dir_init(&sock); if (rv) fail("dir_init err:%d\n", rv); rv = vlink_init(&sock); if (rv) fail("vlink_init err:%d\n", rv); rv = start_receivers(&sock); if (rv) fail("start_receivers err:%d\n", rv); init_switch = getenv("ECP_INITSW"); if (remote) { if (init_switch == NULL) { rv = ecp_dir_set_handler(&ctx, &dir_handler, dir_result); if (rv) fail("ecp_dir_set_handler err:%d\n", rv); rv = ecp_dir_get(&dir_conn, &sock, &remote_pub, &remote_addr, 0); if (rv) fail("ecp_dir_get err:%d\n", rv); pthread_mutex_lock(&dir_sync_mutex); while (dir_sync_res == 0) { pthread_cond_wait(&dir_sync_cond, &dir_sync_mutex); } pthread_mutex_unlock(&dir_sync_mutex); LOG(LOG_DEBUG, "ecp_dir_get: done\n"); } else { rv = dir_init_ann(&sock, &remote_pub, &remote_addr); if (rv) fail("dir_init_ann err:%d\n", rv); } } if (init_switch) { int init_ann; init_ann = (int)strtol(init_switch, &endptr, 10); if (endptr[0] != '\0') fail("Bad environment: ECP_INITSW:%s\n", init_switch); LOG(LOG_DEBUG, "dir_init_switch: start - number of announces:%d\n", init_ann); dir_init_switch(&sock, init_ann); LOG(LOG_DEBUG, "dir_init_switch: done\n"); } rv = dir_start_announce(&sock); if (rv) fail("dir_start_announce err:%d\n", rv); rv = vlink_start_keyx(&sock); if (rv) fail("vlink_start_keyx err:%d\n", rv); while(1) pause(); }