|
15 | 15 | #include "TraceEvent.hpp"
|
16 | 16 | #include "Util.hpp"
|
17 | 17 |
|
| 18 | +#include <cerrno> |
18 | 19 | #include <chrono>
|
19 | 20 | #include <cstring>
|
20 | 21 | #include <cctype>
|
|
26 | 27 | #include <cstdio>
|
27 | 28 | #include <string>
|
28 | 29 | #include <unistd.h>
|
| 30 | +#include <sysexits.h> |
29 | 31 | #include <sys/stat.h>
|
30 | 32 | #include <sys/types.h>
|
31 | 33 | #include <sys/un.h>
|
@@ -67,8 +69,19 @@ std::unique_ptr<Watchdog> SocketPoll::PollWatchdog;
|
67 | 69 |
|
68 | 70 | std::atomic<size_t> StreamSocket::ExternalConnectionCount = 0;
|
69 | 71 |
|
70 |
| -net::DefaultValues net::Defaults = { .inactivityTimeout = std::chrono::seconds(3600), |
71 |
| - .maxExtConnections = 200000 /* arbitrary value to be resolved */ }; |
| 72 | +net::DefaultValues net::Defaults = { .inactivityTimeout = std::chrono::seconds(3600) |
| 73 | + , .maxExtConnections = 0 /* unlimited default */ |
| 74 | +#if !MOBILEAPP && ENABLE_DEBUG |
| 75 | + , .testExternalStreamSocketCtorFailureInterval = 0 |
| 76 | + , .testExternalServerSocketAcceptSimpleErrorInterval = 0 |
| 77 | + , .testExternalServerSocketAcceptFatalErrorInterval = 0 |
| 78 | +#endif |
| 79 | +}; |
| 80 | + |
| 81 | +#if TEST_SERVERSOCKET_FAULT_INJECTION |
| 82 | +std::atomic<size_t> StreamSocket::TestExternalStreamSocketCount = 0; |
| 83 | +std::atomic<size_t> ServerSocket::TestExternalServerSocketAcceptCount = 0; |
| 84 | +#endif |
72 | 85 |
|
73 | 86 | #define SOCKET_ABSTRACT_UNIX_NAME "0coolwsd-"
|
74 | 87 |
|
@@ -1145,63 +1158,156 @@ bool ServerSocket::bind([[maybe_unused]] Type type, [[maybe_unused]] int port)
|
1145 | 1158 | #endif
|
1146 | 1159 | }
|
1147 | 1160 |
|
| 1161 | +bool ServerSocket::isRetryAcceptError(const int cause) |
| 1162 | +{ |
| 1163 | + switch(cause) |
| 1164 | + { |
| 1165 | + case EINTR: |
| 1166 | + case EAGAIN: // == EWOULDBLOCK |
| 1167 | + case ENETDOWN: // man accept(2): to be treated like EAGAIN |
| 1168 | + case EPROTO: // man accept(2): to be treated like EAGAIN |
| 1169 | + case ENOPROTOOPT: // man accept(2): to be treated like EAGAIN |
| 1170 | + case EHOSTDOWN: // man accept(2): to be treated like EAGAIN |
| 1171 | + case ENONET: // man accept(2): to be treated like EAGAIN |
| 1172 | + case EHOSTUNREACH: // man accept(2): to be treated like EAGAIN |
| 1173 | + case EOPNOTSUPP: // man accept(2): to be treated like EAGAIN |
| 1174 | + case ENETUNREACH: // man accept(2): to be treated like EAGAIN |
| 1175 | + case ECONNABORTED: // OK |
| 1176 | + case ETIMEDOUT: // OK, should not happen due ppoll |
| 1177 | + return true; |
| 1178 | + default: |
| 1179 | + return false; |
| 1180 | + } |
| 1181 | +} |
| 1182 | + |
| 1183 | +bool ServerSocket::isRecoverableAcceptError(const int cause) |
| 1184 | +{ |
| 1185 | + switch(cause) |
| 1186 | + { |
| 1187 | + case EINTR: |
| 1188 | + case EAGAIN: // == EWOULDBLOCK |
| 1189 | + case ENETDOWN: // man accept(2): to be treated like EAGAIN |
| 1190 | + case EPROTO: // man accept(2): to be treated like EAGAIN |
| 1191 | + case ENOPROTOOPT: // man accept(2): to be treated like EAGAIN |
| 1192 | + case EHOSTDOWN: // man accept(2): to be treated like EAGAIN |
| 1193 | + case ENONET: // man accept(2): to be treated like EAGAIN |
| 1194 | + case EHOSTUNREACH: // man accept(2): to be treated like EAGAIN |
| 1195 | + case EOPNOTSUPP: // man accept(2): to be treated like EAGAIN |
| 1196 | + case ENETUNREACH: // man accept(2): to be treated like EAGAIN |
| 1197 | + case ECONNABORTED: // OK |
| 1198 | + case ETIMEDOUT: // OK, should not happen due ppoll |
| 1199 | + case EMFILE: // Temporary: No free file descriptors left (per process) |
| 1200 | + case ENFILE: // Temporary: No free file descriptors left (system) |
| 1201 | + case ENOBUFS: // Temporary: No free socket memory left (system) |
| 1202 | + case ENOMEM: // Temporary: No free socket memory left (system) |
| 1203 | + return true; |
| 1204 | + default: |
| 1205 | + return false; |
| 1206 | + } |
| 1207 | +} |
| 1208 | + |
1148 | 1209 | std::shared_ptr<Socket> ServerSocket::accept()
|
1149 | 1210 | {
|
1150 | 1211 | // Accept a connection (if any) and set it to non-blocking.
|
1151 | 1212 | // There still need the client's address to filter request from POST(call from REST) here.
|
| 1213 | + int rc; |
1152 | 1214 | #if !MOBILEAPP
|
1153 | 1215 | assert(_type != Socket::Type::Unix);
|
1154 | 1216 |
|
1155 | 1217 | struct sockaddr_in6 clientInfo;
|
1156 | 1218 | socklen_t addrlen = sizeof(clientInfo);
|
1157 |
| - const int rc = ::accept4(getFD(), (struct sockaddr *)&clientInfo, &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC); |
| 1219 | +#if TEST_SERVERSOCKET_FAULT_INJECTION |
| 1220 | + const size_t acceptCount = ++TestExternalServerSocketAcceptCount; |
| 1221 | + bool injectedError = false; |
| 1222 | + if (net::Defaults.testExternalServerSocketAcceptSimpleErrorInterval > 0 && |
| 1223 | + acceptCount % net::Defaults.testExternalServerSocketAcceptSimpleErrorInterval == 0) |
| 1224 | + { |
| 1225 | + rc = -1; |
| 1226 | + errno = EAGAIN; // recoverable error |
| 1227 | + injectedError = true; |
| 1228 | + LOG_DBG("Injecting recoverable accept failure: EAGAIN: " << acceptCount); |
| 1229 | + } |
| 1230 | + else if (net::Defaults.testExternalServerSocketAcceptFatalErrorInterval > 0 && |
| 1231 | + acceptCount % net::Defaults.testExternalServerSocketAcceptFatalErrorInterval == 0) |
| 1232 | + { |
| 1233 | + rc = -1; |
| 1234 | + errno = EFAULT; // fatal error |
| 1235 | + injectedError = true; |
| 1236 | + LOG_DBG("Injecting fatal accept failure: EAGAIN: " << acceptCount); |
| 1237 | + } |
| 1238 | + else |
| 1239 | +#endif |
| 1240 | + rc = ::accept4(getFD(), (struct sockaddr *)&clientInfo, &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC); |
1158 | 1241 | #else
|
1159 |
| - const int rc = fakeSocketAccept4(getFD()); |
| 1242 | + rc = fakeSocketAccept4(getFD()); |
1160 | 1243 | #endif
|
1161 |
| - LOG_TRC("Accepted socket #" << rc << ", creating socket object."); |
1162 |
| - try |
| 1244 | + if (rc < 0) |
1163 | 1245 | {
|
1164 |
| - // Create a socket object using the factory. |
1165 |
| - if (rc != -1) |
| 1246 | + const int cause = errno; |
| 1247 | + constexpr const char * messagePrefix = "Failed to accept. (errno: "; |
| 1248 | + if (!isRecoverableAcceptError(cause)) |
1166 | 1249 | {
|
1167 |
| -#if !MOBILEAPP |
1168 |
| - char addrstr[INET6_ADDRSTRLEN]; |
1169 |
| - |
1170 |
| - Socket::Type type; |
1171 |
| - const void *inAddr; |
1172 |
| - if (clientInfo.sin6_family == AF_INET) |
1173 |
| - { |
1174 |
| - struct sockaddr_in *ipv4 = (struct sockaddr_in *)&clientInfo; |
1175 |
| - inAddr = &(ipv4->sin_addr); |
1176 |
| - type = Socket::Type::IPv4; |
1177 |
| - } |
| 1250 | + // Unrecoverable error, forceExit instead of throw (closing `ServerSocket` and discard it from `AcceptPoll` during `SocketPoll::poll`) |
| 1251 | + LOG_FTL(messagePrefix << std::to_string(cause) << ", " << std::strerror(cause) << ')'); |
| 1252 | +#if TEST_SERVERSOCKET_FAULT_INJECTION |
| 1253 | + if (injectedError) |
| 1254 | + throw std::runtime_error(std::string(messagePrefix) + std::to_string(cause) + ", " + std::strerror(cause) + ')'); |
1178 | 1255 | else
|
1179 |
| - { |
1180 |
| - struct sockaddr_in6 *ipv6 = &clientInfo; |
1181 |
| - inAddr = &(ipv6->sin6_addr); |
1182 |
| - type = Socket::Type::IPv6; |
1183 |
| - } |
| 1256 | +#endif |
| 1257 | + Util::forcedExit(EX_SOFTWARE); |
| 1258 | + } |
| 1259 | + else // Recoverable error, ignore to retry |
| 1260 | + LOG_DBG(messagePrefix << std::to_string(cause) << ", " << std::strerror(cause) << ')'); |
| 1261 | + return nullptr; |
| 1262 | + } |
| 1263 | + LOG_TRC("Accepted socket #" << rc << ", creating socket object."); |
1184 | 1264 |
|
1185 |
| - std::shared_ptr<Socket> _socket = createSocketFromAccept(rc, type); |
| 1265 | +#if !MOBILEAPP |
| 1266 | + char addrstr[INET6_ADDRSTRLEN]; |
1186 | 1267 |
|
1187 |
| - ::inet_ntop(clientInfo.sin6_family, inAddr, addrstr, sizeof(addrstr)); |
1188 |
| - _socket->setClientAddress(addrstr, clientInfo.sin6_port); |
| 1268 | + Socket::Type type; |
| 1269 | + const void *inAddr; |
| 1270 | + if (clientInfo.sin6_family == AF_INET) |
| 1271 | + { |
| 1272 | + struct sockaddr_in *ipv4 = (struct sockaddr_in *)&clientInfo; |
| 1273 | + inAddr = &(ipv4->sin_addr); |
| 1274 | + type = Socket::Type::IPv4; |
| 1275 | + } |
| 1276 | + else |
| 1277 | + { |
| 1278 | + struct sockaddr_in6 *ipv6 = &clientInfo; |
| 1279 | + inAddr = &(ipv6->sin6_addr); |
| 1280 | + type = Socket::Type::IPv6; |
| 1281 | + } |
| 1282 | + ::inet_ntop(clientInfo.sin6_family, inAddr, addrstr, sizeof(addrstr)); |
1189 | 1283 |
|
1190 |
| - LOG_TRC("Accepted socket #" << _socket->getFD() << " has family " |
1191 |
| - << clientInfo.sin6_family << ", " << *_socket); |
1192 |
| -#else |
1193 |
| - std::shared_ptr<Socket> _socket = createSocketFromAccept(rc, Socket::Type::Unix); |
1194 |
| -#endif |
1195 |
| - return _socket; |
1196 |
| - } |
1197 |
| - return std::shared_ptr<Socket>(nullptr); |
| 1284 | + const size_t extConnCount = StreamSocket::getExternalConnectionCount(); |
| 1285 | + if( 0 < net::Defaults.maxExtConnections && extConnCount >= net::Defaults.maxExtConnections ) |
| 1286 | + { |
| 1287 | + LOG_WRN("Limiter rejected extConn[" << extConnCount << "/" << net::Defaults.maxExtConnections << "]: #" |
| 1288 | + << rc << " has family " |
| 1289 | + << clientInfo.sin6_family << ", address " << addrstr << ":" << clientInfo.sin6_port); |
| 1290 | + ::close(rc); |
| 1291 | + return nullptr; |
| 1292 | + } |
| 1293 | + try |
| 1294 | + { |
| 1295 | + // Create a socket object using the factory. |
| 1296 | + std::shared_ptr<Socket> _socket = createSocketFromAccept(rc, type); |
| 1297 | + _socket->setClientAddress(addrstr, clientInfo.sin6_port); |
| 1298 | + |
| 1299 | + LOG_TRC("Accepted socket #" << _socket->getFD() << " has family " |
| 1300 | + << clientInfo.sin6_family << ", " << *_socket); |
| 1301 | + return _socket; |
1198 | 1302 | }
|
1199 | 1303 | catch (const std::exception& ex)
|
1200 | 1304 | {
|
1201 | 1305 | LOG_ERR("Failed to create client socket #" << rc << ". Error: " << ex.what());
|
1202 | 1306 | }
|
1203 |
| - |
1204 | 1307 | return nullptr;
|
| 1308 | +#else |
| 1309 | + return createSocketFromAccept(rc, Socket::Type::Unix); |
| 1310 | +#endif |
1205 | 1311 | }
|
1206 | 1312 |
|
1207 | 1313 | #if !MOBILEAPP
|
@@ -1247,11 +1353,21 @@ bool Socket::isLocal() const
|
1247 | 1353 | std::shared_ptr<Socket> LocalServerSocket::accept()
|
1248 | 1354 | {
|
1249 | 1355 | const int rc = ::accept4(getFD(), nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
| 1356 | + if (rc < 0) |
| 1357 | + { |
| 1358 | + const int cause = errno; |
| 1359 | + if( cause != EINTR && cause != EAGAIN && cause != EWOULDBLOCK ) |
| 1360 | + { |
| 1361 | + // Unrecoverable error, throw, which will close `ServerSocket` and discard it from `AcceptPoll` during `SocketPoll::poll` |
| 1362 | + constexpr const char * messagePrefix = "Failed to accept. (errno: "; |
| 1363 | + LOG_FTL(messagePrefix << std::to_string(cause) << ", " << std::strerror(cause) << ')'); |
| 1364 | + Util::forcedExit(EX_SOFTWARE); |
| 1365 | + } |
| 1366 | + return nullptr; |
| 1367 | + } |
1250 | 1368 | try
|
1251 | 1369 | {
|
1252 | 1370 | LOG_DBG("Accepted prisoner socket #" << rc << ", creating socket object.");
|
1253 |
| - if (rc < 0) |
1254 |
| - return std::shared_ptr<Socket>(nullptr); |
1255 | 1371 |
|
1256 | 1372 | std::shared_ptr<Socket> _socket = createSocketFromAccept(rc, Socket::Type::Unix);
|
1257 | 1373 | // Sanity check this incoming socket
|
@@ -1301,8 +1417,8 @@ std::shared_ptr<Socket> LocalServerSocket::accept()
|
1301 | 1417 | catch (const std::exception& ex)
|
1302 | 1418 | {
|
1303 | 1419 | LOG_ERR("Failed to create client socket #" << rc << ". Error: " << ex.what());
|
1304 |
| - return std::shared_ptr<Socket>(nullptr); |
1305 | 1420 | }
|
| 1421 | + return nullptr; |
1306 | 1422 | }
|
1307 | 1423 |
|
1308 | 1424 | /// Returns true on success only.
|
|
0 commit comments