├── .dockerignore ├── .github └── workflows │ └── dotnet.yml ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── README.md ├── RtcCallMonitor.sln ├── consolidate-ips.py ├── install.ps1 ├── requirements.txt └── src ├── Configuration ├── Application.cs └── Provider.cs ├── Dockerfile ├── IPHeader.cs ├── NetworkListener.cs ├── Program.cs ├── Properties └── launchSettings.json ├── RtcCallMonitor.csproj ├── TrafficMonitor.cs ├── Webhook.cs ├── Worker.cs └── appsettings.json /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Setup .NET 17 | uses: actions/setup-dotnet@v1 18 | with: 19 | dotnet-version: 5.0.x 20 | - name: Restore dependencies 21 | run: dotnet restore 22 | - name: Build 23 | run: dotnet build --no-restore 24 | - name: Test 25 | run: dotnet test --no-build --verbosity normal 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | src/appsettings.Development.json 352 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | // Use IntelliSense to find out which attributes exist for C# debugging 6 | // Use hover for the description of the existing attributes 7 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/src/bin/Debug/net5.0/RtcCallMonitor.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/src", 16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/src/RtcCallMonitor.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/src/RtcCallMonitor.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/src/RtcCallMonitor.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RTC-Call-Monitor 2 | 3 | ## About 4 | 5 | RTC-Call-Monitor is a tool to detect an active UDP based voice/video call by: 6 | 7 | - Monitoring the rate of incoming/outgoing UDP packets from your machine's IP address. 8 | - Checking the source/destination address against known network blocks for the major voice/video conference providers. 9 | 10 | When an active call is detected, will invoke a webhook for the start and end of a call. 11 | 12 | ### Supported Providers 13 | 14 | - [Zoom](https://assets.zoom.us/docs/ipranges/ZoomMeetings.txt) 15 | - [Microsoft Teams](https://learn.microsoft.com/en-us/microsoft-365/enterprise/urls-and-ip-address-ranges?view=o365-worldwide#skype-for-business-online-and-microsoft-teams) 16 | - ID: 11, Category: Optimize Required 17 | - WebEx 18 | - Slack 19 | - Google Meet 20 | - GoTo Meeting 21 | 22 | Additional providers can be added by modifying the configuration file. See _Adding new providers_ below. 23 | 24 | Consolidating subnet ranges (optional): `python .\consolidate-ips.py --input "D:\Downloads\ZoomMeetings.txt"` 25 | 26 | ## Getting Started 27 | 28 | ### Prerequisites 29 | 30 | - [.NET 5.0 SDK](https://dotnet.microsoft.com/download/dotnet/5.0) 31 | 32 | ### Minimal Configuration 33 | 34 | Before starting the application for the first time, modify the value for `LocalNetwork` in _appsettings.json_ with the details of your local network. The value is in [CIDR Notation](https://www.digitalocean.com/community/tutorials/understanding-ip-addresses-subnets-and-cidr-notation-for-networking). 35 | 36 | Most users will have a network configuration similar to the following: 37 | 38 | ```bash 39 | IP Address: 192.168.1.100 40 | Subnet Mask: 255.255.255.0 41 | ``` 42 | 43 | The configuration will be 44 | 45 | `"LocalNetwork": "192.168.1.0/24"` 46 | 47 | ### Build/Run 48 | 49 | RTC-Call-Monitor requires that your console has elevated privileges in order to monitor network traffic. 50 | 51 | Windows: Open a new Powershell or command prompt using _Run as Administrator_ 52 | 53 | ```bash 54 | cd src 55 | dotnet run 56 | ``` 57 | 58 | Linux/macOS: 59 | 60 | ```bash 61 | cd src 62 | sudo dotnet run 63 | ``` 64 | 65 | ### Additional Configuration 66 | 67 | #### Webhooks 68 | 69 | All webhooks are HTTP POST requests and require a valid URL. 70 | 71 | ##### Call Start 72 | 73 | `CallStart` in _appsettings.json_ 74 | 75 | POST body included when calling the webhook 76 | 77 | ```json 78 | { 79 | "provider": "slack" 80 | } 81 | ``` 82 | 83 | ##### Call End 84 | 85 | `CallEnd` in _appsettings.json_ 86 | 87 | POST body included when calling the webhook 88 | 89 | ```json 90 | { 91 | "duration": 123 92 | } 93 | ``` 94 | 95 | `duration` is the call length in seconds. 96 | 97 | #### Adding new providers 98 | 99 | The configuration key `KnownNetworks` is a JSON dictionary with the key being the provider name, and value is an array of CIDR network blocks. To add a new provider, add a new key/value pair to `KnownNetworks`. 100 | 101 | #### Adding new network blocks 102 | 103 | If the application is not detecting the start of your call, the most likely reason is that the IP address is not in a known network block for your provider. To see the destination address of your call provider, enable debug logging. 104 | 105 | In _appsettings.json:_ 106 | `"RtcCallMonitor": "Debug"` 107 | 108 | In the console a message similar to the following will show in the console: 109 | 110 | `Unmapped network 66.77.89.91 count 122` 111 | 112 | Find your provider in _appsettings.json_ and add a new entry for the network block 113 | 114 | `66.77.0.0/16` 115 | 116 | Restart the application and your call should be detected. 117 | -------------------------------------------------------------------------------- /RtcCallMonitor.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31105.61 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RtcCallMonitor", "src\RtcCallMonitor.csproj", "{5C4114AB-158C-4FFE-B441-4507C790A0EF}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{C0D504BB-01B6-46A8-9E2F-5A72FA6EC46C}" 9 | ProjectSection(SolutionItems) = preProject 10 | install.ps1 = install.ps1 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {5C4114AB-158C-4FFE-B441-4507C790A0EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {5C4114AB-158C-4FFE-B441-4507C790A0EF}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {5C4114AB-158C-4FFE-B441-4507C790A0EF}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {5C4114AB-158C-4FFE-B441-4507C790A0EF}.Release|Any CPU.Build.0 = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | GlobalSection(ExtensibilityGlobals) = postSolution 28 | SolutionGuid = {6A7AD23D-FC81-473B-B09F-9AE5817D5C67} 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /consolidate-ips.py: -------------------------------------------------------------------------------- 1 | from netaddr import * 2 | import argparse 3 | 4 | if __name__ == "__main__": 5 | parser = argparse.ArgumentParser(description="consolidate IP list") 6 | parser.add_argument("--input", "-i", help="Input file") 7 | 8 | args = parser.parse_args() 9 | ip_list = [] 10 | 11 | with open(args.input, "r") as read_file: 12 | for line in read_file.readlines(): 13 | ip_list.append(IPNetwork(line)) 14 | 15 | print(f"IPs: {len(ip_list)}") 16 | consolidated = cidr_merge(ip_list) 17 | print(f"Consolidated IPs: {len(consolidated)}") 18 | print("Merged IP list:") 19 | consolidated.sort() 20 | for ip in consolidated: 21 | print(str(ip)) 22 | -------------------------------------------------------------------------------- /install.ps1: -------------------------------------------------------------------------------- 1 | $serviceName = "RTC Call Monitor" 2 | 3 | if (Get-Service $serviceName -ErrorAction SilentlyContinue) 4 | { 5 | remove-service $serviceName 6 | write-host "$($serviceName) service removed" 7 | } 8 | else 9 | { 10 | write-host "$($serviceName) service does not exist" 11 | } 12 | 13 | write-host "installing service $($serviceName)" 14 | 15 | $binaryPath = resolve-path .\RtcCallMonitor.exe 16 | New-Service -name $serviceName -binaryPathName $binaryPath -displayName $serviceName -startupType Automatic 17 | 18 | 19 | write-host "`n`n`ninstallation completed - Ensure that elevated credentials are entered in the Log On tab before starting the service" -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | netaddr -------------------------------------------------------------------------------- /src/Configuration/Application.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RtcCallMonitor.Configuration 8 | { 9 | public class Application 10 | { 11 | public string[] LocalNetwork { get; set; } 12 | public string CallStartWebhook { get; set; } 13 | public string CallEndWebhook { get; set; } 14 | public int? CheckInterval { get; set; } 15 | public int? MinPacketRate { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Configuration/Provider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RtcCallMonitor.Configuration 8 | { 9 | public class Provider 10 | { 11 | public Dictionary KnownNetworks { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/runtime:5.0 AS base 4 | WORKDIR /app 5 | 6 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 7 | WORKDIR /src 8 | COPY ["src/RtcCallMonitor.csproj", "src/"] 9 | RUN dotnet restore "src/RtcCallMonitor.csproj" 10 | COPY . . 11 | WORKDIR "/src/src" 12 | RUN dotnet build "RtcCallMonitor.csproj" -c Release -o /app/build 13 | 14 | FROM build AS publish 15 | RUN dotnet publish "RtcCallMonitor.csproj" -c Release -o /app/publish 16 | 17 | FROM base AS final 18 | WORKDIR /app 19 | COPY --from=publish /app/publish . 20 | ENTRYPOINT ["dotnet", "RtcCallMonitor.dll"] -------------------------------------------------------------------------------- /src/IPHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | 5 | namespace RtcCallMonitor 6 | { 7 | public class IPHeader 8 | { 9 | private byte _protocol; //Eight bits for the underlying protocol 10 | private uint _sourceIPAddress; //Thirty two bits for the source IP Address 11 | private uint _destinationIPAddress; //Thirty two bits for destination IP Address 12 | 13 | private IPAddress _localIp; 14 | 15 | public IPHeader(byte[] buffer, int length, IPAddress localIp) 16 | { 17 | _localIp = localIp; 18 | 19 | try 20 | { 21 | //Create MemoryStream out of the received bytes 22 | using MemoryStream memoryStream = new MemoryStream(buffer, 0, length); 23 | //Next we create a BinaryReader out of the MemoryStream 24 | using BinaryReader binaryReader = new BinaryReader(memoryStream); 25 | 26 | //The first eight bits of the IP header contain the version and 27 | //header length so we read them 28 | _ = binaryReader.ReadByte(); 29 | 30 | //The next eight bits contain the Differentiated services 31 | _ = binaryReader.ReadByte(); 32 | 33 | //Next sixteen bits hold the total length of the datagram 34 | _ = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16()); 35 | 36 | //Next sixteen have the identification bytes 37 | _ = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16()); 38 | 39 | //Next sixteen bits contain the flags and fragmentation offset 40 | _ = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16()); 41 | 42 | //Next eight bits have the TTL value 43 | _ = binaryReader.ReadByte(); 44 | 45 | //Next eight represents the protocol encapsulated in the datagram 46 | _protocol = binaryReader.ReadByte(); 47 | 48 | //Next sixteen bits contain the checksum of the header 49 | _ = IPAddress.NetworkToHostOrder(binaryReader.ReadInt16()); 50 | 51 | //Next thirty two bits have the source IP address 52 | _sourceIPAddress = (uint)(binaryReader.ReadInt32()); 53 | 54 | //Next thirty two hold the destination IP address 55 | _destinationIPAddress = (uint)(binaryReader.ReadInt32()); 56 | } 57 | catch { } 58 | } 59 | 60 | public bool IsTCP => _protocol == 6; 61 | 62 | public bool IsUDP => _protocol == 17; 63 | 64 | public IPAddress SourceAddress => new(_sourceIPAddress); 65 | 66 | public IPAddress DestinationAddress => new(_destinationIPAddress); 67 | 68 | // Addresses starting with a number between 224 and 239 are used for IP multicast 69 | public bool IsMulticast => DestinationAddress.GetAddressBytes()[0] >= 224 && DestinationAddress.GetAddressBytes()[0] <= 239; 70 | 71 | public bool IsBroadcast => DestinationAddress.ToString() == "255.255.255.255"; 72 | 73 | public bool Inbound => DestinationAddress.Equals(_localIp); 74 | } 75 | } -------------------------------------------------------------------------------- /src/NetworkListener.cs: -------------------------------------------------------------------------------- 1 | using RtcCallMonitor.Configuration; 2 | using Microsoft.Extensions.Logging; 3 | using Microsoft.Extensions.Options; 4 | using System; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Net.Sockets; 8 | using System.Runtime.InteropServices; 9 | using System.Collections.Generic; 10 | 11 | namespace RtcCallMonitor 12 | { 13 | public class NetworkListener 14 | { 15 | private Socket _socket; 16 | private IPAddress _localIp; 17 | private List _localCIDR = new List(); 18 | private byte[] _buffer = new byte[65507]; // max UDP packet size 19 | 20 | private readonly ILogger _logger; 21 | private readonly IOptions _appConfig; 22 | 23 | public delegate void Notify(IPHeader ipHeader); 24 | 25 | public event Notify OnUDPTafficReceived; 26 | 27 | private Socket CreateAndBindSocket() 28 | { 29 | const int SIO_RCVALL = unchecked((int)0x98000001); 30 | 31 | var s = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Udp); 32 | 33 | s.Bind(new IPEndPoint(_localIp, 0)); 34 | s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, true); 35 | 36 | byte[] IN = new byte[4] { 1, 0, 0, 0 }; 37 | byte[] OUT = new byte[4]; 38 | 39 | s.IOControl(SIO_RCVALL, IN, OUT); 40 | 41 | if (BitConverter.ToInt32(OUT) != 0) throw new Exception("Got non-zero result from IOControl"); 42 | 43 | return s; 44 | } 45 | 46 | public NetworkListener(ILogger logger, IOptions appConfig) { 47 | _logger = logger; 48 | _appConfig = appConfig; 49 | 50 | foreach (var network in _appConfig.Value.LocalNetwork) 51 | { 52 | _localCIDR.Add(IPNetwork.Parse(network)); 53 | } 54 | } 55 | 56 | 57 | public void Start() 58 | { 59 | _socket = CreateAndBindSocket(); 60 | 61 | _logger.LogInformation($"monitoring traffic on {_localIp}"); 62 | 63 | _socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(OnReceive), null); 64 | } 65 | 66 | public void UpdateIpAddress(IPAddress newAddress) 67 | { 68 | Stop(); 69 | _localIp = newAddress; 70 | _logger.LogInformation($"Updating monitor to watch traffic on {_localIp}"); 71 | Start(); 72 | } 73 | 74 | public void Stop() 75 | { 76 | if (_socket != null) 77 | { 78 | _socket.Close(); 79 | } 80 | } 81 | 82 | private void OnReceive(IAsyncResult result) 83 | { 84 | try 85 | { 86 | int defaultLength = _buffer.Length; 87 | 88 | if (_socket.Connected) 89 | { 90 | defaultLength = _socket.EndReceive(result); 91 | } 92 | 93 | IPHeader ipHeader = new(_buffer, defaultLength, _localIp); 94 | if (IsOutsideUDPTaffice(ipHeader)) 95 | { 96 | OnUDPTafficReceived?.Invoke(ipHeader); 97 | } 98 | } 99 | catch (Exception e) 100 | { 101 | _logger.LogError(e, "OnReceive Exception"); 102 | } 103 | 104 | try 105 | { 106 | _socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(OnReceive), null); 107 | } 108 | catch (ObjectDisposedException) { } 109 | } 110 | 111 | private bool IsOutsideUDPTaffice(IPHeader header) 112 | { 113 | if (header.IsUDP && !header.IsMulticast && !header.IsBroadcast) 114 | { 115 | if (header.SourceAddress.Equals(_localIp) || header.DestinationAddress.Equals(_localIp)) 116 | { 117 | bool result = true; 118 | foreach (var network in _localCIDR) 119 | { 120 | result &= !(network.Contains(header.SourceAddress) && network.Contains(header.DestinationAddress)); 121 | } 122 | 123 | return result; 124 | } 125 | } 126 | return false; 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Extensions.Logging; 5 | using RtcCallMonitor.Configuration; 6 | 7 | namespace RtcCallMonitor 8 | { 9 | public class Program 10 | { 11 | public static void Main(string[] args) 12 | { 13 | CreateHostBuilder(args).Build().Run(); 14 | } 15 | 16 | private static IConfiguration configuration; 17 | 18 | public static IHostBuilder CreateHostBuilder(string[] args) 19 | { 20 | return Host.CreateDefaultBuilder(args) 21 | .ConfigureAppConfiguration((hostContext, config) => 22 | { 23 | config.Sources.Clear(); 24 | 25 | var env = hostContext.HostingEnvironment; 26 | 27 | config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 28 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", 29 | optional: true, reloadOnChange: true); 30 | 31 | configuration = config.Build(); 32 | }) 33 | .ConfigureLogging(builder => 34 | { 35 | builder.AddConfiguration(configuration); 36 | builder.AddSystemdConsole(); 37 | 38 | }) 39 | .ConfigureServices((hostContext, services) => 40 | { 41 | services.Configure(configuration.GetSection(typeof(Application).Name)); 42 | services.Configure(configuration.GetSection(typeof(Provider).Name)); 43 | services.AddSingleton(); 44 | services.AddSingleton(); 45 | 46 | services.AddHostedService(); 47 | }) 48 | .UseWindowsService(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "RtcCallMonitor": { 4 | "commandName": "Project", 5 | "environmentVariables": { 6 | "DOTNET_ENVIRONMENT": "Development" 7 | } 8 | }, 9 | "Docker": { 10 | "commandName": "Docker" 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/RtcCallMonitor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | win10-x64;osx.10.12-x64;linux-x64 6 | Linux 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/TrafficMonitor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Microsoft.Extensions.Options; 3 | using RtcCallMonitor.Configuration; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Net; 8 | using System.Net.NetworkInformation; 9 | using System.Net.Sockets; 10 | using System.Threading; 11 | 12 | namespace RtcCallMonitor 13 | { 14 | public class TrafficMonitor 15 | { 16 | private readonly Dictionary _knownNetworks = new(); 17 | private bool _callActive; 18 | private string _callIP; 19 | private DateTimeOffset _callStart; 20 | private readonly Dictionary _inboundStats = new(); 21 | private readonly Dictionary _outboundStats = new(); 22 | private readonly ILogger _logger; 23 | private readonly IOptions _appConfig; 24 | private NetworkListener _listener; 25 | private IPAddress _ipAddress = IPAddress.Loopback; 26 | 27 | public delegate void UnknownNetwork(string ip, int packetCount); 28 | public delegate void CallStart(string ip, string provider, int packetCount); 29 | public delegate void CallEnd(TimeSpan ts); 30 | 31 | public event UnknownNetwork OnUnknownProvider; 32 | public event CallStart OnCallStarted; 33 | public event CallEnd OnCallEnded; 34 | 35 | public TrafficMonitor(ILogger logger, NetworkListener listener, IOptions config, IOptions appConfig) 36 | { 37 | _logger = logger; 38 | _listener = listener; 39 | _appConfig = appConfig; 40 | 41 | // build known networks for each defined call provider 42 | foreach (var network in config.Value.KnownNetworks) 43 | { 44 | var cidrList = new List(); 45 | foreach (var cidr in network.Value) 46 | { 47 | cidrList.Add(IPNetwork.Parse(cidr)); 48 | } 49 | _knownNetworks.Add(network.Key, cidrList.ToArray()); 50 | _logger.LogInformation($"loaded provider {network.Key}, {network.Value.Length} network prefixes"); 51 | } 52 | } 53 | 54 | public void Start() 55 | { 56 | _listener.OnUDPTafficReceived += (IPHeader ipHeader) => 57 | { 58 | if (ipHeader.Inbound) 59 | { 60 | var source = ipHeader.SourceAddress.ToString(); 61 | if (!_inboundStats.ContainsKey(source)) 62 | { 63 | _inboundStats.Add(source, 0); 64 | } 65 | _inboundStats[source]++; 66 | } 67 | else 68 | { 69 | var dest = ipHeader.DestinationAddress.ToString(); 70 | if (!_outboundStats.ContainsKey(dest)) 71 | { 72 | _outboundStats.Add(dest, 0); 73 | } 74 | _outboundStats[dest]++; 75 | } 76 | }; 77 | 78 | NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged; 79 | NetworkChange_NetworkAddressChanged(null, null); 80 | } 81 | 82 | private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e) 83 | { 84 | _logger.LogInformation($"network changed!"); 85 | Thread.Sleep(_appConfig.Value.CheckInterval ?? 1000); 86 | IPAddress newIpAddress = IPAddress.Loopback; 87 | try 88 | { 89 | /* 90 | * Attempt to detect the current IP address by defining a fake connection to an arbitrary remote IP 91 | * This implementation is cross-platform and should work on Windows/Linux 92 | */ 93 | using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0)) 94 | { 95 | socket.Connect("8.8.8.8", 65530); 96 | if (socket.LocalEndPoint is IPEndPoint endPoint) 97 | { 98 | newIpAddress = endPoint.Address; 99 | _logger.LogInformation($"Auto-detected IP address: {newIpAddress}"); 100 | } 101 | else 102 | { 103 | _logger.LogWarning("Failed to auto-detect IP adress!"); 104 | } 105 | }; 106 | } 107 | catch (Exception ex) 108 | { 109 | _logger.LogWarning("Unable to detect correct IP address - no valid network available. Exception type: {FullName}", ex.GetType().FullName); 110 | _logger.LogWarning($"Defaulting IP address to loopback: {newIpAddress}"); 111 | } 112 | 113 | if ((newIpAddress != IPAddress.Loopback) && (_ipAddress != newIpAddress)) 114 | { 115 | _ipAddress = newIpAddress; 116 | _listener.UpdateIpAddress(_ipAddress); 117 | } 118 | } 119 | 120 | public void CheckStats() 121 | { 122 | // if there is an active call, filter by the active call ip 123 | // otherwise filter for the top traffic producer for the period 124 | var topInbound = _inboundStats 125 | .Where(x => !_callActive || x.Key == _callIP) 126 | .OrderByDescending(x => x.Value) 127 | .FirstOrDefault(); 128 | 129 | var topOutbound = _outboundStats 130 | .Where(x => !_callActive || x.Key == _callIP) 131 | .OrderByDescending(x => x.Value) 132 | .FirstOrDefault(); 133 | 134 | var (ip, rate) = topInbound.Value > topInbound.Value ? topInbound : topOutbound; 135 | 136 | 137 | if (rate >= (_appConfig.Value.MinPacketRate ?? 1)) 138 | { 139 | var provider = _knownNetworks 140 | .SelectMany(p => p.Value, (kvp, ip) => (kvp.Key, ip)) 141 | .Where(x => x.ip.Contains(IPAddress.Parse(ip))) 142 | .Select(x => x.Key) 143 | .FirstOrDefault(); 144 | 145 | if (provider == null) 146 | { 147 | OnUnknownProvider.Invoke(ip, rate); 148 | } 149 | else if (!_callActive) 150 | { 151 | _callStart = DateTimeOffset.Now; 152 | _callActive = true; 153 | _callIP = ip; 154 | OnCallStarted.Invoke(ip, provider, rate); 155 | } 156 | } 157 | else 158 | { 159 | if (_callActive) 160 | { 161 | _callActive = false; 162 | _callIP = null; 163 | OnCallEnded.Invoke(DateTimeOffset.Now - _callStart); 164 | } 165 | } 166 | _inboundStats.Clear(); 167 | _outboundStats.Clear(); 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/Webhook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Net.Http.Headers; 6 | using System.Text; 7 | using System.Text.Json; 8 | using System.Threading.Tasks; 9 | 10 | namespace RtcCallMonitor 11 | { 12 | public static class Webhook 13 | { 14 | private static readonly HttpClient _client = new(); 15 | 16 | public static Task Invoke(Uri uri, object data) 17 | { 18 | var body = new StringContent(JsonSerializer.Serialize(data), Encoding.UTF8, "application/json"); 19 | 20 | return _client.PostAsync(uri.AbsoluteUri, body); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Worker.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Hosting; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using RtcCallMonitor.Configuration; 7 | using Microsoft.Extensions.Options; 8 | 9 | namespace RtcCallMonitor 10 | { 11 | public class Worker : BackgroundService 12 | { 13 | private readonly IHostApplicationLifetime _host; 14 | private readonly ILogger _logger; 15 | private readonly TrafficMonitor _monitor; 16 | private readonly IOptions _appConfig; 17 | 18 | public Worker(IHostApplicationLifetime host, ILogger logger, NetworkListener listener, TrafficMonitor monitor, IOptions appConfig) => 19 | (_host, _logger, _monitor, _appConfig) = (host, logger, monitor, appConfig); 20 | 21 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 22 | { 23 | _monitor.OnUnknownProvider += OnUnknownProvider; 24 | _monitor.OnCallStarted += OnCallStarted; 25 | _monitor.OnCallEnded += OnCallEnded; 26 | 27 | try 28 | { 29 | _monitor.Start(); 30 | } 31 | catch (Exception e) 32 | { 33 | _logger.LogError(e.ToString()); 34 | _host.StopApplication(); 35 | } 36 | 37 | _logger.LogInformation($"Using check interval of {_appConfig.Value.CheckInterval ?? 1000}ms, min packet rate {_appConfig.Value.MinPacketRate ?? 1}"); 38 | while (!stoppingToken.IsCancellationRequested) 39 | { 40 | try 41 | { 42 | await Task.Delay(_appConfig.Value.CheckInterval ?? 1000, stoppingToken); 43 | _monitor.CheckStats(); 44 | } 45 | catch (Exception e) 46 | { 47 | _logger.LogError(e, String.Empty); 48 | } 49 | } 50 | } 51 | 52 | private void OnUnknownProvider(string ip, int packetCount) 53 | { 54 | _logger.LogDebug($"Unmapped network {ip} count {packetCount}"); 55 | } 56 | 57 | private void OnCallStarted(string ip, string provider, int packetCount) 58 | { 59 | _logger.LogInformation($"call started for {provider} on {ip} count {packetCount}"); 60 | if (Uri.TryCreate(_appConfig.Value.CallStartWebhook, UriKind.Absolute, out Uri uri)) 61 | { 62 | Task.Run( () => Webhook.Invoke(uri, new { provider })); 63 | } 64 | } 65 | 66 | private void OnCallEnded(TimeSpan ts) 67 | { 68 | _logger.LogInformation($"call ended, time {(int)ts.TotalSeconds} seconds"); 69 | if (Uri.TryCreate(_appConfig.Value.CallEndWebhook, UriKind.Absolute, out Uri uri)) 70 | { 71 | Task.Run(() => Webhook.Invoke(uri, new { duration = ts.TotalSeconds })); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Warning", 7 | "RtcCallMonitor": "Information" 8 | } 9 | }, 10 | "Application": { 11 | "LocalNetwork": ["192.168.1.0/24"], 12 | "CallStartWebhook": "", 13 | "CallEndWebhook": "", 14 | "CheckInterval": 2000, 15 | "MinPacketRate": 1 16 | }, 17 | "Provider": { 18 | "KnownNetworks": { 19 | "googlemeet": ["74.125.240.0/20"], 20 | "slack": ["99.77.0.0/16"], 21 | "discord": ["162.245.0.0/16", "213.179.0.0/16"], 22 | "webex": [ 23 | "62.109.0.0/16", 24 | "64.68.0.0/16", 25 | "66.114.0.0/16", 26 | "66.163.0.0/16", 27 | "69.26.0.0/16", 28 | "114.29.0.0/16", 29 | "150.253.0.0/16", 30 | "170.72.0.0/16", 31 | "170.133.0.0/16", 32 | "173.39.0.0/16", 33 | "173.243.0.0/16", 34 | "207.182.0.0/16", 35 | "209.197.0.0/16", 36 | "210.4.0.0/16", 37 | "216.151.0.0/16" 38 | ], 39 | "msteams": [ 40 | "13.107.64.0/18", 41 | "52.112.0.0/14", 42 | "52.122.0.0/15", 43 | "2603:1063::/39", 44 | "52.120.0.0/16", 45 | "52.114.0.0/16", 46 | "52.113.0.0/16", 47 | "52.115.0.0/16" 48 | ], 49 | "zoom": [ 50 | "3.7.35.0/25", 51 | "3.21.137.128/25", 52 | "3.22.11.0/24", 53 | "3.23.93.0/24", 54 | "3.25.41.128/25", 55 | "3.25.42.0/25", 56 | "3.25.49.0/24", 57 | "3.80.20.128/25", 58 | "3.96.19.0/24", 59 | "3.101.32.128/25", 60 | "3.101.52.0/25", 61 | "3.104.34.128/25", 62 | "3.120.121.0/25", 63 | "3.127.194.128/25", 64 | "3.208.72.0/25", 65 | "3.211.241.0/25", 66 | "3.235.69.0/25", 67 | "3.235.71.128/25", 68 | "3.235.72.128/25", 69 | "3.235.73.0/25", 70 | "3.235.82.0/23", 71 | "3.235.96.0/23", 72 | "4.34.125.128/25", 73 | "4.35.64.128/25", 74 | "8.5.128.0/23", 75 | "13.52.6.128/25", 76 | "13.52.146.0/25", 77 | "18.157.88.0/24", 78 | "18.205.93.128/25", 79 | "18.254.23.128/25", 80 | "18.254.61.0/25", 81 | "20.203.158.80/28", 82 | "20.203.190.192/26", 83 | "50.239.202.0/23", 84 | "50.239.204.0/24", 85 | "52.61.100.128/25", 86 | "52.202.62.192/26", 87 | "52.215.168.0/25", 88 | "64.125.62.0/24", 89 | "64.211.144.0/24", 90 | "64.224.32.0/19", 91 | "65.39.152.0/24", 92 | "69.174.57.0/24", 93 | "69.174.108.0/22", 94 | "99.79.20.0/25", 95 | "101.36.167.0/24", 96 | "101.36.170.0/23", 97 | "103.122.166.0/23", 98 | "111.33.115.0/25", 99 | "111.33.181.0/25", 100 | "115.110.154.192/26", 101 | "115.114.56.192/26", 102 | "115.114.115.0/26", 103 | "115.114.131.0/26", 104 | "120.29.148.0/24", 105 | "129.151.1.128/27", 106 | "129.151.1.192/27", 107 | "129.151.2.0/27", 108 | "129.151.3.160/27", 109 | "129.151.7.96/27", 110 | "129.151.11.64/27", 111 | "129.151.11.128/27", 112 | "129.151.12.0/27", 113 | "129.151.13.64/27", 114 | "129.151.15.224/27", 115 | "129.151.16.0/27", 116 | "129.151.31.224/27", 117 | "129.151.40.0/25", 118 | "129.151.40.160/27", 119 | "129.151.40.192/27", 120 | "129.151.41.0/25", 121 | "129.151.41.192/26", 122 | "129.151.42.0/27", 123 | "129.151.42.64/27", 124 | "129.151.42.128/26", 125 | "129.151.42.224/27", 126 | "129.151.43.0/27", 127 | "129.151.43.64/26", 128 | "129.151.48.0/27", 129 | "129.151.48.160/27", 130 | "129.151.49.0/26", 131 | "129.151.49.96/27", 132 | "129.151.49.128/27", 133 | "129.151.49.192/26", 134 | "129.151.50.0/27", 135 | "129.151.50.64/27", 136 | "129.151.52.128/26", 137 | "129.151.53.32/27", 138 | "129.151.53.224/27", 139 | "129.151.55.32/27", 140 | "129.151.56.32/27", 141 | "129.151.57.32/27", 142 | "129.151.60.192/27", 143 | "129.159.2.32/27", 144 | "129.159.2.192/27", 145 | "129.159.3.0/24", 146 | "129.159.4.0/23", 147 | "129.159.6.0/27", 148 | "129.159.6.96/27", 149 | "129.159.6.128/26", 150 | "129.159.6.192/27", 151 | "129.159.160.0/26", 152 | "129.159.160.64/27", 153 | "129.159.163.0/26", 154 | "129.159.163.160/27", 155 | "129.159.208.0/21", 156 | "129.159.216.0/26", 157 | "129.159.216.64/27", 158 | "129.159.216.128/26", 159 | "130.61.164.0/22", 160 | "132.226.176.0/25", 161 | "132.226.176.128/26", 162 | "132.226.177.96/27", 163 | "132.226.177.128/25", 164 | "132.226.178.0/27", 165 | "132.226.178.128/27", 166 | "132.226.178.224/27", 167 | "132.226.179.0/27", 168 | "132.226.179.64/27", 169 | "132.226.180.128/27", 170 | "132.226.183.160/27", 171 | "132.226.185.192/27", 172 | "134.224.0.0/16", 173 | "140.238.128.0/24", 174 | "140.238.232.0/22", 175 | "144.195.0.0/16", 176 | "147.124.96.0/19", 177 | "149.137.0.0/17", 178 | "150.230.224.0/25", 179 | "150.230.224.128/26", 180 | "150.230.224.224/27", 181 | "152.67.20.0/24", 182 | "152.67.118.0/24", 183 | "152.67.168.0/22", 184 | "152.67.180.0/24", 185 | "152.67.184.32/27", 186 | "152.67.240.0/21", 187 | "152.70.0.0/25", 188 | "152.70.0.128/26", 189 | "152.70.0.224/27", 190 | "152.70.1.0/25", 191 | "152.70.1.128/26", 192 | "152.70.1.192/27", 193 | "152.70.2.0/26", 194 | "152.70.7.192/27", 195 | "152.70.10.32/27", 196 | "152.70.224.32/27", 197 | "152.70.224.64/26", 198 | "152.70.224.160/27", 199 | "152.70.224.192/27", 200 | "152.70.225.0/25", 201 | "152.70.225.160/27", 202 | "152.70.225.192/27", 203 | "152.70.226.0/27", 204 | "152.70.227.96/27", 205 | "152.70.227.192/27", 206 | "152.70.228.0/27", 207 | "152.70.228.64/27", 208 | "152.70.228.128/27", 209 | "156.45.0.0/17", 210 | "158.101.64.0/24", 211 | "158.101.184.0/23", 212 | "158.101.186.0/25", 213 | "158.101.186.128/27", 214 | "158.101.186.192/26", 215 | "158.101.187.0/25", 216 | "158.101.187.160/27", 217 | "158.101.187.192/26", 218 | "160.1.56.128/25", 219 | "161.199.136.0/22", 220 | "162.12.232.0/22", 221 | "162.255.36.0/22", 222 | "165.254.88.0/23", 223 | "166.108.64.0/18", 224 | "168.138.16.0/22", 225 | "168.138.48.0/24", 226 | "168.138.56.0/21", 227 | "168.138.72.0/24", 228 | "168.138.74.0/25", 229 | "168.138.80.0/25", 230 | "168.138.80.128/26", 231 | "168.138.80.224/27", 232 | "168.138.81.0/24", 233 | "168.138.82.0/23", 234 | "168.138.84.0/25", 235 | "168.138.84.128/27", 236 | "168.138.84.192/26", 237 | "168.138.85.0/24", 238 | "168.138.86.0/23", 239 | "168.138.96.0/22", 240 | "168.138.116.0/27", 241 | "168.138.116.64/27", 242 | "168.138.116.128/27", 243 | "168.138.116.224/27", 244 | "168.138.117.0/27", 245 | "168.138.117.96/27", 246 | "168.138.117.128/27", 247 | "168.138.118.0/27", 248 | "168.138.118.160/27", 249 | "168.138.118.224/27", 250 | "168.138.119.0/27", 251 | "168.138.119.128/27", 252 | "168.138.244.0/24", 253 | "170.114.0.0/16", 254 | "173.231.80.0/20", 255 | "192.204.12.0/22", 256 | "193.122.16.0/25", 257 | "193.122.16.192/27", 258 | "193.122.17.0/26", 259 | "193.122.17.64/27", 260 | "193.122.17.224/27", 261 | "193.122.18.32/27", 262 | "193.122.18.64/26", 263 | "193.122.18.160/27", 264 | "193.122.18.192/27", 265 | "193.122.19.0/27", 266 | "193.122.19.160/27", 267 | "193.122.19.192/27", 268 | "193.122.20.224/27", 269 | "193.122.21.96/27", 270 | "193.122.32.0/21", 271 | "193.122.40.0/22", 272 | "193.122.44.0/24", 273 | "193.122.45.32/27", 274 | "193.122.45.64/26", 275 | "193.122.45.128/25", 276 | "193.122.46.0/23", 277 | "193.122.208.96/27", 278 | "193.122.216.32/27", 279 | "193.122.222.0/27", 280 | "193.122.223.128/27", 281 | "193.122.226.160/27", 282 | "193.122.231.192/27", 283 | "193.122.232.160/27", 284 | "193.122.237.64/27", 285 | "193.122.244.160/27", 286 | "193.122.244.224/27", 287 | "193.122.245.0/27", 288 | "193.122.247.96/27", 289 | "193.122.252.192/27", 290 | "193.123.0.0/19", 291 | "193.123.40.0/21", 292 | "193.123.128.0/19", 293 | "193.123.168.0/21", 294 | "193.123.192.224/27", 295 | "193.123.193.0/27", 296 | "193.123.193.96/27", 297 | "193.123.194.96/27", 298 | "193.123.194.128/27", 299 | "193.123.194.224/27", 300 | "193.123.195.0/27", 301 | "193.123.196.0/27", 302 | "193.123.196.192/27", 303 | "193.123.197.0/27", 304 | "193.123.197.64/27", 305 | "193.123.198.64/27", 306 | "193.123.198.160/27", 307 | "193.123.199.64/27", 308 | "193.123.200.128/27", 309 | "193.123.201.32/27", 310 | "193.123.201.224/27", 311 | "193.123.202.64/27", 312 | "193.123.202.128/26", 313 | "193.123.203.0/27", 314 | "193.123.203.160/27", 315 | "193.123.203.192/27", 316 | "193.123.204.0/27", 317 | "193.123.204.64/27", 318 | "193.123.205.64/26", 319 | "193.123.205.128/27", 320 | "193.123.206.32/27", 321 | "193.123.206.128/27", 322 | "193.123.207.32/27", 323 | "193.123.208.160/27", 324 | "193.123.209.0/27", 325 | "193.123.209.96/27", 326 | "193.123.210.64/27", 327 | "193.123.211.224/27", 328 | "193.123.212.128/27", 329 | "193.123.215.192/26", 330 | "193.123.216.64/27", 331 | "193.123.216.128/27", 332 | "193.123.217.160/27", 333 | "193.123.219.64/27", 334 | "193.123.220.224/27", 335 | "193.123.222.64/27", 336 | "193.123.222.224/27", 337 | "198.251.128.0/17", 338 | "202.177.207.128/27", 339 | "204.80.104.0/21", 340 | "204.141.28.0/22", 341 | "206.247.0.0/16", 342 | "207.226.132.0/24", 343 | "209.9.211.0/24", 344 | "209.9.215.0/24", 345 | "213.19.144.0/24", 346 | "213.19.153.0/24", 347 | "213.244.140.0/24", 348 | "221.122.63.0/24", 349 | "221.122.64.0/24", 350 | "221.122.88.64/27", 351 | "221.122.88.128/25", 352 | "221.122.89.128/25", 353 | "221.123.139.192/27" 354 | ], 355 | "gotomeeting": [ 356 | "216.115.208.0/20", 357 | "216.219.112.0/20", 358 | "66.151.158.0/24", 359 | "66.151.150.160/27", 360 | "66.151.115.128/26", 361 | "64.74.80.0/24", 362 | "202.173.24.0/20", 363 | "67.217.64.0/19", 364 | "78.108.112.0/20", 365 | "68.64.0.0/19", 366 | "206.183.100.0/22", 367 | "173.199.0.0/18", 368 | "103.15.16.0/22", 369 | "180.153.30.0/24", 370 | "185.36.20.0/22", 371 | "188.66.40.0/21", 372 | "63.251.34.0/24", 373 | "63.251.46.0/23", 374 | "64.74.17.0/24", 375 | "64.74.18.0/23", 376 | "64.74.103.0/24", 377 | "64.94.18.0/24", 378 | "64.94.46.0/23", 379 | "64.95.128.0/23", 380 | "66.150.108.0/24", 381 | "67.217.80.0/23", 382 | "69.25.20.0/23", 383 | "69.25.247.0/24", 384 | "77.242.192.0/24", 385 | "95.172.70.0/24", 386 | "111.221.57.0/24", 387 | "173.199.52.0/23", 388 | "173.199.30.0/23", 389 | "212.118.234.0/24", 390 | "216.219.114.0/23" 391 | ] 392 | } 393 | } 394 | } 395 | --------------------------------------------------------------------------------