commit 250a958832c1a3f68b0216aab0288aed27d925cd Author: aixiao Date: Fri Mar 24 17:48:25 2023 +0800 20230324 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3fb977e --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +CROSS_COMPILE ?= +CC := $(CROSS_COMPILE)gcc +STRIP := $(CROSS_COMPILE)strip +CFLAGS = -Wall -g -O3 +LIB = -lssh2 -pthread +OBJ = tunnel + +SSH2_LIB := $(shell pkg-config --static --libs --cflags libssh2) + + +all:forward-tunnel reverse-tunnel + +forward-tunnel: forward-tunnel.o + $(CC) $(CFLAGS) -o forward-tunnel $^ $(SSH2_LIB) $(LIB) + $(STRIP) forward-tunnel + +reverse-tunnel: reverse-tunnel.o + $(CC) $(CFLAGS) -o reverse-tunnel $^ $(SSH2_LIB) $(LIB) + $(STRIP) reverse-tunnel + +.c.o: + $(CC) $(CFLAGS) -c $< + +clean: + rm -rf *.o + rm reverse-tunnel forward-tunnel diff --git a/README.md b/README.md new file mode 100644 index 0000000..324fb90 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# libssh2_tunnel + + Linux libssh2 正向隧道 + +# build + git clone https://git.aixiao.me/aixiao/libssh2-tunnel.git + cd libssh2-tunnel + make clean; make + +# test + root@NIUYULING:/mnt/c/Users/niuyuling/Desktop/libssh2-tunnel# ./forward-tunnel -h + STunnel SSH forward tunnel + Author: AIXIAO@AIXIAO.ME + Version: 1.0 + Usage: [-d] [-rsplueh?] + + Options: + -d : Background running + -r : SSH Server IP + -p : SSH Server Port (default: 22) + -u : SSH Server User + -e : SSH Server User Passwd + -s : Remote Dest Port (default: 3306 1521) + -l : Local_Listen Port + -? -h : help information + + + Mar 24 2023 17:46:41 Compile、link. + + root@NIUYULING:/mnt/c/Users/niuyuling/Desktop/libssh2-tunnel# \ No newline at end of file diff --git a/forward-tunnel.c b/forward-tunnel.c new file mode 100644 index 0000000..a426aa0 --- /dev/null +++ b/forward-tunnel.c @@ -0,0 +1,391 @@ +#include "forward-tunnel.h" + + +const char *keyfile1 = "/home/aixiao/.ssh/id_rsa.pub"; +const char *keyfile2 = "/home/aixiao/.ssh/id_rsa"; + +char *server_ssh_ip = "47.108.253.59"; +int server_ssh_port = 22; +char *server_ssh_user = "root"; +char *server_ssh_passwd = "123456"; + +const char *local_listenip = "0.0.0.0"; +unsigned int local_listenport = 3009; + +const char *remote_desthost = "0.0.0.0"; /* resolved by the server */ +unsigned int remote_destport = 3306; + +enum { + AUTH_NONE = 0, + AUTH_PASSWORD, + AUTH_PUBLICKEY +}; + +void *forward_tunnel(void *p) +{ + + int forwardsock = *(int *)p; + + int rc, i, auth = AUTH_NONE; + struct sockaddr_in sin; + const char *fingerprint; + char *userauthlist; + LIBSSH2_SESSION *session; + LIBSSH2_CHANNEL *channel = NULL; + const char *shost; + unsigned int sport; + fd_set fds; + struct timeval tv; + ssize_t len, wr; + char buffer[16384]; + int sock; + + bzero(buffer, 0); + rc = libssh2_init(0); + + if (rc) { + fprintf(stderr, "libssh2 initialization failed (%d)\n", rc); + return NULL; + } + + /* Connect to SSH server */ + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1) { + perror("socket"); + return NULL; + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr(server_ssh_ip); + if (INADDR_NONE == sin.sin_addr.s_addr) { + perror("inet_addr"); + return NULL; + } + sin.sin_port = htons(server_ssh_port); + if (connect(sock, (struct sockaddr *)(&sin), sizeof(struct sockaddr_in)) != 0) { + fprintf(stderr, "failed to connect!\n"); + return NULL; + } + + /* Create a session instance */ + session = libssh2_session_init(); + + if (!session) { + fprintf(stderr, "Could not initialize SSH session!\n"); + return NULL; + } + + /* ... start it up. This will trade welcome banners, exchange keys, + * and setup crypto, compression, and MAC layers + */ + rc = libssh2_session_handshake(session, sock); + if (rc) { + fprintf(stderr, "Error when starting up SSH session: %d\n", rc); + return NULL; + } + + /* At this point we havn't yet authenticated. The first thing to do + * is check the hostkey's fingerprint against our known hosts Your app + * may have it hard coded, may go to a file, may present it to the + * user, that's your call + */ + fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); + + fprintf(stderr, "Fingerprint: "); + for (i = 0; i < 20; i++) + fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); + fprintf(stderr, "\n"); + + /* check what authentication methods are available */ + userauthlist = libssh2_userauth_list(session, server_ssh_user, strlen(server_ssh_user)); + + fprintf(stderr, "Authentication methods: %s\n", userauthlist); + if (strstr(userauthlist, "password")) + auth |= AUTH_PASSWORD; + if (strstr(userauthlist, "publickey")) + auth |= AUTH_PUBLICKEY; + + if (auth & AUTH_PASSWORD) { + if (libssh2_userauth_password(session, server_ssh_user, server_ssh_passwd)) { + + fprintf(stderr, "Authentication by password failed.\n"); + goto shutdown; + } + } else if (auth & AUTH_PUBLICKEY) { + if (libssh2_userauth_publickey_fromfile(session, server_ssh_user, keyfile1, keyfile2, server_ssh_passwd)) { + fprintf(stderr, "\tAuthentication by public key failed!\n"); + goto shutdown; + } + fprintf(stderr, "\tAuthentication by public key succeeded.\n"); + } else { + fprintf(stderr, "No supported authentication methods found!\n"); + goto shutdown; + } + + shost = inet_ntoa(sin.sin_addr); + sport = ntohs(sin.sin_port); + + fprintf(stderr, "Forwarding connection from %s:%d here to remote %s:%d\n", shost, sport, remote_desthost, remote_destport); + channel = libssh2_channel_direct_tcpip_ex(session, remote_desthost, remote_destport, shost, sport); + if (!channel) { + fprintf(stderr, "Could not open the direct-tcpip channel!\n" "(Note that this can be a problem at the server!" " Please review the server logs.)\n"); + goto shutdown; + } + + /* Setting session to non-blocking IO */ + libssh2_session_set_blocking(session, 0); + + while (1) { + FD_ZERO(&fds); + FD_SET(forwardsock, &fds); // 设置一个描述符 + tv.tv_sec = 0; + tv.tv_usec = 100000; + rc = select(forwardsock + 1, &fds, NULL, NULL, &tv); + if (-1 == rc) { // 返回-1表示出错 + perror("select"); + goto shutdown; + } + + + if (rc && FD_ISSET(forwardsock, &fds)) { // 测试某一个描述符返回值: 若fd在描述符集中则返回非0,否则返回0 + len = read(forwardsock, buffer, sizeof(buffer)); + if (len < 0) { + perror("read"); + goto shutdown; + } else if (0 == len) { + fprintf(stderr, "The client at %s:%d disconnected!\n", shost, sport); + goto shutdown; + } + + wr = 0; + while (wr < len) { + i = libssh2_channel_write(channel, buffer + wr, len - wr); + + if (LIBSSH2_ERROR_EAGAIN == i) { + continue; + } + if (i < 0) { + fprintf(stderr, "libssh2_channel_write: %d\n", i); + goto shutdown; + } + + wr += i; + } + } + + while (1) { + len = libssh2_channel_read(channel, buffer, sizeof(buffer)); + + if (LIBSSH2_ERROR_EAGAIN == len) + break; + else if (len < 0) { + fprintf(stderr, "libssh2_channel_read: %d", (int)len); + goto shutdown; + } + + wr = 0; + while (wr < len) { + i = write(forwardsock, buffer + wr, len - wr); + if (i <= 0) { + perror("write"); + goto shutdown; + } + + wr += i; + } + + if (libssh2_channel_eof(channel)) { + fprintf(stderr, "The server at %s:%d disconnected!\n", remote_desthost, remote_destport); + goto shutdown; + } + + } + } + +shutdown: + close(forwardsock); + if (channel) + libssh2_channel_free(channel); + libssh2_session_set_blocking(session, 1); + libssh2_session_disconnect(session, "Client disconnecting normally"); + libssh2_session_free(session); + close(sock); + libssh2_exit(); + + + return NULL; + +} + +static char help_info(void) +{ + static const char name[] = "STunnel"; + static const char subject[] = "SSH forward tunnel"; + static const struct { + const char *email; + const char *version; + } author = { + "AIXIAO@AIXIAO.ME", + "1.0", + }; + + static const char usage[] = "Usage: [-d] [-rsplueh?]"; + static const char *s_help[] = { + "", + "Options:", + " -d : Background running", + " -r : SSH Server IP", + " -p : SSH Server Port (default: 22)", + " -u : SSH Server User", + " -e : SSH Server User Passwd", + " -s : Remote Dest Port (default: 3306 1521)", + " -l : Local_Listen Port", + " -? -h : help information", + "", + "", + 0 + }; + + fprintf(stderr, " %s %s\n", name, subject); + fprintf(stderr, "Author: %s\n", author.email); + fprintf(stderr, "Version: %s\n", author.version); + fprintf(stderr, "%s\n", usage); + + int l; + for (l = 0; s_help[l]; l++) { + fprintf(stderr, "%s\n", s_help[l]); + } + + BUILD("Compile、link.\n"); + puts(""); + + return 0; +} + +int main(int argc, char *argv[], char **env) +{ + struct sockaddr_in sin; + struct sockaddr_in server_addr; + socklen_t server_addr_len; + int sockopt = 1; + int listensock = -1, forwardsock = -1; + int daemon_is = 0; + int opt; + + + char optstring[] = ":dr:s:p:l:u:e:h?"; + while (-1 != (opt = getopt(argc, argv, optstring))) { + switch (opt) { + case 'd': + daemon_is = 1; + break; + case 'r': + server_ssh_ip = strdup(optarg); + break; + case 's': + remote_destport = atoi(optarg); + break; + case 'p': + server_ssh_port = atoi(optarg); + break; + case 'l': + local_listenport = atoi(optarg); + break; + case 'u': + server_ssh_user = strdup(optarg); + break; + case 'e': + server_ssh_passwd = strdup(optarg);; + break; + case 'h': + case '?': + help_info(); + exit(0); + break; + default: + ; + } + } + + + printf("SSH Server: %s:%d, Local listen: %s:%d, User&Passwd: [%s]['%s']\n", server_ssh_ip, remote_destport, local_listenip, local_listenport, server_ssh_user, server_ssh_passwd); + + listensock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listensock == -1) { + perror("socket"); + return -1; + } + + sin.sin_family = AF_INET; + sin.sin_port = htons(local_listenport); + sin.sin_addr.s_addr = inet_addr(local_listenip); + if (INADDR_NONE == sin.sin_addr.s_addr) { + perror("inet_addr"); + goto shutdown; + } + + if (setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)) < 0) { + perror("setsockopt"); + return -1; + } + + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(local_listenport); + server_addr.sin_addr.s_addr = INADDR_ANY; + if (bind(listensock, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) { + perror("bind"); + goto shutdown; + } + + if (listen(listensock, 1024) < 0) { + perror("listen"); + goto shutdown; + } + + fprintf(stderr, "Waiting for TCP connection on %s:%d...\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + + pthread_t thread_id = 0; + sigset_t signal_mask; + sigemptyset(&signal_mask); + sigaddset(&signal_mask, SIGPIPE); // 忽略PIPE信号 + if (pthread_sigmask(SIG_BLOCK, &signal_mask, NULL) != 0) { + printf("block sigpipe error\n"); + } + + if (daemon_is == 1) { + if (daemon(1, 1)) { + perror("daemon"); + return -1; + } + } + + while (1) { + server_addr_len = sizeof(server_addr); + forwardsock = accept(listensock, (struct sockaddr *)&sin, &server_addr_len); + if (forwardsock == -1) { + perror("accept"); + goto shutdown; + } + + pthread_create(&thread_id, NULL, &forward_tunnel, (void *)&forwardsock); + + } + + pthread_join(thread_id, NULL); + pthread_exit(NULL); + + +shutdown: + close(forwardsock); + close(listensock); + libssh2_exit(); + if (server_ssh_ip) + free(server_ssh_ip); + if (server_ssh_user) + free(server_ssh_user); + if (server_ssh_passwd) + free(server_ssh_passwd); + return 0; +} diff --git a/forward-tunnel.h b/forward-tunnel.h new file mode 100644 index 0000000..6ef649d --- /dev/null +++ b/forward-tunnel.h @@ -0,0 +1,24 @@ +#ifndef FORWARD_TUNNEL +#define FORWARD_TUNNEL + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUILD(fmt...) do { fprintf(stderr,"%s %s ",__DATE__,__TIME__); fprintf(stderr, ##fmt); } while(0) +#ifndef INADDR_NONE +#define INADDR_NONE (in_addr_t)-1 +#endif + +#endif \ No newline at end of file diff --git a/reverse-tunnel.c b/reverse-tunnel.c new file mode 100644 index 0000000..c9f9867 --- /dev/null +++ b/reverse-tunnel.c @@ -0,0 +1,287 @@ +/* + * Example of SSH tunnel using libssh2. + * + * This code was based on the 'tcpip-forward.c' example: + * https://github.com/libssh2/libssh2/blob/master/example/tcpip-forward.c + * + * Author: marianafranco (https://github.com/marianafranco) + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef INADDR_NONE +#define INADDR_NONE (in_addr_t)-1 +#endif + +const char *keyfile1 = "/home/username/.ssh/id_rsa.pub"; +const char *keyfile2 = "/home/username/.ssh/id_rsa"; +const char *username = "root"; +const char *password = "12345"; + +const char *server_ip = "47.240.75.93"; + +const char *remote_listenhost = "0.0.0.0"; /* resolved by the remote server */ +int remote_wantport = 33; +int remote_listenport; + +const char *local_destip = "0.0.0.0"; +int local_destport = 22; + +enum { + AUTH_NONE = 0, + AUTH_PASSWORD, + AUTH_PUBLICKEY +}; + +int forward_tunnel(LIBSSH2_SESSION * session, LIBSSH2_CHANNEL * channel) +{ + int i, rc = 0; + struct sockaddr_in sin; + fd_set fds; + struct timeval tv; + ssize_t len, wr; + char buf[16384]; + int forwardsock = -1; + + fprintf(stdout, "Accepted remote connection. Connecting to local server %s:%d\n", local_destip, local_destport); + forwardsock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (forwardsock == -1) { + fprintf(stderr, "Error opening socket\n"); + goto shutdown; + } + + sin.sin_family = AF_INET; + sin.sin_port = htons(local_destport); + if (INADDR_NONE == (sin.sin_addr.s_addr = inet_addr(local_destip))) { + fprintf(stderr, "Invalid local IP address\n"); + goto shutdown; + } + + if (-1 == connect(forwardsock, (struct sockaddr *)&sin, sizeof(struct sockaddr_in))) { + fprintf(stderr, "Failed to connect!\n"); + goto shutdown; + } + + fprintf(stdout, "Forwarding connection from remote %s:%d to local %s:%d\n", remote_listenhost, remote_listenport, local_destip, local_destport); + + /* Setting session to non-blocking IO */ + libssh2_session_set_blocking(session, 0); + + while (1) { + FD_ZERO(&fds); + FD_SET(forwardsock, &fds); + tv.tv_sec = 0; + tv.tv_usec = 100000; + rc = select(forwardsock + 1, &fds, NULL, NULL, &tv); + if (-1 == rc) { + fprintf(stderr, "Socket not ready!\n"); + goto shutdown; + } + if (rc && FD_ISSET(forwardsock, &fds)) { + len = recv(forwardsock, buf, sizeof(buf), 0); + if (len < 0) { + fprintf(stderr, "Error reading from the forwardsock!\n"); + goto shutdown; + } else if (0 == len) { + fprintf(stderr, "The local server at %s:%d disconnected!\n", local_destip, local_destport); + goto shutdown; + } + wr = 0; + do { + i = libssh2_channel_write(channel, buf, len); + if (i < 0) { + fprintf(stderr, "Error writing on the SSH channel: %d\n", i); + goto shutdown; + } + wr += i; + } while (i > 0 && wr < len); + } + while (1) { + len = libssh2_channel_read(channel, buf, sizeof(buf)); + if (LIBSSH2_ERROR_EAGAIN == len) + break; + else if (len < 0) { + fprintf(stderr, "Error reading from the SSH channel: %d\n", (int)len); + goto shutdown; + } + wr = 0; + while (wr < len) { + i = send(forwardsock, buf + wr, len - wr, 0); + if (i <= 0) { + fprintf(stderr, "Error writing on the forwardsock!\n"); + goto shutdown; + } + wr += i; + } + if (libssh2_channel_eof(channel)) { + fprintf(stderr, "The remote client at %s:%d disconnected!\n", remote_listenhost, remote_listenport); + goto shutdown; + } + } + } + +shutdown: + close(forwardsock); + /* Setting the session back to blocking IO */ + libssh2_session_set_blocking(session, 1); + return rc; +} + +int main(int argc, char *argv[]) +{ + int rc, i, auth = AUTH_NONE; + struct sockaddr_in sin; + const char *fingerprint; + char *userauthlist; + LIBSSH2_SESSION *session; + LIBSSH2_LISTENER *listener = NULL; + LIBSSH2_CHANNEL *channel = NULL; + + int sock = -1; + + if (argc > 1) + server_ip = argv[1]; + if (argc > 2) + username = argv[2]; + if (argc > 3) + password = argv[3]; + if (argc > 4) + remote_listenhost = argv[4]; + if (argc > 5) + remote_wantport = atoi(argv[5]); + if (argc > 6) + local_destip = argv[6]; + if (argc > 7) + local_destport = atoi(argv[7]); + + rc = libssh2_init(0); + if (rc != 0) { + fprintf(stderr, "libssh2 initialization failed (%d)\n", rc); + return 1; + } + + /* Connect to SSH server */ + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1) { + fprintf(stderr, "Error opening socket\n"); + return -1; + } + + sin.sin_family = AF_INET; + if (INADDR_NONE == (sin.sin_addr.s_addr = inet_addr(server_ip))) { + fprintf(stderr, "Invalid remote IP address\n"); + return -1; + } + sin.sin_port = htons(22); /* SSH port */ + if (connect(sock, (struct sockaddr *)(&sin), sizeof(struct sockaddr_in)) != 0) { + fprintf(stderr, "Failed to connect!\n"); + return -1; + } + + /* Create a session instance */ + session = libssh2_session_init(); + if (!session) { + fprintf(stderr, "Could not initialize the SSH session!\n"); + return -1; + } + + /* ... start it up. This will trade welcome banners, exchange keys, + * and setup crypto, compression, and MAC layers + */ + rc = libssh2_session_handshake(session, sock); + if (rc) { + fprintf(stderr, "Error when starting up SSH session: %d\n", rc); + return -1; + } + + /* At this point we havn't yet authenticated. The first thing to do + * is check the hostkey's fingerprint against our known hosts Your app + * may have it hard coded, may go to a file, may present it to the + * user, that's your call + */ + fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); + fprintf(stdout, "Fingerprint: "); + for (i = 0; i < 20; i++) + fprintf(stdout, "%02X ", (unsigned char)fingerprint[i]); + fprintf(stdout, "\n"); + + /* check what authentication methods are available */ + userauthlist = libssh2_userauth_list(session, username, strlen(username)); + fprintf(stderr, "Authentication methods: %s\n", userauthlist); + if (strstr(userauthlist, "password")) + auth |= AUTH_PASSWORD; + if (strstr(userauthlist, "publickey")) + auth |= AUTH_PUBLICKEY; + + /* check for options */ + if (argc > 8) { + if ((auth & AUTH_PASSWORD) && !strcasecmp(argv[8], "-p")) + auth = AUTH_PASSWORD; + if ((auth & AUTH_PUBLICKEY) && !strcasecmp(argv[8], "-k")) + auth = AUTH_PUBLICKEY; + } + + if (auth & AUTH_PASSWORD) { + if (libssh2_userauth_password(session, username, password)) { + fprintf(stderr, "Authentication by password failed.\n"); + goto shutdown; + } + } else if (auth & AUTH_PUBLICKEY) { + if (libssh2_userauth_publickey_fromfile(session, username, keyfile1, keyfile2, password)) { + fprintf(stderr, "\tAuthentication by public key failed!\n"); + goto shutdown; + } + fprintf(stderr, "\tAuthentication by public key succeeded.\n"); + } else { + fprintf(stderr, "No supported authentication methods found!\n"); + goto shutdown; + } + + fprintf(stdout, "Asking server to listen on remote %s:%d\n", remote_listenhost, remote_wantport); + + //listener = libssh2_channel_forward_listen_ex(session, NULL, remote_wantport, &remote_listenport, 1); + listener = libssh2_channel_forward_listen(session, remote_wantport); + if (!listener) { + fprintf(stderr, "Could not start the tcpip-forward listener!\n" "(Note that this can be a problem at the server!" " Please review the server logs.)\n"); + goto shutdown; + } + + fprintf(stdout, "Server is listening on %s:%d\n", remote_listenhost, remote_listenport); + + while (1) { + fprintf(stdout, "Waiting for remote connection\n"); + channel = libssh2_channel_forward_accept(listener); + if (!channel) { + fprintf(stderr, "Could not accept connection!\n" "(Note that this can be a problem at the server!" " Please review the server logs.)\n"); + goto shutdown; + } + + forward_tunnel(session, channel); + + libssh2_channel_free(channel); + } + +shutdown: + if (channel) + libssh2_channel_free(channel); + if (listener) + libssh2_channel_forward_cancel(listener); + libssh2_session_disconnect(session, "Client disconnecting normally"); + libssh2_session_free(session); + close(sock); + libssh2_exit(); + return 0; +}