summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Schneider <mail@cynapses.org>2009-09-08 11:33:46 +0200
committerAndreas Schneider <mail@cynapses.org>2009-09-15 17:26:13 +0200
commit48456426112eefd3a852138d9c9e3eb4cf84d29b (patch)
treec7fc17fb7197979d4f4160b6d56035880d59ae1e
parentde8808cb47470638359dc7eb8343552d3b94ec38 (diff)
downloadlibssh-48456426112eefd3a852138d9c9e3eb4cf84d29b.tar.gz
libssh-48456426112eefd3a852138d9c9e3eb4cf84d29b.tar.xz
libssh-48456426112eefd3a852138d9c9e3eb4cf84d29b.zip
Added support for ~/.ssh/config.
-rw-r--r--include/libssh/libssh.h1
-rw-r--r--include/libssh/priv.h3
-rw-r--r--libssh/CMakeLists.txt1
-rw-r--r--libssh/config.c305
-rw-r--r--libssh/options.c35
-rw-r--r--sample.c3
6 files changed, 348 insertions, 0 deletions
diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index 3ab9b49..7e4a99d 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
@@ -401,6 +401,7 @@ LIBSSH_API int ssh_options_set_dsa_server_key(SSH_OPTIONS *opt, const char *dsak
LIBSSH_API int ssh_options_set_rsa_server_key(SSH_OPTIONS *opt, const char *rsakey);
LIBSSH_API int ssh_options_set_auth_callback(SSH_OPTIONS *opt, ssh_auth_callback cb,
void *userdata);
+LIBSSH_API int ssh_options_parse_config(SSH_OPTIONS *opt, const char *filename);
/* buffer.c */
diff --git a/include/libssh/priv.h b/include/libssh/priv.h
index 1318cb0..f24102f 100644
--- a/include/libssh/priv.h
+++ b/include/libssh/priv.h
@@ -634,6 +634,9 @@ void ssh_cleanup(SSH_SESSION *session);
int ssh_send_banner(SSH_SESSION *session, int is_server);
char *ssh_get_banner(SSH_SESSION *session);
+/* config.c */
+int ssh_config_parse_file(ssh_options opt, const char *filename);
+
/* errors.c */
void ssh_set_error(void *error, int code, const char *descr, ...) PRINTF_ATTRIBUTE(3, 4);
diff --git a/libssh/CMakeLists.txt b/libssh/CMakeLists.txt
index ad13b8f..1def4d9 100644
--- a/libssh/CMakeLists.txt
+++ b/libssh/CMakeLists.txt
@@ -74,6 +74,7 @@ set(libssh_SRCS
buffer.c
channels.c
client.c
+ config.c
connect.c
crc32.c
crypt.c
diff --git a/libssh/config.c b/libssh/config.c
new file mode 100644
index 0000000..eaf3a91
--- /dev/null
+++ b/libssh/config.c
@@ -0,0 +1,305 @@
+/*
+ * config.c - parse the ssh config file
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2009 by Andreas Schneider <mail@cynapses.org>
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libssh/priv.h"
+
+enum ssh_config_opcode_e {
+ SOC_UNSUPPORTED = -1,
+ SOC_HOST,
+ SOC_HOSTNAME,
+ SOC_PORT,
+ SOC_USERNAME,
+ SOC_IDENTITY,
+ SOC_CIPHER,
+ SOC_CIPHERS,
+ SOC_COMPRESSION,
+ SOC_TIMEOUT,
+ SOC_PROTOCOL
+};
+
+struct ssh_config_keyword_table_s {
+ const char *name;
+ enum ssh_config_opcode_e opcode;
+};
+
+static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
+ { "host", SOC_HOST },
+ { "hostname", SOC_HOSTNAME },
+ { "port", SOC_PORT },
+ { "user", SOC_USERNAME },
+ { "identityfile", SOC_IDENTITY },
+ { "cipher", SOC_CIPHER },
+ { "ciphers", SOC_CIPHERS },
+ { "compression", SOC_COMPRESSION },
+ { "connecttimeout", SOC_TIMEOUT },
+ { "protocol", SOC_PROTOCOL },
+ { NULL, SOC_UNSUPPORTED }
+};
+
+static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
+ int i;
+
+ for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) {
+ if (strcasecmp(keyword, ssh_config_keyword_table[i].name) == 0) {
+ return ssh_config_keyword_table[i].opcode;
+ }
+ }
+
+ return SOC_UNSUPPORTED;
+}
+
+static char *ssh_config_get_token(char **str) {
+ register char *c;
+ char *r;
+
+ /* Ignore leading spaces */
+ for (c = *str; *c; c++) {
+ if (! isblank(*c)) {
+ break;
+ }
+ }
+
+ if (*c == '\"') {
+ for (r = ++c; *c; c++) {
+ if (*c == '\"') {
+ *c = '\0';
+ goto out;
+ }
+ }
+ }
+
+ for (r = c; *c; c++) {
+ if (isblank(*c)) {
+ *c = '\0';
+ goto out;
+ }
+ }
+
+out:
+ *str = c + 1;
+
+ return r;
+}
+
+static int ssh_config_get_int(char **str, int notfound) {
+ char *p, *endp;
+ int i;
+
+ p = ssh_config_get_token(str);
+ if (p && *p) {
+ i = strtol(p, &endp, 10);
+ if (p == endp) {
+ return notfound;
+ }
+ return i;
+ }
+
+ return notfound;
+}
+
+static const char *ssh_config_get_str(char **str, const char *def) {
+ char *p;
+
+ p = ssh_config_get_token(str);
+ if (p && *p) {
+ return p;
+ }
+
+ return def;
+}
+
+static int ssh_config_get_yesno(char **str, int notfound) {
+ const char *p;
+
+ p = ssh_config_get_str(str, NULL);
+ if (p == NULL) {
+ return notfound;
+ }
+
+ if (strncasecmp(p, "yes", 3) == 0) {
+ return 1;
+ } else if (strncasecmp(p, "no", 2) == 0) {
+ return 0;
+ }
+
+ return notfound;
+}
+
+static int ssh_config_parse_line(ssh_options opt, const char *line,
+ unsigned int count, int *parsing) {
+ enum ssh_config_opcode_e opcode;
+ const char *p;
+ char *s, *x;
+ char *keyword;
+ size_t len;
+ int i;
+
+ x = s = strdup(line);
+ if (s == NULL) {
+ return -1;
+ }
+
+ /* Remove trailing spaces */
+ for (len = strlen(s) - 1; len > 0; len--) {
+ if (! isspace(s[len])) {
+ break;
+ }
+ s[len] = '\0';
+ }
+
+ keyword = ssh_config_get_token(&s);
+ if (keyword == NULL || *keyword == '#' ||
+ *keyword == '\0' || *keyword == '\n') {
+ SAFE_FREE(x);
+ return 0;
+ }
+
+ opcode = ssh_config_get_opcode(keyword);
+
+ switch (opcode) {
+ case SOC_HOST:
+ *parsing = 0;
+ for (p = ssh_config_get_str(&s, NULL); p && *p;
+ p = ssh_config_get_str(&s, NULL)) {
+ if (match_hostname(opt->host, p, strlen(p))) {
+ *parsing = 1;
+ }
+ }
+ break;
+ case SOC_HOSTNAME:
+ p = ssh_config_get_str(&s, NULL);
+ if (p && *parsing) {
+ ssh_options_set_host(opt, p);
+ }
+ break;
+ case SOC_PORT:
+ i = ssh_config_get_int(&s, -1);
+ if (i > 0 && *parsing) {
+ ssh_options_set_port(opt, (unsigned int) i);
+ }
+ break;
+ case SOC_USERNAME:
+ p = ssh_config_get_str(&s, NULL);
+ if (p && *parsing) {
+ ssh_options_set_username(opt, p);
+ }
+ break;
+ case SOC_IDENTITY:
+ p = ssh_config_get_str(&s, NULL);
+ if (p && *parsing) {
+ ssh_options_set_identity(opt, p);
+ }
+ break;
+ case SOC_CIPHERS:
+ /* TODO */
+ break;
+ case SOC_COMPRESSION:
+ i = ssh_config_get_yesno(&s, -1);
+ if (i >= 0 && *parsing) {
+ if (i) {
+ ssh_options_set_wanted_algos(opt, SSH_COMP_C_S, "zlib");
+ ssh_options_set_wanted_algos(opt, SSH_COMP_S_C, "zlib");
+ } else {
+ ssh_options_set_wanted_algos(opt, SSH_COMP_C_S, "none");
+ ssh_options_set_wanted_algos(opt, SSH_COMP_S_C, "none");
+ }
+ }
+ break;
+ case SOC_PROTOCOL:
+ p = ssh_config_get_str(&s, NULL);
+ if (p && *parsing) {
+ char *a, *b;
+ b = strdup(p);
+ if (b == NULL) {
+ SAFE_FREE(x);
+ return -1;
+ }
+ ssh_options_allow_ssh1(opt, 0);
+ ssh_options_allow_ssh2(opt, 0);
+
+ for (a = strtok(b, ","); a; a = strtok(NULL, ",")) {
+ switch (atoi(a)) {
+ case 1:
+ ssh_options_allow_ssh1(opt, 1);
+ break;
+ case 2:
+ ssh_options_allow_ssh2(opt, 1);
+ break;
+ default:
+ break;
+ }
+ }
+ SAFE_FREE(b);
+ }
+ break;
+ case SOC_TIMEOUT:
+ i = ssh_config_get_int(&s, -1);
+ if (i >= 0 && *parsing) {
+ ssh_options_set_timeout(opt, (long) i, 0);
+ }
+ break;
+ case SOC_UNSUPPORTED:
+ fprintf(stderr, "Unsupported option: %s, line: %d\n", keyword, count);
+ break;
+ default:
+ fprintf(stderr, "ERROR - unimplemented opcode: %d\n", opcode);
+ SAFE_FREE(x);
+ return -1;
+ break;
+ }
+
+ SAFE_FREE(x);
+ return 0;
+}
+
+/* ssh_config_parse_file */
+int ssh_config_parse_file(ssh_options opt, const char *filename) {
+ char line[1024] = {0};
+ unsigned int count = 0;
+ FILE *f;
+ int parsing;
+
+ if ((f = fopen(filename, "r")) == NULL) {
+ return -1;
+ }
+
+ if (opt->log_verbosity) {
+ fprintf(stderr, "Reading configuration data from %s\n", filename);
+ }
+
+ parsing = 1;
+ while (fgets(line, sizeof(line), f)) {
+ count++;
+ if (ssh_config_parse_line(opt, line, count, &parsing) < 0) {
+ fclose(f);
+ return -1;
+ }
+ }
+
+ fclose(f);
+ return 0;
+}
diff --git a/libssh/options.c b/libssh/options.c
index 4e22b77..5d21408 100644
--- a/libssh/options.c
+++ b/libssh/options.c
@@ -1018,5 +1018,40 @@ int ssh_options_set_auth_callback(SSH_OPTIONS *opt, ssh_auth_callback cb,
return 0;
}
+/**
+ * @brief Parse the ssh config file.
+ *
+ * This must be the last call of all options, it may overwrite options which
+ * are already set. It requires at least the hostname to be set.
+ *
+ * @param opt The options structure to use.
+ *
+ * @param filename The options file to use, if NULL the default
+ * ~/.ssh/config will be used.
+ *
+ * @return 0 on success, < 0 on error.
+ */
+int ssh_options_parse_config(ssh_options opt, const char *filename) {
+ char buffer[1024] = {0};
+
+ if (opt == NULL || opt->host == NULL) {
+ return -1;
+ }
+
+ if (opt->ssh_dir == NULL) {
+ if (ssh_options_default_ssh_dir(opt) < 0) {
+ return -1;
+ }
+ }
+
+ /* set default filename */
+ if (filename == NULL) {
+ snprintf(buffer, 1024, "%s/config", opt->ssh_dir);
+ filename = buffer;
+ }
+
+ return ssh_config_parse_file(opt, filename);
+}
+
/** @} */
/* vim: set ts=2 sw=2 et cindent: */
diff --git a/sample.c b/sample.c
index cc3df54..f028ceb 100644
--- a/sample.c
+++ b/sample.c
@@ -531,6 +531,9 @@ int main(int argc, char **argv){
ssh_options_free(options);
return 1;
}
+
+ ssh_options_parse_config(options, NULL);
+
session=ssh_new();
ssh_set_options(session,options);
if(ssh_connect(session)){