forked from fuzziqersoftware/phosg
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathNetwork.cc
249 lines (206 loc) · 6.7 KB
/
Network.cc
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
#define __STDC_FORMAT_MACROS
#include "Network.hh"
#ifndef WINDOWS
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdexcept>
#include <string>
#include "Filesystem.hh"
#include "Strings.hh"
using namespace std;
pair<struct sockaddr_storage, size_t> make_sockaddr_storage(const string& addr,
int port) {
struct sockaddr_storage s;
memset(&s, 0, sizeof(s));
if (port == 0) {
// unix socket
#ifdef WINDOWS
throw logic_error("Unix sockets cannot be used on Windows");
#else
struct sockaddr_un* sun = (struct sockaddr_un*)&s;
if ((addr.size() + 1) > sizeof(sun->sun_path)) {
throw runtime_error("socket path is too long");
}
sun->sun_family = AF_UNIX;
strcpy(sun->sun_path, addr.c_str());
return make_pair(s, sizeof(sockaddr_un));
#endif
}
// inet or inet6
size_t ret_size = sizeof(struct sockaddr_in);
if (addr.empty()) {
struct sockaddr_in* sin = (struct sockaddr_in*)&s;
sin->sin_family = AF_INET;
sin->sin_port = (port > 0) ? htons(port) : 0;
sin->sin_addr.s_addr = htonl(INADDR_ANY);
} else {
struct addrinfo *res0;
if (getaddrinfo(addr.c_str(), NULL, NULL, &res0)) {
throw runtime_error("can\'t resolve hostname " + addr + ": " + string_for_error(errno));
}
std::unique_ptr<struct addrinfo, void(*)(struct addrinfo*)> res0_unique(
res0, freeaddrinfo);
struct addrinfo *res4 = NULL, *res6 = NULL;
for (struct addrinfo* res = res0; res; res = res->ai_next) {
if (!res4 && (res->ai_family == AF_INET)) {
res4 = res;
} else if (!res6 && (res->ai_family == AF_INET6)) {
res6 = res;
}
}
if (!res4 && !res6) {
throw runtime_error("can\'t resolve hostname " + addr + ": no usable data");
}
if (res4) {
struct sockaddr_in* res_sin = (struct sockaddr_in*)res4->ai_addr;
struct sockaddr_in* sin = (struct sockaddr_in*)&s;
sin->sin_family = AF_INET;
sin->sin_port = (port > 0) ? htons(port) : 0;
sin->sin_addr.s_addr = res_sin->sin_addr.s_addr;
} else { // res->ai_family == AF_INET6
struct sockaddr_in6* res_sin6 = (struct sockaddr_in6*)res6->ai_addr;
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&s;
sin6->sin6_family = AF_INET6;
sin6->sin6_port = (port > 0) ? htons(port) : 0;
memcpy(&sin6->sin6_addr, &res_sin6->sin6_addr, sizeof(sin6->sin6_addr));
ret_size = sizeof(struct sockaddr_in6);
}
}
return make_pair(s, ret_size);
}
string render_sockaddr_storage(const sockaddr_storage& s) {
switch (s.ss_family) {
#ifndef WINDOWS
case AF_UNIX: {
struct sockaddr_un* sun = (struct sockaddr_un*)&s;
return string(sun->sun_path);
}
#endif
case AF_INET: {
struct sockaddr_in* sin = (struct sockaddr_in*)&s;
char buf[INET_ADDRSTRLEN];
if (!inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf) / sizeof(buf[0]))) {
return string_printf("<UNPRINTABLE-IPV4-ADDRESS>:%" PRIu16,
ntohs(sin->sin_port));
}
return string_printf("%s:%" PRIu16, buf, ntohs(sin->sin_port));
}
case AF_INET6: {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&s;
char buf[INET6_ADDRSTRLEN];
if (!inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof(buf) / sizeof(buf[0]))) {
return string_printf("<UNPRINTABLE-IPV6-ADDRESS>:%" PRIu16,
ntohs(sin6->sin6_port));
}
return string_printf("[%s]:%" PRIu16, buf, ntohs(sin6->sin6_port));
}
default:
return "<INVALID-ADDRESS-FAMILY>";
}
}
int listen(const string& addr, int port, int backlog, bool nonblocking) {
pair<struct sockaddr_storage, size_t> s = make_sockaddr_storage(addr, port);
int fd = socket(s.first.ss_family, backlog ? SOCK_STREAM : SOCK_DGRAM,
port ? (backlog ? IPPROTO_TCP : IPPROTO_UDP) : 0);
if (fd == -1) {
throw runtime_error("can\'t create socket: " + string_for_error(errno));
}
int y = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&y), sizeof(y)) == -1) {
close(fd);
throw runtime_error("can\'t enable address reuse: " + string_for_error(errno));
}
if (port == 0) {
// delete the socket file before we start listening on it
unlink(addr);
}
if (::bind(fd, (struct sockaddr*)&s.first, s.second) != 0) {
close(fd);
throw runtime_error("can\'t bind socket to " + render_sockaddr_storage(s.first) +
": " + string_for_error(errno));
}
// only listen() on stream sockets
if (backlog && (listen(fd, backlog) != 0)) {
close(fd);
throw runtime_error("can\'t listen on socket: " + string_for_error(errno));
}
if (nonblocking) {
make_fd_nonblocking(fd);
}
return fd;
}
int connect(const string& addr, int port, bool nonblocking) {
pair<struct sockaddr_storage, size_t> s = make_sockaddr_storage(addr, port);
int fd = socket(s.first.ss_family, SOCK_STREAM, port ? IPPROTO_TCP : 0);
if (fd == -1) {
throw runtime_error("can\'t create socket: " + string_for_error(errno));
}
if (connect(fd, (struct sockaddr*)&s.first, s.second) == -1) {
close(fd);
throw runtime_error("can\'t connect socket: " + string_for_error(errno));
}
if (nonblocking) {
make_fd_nonblocking(fd);
}
return fd;
}
void get_socket_addresses(int fd, struct sockaddr_storage* local,
struct sockaddr_storage* remote) {
socklen_t len;
if (local) {
len = sizeof(struct sockaddr_storage);
getsockname(fd, (struct sockaddr*)local, &len);
}
if (remote) {
len = sizeof(struct sockaddr_storage);
getpeername(fd, (struct sockaddr*)remote, &len);
}
}
string render_netloc(const string& addr, int port) {
if (addr.empty()) {
if (!port) {
return "<unknown>";
} else {
return to_string(port);
}
} else {
if (!port) {
return addr;
} else {
return addr + ":" + to_string(port);
}
}
}
pair<string, uint16_t> parse_netloc(const string& netloc, int default_port) {
size_t colon_loc = netloc.find(':');
if (colon_loc == string::npos) {
return make_pair(netloc, default_port);
}
return make_pair(netloc.substr(0, colon_loc), stod(netloc.substr(colon_loc + 1)));
}
string gethostname() {
#ifdef WINDOWS
// this is actually the max size according to MS documentation; there doesn't
// appear to be a define for this or anything
string buf(0x100, '\0');
#else
string buf(sysconf(_SC_HOST_NAME_MAX) + 1, '\0');
#endif
if (gethostname(const_cast<char*>(buf.data()), buf.size())) {
throw runtime_error("can\'t get hostname");
}
buf.resize(strlen(buf.c_str()));
return buf;
}