libssh2-tunnel/reverse-tunnel.c

286 lines
8.8 KiB
C
Raw Normal View History

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;
}