#include "forward-tunnel.h" #include "thpool.h" #include "ip2region/ip2region.h" const char *keyfile1 = "/home/aixiao/.ssh/id_rsa.pub"; const char *keyfile2 = "/home/aixiao/.ssh/id_rsa"; char *server_ssh_ip = "127.0.0.1"; int server_ssh_port = 22; char *server_ssh_user = "aixiao"; char *server_ssh_passwd = "123456"; const char *local_listenip = "0.0.0.0"; unsigned int local_listenport = 3306; 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 *sock_) { int forwardsock = *(int *)sock_; 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; } /* Connect to SSH server */ sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == -1) { perror("socket"); return; } int optval = SO_REUSEADDR; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { perror("setsockopt"); return; } 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; } 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; } /* Create a session instance */ session = libssh2_session_init(); if (!session) { fprintf(stderr, "Could not initialize SSH session!\n"); return; } /* ... 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; } /* 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; } static char help_info(void) { int l; 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; } int nice_(int increment) { int oldprio = getpriority(PRIO_PROCESS, getpid()); printf("%d\n", oldprio); return setpriority(PRIO_PROCESS, getpid(), oldprio + increment); } 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; } // 地域段白名单对比 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); } 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 = 0; int opt; char *p = NULL; char optstring[] = ":dr:s:p:l:u:e:h?"; while (-1 != (opt = getopt(argc, argv, optstring))) { switch (opt) { case 'd': _daemon = 1; break; case 'r': server_ssh_ip = strdup(optarg); break; case 's': p = strchr(optarg, ':'); if (p != NULL) { remote_destport = atoi(p + 1); *p = '\0'; remote_desthost = optarg; } else { if (NULL == (p = strchr(optarg, '.'))) { remote_destport = atoi(optarg); remote_desthost = "0.0.0.0"; } else { help_info(); exit(0); } } 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: ; } } modif_argv(argc, argv); 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; } int optval = SO_REUSEADDR; if (setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { perror("setsockopt"); 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, 500) < 0) { perror("listen"); goto shutdown; } fprintf(stderr, "Waiting for TCP connection on %s:%d ...\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); // 后台运行 if (_daemon == 1) { if (daemon(1, 1)) { perror("daemon"); return -1; } } // 进程优先级 if (-1 == (nice_(-20))) perror("nice_"); // 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); } } threadpool thpool = thpool_init(1024); while (1) { //printf("%d\n", get_threads()); int *forwardsock = (int *)malloc(sizeof(int)); // 分配内存空间保存线程编号 *forwardsock = accept(listensock, (struct sockaddr *)&sin, &server_addr_len); // 获取客户端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 } // 接受连接 if (*forwardsock == -1) { perror("accept"); goto shutdown; } thpool_add_work(thpool, forward_tunnel, (void *)(uintptr_t) forwardsock); } thpool_wait(thpool); thpool_destroy(thpool); 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; }