|
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>
|
@@ -1149,63 +1151,119 @@ bool ServerSocket::bind([[maybe_unused]] Type type, [[maybe_unused]] int port)
|
1149 | 1151 | #endif
|
1150 | 1152 | }
|
1151 | 1153 |
|
| 1154 | +bool ServerSocket::isRetryAcceptError(const int cause) |
| 1155 | +{ |
| 1156 | + switch(cause) |
| 1157 | + { |
| 1158 | + case EINTR: |
| 1159 | + case EAGAIN: // == EWOULDBLOCK |
| 1160 | + case ENETDOWN: // man accept(2): to be treated like EAGAIN |
| 1161 | + case EPROTO: // man accept(2): to be treated like EAGAIN |
| 1162 | + case ENOPROTOOPT: // man accept(2): to be treated like EAGAIN |
| 1163 | + case EHOSTDOWN: // man accept(2): to be treated like EAGAIN |
| 1164 | +#ifdef ENONET |
| 1165 | + case ENONET: // man accept(2): to be treated like EAGAIN (undef on BSD, APPLE, ..) |
| 1166 | +#endif |
| 1167 | + case EHOSTUNREACH: // man accept(2): to be treated like EAGAIN |
| 1168 | + case EOPNOTSUPP: // man accept(2): to be treated like EAGAIN |
| 1169 | + case ENETUNREACH: // man accept(2): to be treated like EAGAIN |
| 1170 | + case ECONNABORTED: // OK |
| 1171 | + case ETIMEDOUT: // OK, should not happen due ppoll |
| 1172 | + return true; |
| 1173 | + default: |
| 1174 | + return false; |
| 1175 | + } |
| 1176 | +} |
| 1177 | + |
1152 | 1178 | std::shared_ptr<Socket> ServerSocket::accept()
|
1153 | 1179 | {
|
1154 | 1180 | // Accept a connection (if any) and set it to non-blocking.
|
1155 | 1181 | // There still need the client's address to filter request from POST(call from REST) here.
|
1156 | 1182 | #if !MOBILEAPP
|
1157 | 1183 | assert(_type != Socket::Type::Unix);
|
1158 | 1184 |
|
| 1185 | +#if ENABLE_DEBUG |
| 1186 | + UnitWSD* const unitWsd = UnitWSD::isUnitTesting() ? &UnitWSD::get() : nullptr; |
| 1187 | + if (unitWsd && unitWsd->simulateExternalAcceptError()) |
| 1188 | + return nullptr; // Recoverable error, ignore to retry |
| 1189 | +#endif |
| 1190 | + |
1159 | 1191 | struct sockaddr_in6 clientInfo;
|
1160 | 1192 | socklen_t addrlen = sizeof(clientInfo);
|
1161 | 1193 | const int rc = ::accept4(getFD(), (struct sockaddr *)&clientInfo, &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
1162 | 1194 | #else
|
1163 | 1195 | const int rc = fakeSocketAccept4(getFD());
|
1164 | 1196 | #endif
|
1165 |
| - LOG_TRC("Accepted socket #" << rc << ", creating socket object."); |
1166 |
| - try |
| 1197 | + if (rc < 0) |
1167 | 1198 | {
|
1168 |
| - // Create a socket object using the factory. |
1169 |
| - if (rc != -1) |
| 1199 | + const int cause = errno; |
| 1200 | + constexpr const char * messagePrefix = "Failed to accept. (errno: "; |
| 1201 | + if (isRetryAcceptError(cause)) |
1170 | 1202 | {
|
1171 |
| -#if !MOBILEAPP |
1172 |
| - char addrstr[INET6_ADDRSTRLEN]; |
1173 |
| - |
1174 |
| - Socket::Type type; |
1175 |
| - const void *inAddr; |
1176 |
| - if (clientInfo.sin6_family == AF_INET) |
1177 |
| - { |
1178 |
| - struct sockaddr_in *ipv4 = (struct sockaddr_in *)&clientInfo; |
1179 |
| - inAddr = &(ipv4->sin_addr); |
1180 |
| - type = Socket::Type::IPv4; |
1181 |
| - } |
1182 |
| - else |
1183 |
| - { |
1184 |
| - struct sockaddr_in6 *ipv6 = &clientInfo; |
1185 |
| - inAddr = &(ipv6->sin6_addr); |
1186 |
| - type = Socket::Type::IPv6; |
1187 |
| - } |
| 1203 | + // Recoverable error, ignore to retry |
| 1204 | + LOG_DBG(messagePrefix << std::to_string(cause) << ", " << std::strerror(cause) << ')'); |
| 1205 | + } |
| 1206 | + else |
| 1207 | + { |
| 1208 | + // Unrecoverable error, forceExit instead of throw (closing `ServerSocket` and discard it from `AcceptPoll` during `SocketPoll::poll`) |
| 1209 | + // May allow recovery from temporary EMFILE, ENFILE, ENOBUFS and ENOMEM errors? |
| 1210 | + LOG_FTL(messagePrefix << std::to_string(cause) << ", " << std::strerror(cause) << ')'); |
| 1211 | + Util::forcedExit(EX_SOFTWARE); |
| 1212 | + } |
| 1213 | + return nullptr; |
| 1214 | + } |
| 1215 | + LOG_TRC("Accepted socket #" << rc << ", creating socket object."); |
1188 | 1216 |
|
1189 |
| - std::shared_ptr<Socket> _socket = createSocketFromAccept(rc, type); |
| 1217 | +#if !MOBILEAPP |
| 1218 | + char addrstr[INET6_ADDRSTRLEN]; |
1190 | 1219 |
|
1191 |
| - ::inet_ntop(clientInfo.sin6_family, inAddr, addrstr, sizeof(addrstr)); |
1192 |
| - _socket->setClientAddress(addrstr, clientInfo.sin6_port); |
| 1220 | + Socket::Type type; |
| 1221 | + const void *inAddr; |
| 1222 | + if (clientInfo.sin6_family == AF_INET) |
| 1223 | + { |
| 1224 | + struct sockaddr_in *ipv4 = (struct sockaddr_in *)&clientInfo; |
| 1225 | + inAddr = &(ipv4->sin_addr); |
| 1226 | + type = Socket::Type::IPv4; |
| 1227 | + } |
| 1228 | + else |
| 1229 | + { |
| 1230 | + struct sockaddr_in6 *ipv6 = &clientInfo; |
| 1231 | + inAddr = &(ipv6->sin6_addr); |
| 1232 | + type = Socket::Type::IPv6; |
| 1233 | + } |
| 1234 | + ::inet_ntop(clientInfo.sin6_family, inAddr, addrstr, sizeof(addrstr)); |
1193 | 1235 |
|
1194 |
| - LOG_TRC("Accepted socket #" << _socket->getFD() << " has family " |
1195 |
| - << clientInfo.sin6_family << ", " << *_socket); |
1196 |
| -#else |
1197 |
| - std::shared_ptr<Socket> _socket = createSocketFromAccept(rc, Socket::Type::Unix); |
| 1236 | + const size_t extConnCount = StreamSocket::getExternalConnectionCount(); |
| 1237 | + if (0 < net::Defaults.maxExtConnections && extConnCount >= net::Defaults.maxExtConnections) |
| 1238 | + { |
| 1239 | + LOG_WRN("Limiter rejected extConn[" << extConnCount << "/" << net::Defaults.maxExtConnections << "]: #" |
| 1240 | + << rc << " has family " |
| 1241 | + << clientInfo.sin6_family << ", address " << addrstr << ":" << clientInfo.sin6_port); |
| 1242 | + ::close(rc); |
| 1243 | + return nullptr; |
| 1244 | + } |
| 1245 | + try |
| 1246 | + { |
| 1247 | + // Create a socket object using the factory. |
| 1248 | + std::shared_ptr<Socket> _socket = createSocketFromAccept(rc, type); |
| 1249 | +#if ENABLE_DEBUG |
| 1250 | + if (unitWsd) |
| 1251 | + unitWsd->simulateExternalSocketCtorException(_socket); |
1198 | 1252 | #endif
|
1199 |
| - return _socket; |
1200 |
| - } |
1201 |
| - return std::shared_ptr<Socket>(nullptr); |
| 1253 | + _socket->setClientAddress(addrstr, clientInfo.sin6_port); |
| 1254 | + |
| 1255 | + LOG_TRC("Accepted socket #" << _socket->getFD() << " has family " |
| 1256 | + << clientInfo.sin6_family << ", " << *_socket); |
| 1257 | + return _socket; |
1202 | 1258 | }
|
1203 | 1259 | catch (const std::exception& ex)
|
1204 | 1260 | {
|
1205 | 1261 | LOG_ERR("Failed to create client socket #" << rc << ". Error: " << ex.what());
|
1206 | 1262 | }
|
1207 |
| - |
1208 | 1263 | return nullptr;
|
| 1264 | +#else |
| 1265 | + return createSocketFromAccept(rc, Socket::Type::Unix); |
| 1266 | +#endif |
1209 | 1267 | }
|
1210 | 1268 |
|
1211 | 1269 | #if !MOBILEAPP
|
@@ -1251,11 +1309,27 @@ bool Socket::isLocal() const
|
1251 | 1309 | std::shared_ptr<Socket> LocalServerSocket::accept()
|
1252 | 1310 | {
|
1253 | 1311 | const int rc = ::accept4(getFD(), nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
| 1312 | + if (rc < 0) |
| 1313 | + { |
| 1314 | + constexpr const char * messagePrefix = "Failed to accept. (errno: "; |
| 1315 | + const int cause = errno; |
| 1316 | + if (isRetryAcceptError(cause)) |
| 1317 | + { |
| 1318 | + // Recoverable error, ignore to retry |
| 1319 | + LOG_DBG(messagePrefix << std::to_string(cause) << ", " << std::strerror(cause) << ')'); |
| 1320 | + } |
| 1321 | + else |
| 1322 | + { |
| 1323 | + // Unrecoverable error, forced exit. Throw would close `ServerSocket` and discard it from `AcceptPoll` during `SocketPoll::poll`. |
| 1324 | + // May allow recovery from temporary EMFILE, ENFILE, ENOBUFS and ENOMEM errors? |
| 1325 | + LOG_FTL(messagePrefix << std::to_string(cause) << ", " << std::strerror(cause) << ')'); |
| 1326 | + Util::forcedExit(EX_SOFTWARE); |
| 1327 | + } |
| 1328 | + return nullptr; |
| 1329 | + } |
1254 | 1330 | try
|
1255 | 1331 | {
|
1256 | 1332 | LOG_DBG("Accepted prisoner socket #" << rc << ", creating socket object.");
|
1257 |
| - if (rc < 0) |
1258 |
| - return std::shared_ptr<Socket>(nullptr); |
1259 | 1333 |
|
1260 | 1334 | std::shared_ptr<Socket> _socket = createSocketFromAccept(rc, Socket::Type::Unix);
|
1261 | 1335 | // Sanity check this incoming socket
|
@@ -1305,8 +1379,8 @@ std::shared_ptr<Socket> LocalServerSocket::accept()
|
1305 | 1379 | catch (const std::exception& ex)
|
1306 | 1380 | {
|
1307 | 1381 | LOG_ERR("Failed to create client socket #" << rc << ". Error: " << ex.what());
|
1308 |
| - return std::shared_ptr<Socket>(nullptr); |
1309 | 1382 | }
|
| 1383 | + return nullptr; |
1310 | 1384 | }
|
1311 | 1385 |
|
1312 | 1386 | /// Returns true on success only.
|
|
0 commit comments