libssh2-tunnel/forward-tunnel.c

581 lines
16 KiB
C
Raw Normal View History

2023-03-24 17:48:25 +08:00
#include "forward-tunnel.h"
2024-05-14 15:11:08 +08:00
#include "thpool.h"
2024-08-07 16:48:01 +08:00
#include "ip2region/ip2region.h"
2023-03-24 17:48:25 +08:00
const char *keyfile1 = "/home/aixiao/.ssh/id_rsa.pub";
const char *keyfile2 = "/home/aixiao/.ssh/id_rsa";
2024-02-22 10:36:59 +08:00
char *server_ssh_ip = "127.0.0.1";
2023-03-24 17:48:25 +08:00
int server_ssh_port = 22;
2024-02-22 10:36:59 +08:00
char *server_ssh_user = "aixiao";
2023-03-24 17:48:25 +08:00
char *server_ssh_passwd = "123456";
const char *local_listenip = "0.0.0.0";
2024-05-13 15:20:25 +08:00
unsigned int local_listenport = 3306;
2023-03-24 17:48:25 +08:00
char *remote_desthost = "0.0.0.0"; /* resolved by the server */
2023-03-24 17:48:25 +08:00
unsigned int remote_destport = 3306;
enum {
AUTH_NONE = 0,
AUTH_PASSWORD,
AUTH_PUBLICKEY
};
2024-05-14 15:11:08 +08:00
void forward_tunnel(void *sock_)
2023-03-24 17:48:25 +08:00
{
2024-01-02 18:28:44 +08:00
int forwardsock = *(int *)sock_;
2023-03-24 17:48:25 +08:00
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);
2024-05-14 15:11:08 +08:00
return;
2023-03-24 17:48:25 +08:00
}
/* Connect to SSH server */
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == -1) {
perror("socket");
2024-05-14 15:11:08 +08:00
return;
2023-03-24 17:48:25 +08:00
}
2024-01-03 09:19:09 +08:00
2024-05-13 17:46:18 +08:00
int optval = SO_REUSEADDR;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
perror("setsockopt");
2024-05-14 15:11:08 +08:00
return;
2024-05-13 17:46:18 +08:00
}
2023-03-24 17:48:25 +08:00
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");
2024-05-14 15:11:08 +08:00
return;
2023-03-24 17:48:25 +08:00
}
sin.sin_port = htons(server_ssh_port);
if (connect(sock, (struct sockaddr *)(&sin), sizeof(struct sockaddr_in)) != 0) {
fprintf(stderr, "failed to connect!\n");
2024-05-14 15:11:08 +08:00
return;
2023-03-24 17:48:25 +08:00
}
/* Create a session instance */
session = libssh2_session_init();
if (!session) {
fprintf(stderr, "Could not initialize SSH session!\n");
2024-05-14 15:11:08 +08:00
return;
2023-03-24 17:48:25 +08:00
}
/* ... 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);
2024-05-14 15:11:08 +08:00
return;
2023-03-24 17:48:25 +08:00
}
/* 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);
2024-01-03 09:19:09 +08:00
FD_SET(forwardsock, &fds); // 设置一个描述符
2023-03-24 17:48:25 +08:00
tv.tv_sec = 0;
tv.tv_usec = 100000;
rc = select(forwardsock + 1, &fds, NULL, NULL, &tv);
2024-01-03 09:19:09 +08:00
if (-1 == rc) { // 返回-1表示出错
2023-03-24 17:48:25 +08:00
perror("select");
goto shutdown;
}
2024-01-03 09:19:09 +08:00
if (rc && FD_ISSET(forwardsock, &fds)) { // 测试某一个描述符返回值: 若fd在描述符集中则返回非0,否则返回0
2023-03-24 17:48:25 +08:00
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;
}
2024-01-03 09:19:09 +08:00
2023-03-24 17:48:25 +08:00
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;
}
2024-01-03 09:19:09 +08:00
2023-03-24 17:48:25 +08:00
wr += i;
}
}
2024-01-03 09:19:09 +08:00
2023-03-24 17:48:25 +08:00
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;
}
2024-01-03 09:19:09 +08:00
2023-03-24 17:48:25 +08:00
wr = 0;
while (wr < len) {
i = write(forwardsock, buffer + wr, len - wr);
if (i <= 0) {
perror("write");
goto shutdown;
}
2024-01-03 09:19:09 +08:00
2023-03-24 17:48:25 +08:00
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();
2024-05-14 15:11:08 +08:00
return;
2023-03-24 17:48:25 +08:00
}
static char help_info(void)
{
2024-01-02 18:28:44 +08:00
int l;
2023-03-24 17:48:25 +08:00
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);
for (l = 0; s_help[l]; l++) {
fprintf(stderr, "%s\n", s_help[l]);
}
BUILD("Compile、link.\n");
puts("");
return 0;
}
2024-01-03 09:19:09 +08:00
int nice_(int increment)
2023-06-05 11:47:35 +08:00
{
int oldprio = getpriority(PRIO_PROCESS, getpid());
printf("%d\n", oldprio);
return setpriority(PRIO_PROCESS, getpid(), oldprio + increment);
}
2024-05-13 15:20:25 +08:00
int modif_argv(int argc, char *argv[])
{
int i, j;
// 加密参数(账号密码)
for (i = 1; i < argc; i++) {
if (0 == strcmp(argv[i], "-e") || 0 == strcmp(argv[i], "-u") || 0 == strcmp(argv[i], "-p")) {
for (j = strlen(argv[i + 1]) - 1; j >= 0; j--) {
argv[i + 1][j] = 'x';
}
}
}
return 0;
}
int get_threads()
{
pid_t pid;
char line[256];
char path[256];
int threads = 0;
pid = getpid();
snprintf(path, sizeof(path), "/proc/%d/status", pid);
FILE *file = fopen(path, "r");
if (file == NULL) {
perror("Failed to open file");
return EXIT_FAILURE;
}
while (fgets(line, sizeof(line), file)) {
if (strncmp(line, "Threads:", 8) == 0) {
sscanf(line, "Threads:\t%d", &threads);
break;
}
}
return threads;
}
2024-08-07 16:48:01 +08:00
// 地域段白名单对比
int isregion(char *str, char (*region_list)[WHITELIST_IP_NUM])
{
int i;
char *p;
for (i = 1; i < WHITELIST_IP_NUM - 1; i++) {
if (strcmp(region_list[i], "\0") == 0) // 如果字符串为空就跳出循环
{
break;
}
//printf("%s %s\n", str, region_list[i]);
// 在str中查找region_list[i]
p = strstr(str, region_list[i]);
if (p != NULL) {
return 1;
}
}
return 0;
}
void split_string(char string[], char delims[], char (*whitelist_ip)[WHITELIST_IP_NUM])
{
int i = 0;
char *result = NULL;
result = strtok(string, delims);
while (result != NULL) {
i++;
strcpy(whitelist_ip[i], result);
result = strtok(NULL, delims);
}
}
char *_time()
{
char temp[BUFFER];
char *wday[] = { "0", "1", "2", "3", "4", "5", "6" };
time_t t;
struct tm *p;
time(&t);
p = localtime(&t); // 取得当地时间
memset(temp, 0, BUFFER);
snprintf(temp, BUFFER, "[%d/%02d/%02d %s %02d:%02d:%02d] ", (1900 + p->tm_year), (1 + p->tm_mon), p->tm_mday, wday[p->tm_wday], p->tm_hour, p->tm_min, p->tm_sec);
return strdup(temp);
}
2023-03-24 17:48:25 +08:00
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;
2024-05-13 15:20:25 +08:00
int _daemon = 0;
2023-03-24 17:48:25 +08:00
int opt;
char *p = NULL;
2024-01-03 09:19:09 +08:00
2023-03-24 17:48:25 +08:00
char optstring[] = ":dr:s:p:l:u:e:h?";
while (-1 != (opt = getopt(argc, argv, optstring))) {
switch (opt) {
case 'd':
2024-05-13 15:20:25 +08:00
_daemon = 1;
2023-03-24 17:48:25 +08:00
break;
case 'r':
server_ssh_ip = strdup(optarg);
break;
case 's':
p = strchr(optarg, ':');
if (p != NULL) {
remote_destport = atoi(p + 1);
2024-01-03 09:19:09 +08:00
*p = '\0';
remote_desthost = optarg;
2024-01-03 09:19:09 +08:00
} else {
2024-01-03 09:19:09 +08:00
if (NULL == (p = strchr(optarg, '.'))) {
remote_destport = atoi(optarg);
remote_desthost = "0.0.0.0";
} else {
help_info();
exit(0);
}
}
2024-01-03 09:19:09 +08:00
2023-03-24 17:48:25 +08:00
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:
;
}
}
2024-01-02 18:28:44 +08:00
2024-05-13 15:20:25 +08:00
modif_argv(argc, argv);
2023-03-24 17:48:25 +08:00
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;
}
2024-05-13 17:46:18 +08:00
int optval = SO_REUSEADDR;
if (setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
perror("setsockopt");
return -1;
}
2023-03-24 17:48:25 +08:00
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;
}
2024-01-03 09:19:09 +08:00
2023-03-24 17:48:25 +08:00
if (setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)) < 0) {
perror("setsockopt");
return -1;
}
2024-01-03 09:19:09 +08:00
2023-03-24 17:48:25 +08:00
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;
}
2024-01-03 09:19:09 +08:00
2023-06-05 11:47:35 +08:00
if (listen(listensock, 500) < 0) {
2023-03-24 17:48:25 +08:00
perror("listen");
goto shutdown;
}
2024-01-03 09:19:09 +08:00
2024-05-13 15:20:25 +08:00
fprintf(stderr, "Waiting for TCP connection on %s:%d ...\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
2023-03-24 17:48:25 +08:00
2024-01-02 18:28:44 +08:00
// 后台运行
2024-05-13 15:20:25 +08:00
if (_daemon == 1) {
2023-03-24 17:48:25 +08:00
if (daemon(1, 1)) {
perror("daemon");
return -1;
}
}
2024-01-02 18:28:44 +08:00
// 进程优先级
if (-1 == (nice_(-20)))
perror("nice_");
2023-06-05 11:47:35 +08:00
2024-08-07 16:48:01 +08:00
// IP地域
char *area = NULL;
char *xdb_path = "ip2region.xdb";
if (access(xdb_path, F_OK) == -1) { // 判断 ip2region 地址定位库是否存在
xdb_path = "ip2region/ip2region.xdb";
if (access(xdb_path, F_OK) == -1) {
printf("ip2region/ip2region.xdb DOESN'T EXIST!\n");
exit(-1);
}
}
2024-05-14 15:11:08 +08:00
threadpool thpool = thpool_init(1024);
2023-03-24 17:48:25 +08:00
while (1) {
2024-08-07 16:48:01 +08:00
//printf("%d\n", get_threads());
2024-05-13 15:20:25 +08:00
2024-05-14 15:11:08 +08:00
int *forwardsock = (int *)malloc(sizeof(int)); // 分配内存空间保存线程编号
*forwardsock = accept(listensock, (struct sockaddr *)&sin, &server_addr_len);
2024-08-07 16:48:01 +08:00
// 获取客户端IP地址
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
if (getpeername(*forwardsock, (struct sockaddr *)&client_addr, &client_addr_len) == -1) {
perror("getpeername");
} else {
char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
//printf("Client IP address: %s\n", client_ip);
#if _REGION
{
char *t = _time();
area = ip2region(xdb_path, client_ip);
if (area == NULL) {
printf("ip2region解析地域错误\n");
continue;
}
// 地域白名单
char region_list[WHITELIST_IP_NUM][WHITELIST_IP_NUM] = { { 0 }, { 0 } };
char REGION_LIST_COPY[WHITELIST_IP_NUM] = { 0 };
// 取环境变量
const char *REGION_ENV = getenv("REGION");
if (REGION_ENV != NULL) {
printf("REGION: %s\n", REGION_ENV);
strcpy(REGION_LIST_COPY, REGION_ENV);
} else {
strcpy(REGION_LIST_COPY, "中国 郑州 上海 内网");
}
printf("REGION_LIST : %s\n", REGION_LIST_COPY);
split_string(REGION_LIST_COPY, " ", region_list);
if (isregion(area, region_list) == 1) { // 返回1表示在白名单列表
printf(RED "%s 隧道Ip地址: %s, 属于地域白名单: %s\n" RESET, t, client_ip, area);
} else {
printf(RED "%s 隧道客户端Ip地址: %s, 不属于地域白名单地址: %s!!!\n" RESET, t, client_ip, area);
if (*forwardsock)
close(*forwardsock);
if (t)
free(t);
free(area);
continue;
}
}
#endif
}
2024-05-14 15:11:08 +08:00
// 接受连接
if (*forwardsock == -1) {
2023-03-24 17:48:25 +08:00
perror("accept");
goto shutdown;
}
2024-05-14 15:11:08 +08:00
thpool_add_work(thpool, forward_tunnel, (void *)(uintptr_t) forwardsock);
2023-03-24 17:48:25 +08:00
2024-05-14 15:11:08 +08:00
}
thpool_wait(thpool);
thpool_destroy(thpool);
2023-03-24 17:48:25 +08:00
shutdown:
2024-08-07 16:48:01 +08:00
2023-03-24 17:48:25 +08:00
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);
2024-05-13 15:20:25 +08:00
2023-03-24 17:48:25 +08:00
return 0;
}