端口转发工具关于 epoll 框架使用

本文最后更新于 2023年3月24日 中午

端口映射也叫做端口转发,或者叫做虚拟服务器

epoll API接口

文档:https://man7.org/linux/man-pages/man7/epoll.7.html

操作流程:

1、使用epoll_create()

2、调用epoll_ctl()注册fd的interest ,这会将项目添加 interest list

1
2
#include <sys/epoll.h>
int epoll_ctl(int epfd , int op , int fd , struct epoll_event *_Nullable event );

3、调用epoll_wait(),等待I/O 事件,如果当前没有可用的事件,则阻塞调用线程。(这个系统调用可以被认为是从epoll实例的就绪列表中获取项目。)水平触发和边缘触发

4、处理事件

示例:

1
2
3
在此示例中,侦听器是已调用listen(2)的非阻塞套接字。函数do_use_fd()使用新的就绪文件描述符,直到
read(2)或write(2)返回EAGAIN为止。一个事件-
驱动状态机应用程序应该在收到EAGAIN,记录其当前状态,以便在下次调用do_use_fd()时它将继续从其位置读取(2)或写入(2)之前就停了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;

/* Code to set up listening socket, 'listen_sock',
(socket(), bind(), listen()) omitted. */

epollfd = epoll_create1(0);
if (epollfd == -1) {
perror("epoll_create1");
exit(EXIT_FAILURE);
}

ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}

for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_wait");
exit(EXIT_FAILURE);
}

for (n = 0; n < nfds; ++n) {
if (events[n].data.fd == listen_sock) {
conn_sock = accept(listen_sock,
(struct sockaddr *) &addr, &addrlen);
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
&ev) == -1) {
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
} else {
do_use_fd(events[n].data.fd);
}
}
}

拉了N级运营商的宽带 (多级路由),没有外网ip,人在外面的时候(攻击者)无法访问家里的电脑(内网)

在外网机器运行:

tunnel -s port1[给内网机器连接的端口] prot2[给客户端连接的端口,

比如secureCRT 这种ssh客户端] 如:tunnel -s 2222 3333

在内网机器上运行:

tunnel -c port1[要连接的本机服务端口,比如家里的ssh服务22端口] prot2[外网机器的监听的端口]

如:tunnel -c 22 2222

将22端口转发出来了,给了外网机器

优化,中间的2222端口可以隐藏起来,以提高用户体验

任意机器连接外网的3333端口,发送的数据都将转发给内网机器22端口上(如secureCRT连接 外网ip:3333)

往往这个服务端机器也作为客户端使用了。

示例源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#define MAX_EVENTS 100
#define MAX_BUFFER_SIZE 65535
struct my_tcp_info
{
uint8_t tcpi_state;
uint8_t tcpi_ca_state;
uint8_t tcpi_retransmits;
uint8_t tcpi_probes;
uint8_t tcpi_backoff;
uint8_t tcpi_options;
uint8_t tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4;

uint32_t tcpi_rto;
uint32_t tcpi_ato;
uint32_t tcpi_snd_mss;
uint32_t tcpi_rcv_mss;

uint32_t tcpi_unacked;
uint32_t tcpi_sacked;
uint32_t tcpi_lost;
uint32_t tcpi_retrans;
uint32_t tcpi_fackets;

/* Times. */
uint32_t tcpi_last_data_sent;
uint32_t tcpi_last_ack_sent; /* Not remembered, sorry. */
uint32_t tcpi_last_data_recv;
uint32_t tcpi_last_ack_recv;

/* Metrics. */
uint32_t tcpi_pmtu;
uint32_t tcpi_rcv_ssthresh;
uint32_t tcpi_rtt;
uint32_t tcpi_rttvar;
uint32_t tcpi_snd_ssthresh;
uint32_t tcpi_snd_cwnd;
uint32_t tcpi_advmss;
uint32_t tcpi_reordering;

uint32_t tcpi_rcv_rtt;
uint32_t tcpi_rcv_space;

uint32_t tcpi_total_retrans;
};



struct epoll_event *events = NULL;
int epoll_fd = -1,listen_fd,trigger_count;
char *real_server_name;
unsigned short real_server_port;
void TimePrinter()
{
time_t t = time(NULL);
struct tm* lt = localtime(&t);
fflush(stdout);
printf("[ %d-%d-%d %d:%d:%d ] ", lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min,
lt->tm_sec);
}
int setup_socket_server(unsigned short port)
{
struct sockaddr_in addr;
socklen_t addr_length;
int fd, opt, opt_length;
if((fd = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0)) < 0)
return -1;
opt = 1;
opt_length = sizeof(opt);
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, opt_length) == -1)
return -1;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
addr_length = sizeof(addr);
if(bind(fd, (struct sockaddr *)&addr, addr_length) < 0)
return -1;
if(listen(fd, 3) < 0)
return -1;
return fd;
}
int setup_epoll(int size, struct epoll_event **events)
{
int fd;
if((*events = (struct epoll_event*)malloc(sizeof(struct epoll_event) * size)) == NULL)
return -1;
if((fd = epoll_create(size)) == -1)
free(*events);
return fd;
}
int add_event(int epoll_fd, int flags, int socket_fd)
{
struct epoll_event event;
event.data.fd = socket_fd;
event.events = flags;
return epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &event);
}
int delete_event(int epoll_fd, int socket_fd)
{
return epoll_ctl(epoll_fd, EPOLL_CTL_DEL, socket_fd, NULL);
}
int create_socket(char *host, int port)
{
int fd;
int flag = 1;
struct sockaddr_in server_info;
if((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return -1;
server_info.sin_family = AF_INET;
server_info.sin_port = htons(port);
server_info.sin_addr.s_addr = inet_addr(host);
if(connect(fd, (struct sockaddr *)&server_info, sizeof(server_info)) == -1)
return -1;
return fd;
}
int is_server_alive(int confd)
{
struct my_tcp_info info;
int len=sizeof(info);
getsockopt(confd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
if((info.tcpi_state == TCP_ESTABLISHED))
{
return 0;
}
else{return -1;}
}
void epoll_lt(int sockfd,int sendfd)
{
char buffer[MAX_BUFFER_SIZE] ;
int ret;
memset(buffer, 0, MAX_BUFFER_SIZE);
ret = recv(sockfd, buffer, MAX_BUFFER_SIZE, 0);
char s[ret];
memset(s,0,ret);
if (ret > 0){
TimePrinter();
memcpy(s,buffer,ret-1);
printf("Receve msg\"%s\"from client.\t Massage size:%d bytes\n",s, ret-1);
if(is_server_alive(sendfd) == 0)
{
send(sendfd,buffer,MAX_BUFFER_SIZE,0);
}
}
else
{
if (ret == 0)
TimePrinter();
printf("client close!!!\n");
close(sockfd);
delete_event(epoll_fd,sockfd);
}

}
void epoll_lt1(int sockfd)
{
int ret;
char buffer[MAX_BUFFER_SIZE] ;
memset(buffer,0,MAX_BUFFER_SIZE);
ret = recv(sockfd,buffer,MAX_BUFFER_SIZE,0);
char s[ret];
memset(s,0,ret);
if (ret > 0) {
memcpy(s,buffer,ret-1);
TimePrinter();
printf("Receve msg\"%s\"from server.\t Massage size:%d bytes\n", s, ret - 1);
for(int j = 0;j<trigger_count;j++) {
if ((events[j].events & EPOLLOUT) && (events[j].data.fd != sockfd)&& (events[j].data.fd != listen_fd)) {
send(events[j].data.fd, buffer, MAX_BUFFER_SIZE, 0);
}
}
} else {
if (ret == 0)
TimePrinter();
printf("server close!!!\n");
close(sockfd);
}
}

int main(int argc, char *argv[])
{
struct sockaddr_in addr;
socklen_t addr_length;
int i, client_fd,user_fd;
unsigned short port;
if(argc != 6)
{
fprintf(stdout, "usage: pfwd -l [listen port] -c [forward host] [forward port]\n");
fprintf(stdout, "\tlisten port: The port pfwd sould listen on.\n");
fprintf(stdout, "\tforward host: The forwarding server ip.\n");
fprintf(stdout, "\tforward port: The forwarding server port.\n\n");
exit(1);
}
for( i = 1;i<argc;) {
if(!strcmp(argv[i],"-c"))
{
real_server_name = argv[i+1];
real_server_port = (unsigned short)atoi(argv[i+2]);
i = i+3;
}
else if(!strcmp(argv[i],"-l"))
{
port = (unsigned short)atoi(argv[i + 1]);
i =i+2;
}
}
if((listen_fd = setup_socket_server(port)) == -1)
fprintf(stderr,"Unable to setup socket.");
if((epoll_fd = setup_epoll(MAX_EVENTS, &events)) == -1)
fprintf(stderr,"Unable to setup Epoll events.");
if(add_event(epoll_fd, EPOLLIN, listen_fd) == -1)
fprintf(stderr,"Unable to add listening socket to Epoll event queue.");
fprintf(stdout, "Resquests in port %d will be forwarded to %s:%d...\n", port, real_server_name, real_server_port);
addr_length = sizeof(addr);
user_fd =-1;
while(1)
{
if(is_server_alive(user_fd) == -1)
{
close(user_fd);
user_fd = create_socket(real_server_name, real_server_port);
add_event(epoll_fd,EPOLLIN,user_fd);
}
if((trigger_count = epoll_wait(epoll_fd, events, MAX_EVENTS, -1)) < 0)
fprintf(stderr,"Unable to Epoll wait.");
for(i = 0; i < trigger_count; i++)
{
client_fd = events[i].data.fd;
if(events[i].events & (EPOLLHUP | EPOLLERR))
{
fprintf(stderr, "Client disconnected (socket %d).\n", client_fd);
delete_event(epoll_fd, client_fd);
break;
}
else if(client_fd == listen_fd)
{
if((client_fd= accept(listen_fd, (struct sockaddr *)&addr, &addr_length)) == -1)
{
fprintf(stderr, "Unable to accept new client connection.\n");
continue;
}
if(add_event(epoll_fd, EPOLLIN|EPOLLOUT, client_fd) == -1)
{
fprintf(stderr, "Unable to add soecket to Epoll event queue, closing (socket %d).\n", client_fd);
close(client_fd);
continue;
}
TimePrinter();
fprintf(stdout, "New client connection from %s (socket %d).\n", inet_ntoa(addr.sin_addr), client_fd);
}
else if ((events[i].events & EPOLLIN))
{
if(is_server_alive(user_fd) == 0){
if (client_fd == user_fd)
{
epoll_lt1(client_fd);
}
else{
epoll_lt(client_fd,user_fd);
}
}
}
}
}
exit(0);
}

端口转发攻击场景使用:

建立反向连接:

有一台内网机器(被攻陷,具有双网卡,跳板机,类似路由器等)

一台攻击机(本地机)(同某一个网段的机器)

攻击机作为服务端监听 8888 ;

跳板机运行客户端连接服务器 host:8888(攻击机);

端口转发的两个应用:本地端口转发和远程端口转发


端口转发工具关于 epoll 框架使用
https://k3ppf0r.github.io/2023/03/04/编程/端口转发工具关于 epoll 框架使用/
作者
k3ppf0r
发布于
2023年3月4日
许可协议