mirror of
https://github.com/Sandoun/DynDNSv2.git
synced 2025-12-06 02:41:24 +00:00
Intial
This commit is contained in:
30
src/.dockerignore
Normal file
30
src/.dockerignore
Normal file
@@ -0,0 +1,30 @@
|
||||
**/.classpath
|
||||
**/.dockerignore
|
||||
**/.env
|
||||
**/.git
|
||||
**/.gitignore
|
||||
**/.project
|
||||
**/.settings
|
||||
**/.toolstarget
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/*.*proj.user
|
||||
**/*.dbmdl
|
||||
**/*.jfm
|
||||
**/azds.yaml
|
||||
**/bin
|
||||
**/charts
|
||||
**/docker-compose*
|
||||
**/Dockerfile*
|
||||
**/node_modules
|
||||
**/npm-debug.log
|
||||
**/obj
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
README.md
|
||||
!**/.gitignore
|
||||
!.git/HEAD
|
||||
!.git/config
|
||||
!.git/packed-refs
|
||||
!.git/refs/heads/**
|
||||
25
src/DynDNSv2.sln
Normal file
25
src/DynDNSv2.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.8.34309.116
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynDNSv2", "DynDNSv2\DynDNSv2.csproj", "{92FB2846-1FDE-475E-B29F-32315BA30B0C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{92FB2846-1FDE-475E-B29F-32315BA30B0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{92FB2846-1FDE-475E-B29F-32315BA30B0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{92FB2846-1FDE-475E-B29F-32315BA30B0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{92FB2846-1FDE-475E-B29F-32315BA30B0C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {5B07CCDA-2105-4E16-9ADF-77950F1724D9}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
23
src/DynDNSv2/Dockerfile
Normal file
23
src/DynDNSv2/Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
||||
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base
|
||||
USER app
|
||||
WORKDIR /app
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
WORKDIR /src
|
||||
COPY ["DynDNSv2/DynDNSv2.csproj", "DynDNSv2/"]
|
||||
RUN dotnet restore "./DynDNSv2/./DynDNSv2.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/DynDNSv2"
|
||||
RUN dotnet build "./DynDNSv2.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
RUN dotnet publish "./DynDNSv2.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "DynDNSv2.dll"]
|
||||
17
src/DynDNSv2/DynDNSv2.csproj
Normal file
17
src/DynDNSv2/DynDNSv2.csproj
Normal file
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<UserSecretsId>abe55787-bc11-416d-85aa-eaf544f21fe1</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
16
src/DynDNSv2/IPTestResult.cs
Normal file
16
src/DynDNSv2/IPTestResult.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DynDNSv2;
|
||||
internal record IPTestResult
|
||||
{
|
||||
|
||||
public bool WasChanged { get; set; }
|
||||
|
||||
public IPAddress? UpdatedIP { get; set; }
|
||||
|
||||
}
|
||||
33
src/DynDNSv2/Program.cs
Normal file
33
src/DynDNSv2/Program.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DynDNSv2;
|
||||
|
||||
internal class Program
|
||||
{
|
||||
static void Main(string[] args) => Task.Run(AsyncMain).Wait();
|
||||
|
||||
static async Task AsyncMain()
|
||||
{
|
||||
|
||||
var builder = Host.CreateDefaultBuilder()
|
||||
.ConfigureLogging(logging =>
|
||||
{
|
||||
logging.AddSimpleConsole(settings =>
|
||||
{
|
||||
settings.SingleLine = true;
|
||||
});
|
||||
})
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddHostedService<Updater>();
|
||||
});
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
await host.RunAsync();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
244
src/DynDNSv2/Updater.cs
Normal file
244
src/DynDNSv2/Updater.cs
Normal file
@@ -0,0 +1,244 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DynDNSv2;
|
||||
|
||||
internal class Updater : BackgroundService
|
||||
{
|
||||
|
||||
private string dynDnsUrl;
|
||||
|
||||
private string username;
|
||||
|
||||
private string password;
|
||||
|
||||
private string[] updateDomains;
|
||||
|
||||
private IPAddress lastPublicIp;
|
||||
|
||||
private CancellationToken cToken;
|
||||
|
||||
private ILogger logger;
|
||||
|
||||
public Updater(ILogger<Updater> logger)
|
||||
{
|
||||
|
||||
this.logger = logger;
|
||||
lastPublicIp = IPAddress.Parse("127.0.0.1");
|
||||
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
|
||||
cToken = stoppingToken;
|
||||
|
||||
var _dynDNSUrl = Environment.GetEnvironmentVariable("UPDATE_URL");
|
||||
var _username = Environment.GetEnvironmentVariable("USERNAME");
|
||||
var _password = Environment.GetEnvironmentVariable("PASSWORD");
|
||||
|
||||
if (string.IsNullOrEmpty(_dynDNSUrl))
|
||||
throw new Exception("The UPDATE_URL env variable was not set");
|
||||
|
||||
if (string.IsNullOrEmpty(_username))
|
||||
throw new Exception("The USERNAME env variable was not set");
|
||||
|
||||
if (string.IsNullOrEmpty(_password))
|
||||
throw new Exception("The PASSWORD env variable was not set");
|
||||
|
||||
dynDnsUrl = _dynDNSUrl;
|
||||
username = _username;
|
||||
password = _password;
|
||||
|
||||
var updateLst = new List<string>();
|
||||
|
||||
int i = 1;
|
||||
while(true)
|
||||
{
|
||||
var found = Environment.GetEnvironmentVariable($"UPDATE_URL_{i}");
|
||||
|
||||
if(!string.IsNullOrEmpty(found))
|
||||
{
|
||||
updateLst.Add(found);
|
||||
} else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
}
|
||||
|
||||
if(updateLst.Count <= 0)
|
||||
throw new Exception("No UPDATE_URL_[N] env variable found, pleace specify one with UPDATE_URL_1, UPDATE_URL_2 ...");
|
||||
|
||||
updateDomains = updateLst.ToArray();
|
||||
|
||||
await RunUpdaterAsync();
|
||||
|
||||
}
|
||||
|
||||
private async Task RunUpdaterAsync()
|
||||
{
|
||||
|
||||
await RunIpTester();
|
||||
|
||||
}
|
||||
|
||||
private async Task RunIpTester()
|
||||
{
|
||||
|
||||
while(!cToken.IsCancellationRequested) {
|
||||
|
||||
var ipChangeResult = await CheckPublicIpAsync();
|
||||
|
||||
if (ipChangeResult.WasChanged)
|
||||
{
|
||||
logger.LogInformation("Public IP change detected");
|
||||
await RunDnsUpdateAsync(ipChangeResult.UpdatedIP!);
|
||||
}
|
||||
|
||||
await Task.Delay(5000);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async Task<IPTestResult> CheckPublicIpAsync()
|
||||
{
|
||||
|
||||
List<string> services = new List<string>() {
|
||||
"https://ipv4.icanhazip.com",
|
||||
"https://api.ipify.org",
|
||||
"https://ipinfo.io/ip",
|
||||
"https://checkip.amazonaws.com",
|
||||
"https://wtfismyip.com/text",
|
||||
"http://icanhazip.com"
|
||||
};
|
||||
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
|
||||
foreach (var service in services)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
var str = await client.GetStringAsync(service);
|
||||
var match = Regex.Match(str, @"(?:[0-9]{1,3}\.){3}[0-9]{1,3}");
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
str = match.Value;
|
||||
}
|
||||
|
||||
if (!IPAddress.TryParse(str, out var ip)) continue;
|
||||
|
||||
if (ip.ToString() != lastPublicIp.ToString())
|
||||
{
|
||||
|
||||
lastPublicIp = ip;
|
||||
return new IPTestResult
|
||||
{
|
||||
WasChanged = true,
|
||||
UpdatedIP = ip,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
return new IPTestResult
|
||||
{
|
||||
WasChanged = false,
|
||||
};
|
||||
|
||||
}
|
||||
catch { }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("The public ip could not be resolved");
|
||||
|
||||
}
|
||||
|
||||
private async Task RunDnsUpdateAsync(IPAddress publicIp)
|
||||
{
|
||||
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "Server");
|
||||
|
||||
var byteArray = new UTF8Encoding().GetBytes($"{username}:{password}");
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
|
||||
|
||||
var hostNames = string.Join(",", updateDomains);
|
||||
|
||||
var reqURL = $"{dynDnsUrl}?hostname={hostNames}&myip={publicIp}";
|
||||
|
||||
var result = await client.GetAsync(reqURL, cToken);
|
||||
|
||||
if (result.IsSuccessStatusCode)
|
||||
{
|
||||
|
||||
string? responseStr = result.Content != null ? await result.Content.ReadAsStringAsync() : null;
|
||||
|
||||
if (responseStr == null)
|
||||
{
|
||||
|
||||
logger.LogError("No response body");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
responseStr = responseStr.Substring(0, responseStr.Length - 1);
|
||||
|
||||
var updateMsgs = responseStr.Split("\n");
|
||||
|
||||
foreach (var msg in updateMsgs)
|
||||
{
|
||||
|
||||
var sb = new StringBuilder("DynDNS response from ");
|
||||
|
||||
sb.Append($"[{reqURL}]: ");
|
||||
sb.Append($"'{msg}', ");
|
||||
sb.Append($"Code: {(int)result.StatusCode}");
|
||||
|
||||
if (msg.Contains("good") || msg.Contains("nochg"))
|
||||
{
|
||||
|
||||
logger.LogInformation($"Updated DNS: {msg}");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
logger.LogError($"DynDNS error response: {responseStr}");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
logger.LogError($"HTTP Error code: {result.StatusCode}");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user