New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can't get TCP logging to work #139
Comments
Try all the steps in this guide: Then run the sample application from Visual Studio, editing the text configuration file, to debug the error in SafeFromAsync Finally try running this code to see if you still get an error: var tcp = new TcpClient();
tcp.Connect(stringIpAddress, integerPort);
byte[] data = { 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A };
tcp.GetStream().Write(data, 0, data.Length); |
Closing for inactivity: feel free to reopen if needed |
Hi, it is vacation times in Sweden so I will be unresponsive from time to time. I added your sample code (above) to my project and it works like a charm - "hello" to you too! I have no clue about what is wrong... |
You should then debug my code because I extracted those lines from what it does Try also to run a Syslog Linux VM or container to reduce the complexity due to Kubernetes |
I have tried to make a code review of the source here in github and I can not really see any problem... Very confusing. |
I will try to make a local dependency and debug your code and see what happen. |
While debugging I never reached Tcp.WriteAsync(..). I tried to change from the default FramingMethod.OctetCounting to FramingMethod.NonTransparent. By doing so, I got calls through to WriteAsync and logs started to appear in papertrail. My project use TargetFramework netcoreapp2.0, could that be a problem? In some documentation it seems like the methods Stream.BeginWrite and Stream.EndWrite is legacy and should be avoided. To me it looks complicated to use them, but maybe this is a compability issue? |
The original error you reported is
The framing method should be supported and configured by the Syslog server and cannot cause an IOException: can you debug framing and tell me what exception prevents
The target framework is not a problem and you can see that the test console application is targeting netcoreapp2.0
SafeFromAsync is doing what it is advised to do with FromAsync but avoiding problems related to errors not being wrapped in a Task, therefore it is correct to use Begin/End methods |
It seems like papertrail closes the remote connection whenever the framing header is written. I suspect that papertrail do not support framing for unsecure TCP connections. As soon as I disable octet framing, it works fine. If I try to enable TLS without client certificates the connection is closed immediately when AuthenticateAsClient is called. Looking into papertrail documentation it seems like client certificate is required (but it is not clearly stated). If I install the certificates and enable client certificates, the remote server still close the connection as soon as I try to authenticate. And I have enabled all connection methods UDP, TCP and TCP/TLS in papertrail so that should not be the issue. |
I'm seeing the same IOException with v5.0.0 (full framework) with a tcp connection to an rsyslog server. Missing messages are accompanied by the following in
I tried switching the framing to |
I'm back from vacation now - and will be much more responsive. Is there anything more I could do to get closer to a solution of this issue? |
The biggest changes from 4.1.0 to 5.0.0 are:
What you can try to do is commenting the following lines and see if the error is still present: var socketInitialization = SocketInitialization.ForCurrentOs(tcp.Client);
socketInitialization.DisableAddressSharing();
socketInitialization.DiscardPendingDataOnClose();
socketInitialization.SetKeepAlive(keepAliveConfig); |
I used the latest code in the master branch and made the change you suggested above (commenting out the lines in Tcp.cs). Now logging over TCP (without touching the framing method) works like a charm. Also, I tried activating TLS and that works fine too! So how do we take this further? |
If possible try to identify which line or lines of the three aboves is the source of the issue, thanks for the support! |
I will try to comment out different combinations of the socket initialization stuff and see if I can narrow down the problem somehow. Just realize I forgot to mention, that on my own machine I run windows but I host my services in linux dockers using kubernetes in Azure. The problem appear both on my machine and in my hosted environment. So the problem seems to be cross platform :-) |
Papertrail had an outage most of yesterday(!), but I have now tested and found out that it was the keep alive configuration that messed up things for me. If I change my configuration to set KeepAlive.Enabled = false everything seems to work fine. No idea why... Still, I do not really understand why a keepalive configuration would mess up things and close the connection all the time. The default times in the keepalive configuration was single digit times, can that be a problem? |
As per my comment it seems that the problem is due to the Version 5.0.0 introduced the implementation for Linux and OS X, but, since you said version 4.1.0 was working, you are using Windows. Version 4.1.0 // Disable address sharing
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, true);
// Discard pending data on close
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, false);
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(true, 0));
// Set keep-alive
tcp.Client.IOControl(IOControlCode.KeepAliveValues, keepAlive.ToByteArray(), null); Version 5.0.1 // socketInitialization.DisableAddressSharing();
Socket.ExclusiveAddressUse = true;
// socketInitialization.DiscardPendingDataOnClose();
Socket.LingerState = new LingerOption(true, 0);
// socketInitialization.SetKeepAlive(keepAliveConfig);
Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, keepAliveConfig.Enabled);
if (keepAliveConfig.Enabled)
{
if (isWin10V1703OrLater)
Socket.SetSocketOption(SocketOptionLevel.Tcp, TcpKeepAliveRetryCount, keepAliveConfig.RetryCount);
if (isBelowWin10V1709)
{
// Call WSAIoctl via IOControl
Socket.IOControl(IOControlCode.KeepAliveValues, new IOControlKeepAliveValues(keepAliveConfig).ToByteArray(), null);
return;
}
Socket.SetSocketOption(SocketOptionLevel.Tcp, TcpKeepAliveTime, keepAliveConfig.Time);
Socket.SetSocketOption(SocketOptionLevel.Tcp, TcpKeepAliveInterval, keepAliveConfig.Interval);
} You should try to use version 4.1.0 code for the |
I have a hard time figuring out if we understand each other or not. I'm trying to be helpful, but I am not sure I am... Sorry. When it comes to operating systems, I have problem with "vanilla" 5.0.1 on both windows and linux. I have cloned your master branch and is currently located at the tag "5.0.1". Test 1 Test 2 Test 3 protected override Task Init()
{
tcp = new TcpClient();
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, true);
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, false);
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(true, 0));
/*
var socketInitialization = SocketInitialization.ForCurrentOs(tcp.Client);
socketInitialization.DisableAddressSharing();
socketInitialization.DiscardPendingDataOnClose();
socketInitialization.SetKeepAlive(keepAliveConfig);
*/
return tcp
.ConnectAsync(IpAddress, Port)
.Then(_ => stream = SslDecorate(tcp), CancellationToken.None);
} Result: Logging over https works fine. Test 4 protected override Task Init()
{
tcp = new TcpClient();
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, true);
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, false);
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(true, 0));
var socketInitialization = SocketInitialization.ForCurrentOs(tcp.Client);
// socketInitialization.DisableAddressSharing();
// socketInitialization.DiscardPendingDataOnClose();
socketInitialization.SetKeepAlive(keepAliveConfig);
return tcp
.ConnectAsync(IpAddress, Port)
.Then(_ => stream = SslDecorate(tcp), CancellationToken.None);
} Result: I get exception at Tcp.cs at line 88 (in SslDecorate) while calling Test 5 Did this contain the information you wanted? |
Thanks @mrtoby, you did a great job and I am sorry that what I said was not clear. Let's address first the behavior on Windows, since Linux was not supported before and, at the moment, we cannot know if it can work with different code. Supposing, as you did, to clone the master branch (v5.0.1 tag) I need this further tests: Test A protected override Task Init()
{
tcp = new TcpClient();
// Disable address sharing
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, true);
// Discard pending data on close
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, false);
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(true, 0));
// Set keep-alive
tcp.Client.IOControl(IOControlCode.KeepAliveValues, new IOControlKeepAliveValues(keepAliveConfig).ToByteArray(), null);
return tcp
.ConnectAsync(IpAddress, Port)
.Then(_ => stream = SslDecorate(tcp), CancellationToken.None);
} Test B protected override Task Init()
{
tcp = new TcpClient();
// Disable address sharing
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, true);
// Discard pending data on close
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, false);
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(true, 0));
// Set keep-alive
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, keepAliveConfig.Enabled);
tcp.Client.IOControl(IOControlCode.KeepAliveValues, new IOControlKeepAliveValues(keepAliveConfig).ToByteArray(), null);
return tcp
.ConnectAsync(IpAddress, Port)
.Then(_ => stream = SslDecorate(tcp), CancellationToken.None);
} Test C protected override Task Init()
{
tcp = new TcpClient();
var socketInitialization = SocketInitialization.ForCurrentOs(tcp.Client);
// Disable address sharing
socketInitialization.DisableAddressSharing();
// Discard pending data on close
socketInitialization.DiscardPendingDataOnClose();
// Set keep-alive
tcp.Client.IOControl(IOControlCode.KeepAliveValues, new IOControlKeepAliveValues(keepAliveConfig).ToByteArray(), null);
return tcp
.ConnectAsync(IpAddress, Port)
.Then(_ => stream = SslDecorate(tcp), CancellationToken.None);
} Test D protected override Task Init()
{
tcp = new TcpClient();
var socketInitialization = SocketInitialization.ForCurrentOs(tcp.Client);
// Disable address sharing
socketInitialization.DisableAddressSharing();
// Discard pending data on close
socketInitialization.DiscardPendingDataOnClose();
// Set keep-alive
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, keepAliveConfig.Enabled);
tcp.Client.IOControl(IOControlCode.KeepAliveValues, new IOControlKeepAliveValues(keepAliveConfig).ToByteArray(), null);
return tcp
.ConnectAsync(IpAddress, Port)
.Then(_ => stream = SslDecorate(tcp), CancellationToken.None);
} |
I performed the tests above with TLS enabled and I got the same result:
Exception at Tcp.cs line 105, sslStream.AuthenticateAsClient
System.IO.IOException: Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host.
Inner exception: SocketException: An existing connection was forcibly closed by the remote host
I tried this at two different offices and at home: it seems unlikely that it is a local network issue or something like that.
|
public class MyTest
{
private readonly bool useTls;
private readonly TlsConfig tlsConfig;
private readonly string ipAddress;
private readonly int port;
private readonly string serverNameInTheCertificate;
public MyTest()
{
useTls = false;
tlsConfig = new TlsConfig();
//
// Setup TLS configuration
//
ipAddress = '127.0.0.1';
port = 60123;
serverNameInTheCertificate = 'myAmazingSecureServer';
}
public void Go()
{
var tcp = new TcpClient();
// Disable address sharing
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, true);
// Discard pending data on close
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, false);
tcp.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(true, 0));
// Set keep-alive
tcp.Client.IOControl(IOControlCode.KeepAliveValues, new IOControlKeepAliveValues(keepAliveConfig).ToByteArray(), null);
tcp.Connect(ipAddress, port);
byte[] data = { 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A };
var tcpStream = tcp.GetStream();
if (!useTls)
{
tcpStream.Write(data, 0, data.Length);
return;
}
var sslStream = new SslStream(tcpStream, true);
sslStream.AuthenticateAsClient(serverNameInTheCertificate, tlsConfig.RetrieveClientCertificates(), SslProtocols.Tls12, false);
sslStream.Write(data, 0, data.Length);
}
}
public class TlsConfig
{
private bool enabled;
private bool useClientCertificates;
private StoreLocation certificateStoreLocation;
private StoreName certificateStoreName;
private X509FindType certificateFilterType;
private string certificateFilterValue;
public bool Enabled { get; set; }
public bool UseClientCertificates { get; set; }
public StoreLocation CertificateStoreLocation { get; set; }
public StoreName CertificateStoreName { get; set; }
public X509FindType CertificateFilterType { get; set; }
public string CertificateFilterValue { get; set; }
public TlsConfig()
{
enabled = false;
useClientCertificates = false;
certificateStoreLocation = StoreLocation.CurrentUser;
certificateStoreName = StoreName.My;
certificateFilterType = X509FindType.FindBySubjectName;
certificateFilterValue = null;
}
public X509Certificate2Collection RetrieveClientCertificates()
{
if (!useClientCertificates)
return null;
var store = new X509Store(certificateStoreName, certificateStoreLocation);
try
{
store.Open(OpenFlags.ReadOnly);
return certificateFilterValue == null ?
store.Certificates :
store.Certificates.Find(certificateFilterType, BuildFindValue(), false);
}
finally
{
store.Close();
}
}
private object BuildFindValue()
{
switch (certificateFilterType)
{
case X509FindType.FindByTimeExpired:
case X509FindType.FindByTimeNotYetValid:
case X509FindType.FindByTimeValid:
{
return DateTime.Parse(certificateFilterValue);
}
case X509FindType.FindByKeyUsage:
{
if (int.TryParse(certificateFilterValue, out var keyUsages))
return keyUsages;
return certificateFilterValue;
}
case X509FindType.FindByThumbprint:
case X509FindType.FindBySubjectName:
case X509FindType.FindBySubjectDistinguishedName:
case X509FindType.FindByIssuerName:
case X509FindType.FindByIssuerDistinguishedName:
case X509FindType.FindBySerialNumber:
case X509FindType.FindByTemplateName:
case X509FindType.FindByApplicationPolicy:
case X509FindType.FindByCertificatePolicy:
case X509FindType.FindByExtension:
case X509FindType.FindBySubjectKeyIdentifier:
{
return certificateFilterValue;
}
default:
{
throw new ArgumentOutOfRangeException();
}
}
}
}
public class IOControlKeepAliveValues
{
private readonly int onOffOffset;
private readonly int timeOffset;
private readonly int intervalOffset;
private readonly int structSize;
private readonly uint onOff;
private readonly uint time;
private readonly uint interval;
public IOControlKeepAliveValues(KeepAliveConfig keepAliveConfig)
{
var uintSize = Marshal.SizeOf(typeof(uint));
onOffOffset = 0;
timeOffset = uintSize;
intervalOffset = 2 * uintSize;
structSize = 3 * uintSize;
onOff = (uint)(keepAliveConfig.Enabled ? 1 : 0);
time = (uint)keepAliveConfig.Time;
interval = (uint)keepAliveConfig.Interval;
}
public byte[] ToByteArray()
{
var keepAliveSettings = new byte[structSize];
BitConverter.GetBytes(onOff).CopyTo(keepAliveSettings, onOffOffset);
BitConverter.GetBytes(time).CopyTo(keepAliveSettings, timeOffset);
BitConverter.GetBytes(interval).CopyTo(keepAliveSettings, intervalOffset);
return keepAliveSettings;
}
} |
Ok, so here are my results:
*Test without TLS enabled*
A - No exception thrown, but no logs appear on the server
B - No exception thrown, but no logs appear on the server
C - No exception thrown, but no logs appear on the server
D - No exception thrown, but no logs appear on the server
For all cases A-D above, if I turn off keep-alive, it works.
*Test with 4.1.0*
Since I am running dotnet core, changing to 4.1.0 do not really work since
they not are compatible.
So I made a different test program for testing 4.1.0 on my computer/network.
The test program for dotnet framework 4.6 and NLog.Targets.Syslog 4.1.0
works with TLS on and keep-alive on or off.
*Test with your custom program*
I had to add a main function that create an instance of MyTest and then
call Go().
I also had to add the field keepAliveConfig.
I added this in the constructor to MyTest:
tlsConfig = new TlsConfig();
tlsConfig.Enabled = false;
keepAliveConfig = new KeepAliveConfig();
keepAliveConfig.Enabled = true;
All 4 permutations of true/false for tls and keep alive works fine. No
exception and the test "hello" reach the server.
Hope this gives you more clues... :-)
Tobias
|
Thanks @mrtoby! My source code include a test utility with GUI: you did not need to create custom code to test the 4.1.0 .NET classic version Apart from being synchronous, the code above has been extracted from my 5.0.1 code: I really do not understant where is the problem 😞 I just found out that Papertrail has free accounts: I will try to debug myself, but I do not think the solution can come anytime soon. Thanks a lot for your support 👍 |
We've worked with several customers on this issue recently. Running a packet trace shows the TCP three-way handshake, followed by the TLS Client Hello, and then the client inexplicably resets the connection without waiting for a response. Adding |
I have made some changes in the management of keep-alive and removed the connection check and I am going to publish a new release soon It would be really great if you could help me see if there are some regressions or if this issue is solved with the latest build artifacts |
It works perfect - thanks!
|
With keep-alive enabled? 😲 |
Yup, keep-alive and TLS enabled and everything works fine!
Really nice!
|
Closed by #176 |
I have no problems to configure and log over UDP to my papertrail destination using this library.
Also, I can easily get data into papertrail by using telnet (over unsecured TCP connection).
But when I try to do the same using this library I only get an exception in the internal NLog log.
My configuration code looks like this:
Since manual logging using telnet works like a charm from the same computer - I have a hard time believing that the problem is a network problem, or a papertrail related problem.
The text was updated successfully, but these errors were encountered: