2024-08-07 16:48:01 +08:00
|
|
|
|
#include "reverse-tunnel.h"
|
|
|
|
|
#include <pthread.h>
|
2023-03-24 17:48:25 +08:00
|
|
|
|
#include <libssh2.h>
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
#include <unistd.h>
|
2024-08-07 16:48:01 +08:00
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
2023-03-24 17:48:25 +08:00
|
|
|
|
|
2024-02-22 10:36:59 +08:00
|
|
|
|
const char *keyfile1 = "/home/aixiao/.ssh/id_rsa.pub";
|
|
|
|
|
const char *keyfile2 = "/home/aixiao/.ssh/id_rsa";
|
|
|
|
|
const char *username = "aixiao";
|
2023-03-24 17:48:25 +08:00
|
|
|
|
const char *password = "12345";
|
|
|
|
|
|
2024-02-22 10:36:59 +08:00
|
|
|
|
const char *server_ip = "127.0.0.1";
|
2023-03-24 17:48:25 +08:00
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
const char *remote_listenhost = "0.0.0.0"; /* 由远程服务器解析 */
|
2023-03-24 17:48:25 +08:00
|
|
|
|
int remote_wantport = 33;
|
|
|
|
|
int remote_listenport;
|
|
|
|
|
|
|
|
|
|
const char *local_destip = "0.0.0.0";
|
|
|
|
|
int local_destport = 22;
|
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
struct tunnel_args {
|
|
|
|
|
LIBSSH2_CHANNEL *channel;
|
|
|
|
|
LIBSSH2_SESSION *session;
|
|
|
|
|
};
|
|
|
|
|
|
2023-03-24 17:48:25 +08:00
|
|
|
|
enum {
|
|
|
|
|
AUTH_NONE = 0,
|
|
|
|
|
AUTH_PASSWORD,
|
|
|
|
|
AUTH_PUBLICKEY
|
|
|
|
|
};
|
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
int forward_tunnel(LIBSSH2_SESSION *session, LIBSSH2_CHANNEL *channel)
|
2023-03-24 17:48:25 +08:00
|
|
|
|
{
|
|
|
|
|
int i, rc = 0;
|
|
|
|
|
struct sockaddr_in sin;
|
|
|
|
|
fd_set fds;
|
|
|
|
|
struct timeval tv;
|
|
|
|
|
ssize_t len, wr;
|
|
|
|
|
char buf[16384];
|
|
|
|
|
int forwardsock = -1;
|
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stdout, "接受到远程连接。连接到本地服务器 %s:%d\n", local_destip, local_destport);
|
2023-03-24 17:48:25 +08:00
|
|
|
|
forwardsock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
|
|
|
if (forwardsock == -1) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "打开socket错误\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
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))) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "无效的本地IP地址\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (-1 == connect(forwardsock, (struct sockaddr *)&sin, sizeof(struct sockaddr_in))) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "连接失败!\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stdout, "将连接从远程 %s:%d 转发到本地 %s:%d\n", remote_listenhost, remote_listenport, local_destip, local_destport);
|
2023-03-24 17:48:25 +08:00
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
/* 设置会话为非阻塞IO */
|
2023-03-24 17:48:25 +08:00
|
|
|
|
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) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "socket未准备好!\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
if (rc && FD_ISSET(forwardsock, &fds)) {
|
|
|
|
|
len = recv(forwardsock, buf, sizeof(buf), 0);
|
|
|
|
|
if (len < 0) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "从forwardsock读取时出错!\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
goto shutdown;
|
|
|
|
|
} else if (0 == len) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "本地服务器 %s:%d 断开连接!\n", local_destip, local_destport);
|
2023-03-24 17:48:25 +08:00
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
wr = 0;
|
|
|
|
|
do {
|
|
|
|
|
i = libssh2_channel_write(channel, buf, len);
|
|
|
|
|
if (i < 0) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "在SSH通道上写入时出错: %d\n", i);
|
2023-03-24 17:48:25 +08:00
|
|
|
|
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) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "从SSH通道读取时出错: %d\n", (int)len);
|
2023-03-24 17:48:25 +08:00
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
wr = 0;
|
|
|
|
|
while (wr < len) {
|
|
|
|
|
i = send(forwardsock, buf + wr, len - wr, 0);
|
|
|
|
|
if (i <= 0) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "在forwardsock上写入时出错!\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
wr += i;
|
|
|
|
|
}
|
|
|
|
|
if (libssh2_channel_eof(channel)) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "远程客户端 %s:%d 断开连接!\n", remote_listenhost, remote_listenport);
|
2023-03-24 17:48:25 +08:00
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
shutdown:
|
|
|
|
|
close(forwardsock);
|
2024-08-07 16:48:01 +08:00
|
|
|
|
/* 将会话设置回阻塞IO */
|
2023-03-24 17:48:25 +08:00
|
|
|
|
libssh2_session_set_blocking(session, 1);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
void *handle_tunnel(void *arg)
|
|
|
|
|
{
|
|
|
|
|
LIBSSH2_CHANNEL *channel = ((struct tunnel_args *)arg)->channel;
|
|
|
|
|
LIBSSH2_SESSION *session = ((struct tunnel_args *)arg)->session;
|
|
|
|
|
forward_tunnel(session, channel);
|
|
|
|
|
libssh2_channel_free(channel);
|
|
|
|
|
free(arg); // 释放为参数分配的内存
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-24 17:48:25 +08:00
|
|
|
|
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) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "libssh2初始化失败 (%d)\n", rc);
|
2023-03-24 17:48:25 +08:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
/* 连接到SSH服务器 */
|
2023-03-24 17:48:25 +08:00
|
|
|
|
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
|
|
|
if (sock == -1) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "打开socket错误\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sin.sin_family = AF_INET;
|
|
|
|
|
if (INADDR_NONE == (sin.sin_addr.s_addr = inet_addr(server_ip))) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "无效的远程IP地址\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
2024-08-07 16:48:01 +08:00
|
|
|
|
|
|
|
|
|
int optval = 1;
|
|
|
|
|
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0)
|
|
|
|
|
{
|
|
|
|
|
perror("setsockopt");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sin.sin_port = htons(22); /* SSH端口 */
|
2023-03-24 17:48:25 +08:00
|
|
|
|
if (connect(sock, (struct sockaddr *)(&sin), sizeof(struct sockaddr_in)) != 0) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "连接失败!\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
/* 创建会话实例 */
|
2023-03-24 17:48:25 +08:00
|
|
|
|
session = libssh2_session_init();
|
|
|
|
|
if (!session) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "无法初始化SSH会话!\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
/* 启动会话。这将交换欢迎消息,交换密钥,并设置加密、压缩和MAC层 */
|
|
|
|
|
|
2023-03-24 17:48:25 +08:00
|
|
|
|
rc = libssh2_session_handshake(session, sock);
|
|
|
|
|
if (rc) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "启动SSH会话时出错: %d\n", rc);
|
2023-03-24 17:48:25 +08:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
/* 此时我们还没有进行身份验证。首先要做的是检查主机密钥的指纹是否与我们的已知主机匹配。你的应用程序可以硬编码它,可以去文件中读取,可以向用户展示,这取决于你。 */
|
2023-03-24 17:48:25 +08:00
|
|
|
|
fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stdout, "指纹: ");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
for (i = 0; i < 20; i++)
|
|
|
|
|
fprintf(stdout, "%02X ", (unsigned char)fingerprint[i]);
|
|
|
|
|
fprintf(stdout, "\n");
|
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
/* 检查可用的身份验证方法 */
|
2023-03-24 17:48:25 +08:00
|
|
|
|
userauthlist = libssh2_userauth_list(session, username, strlen(username));
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "身份验证方法: %s\n", userauthlist);
|
2023-03-24 17:48:25 +08:00
|
|
|
|
if (strstr(userauthlist, "password"))
|
|
|
|
|
auth |= AUTH_PASSWORD;
|
|
|
|
|
if (strstr(userauthlist, "publickey"))
|
|
|
|
|
auth |= AUTH_PUBLICKEY;
|
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
/* 检查选项 */
|
2023-03-24 17:48:25 +08:00
|
|
|
|
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)) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "密码认证失败。\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
} else if (auth & AUTH_PUBLICKEY) {
|
|
|
|
|
if (libssh2_userauth_publickey_fromfile(session, username, keyfile1, keyfile2, password)) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "公钥认证失败!\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "公钥认证成功。\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
} else {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "未找到支持的认证方法!\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stdout, "请求服务器监听远程 %s:%d\n", remote_listenhost, remote_wantport);
|
2023-03-24 17:48:25 +08:00
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
listener = libssh2_channel_forward_listen_ex(session, NULL, remote_wantport, &remote_listenport, 2);
|
2023-03-24 17:48:25 +08:00
|
|
|
|
if (!listener) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "无法启动tcpip-forward监听器!\n" "(请注意,这可能是服务器的问题!请查看服务器日志。)\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stdout, "服务器正在监听 %s:%d\n", remote_listenhost, remote_listenport);
|
2023-03-24 17:48:25 +08:00
|
|
|
|
|
|
|
|
|
while (1) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stdout, "等待远程连接\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
channel = libssh2_channel_forward_accept(listener);
|
|
|
|
|
if (!channel) {
|
2024-08-07 16:48:01 +08:00
|
|
|
|
fprintf(stderr, "libssh2_channel_forward_accept() 无法接受连接!\n" "(请注意,这可能是服务器的问题!请查看服务器日志。)\n");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
forward_tunnel(session, channel);
|
|
|
|
|
libssh2_channel_free(channel);
|
2024-08-07 16:48:01 +08:00
|
|
|
|
|
2023-03-24 17:48:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
shutdown:
|
|
|
|
|
if (channel)
|
|
|
|
|
libssh2_channel_free(channel);
|
|
|
|
|
if (listener)
|
|
|
|
|
libssh2_channel_forward_cancel(listener);
|
2024-08-07 16:48:01 +08:00
|
|
|
|
libssh2_session_disconnect(session, "客户端正常断开连接");
|
2023-03-24 17:48:25 +08:00
|
|
|
|
libssh2_session_free(session);
|
|
|
|
|
close(sock);
|
|
|
|
|
libssh2_exit();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|