#include #include #include #include static char* resolve_listen(const char *hostname, const char *port) { /* Need room in the strcat for \0 and : * the format in the socket unit file is hostname:port */ char *conn = (char*)malloc(strlen(hostname)+strlen(port)+2); strcpy(conn, hostname); strcat(conn, ":"); strcat(conn, port); return conn; } static int get_listen_from_conf(const char *filename, char **listen) { config_t config; config_setting_t *setting, *addr; const char *hostname, *port; int len = 0; /* look up the listen stanzas in the config file so these * can be used in the socket file generated */ config_init(&config); if (config_read_file(&config, filename) == CONFIG_FALSE) { /* we don't care if file is missing, skip it */ if (config_error_line(&config) != 0) { fprintf(stderr, "%s:%d:%s\n", filename, config_error_line(&config), config_error_text(&config)); return -1; } } else { setting = config_lookup(&config, "listen"); if (setting) { len = config_setting_length(setting); for (int i = 0; i < len; i++) { addr = config_setting_get_elem(setting, i); if (! (config_setting_lookup_string(addr, "host", &hostname) && config_setting_lookup_string(addr, "port", &port))) { fprintf(stderr, "line %d:Incomplete specification (hostname and port required)\n", config_setting_source_line(addr)); return -1; } else { listen[i] = malloc(strlen(resolve_listen(hostname, port))); strcpy(listen[i], resolve_listen(hostname, port)); } } } } return len; } static int write_socket_unit(FILE *socket, char **listen, int num_addr, const char *source) { fprintf(socket, "# Automatically generated by systemd-sslh-generator\n\n" "[Unit]\n" "Before=sslh.service\n" "SourcePath=%s\n" "Documentation=man:sslh(8) man:systemd-sslh-generator(8)\n\n" "[Socket]\n" "FreeBind=true\n", source); for (int i = 0; i < num_addr; i++) { fprintf(socket, "ListenStream=%s\n", listen[i]); } return 0; } static int gen_sslh_config(char *runtime_unit_dir) { char *sslh_conf; int num_addr; FILE *config; char **listen; FILE *runtime_conf_fd = stdout; const char *unit_file; /* There are two default locations so check both with first given preference */ sslh_conf = "/etc/sslh.cfg"; config = fopen(sslh_conf, "r"); if (config == NULL) { sslh_conf="/etc/sslh/sslh.cfg"; config = fopen(sslh_conf, "r"); if (config == NULL) { return -1; } } fclose(config); num_addr = get_listen_from_conf(sslh_conf, listen); if (num_addr < 0) return -1; /* If this is run by systemd directly write to the location told to * otherwise write to standard out so that it's trivial to check what * will be written */ if (runtime_unit_dir != "") { unit_file = "/sslh.socket"; size_t uf_len = strlen(unit_file); size_t runtime_len = strlen(runtime_unit_dir) + uf_len + 1; char *runtime_conf = malloc(runtime_len); strcpy(runtime_conf, runtime_unit_dir); strcat(runtime_conf, unit_file); runtime_conf_fd = fopen(runtime_conf, "w"); } return write_socket_unit(runtime_conf_fd, listen, num_addr, sslh_conf); } int main(int argc, char *argv[]){ int r = 0; int k; char *runtime_unit_dest = ""; if (argc > 1 && (argc != 4) ) { printf("This program takes three or no arguments.\n"); return -1; } if (argc > 1) runtime_unit_dest = argv[1]; k = gen_sslh_config(runtime_unit_dest); if (k < 0) r = k; return r < 0 ? -1 : 0; }