private async Task <IntPtr> DialAsync(VpnData vpnData, CancellationToken cancellationToken) { Log.Information($"VPN dialing"); TaskCompletionSource <bool> tcs = new TaskCompletionSource <bool>(); var handle = VpnApi.Dial(EntryName, vpnData.User, vpnData.Pwd, (message, state, errorCode, extendedErrorCode) => { Log.Information($"VPN dialing {state} ({VpnApi.RasErrorMessage(errorCode)}, {message}, {extendedErrorCode})"); if (state == RasConnectionState.Connected) { tcs.TrySetResult(true); } else { if (errorCode != 0) { tcs.TrySetException(new VpnException($"VPN setup failed ({VpnApi.RasErrorMessage(errorCode)})")); } } }); try { var timeout = Task.Delay(10000, cancellationToken); var res = await Task.WhenAny(timeout, tcs.Task); if (res == timeout) { throw new VpnException("Timeout during VPN setup"); } return(handle); } catch { await HangUpAsync(handle); throw; } }
private async Task MaintainConnection(VpnData vpnData, CancellationToken cancellationToken, TaskCompletionSource <bool> firstTime, Action <VpnRuntimeState> onStateChanged) { Task awaitConnectionChanged = null; IntPtr handle = IntPtr.Zero; bool statInitialized = false; RAS_STATS prevStat = new RAS_STATS(); string serverIpAddress = null; try { while (!_cts.IsCancellationRequested) { try { if (handle == IntPtr.Zero) { Log.Information("VPN Connecting"); handle = await ReconnectAsync(vpnData, _cts.Token); var info = VpnApi.GetProjectionInfoEx(handle); serverIpAddress = $"{info.ipv4ServerAddress.addr[0]}.{info.ipv4ServerAddress.addr[1]}.{info.ipv4ServerAddress.addr[2]}.{info.ipv4ServerAddress.addr[3]}"; firstTime?.TrySetResult(true); firstTime = null; statInitialized = false; var eventHandle = VpnApi.RegisterForTermination(handle); awaitConnectionChanged = TaskHelper.WaitOneAsync(eventHandle, "VPN state changed"); } if (handle != IntPtr.Zero) { var status = VpnApi.GetState(handle); var stat = VpnApi.GetStatisitcs(handle); if (status.connectionState != RasConnectionState.Connected) { if (status.errorCode == 0) { throw new VpnException($"VPN in wrong '{status.connectionState}' state"); } else { throw new VpnException($"VPN error '{VpnApi.RasErrorMessage(status.errorCode)}'"); } } if (!statInitialized) { statInitialized = true; prevStat = stat; } var delta = GetStatDelta(stat, prevStat); prevStat = stat; if (delta.dwTimeoutErr + delta.dwFramingErr + delta.dwCrcErr + delta.dwHardwareOverrunErr + delta.dwBufferOverrunErr + delta.dwAlignmentErr > 0) { Log.Warning($"VPN errors {delta.dwTimeoutErr},{delta.dwFramingErr},{delta.dwCrcErr},{delta.dwHardwareOverrunErr},{delta.dwBufferOverrunErr},{delta.dwAlignmentErr}"); } onStateChanged(new VpnRuntimeState { Connected = true, SentKbs = (int)delta.dwBytesXmited / (1024 / 8), ReceivedKbs = (int)delta.dwBytesRcved / (1024 / 8), ServerIpAddress = serverIpAddress }); } } catch (Exception e) when(TaskHelper.IsCancellation(e)) { Log.Error(e, "VPN cancellation"); throw; } catch (Exception e) { Log.Error(e, "VPN maintanance error"); firstTime?.TrySetException(e); firstTime = null; serverIpAddress = null; onStateChanged(new VpnRuntimeState { Connected = false, ErrorMessage = e.Message }); await HangUpAsync(handle); handle = IntPtr.Zero; awaitConnectionChanged = null; } var delay = Task.Delay(1000, cancellationToken); if (awaitConnectionChanged != null) { var result = await Task.WhenAny(delay, awaitConnectionChanged); if (result == awaitConnectionChanged) { Log.Information("VPN short wait"); awaitConnectionChanged = null; //it is raised once and not usable anymore } } else { await delay; } } } finally { Log.Information("VPN exiting maintainance"); await HangUpAsync(handle); onStateChanged(new VpnRuntimeState { Connected = false }); _disconnected?.TrySetResult(true); } }