Rans4ckeR / RS.Fritz.Manager

A .NET library and Windows WPF app for FritzBox devices using WCF.
7 stars 1 forks source link

Does Fritzbox TR-064 provide means to implement packet capturing #6

Closed RoSchmi closed 2 years ago

RoSchmi commented 2 years ago

Not an issue but perhaps a nice use case. It is posible to capure packets using the Fritzbox web gui. Is it posible to use these functions via the TR-064 Api as well?

Rans4ckeR commented 2 years ago

@RoSchmi I haven't seen such functionality in the API. It should however be possible to grab them using HTTP requests from a UI.

After sending a HTTP request to log on you can grab the sid from the response. You can then for example request the capture page and go from there to see which requests to send to start/stop/download capture files.

POST /capture.lua HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Accept: */*
Host: x.x.x.x
Content-Length: 20

sid=a0a0aa00000000a0
RoSchmi commented 2 years ago

@Rans4ckeR Thank you, yes this should work. Still thinking about if it is worth the effort.

RoSchmi commented 2 years ago

Hi, I couldn't resist to implement the capture feature. If you are interested you can have a look on my branch 'sniffer'. It has the user control 'Capture' and the 'CaptureControlService' in the API. Writing the network trafic to a file 'FritzboxCapture....' to the 'Documents' folder is basically working. It would be nice if you could give me some feedback whether I used the proper Stream functions or if there are better ways to do this. Besides writing to a file I would like to implement a direct transfer of the the stream content to wireshark. Actually I don't have an idea how this could be done. Perhaps you could give me a hint. Are you interested to later add these functions to RS.Fritz.Manager? Kind regards RoSchmi

Rans4ckeR commented 2 years ago

@RoSchmi yes I'm interested in adding this.

Unfortunately HttpClient.GetStreamAsync() does not provide an option to specify HttpCompletionOption.ResponseHeadersRead. This means the entire response is read into memory first. So I think your current code is fine using:

  1. HttpClient.GetAsync(_, HttpCompletionOption.ResponseHeadersRead)
  2. HttpResponseMessage.EnsureSuccessStatusCode()
  3. HttpResponseMessage.Content.ReadAsStreamAsync()

You should however also use Stream.CopyToAsync() instead of Stream.CopyTo().

To display the contents in Wireshark once the file is complete can be accomplished by just calling Wireshark from code with the file as a command line argument. As to displaying it without writing anything to file I think you already have to write your own network adapter driver similar to a loopback adapter which then replays the traffic as you send the packets to it? But I can only guess here, never looked into this sort of stuff.

RoSchmi commented 2 years ago

Thanks, for live capturing, I think I would need something like this: which would additionally need authentication. https://docs.microsoft.com/de-de/dotnet/framework/network-programming/asynchronous-server-socket-example Since this would need authentication it seems to be too complex. Starting Wireshark with command line argument is a good idea. I think I'll try this.

RoSchmi commented 2 years ago

Hi @Rans4ckeR, I made some more tests with packet capturing and I saw that the version:

` public async Task GetStartCaptureResponseAsync(Uri uri) { .....

.....

var response = await httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
        response.EnsureSuccessStatusCode();
        using (var downloadStream = await response.Content.ReadAsStreamAsync(cancToken))
        {
            using (var fileStream = file.Create())
            {
                await downloadStream.CopyToAsync(fileStream);
            }
        }`

throwed an exception after about 10 seconds of capturing a video stream. Perhaps because of exceeding the size of internal buffers.

So I tried an alternative using sockets which seemed to give better results (see Task in my branch sniffer_sockets): public async Task<bool> GetStartCaptureResponseSocketAsync(string scheme, string host, string capturePath, string query) {.....

So I would like to go with this alternative, knowing that it doesn't match the style of your App. It's also a matter of how much time I'm going to spend to get it working perfectly. What do you think?

Rans4ckeR commented 2 years ago

@RoSchmi can you provide me the exception type and stack trace including all inner exceptions? On my side that code works fine and does not throw any exception. I let it run for a few minutes while doing some speed tests on my connection. It captured several hundreds of megabytes to file successfully. The memory, handles, threads etc for the process had perfectly acceptable values.

I took commit 41270143 from your branch sniffer and changed downloadStream.CopyTo(fileStream) to await downloadStream.CopyToAsync(fileStream);

RoSchmi commented 2 years ago

@Rans4ckeR Ups...., I'm happy to say that I cannot reproduce the issue. I tried again and it took a 100 MByte stream without exceptions. I will look if I see the issue again. Another question: I think that it should be possible to limit the size of the written file to a maximal value. (For the case that you forget to switch it off). Any ideas?

Rans4ckeR commented 2 years ago

@RoSchmi personally I wouldn't worry about it. I generally assume anybody using this repository is not an average user and is tech-savvy enough to decide for themselves whether they want a capture over a minute or over several days (if the device allows this). I would not build in a hardcoded size or time limit as some people might like having an unlimited capture option. Anyway when the application process is stopped it will end the streaming as well.

I can imagine however that perhaps some users might find it useful to have an optional time limit for some scenarios. You could then start a timer at the same time when the capture starts and when it triggers you can let it call the stop capture. WanDslInterfaceConfigViewModel for example uses a timer.

Another possibility is to cancel the capture by using a CancellationToken with a TimeSpan:

HttpClient httpClient = httpClientFactory.CreateClient(Constants.HttpClientName);
using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromHours(1));
CancellationToken cancellationToken = cancellationTokenSource.Token;
FileInfo fileInfo = new FileInfo(string.Empty);

try
{
    HttpResponseMessage response = await httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, cancellationToken);

    response.EnsureSuccessStatusCode();

    await using Stream downloadStream = await response.Content.ReadAsStreamAsync(cancellationToken);
    await using FileStream fileStream = fileInfo.Create();
    await downloadStream.CopyToAsync(fileStream, cancellationToken);
}
catch (OperationCanceledException)
{
    // Show message to user or just ignore
}

But this seems less clean then simply calling stop capture.