From 48591c765f41cd01351d0bcdcb90717db423eeef Mon Sep 17 00:00:00 2001 From: Amir Wiener Date: Thu, 5 Jan 2023 00:36:18 +0200 Subject: [PATCH] we may have received a tcp FIN. in such case we should establish a new connection. If the syslog server have closed the connection it will send a TCP FIN. Current ensureSyslogServerConnection() implementation only check the socket state. Those checks don't cover the TCP FIN case. In order to check it, we should try and read from the socket. If a FIN was sent, read() will return -1. In such case we should mark the socket as invalid, and reconnect. If we won't, the next message we'll send will be rejected with a RST. No exception thrown outside to the caller, and no indication that the message was lost. --- .../syslog/sender/TcpSyslogMessageSender.java | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/cloudbees/syslog/sender/TcpSyslogMessageSender.java b/src/main/java/com/cloudbees/syslog/sender/TcpSyslogMessageSender.java index 249556f..b8b6a04 100644 --- a/src/main/java/com/cloudbees/syslog/sender/TcpSyslogMessageSender.java +++ b/src/main/java/com/cloudbees/syslog/sender/TcpSyslogMessageSender.java @@ -33,11 +33,7 @@ import java.io.OutputStreamWriter; import java.io.Writer; import java.math.BigInteger; -import java.net.ConnectException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.UnknownHostException; +import java.net.*; import java.nio.charset.StandardCharsets; import java.security.cert.Certificate; import java.security.cert.X509Certificate; @@ -143,6 +139,30 @@ private synchronized void ensureSyslogServerConnection() throws IOException { } catch (Exception e) { socketIsValid = false; } + if (socketIsValid) { //we may have received a tcp FIN. in such case we should establish a new connection. + int configuredSoTimeout = socket.getSoTimeout();//keep the current value + try { + //(Note: no 'real' data is expected to be received from the syslog server) + //we intend to read from the socket in order to check if a FIN was sent. + //if it was, read will return -1. + //but if not (which is usual case) we will remain blocked by the read(). + //in order to minimize the blocking time, we're temporarily setting the timeout to the minimal possible value (1 millisecond) + socket.setSoTimeout(1); + int read = socket.getInputStream().read(); + if (read == -1) { //we've received a FIN from the server + logger.fine("A TCP FIN was received from the syslog server. marking current socket as invalid"); + socketIsValid = false; + } + } catch (SocketTimeoutException socketTimeoutException) { + //do nothing. this is the expected. + } catch (IOException e) { + //if any other exception, we don't know what is the problem, but we better mark the socket as invalid. + logger.finer("couldn't read from socket. (for checking if a FIN was received). marking current socket as invalid. " + e.getMessage()); + socketIsValid = false; + } finally { + socket.setSoTimeout(configuredSoTimeout);//restore + } + } if (!socketIsValid) { writer = null; try {