How to enable network tracing in .NET on mono? - c#

I'm trying to log all outgoing HTTP requests and responses a .NET application makes. I am following this article from the Microsoft docs: https://learn.microsoft.com/en-us/dotnet/framework/network-programming/how-to-configure-network-tracing
I am providing a short example of my code and app.config.
The following works as expected and produces a file network.log which includes the full request and response body. However this only works on Windows and .NET Framework. Running the same code on Linux using mono version 5.18.1.0 - mono Program.exe does not create the network.log file and does not throw any exception.
C# Code:
using System;
using System.IO;
using System.Net;
namespace TraceTest
{
class Program
{
static void Main(string[] args)
{
WebRequest request = HttpWebRequest.Create("http://example.com");
using (WebResponse response = request.GetResponse())
using (Stream responseStream = response.GetResponseStream())
using (StreamReader streamReader = new StreamReader(responseStream))
{
Console.WriteLine("Received: " + streamReader.ReadToEnd());
}
}
}
}
App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<system.diagnostics>
<sources>
<source name="System.Net" tracemode="includehex" maxdatasize="1024">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
</sources>
<switches>
<add name="System.Net" value="Verbose"/>
</switches>
<sharedListeners>
<add name="System.Net"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="network.log"
traceOutputOptions="ProcessId, DateTime"
/>
</sharedListeners>
<trace autoflush="true"/>
</system.diagnostics>
</configuration>
Is there any way to log all network traffic on Mono from the application itself? I do not want to run an external program to capture the traffic.

Related

Write Trace Logs to text file in ASP.NET Core

I have an application written in .NetFramework 4.5 and the trace logs are added in the simplest possible way:
System.Diagnostics.Trace.WriteLine("Method in: ");
//
//
//
System.Diagnostics.Trace.WriteLine("Method out: ");
We use to write the trace logs into a text file using the <configuration /> tag in web.config.
<configuration>
<system.diagnostics>
<trace autoflush="true" indentsize="4">
<listeners>
<add name="app-logger" type="System.Diagnostics.TextWriterTraceListener"
initializeData="logfile.txt" />
<remove name="Default" />
</listeners>
</trace>
</system.diagnostics>
</configuration>
I'm trying to implement the same functionality in .NetCore 3.1 but where should I place the <configuration /> tag?
You can use Serilog for logging into Text file.
https://nblumhardt.com/2016/10/aspnet-core-file-logger/

What are the details of how HttpClient handles encryption?

Related: Make Https call using HttpClient
Here's an example that illustrates how I'm doing Web Service calls:
class Program
{
static void Main(string[] args)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(#"text/javascript") { CharSet = "utf-8" });
client.BaseAddress = new Uri("https://...");
HttpResponseMessage msg = client.GetAsync("jobs").Result;
string json = msg.Content.ReadAsStringAsync().Result;
// Deserialize this for later use
}
}
As I understand it from the answers there, as well as from this, this will establish a secure TLS connection and messages will be encrypted.
My understanding from the Wikipedia description of TLS 1.2 as well as the previously-mentioned documents and Q&A is that it uses public-key cryptography to authenticate the identity of the server and AES to do the exchange. However, Wikipedia's vague as to the exact details (e.g. the AES key size and mode, how the key is exchanged and stored, etc.). How is this handled in HttpClient? What AES key size and mode is it using (if it is, in fact, using AES), how does it do the key exchange, and how does it store the key?
To summarize the comments (as a Community Wiki answer since I'm summarizing other people's material), the content will be encrypted, but the exact details of the encryption are negotiated with the server (meaning that there's not one correct answer - it depends on configuration).
Microsoft has instructions on how to find out details here. They suggest adding the following to your App.config:
<system.diagnostics>
<sources>
<source name="System.Net" tracemode="includehex" maxdatasize="1024">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Cache">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Http">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Sockets">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.WebSockets">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
</sources>
<switches>
<add name="System.Net" value="Verbose"/>
<add name="System.Net.Cache" value="Verbose"/>
<add name="System.Net.Http" value="Verbose"/>
<add name="System.Net.Sockets" value="Verbose"/>
<add name="System.Net.WebSockets" value="Verbose"/>
</switches>
<sharedListeners>
<add name="System.Net"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="network.log"
/>
</sharedListeners>
<trace autoflush="true"/>
</system.diagnostics>
This results in highly detailed logs about the connection (including very specific details about what encryption schemes are being used). For example, I was able to discover that the AES key length is 128 bits, that they're using Elliptic Curve Diffie-Hellman for the key agreement, and various other details of the security.

Loading configuration for System.Diagnostics.TraceSource from an xml file

In log4net, it is possible to choose between loading the configuration from the app.config, or from an arbitrary xml file.
Is it possible to load the configuration for System.Diagnostics.TraceSource from an arbitrary xml file?
System.Diagnostics classes look only at application configuration file. E.g. remarks section of SourceSwitch says:
To configure a SourceSwitch, edit the configuration file that
corresponds to the name of your application.
If you will look into code, you'll see that internally these classes use static DiagnosticConfiguration class which simply gets system.diagonostics configuration section from current app.config
BUT you can move system.diagonostics configuratin section to separate xml file. Just specify name of file where section will be defined:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics configSource="diagnostics.xml"/>
</configuration>
diagnostics.xml
<system.diagnostics>
<sources>
<source name="foo" switchName="bar"
switchType="System.Diagnostics.SourceSwitch">
<listeners>
<add name="console"/>
</listeners>
</source>
</sources>
<switches>
<add name="bar" value="Warning"/>
</switches>
<sharedListeners>
<add name="console"
type="System.Diagnostics.ConsoleTraceListener" initializeData="false"/>
</sharedListeners>
<trace autoflush="true" indentsize="4">
<listeners>
<add name="console"/>
</listeners>
</trace>
</system.diagnostics>

Logging request/response messages when using HttpClient

I have a method that does a POST like below
var response = await client.PostAsJsonAsync(url, entity);
if (response.IsSuccessStatusCode)
{
// read the response as strongly typed object
return await response.Content.ReadAsAsync<T>();
}
My question is how can I obtain the actual JSON that got posted from the entity object. I would like to log the JSON that gets POSTED, so it will be nice to have that without me having to do a json serialize myself.
An example of how you could do this:
Some notes:
LoggingHandler intercepts the request before it handles it to HttpClientHandler which finally writes to the wire.
PostAsJsonAsync extension internally creates an ObjectContent and when ReadAsStringAsync() is called in the LoggingHandler, it causes the formatter
inside ObjectContent to serialize the object and that's the reason you are seeing the content in json.
Logging handler:
public class LoggingHandler : DelegatingHandler
{
public LoggingHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Console.WriteLine("Request:");
Console.WriteLine(request.ToString());
if (request.Content != null)
{
Console.WriteLine(await request.Content.ReadAsStringAsync());
}
Console.WriteLine();
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
Console.WriteLine("Response:");
Console.WriteLine(response.ToString());
if (response.Content != null)
{
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
Console.WriteLine();
return response;
}
}
Chain the above LoggingHandler with HttpClient:
HttpClient client = new HttpClient(new LoggingHandler(new HttpClientHandler()));
HttpResponseMessage response = client.PostAsJsonAsync(baseAddress + "/api/values", "Hello, World!").Result;
Output:
Request:
Method: POST, RequestUri: 'http://kirandesktop:9095/api/values', Version: 1.1, Content: System.Net.Http.ObjectContent`1[
[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Headers:
{
Content-Type: application/json; charset=utf-8
}
"Hello, World!"
Response:
StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Date: Fri, 20 Sep 2013 20:21:26 GMT
Server: Microsoft-HTTPAPI/2.0
Content-Length: 15
Content-Type: application/json; charset=utf-8
}
"Hello, World!"
NOTE: This works with .NET Framework ONLY!
May be working with .NET 7+ (https://github.com/dotnet/runtime/issues/23937)
See http://mikehadlow.blogspot.com/2012/07/tracing-systemnet-to-debug-http-clients.html
To configure a System.Net listener to output to both the console and a log file, add the following to your assembly configuration file:
<system.diagnostics>
<trace autoflush="true" />
<sources>
<source name="System.Net">
<listeners>
<add name="MyTraceFile"/>
<add name="MyConsole"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add
name="MyTraceFile"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="System.Net.trace.log" />
<add name="MyConsole" type="System.Diagnostics.ConsoleTraceListener" />
</sharedListeners>
<switches>
<add name="System.Net" value="Verbose" />
</switches>
</system.diagnostics>
Network tracing also available for next objects (see article on msdn)
System.Net.Sockets
Some public methods of the Socket, TcpListener, TcpClient, and Dns classes
System.Net
Some public methods of the HttpWebRequest, HttpWebResponse, FtpWebRequest, and FtpWebResponse classes, and SSL debug information (invalid certificates, missing issuers list, and client certificate errors.)
System.Net.HttpListener
Some public methods of the HttpListener, HttpListenerRequest, and HttpListenerResponse classes.
System.Net.Cache
Some private and internal methods in System.Net.Cache.
System.Net.Http
Some public methods of the HttpClient, DelegatingHandler, HttpClientHandler, HttpMessageHandler, MessageProcessingHandler, and WebRequestHandler classes.
System.Net.WebSockets.WebSocket
Some public methods of the ClientWebSocket and WebSocket classes.
Put the following lines of code to the configuration file:
<configuration>
<system.diagnostics>
<sources>
<source name="System.Net" tracemode="includehex" maxdatasize="1024">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Cache">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Http">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Sockets">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.WebSockets">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
</sources>
<switches>
<add name="System.Net" value="Verbose"/>
<add name="System.Net.Cache" value="Verbose"/>
<add name="System.Net.Http" value="Verbose"/>
<add name="System.Net.Sockets" value="Verbose"/>
<add name="System.Net.WebSockets" value="Verbose"/>
</switches>
<sharedListeners>
<add name="System.Net"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="network.log"
/>
</sharedListeners>
<trace autoflush="true"/>
</system.diagnostics>
</configuration>
The easiest solution would be to use Wireshark and trace the HTTP tcp flow.

Auto-load trace listener to all new trace sources

I am converting some code to use Microsoft tracing. What I'd like is to define all the listeners in one project and then use them from other assemblies, without having to explicitly load them there.
To clarify, this is what I'm doing now:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<trace autoflush="true">
<listeners>
<add name="myListener" type="ConsoleApplication4.LogListener, ConsoleApplication4"/>
<remove name="Default" />
</listeners>
</trace>
</system.diagnostics>
</configuration>
And in the C# code:
var b = Trace.Listeners;
TraceSource tr = new TraceSource("Blah", SourceLevels.All);
tr.Listeners.Add(b["myListener"]);
tr.TraceEvent(TraceEventType.Warning, 5, "Hello");
What I would like is for myListener to be automatically added to any new trace source I create without having to look it up the way I'm doing now. Is this possible?
Define the trace source along with its listeners in config:
<system.diagnostics>
<sources>
<source name="Blah" switchValue="Warning">
<listeners>
<add name="myListener" />
</listeners>
</source>
</sources>
<!-- Note these are in sharedListeners rather than trace -->
<sharedListeners>
<add name="myListener" ... />
</sharedListeners>
<!-- Autoflush still works as expected -->
<trace autoflush="true" />
</system.diagnostics>
Then construct the TraceSource in code the way you already are (its trace level will be overridden by the switchValue in config), don't add any listeners to it and log to it as normal.

Categories

Resources