libssh2-tunnel/reverse-tunnel.c
2024-08-07 16:48:01 +08:00

286 lines
8.8 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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