-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathUtils.cpp
265 lines (240 loc) · 7.66 KB
/
Utils.cpp
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
#include <arpa/inet.h>
#include <cctype>
#include <cstring>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "Log.h"
#include "MutexLock.h"
#include "Utils.h"
int socket_bind_and_listen(int port)
{
int listen_fd = 0;
// 开始创建 socket, 注意这是阻塞模式的socket
// AF_INET : IPv4 Internet protocols
// SOCK_STREAM : TCP socket
if((listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) == -1)
return -1;
// 绑定端口
sockaddr_in server_addr;
// 初始化一下
memset(&server_addr, '\0', sizeof(server_addr));
// 设置一下基本操作
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons((unsigned short)port);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
// 端口复用
int opt = 1;
if(setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1)
return -1;
// 试着bind
if(bind(listen_fd, (sockaddr*)&server_addr, sizeof(server_addr)) == -1)
return -1;
// 试着listen, 设置最大队列长度为 1024
if(listen(listen_fd, 1024) == -1)
return -1;
return listen_fd;
}
bool setFdNoBlock(int fd)
{
// 获取fd对应的flag
int flag = fcntl(fd, F_GETFD);
if(flag == -1)
return -1;
flag |= O_NONBLOCK;
if(fcntl(fd, F_SETFL, flag) == -1)
return false;
return true;
}
bool setSocketNoDelay(int fd)
{
int enable = 1;
if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&enable, sizeof(enable)) == -1)
return false;
return true;
}
ssize_t readn(int fd, void* buf, size_t len)
{
// 这里将 void* 转换成 char* 是为了在下面进行自增操作
char *pos = (char*)buf;
size_t leftNum = len;
ssize_t readNum = 0;
while(leftNum > 0)
{
// 尝试循环读取,如果报错,则进行判断
// 注意, read 的返回值为0则表示读取到 EOF,是正常现象
ssize_t tmpRead = read(fd, pos, leftNum);
if(tmpRead < 0)
{
if(errno == EINTR)
tmpRead = 0;
// 如果始终读取不到数据,则提前返回,因为这个取决于远程 fd,无法预测要等多久
else if (errno == EAGAIN)
return readNum;
else
return -1;
}
// 读取的0,则说明远程连接已被关闭
if(tmpRead == 0)
break;
readNum += tmpRead;
pos += tmpRead;
leftNum -= tmpRead;
}
return readNum;
}
ssize_t writen(int fd, const void* buf, size_t len, bool isWrite)
{
// 这里将 void* 转换成 char* 是为了在下面进行自增操作
char *pos = (char*)buf;
size_t leftNum = len;
ssize_t writtenNum = 0;
while(leftNum > 0)
{
ssize_t tmpWrite = 0;
if(isWrite)
tmpWrite = write(fd, pos, leftNum);
else
tmpWrite = send(fd, pos, leftNum, 0);
// 尝试循环写入,如果报错,则进行判断
// 注意,write返回0属于异常现象,因此判断时需要包含
if(tmpWrite < 0)
{
// 与read不同的是,如果 EAGAIN,则继续重复写入,因为写入操作是有Server这边决定的
if(errno == EINTR || errno == EAGAIN)
tmpWrite = 0;
else
return -1;
}
if(tmpWrite == 0)
break;
writtenNum += tmpWrite;
pos += tmpWrite;
leftNum -= tmpWrite;
}
return writtenNum;
}
void handleSigpipe()
{
struct sigaction sa;
memset(&sa, '\0', sizeof(sa));
sa.sa_handler = SIG_IGN;
sa.sa_flags = 0;
if(sigaction(SIGPIPE, &sa, NULL) == -1)
ERROR("Ignore SIGPIPE failed! (%s)", strerror(errno));
}
void printConnectionStatus(int client_fd_, string prefix)
{
// 输出连接信息 [Server]IP:PORT <---> [Client]IP:PORT
sockaddr_in serverAddr, peerAddr;
socklen_t serverAddrLen = sizeof(serverAddr);
socklen_t peerAddrLen = sizeof(peerAddr);
if((getsockname(client_fd_, (struct sockaddr *)&serverAddr, &serverAddrLen) != -1)
&& (getpeername(client_fd_, (struct sockaddr *)&peerAddr, &peerAddrLen) != -1))
INFO("%s: (socket %d) [Server] %s:%d <---> [Client] %s:%d",
prefix.c_str(), client_fd_,
inet_ntoa(serverAddr.sin_addr), ntohs(serverAddr.sin_port),
inet_ntoa(peerAddr.sin_addr), ntohs(peerAddr.sin_port));
else
ERROR("printConnectionStatus failed ! (%s)", strerror(errno));
}
string escapeStr(const string& str, size_t MAXBUF)
{
string msg = str;
// 遍历所有字符
for(size_t i = 0; i < msg.length(); i++)
{
char ch = msg[i];
// 如果当前字符无法打印,则转义
if(!isprint(ch))
{
// 这里只对\r\n做特殊处理
string substr;
if(ch == '\r')
substr = "\\r";
else if(ch == '\n')
substr = "\\n";
else
{
char hex[10];
// 注意这里要设置成 unsigned,即零扩展
snprintf(hex, 10, "\\x%02x", static_cast<unsigned char>(ch));
substr = hex;
}
msg.replace(i, 1, substr);
}
}
// 将读取到的数据输出
if(msg.length() > MAXBUF)
return msg.substr(0, MAXBUF) + " ... ... ";
else
return msg;
}
bool isNumericStr(string str)
{
for(size_t i = 0; i < str.length(); i++)
if(!isdigit(str[i]))
return false;
return true;
}
size_t closeRemainingConnect(int listen_fd, int* idle_fd) {
close(*idle_fd);
size_t count = 0;
for(;;) {
int client_fd = accept4(listen_fd, nullptr, nullptr,
SOCK_NONBLOCK | SOCK_CLOEXEC);
if(client_fd == -1 && errno == EAGAIN)
break;
close(client_fd);
++count;
}
// 重新恢复空闲描述符
*idle_fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
return count;
}
bool is_path_parent(const string& parent_path, const string& child_path) {
bool result = false;
char* parent_p = nullptr, *child_p = nullptr;
char separator;
parent_p = canonicalize_file_name(parent_path.c_str());
if(!parent_p) {
ERROR("is_path_parent failed, cannot get parent path [%s] (%s)",
parent_path.c_str(),
strerror(errno));
goto clean_parent;
}
child_p = canonicalize_file_name(child_path.c_str());
if(!child_p) {
ERROR("is_path_parent failed, cannot get child path [%s] (%s)",
child_path.c_str(),
strerror(errno));
goto clean_child;
}
// INFO("resolved parent path: %s", parent_p);
INFO("resolved path: %s", child_p);
/* 判断是否存在目录穿越漏洞,判断条件:
1. parent_path 是否在 child_path 的起始位置,
例如 parent: /usr/class/html
与 child: /usr/class/html/index.html
2. 判断 parent_path 末尾是否分割符
例如 parent: /usr/class/html
与 child: /usr/class/htmlflag/../../../../flag
----------------------------A--------------------
这里没有在 html 后面加 /,说明两个路径不对应
*/
if(child_p == strstr(child_p, parent_p)) {
// parent 在 child 中,因此 child[parent.len] 不会越界
separator = child_p[strlen(parent_p)];
if (separator == '\0' || separator == '/')
return true;
}
free(child_p);
clean_child:
free(parent_p);
clean_parent:
return result;
}