/*++

Copyright (c) 2001  Microsoft Corporation

Module Name:

    rmALG.cpp

Abstract:

    This module contains routines for the ALG Manager module's 
    private interface to be used only by the ALG.exe manager.

Author:

    JPDup		10-Nov-2000

Revision History:

--*/


#include "precomp.h"

#include <atlbase.h>

extern CComModule _Module;
#include <atlcom.h>

#include "Alg.h"
#include "NatPrivateAPI_Imp.h"

#include <MyTrace.h>

#include <Rtutils.h>



extern HANDLE                   AlgPortReservationHandle;   // see rmALG.CPP




/////////////////////////////////////////////////////////////////////////////
// CNat






//
// Standard destructor
//
CNat::~CNat(void)
{
    MYTRACE_ENTER("CNat::~CNat(void)");

    if ( m_hTranslatorHandle )
        NatShutdownTranslator(m_hTranslatorHandle);

}







STDMETHODIMP 
CNat::CreateRedirect(
    IN  ULONG       Flags, 
    IN  UCHAR       Protocol, 

    IN  ULONG       DestinationAddress, 
    IN  USHORT      DestinationPort, 

    IN  ULONG       SourceAddress, 
    IN  USHORT      SourcePort, 

    IN  ULONG       NewDestinationAddress, 
    IN  USHORT      NewDestinationPort, 

    IN  ULONG       NewSourceAddress, 
    IN  USHORT      NewSourcePort, 

    IN  ULONG       RestrictAdapterIndex, 

    IN  DWORD_PTR   dwAlgProcessId,
    IN  HANDLE_PTR  hCreateEvent, 
    IN  HANDLE_PTR  hDeleteEvent
    )
{
/*++

Routine Description:

    Creates a Redirect PORT

Arguments:

    Flags                   - Specifies options for the redirect
    Protocol                - IP protocol of the session to be redirected

    DestinationAddress      - destination endpoint of the session to be redirected
    DestinationPort         - "

    SourceAddress           - source endpoint of the session to be redirected
    SourcePort              - "

    NewDestinationAddress   - replacement destination endpoint for the session
    NewDestinationPort      - "

    NewSourceAddress        - replacement source endpoint for the session
    NewSourcePort           - "

    RestrictAdapterIndex    - optionally specifies the adapter index that this redirect should be restricted to 

    hCreateEvent            - optionally specifies an event to be signalled when a session matches the redirect.

    hDeleteEvent            - optionally specifies an event to be signalled when a session is delete.


Return Value:

    HRESULT                 - S_OK for success or and HRESULT error

Environment:

    The routine runs in the context of the ALG Manager and cant only be invoke by the ALG.EXE

--*/

    MYTRACE_ENTER("CNat::CreateRedirect");

    MYTRACE("ProtocolPublic %d, ProtocolInternal %d", Protocol, ProtocolConvertToNT(Protocol));
    MYTRACE("Destination    %s:%d", MYTRACE_IP(DestinationAddress),      ntohs(DestinationPort));
    MYTRACE("Source         %s:%d", MYTRACE_IP(SourceAddress),           ntohs(SourcePort));
    MYTRACE("NewDestination %s:%d", MYTRACE_IP(NewDestinationAddress),   ntohs(NewDestinationPort));
    MYTRACE("NewSource      %s:%d", MYTRACE_IP(NewSourceAddress),        ntohs(NewSourcePort));

    HANDLE  hThisEventForCreate=NULL;
    HANDLE  hThisEventForDelete=NULL;


    //
    // Duplicate the requested Event handles
    //
    if ( dwAlgProcessId )
    {

        HANDLE hAlgProcess = OpenProcess(
            PROCESS_DUP_HANDLE,     // access flag
            false,                  // handle inheritance option
            (DWORD)dwAlgProcessId   // process identifier
            );

        if ( !hAlgProcess )
        {
            MYTRACE_ERROR("Could not open the Process ID of ALG.exe", 0);
            return HRESULT_FROM_WIN32(GetLastError());
        }



        if ( hCreateEvent )
        {
        
            //
            // a create event was requested 
            //
            if ( !DuplicateHandle(
                    hAlgProcess,
                    (HANDLE)hCreateEvent,
                    GetCurrentProcess(),
                    &hThisEventForCreate,
                    0,
                    FALSE,
                    DUPLICATE_SAME_ACCESS
                    )
                )
            {
                MYTRACE_ERROR("DuplicateHandle on the CREATE handle", 0);
                CloseHandle(hAlgProcess);
                return HRESULT_FROM_WIN32(GetLastError());
            }

            MYTRACE("New DuplicateHandle 'CREATE'=%d base on=%d", hThisEventForCreate, hCreateEvent);
        }
        else
        {
            MYTRACE("No event for Creation requested");
        }



        if ( hDeleteEvent )
        {
            //
            // a delete event was requested
            //
            if ( !DuplicateHandle(
                    hAlgProcess,
                    (HANDLE)hDeleteEvent,
                    GetCurrentProcess(),
                    &hThisEventForDelete,
                    0,
                    FALSE,
                    DUPLICATE_SAME_ACCESS
                    )
                )
            {
                MYTRACE_ERROR("DuplicateHandle on the DELETE handle", 0);

                if ( hThisEventForCreate )
	            CloseHandle(hThisEventForCreate);

                CloseHandle(hAlgProcess);

                return HRESULT_FROM_WIN32(GetLastError());
            }

            MYTRACE("New DuplicateHandle 'DELETE'=%d base on=%d", hThisEventForDelete, hDeleteEvent);

        }
        else
        {
            MYTRACE("No event for Delete requested");
        }

        CloseHandle(hAlgProcess);
    }
    else
    {
        MYTRACE("NO EVENT Requested");
    }


    ULONG Error = NatCreateRedirectEx(
        GetTranslatorHandle(),
        Flags,
        ProtocolConvertToNT(Protocol),

        DestinationAddress,
        DestinationPort,

        SourceAddress,      
        SourcePort,

        NewDestinationAddress,
        NewDestinationPort,

        NewSourceAddress,
        NewSourcePort,

        RestrictAdapterIndex,
        IPNATAPI_SET_EVENT_ON_COMPLETION, // Special constant to use Event vs. a callback to a CompletionRoutine
        (PVOID)hThisEventForDelete,       //HANDLE for DELETE sessions
        (HANDLE)hThisEventForCreate       //HANDLE                    NotifyEvent         OPTIONAL
        ); 

    if ( hThisEventForCreate )
        CloseHandle(hThisEventForCreate);

    if ( hThisEventForDelete )
        CloseHandle(hThisEventForDelete);

    if ( ERROR_SUCCESS != Error )
    {
        MYTRACE_ERROR("From NatCreateRedirectEx", Error);
        return HRESULT_FROM_WIN32(Error);
    }

    return S_OK;
}


//
//
//

STDMETHODIMP 
CNat::CancelRedirect(
    IN  UCHAR    Protocol, 

    IN  ULONG    DestinationAddress, 
    IN  USHORT   DestinationPort, 

    IN  ULONG    SourceAddress, 
    IN  USHORT   SourcePort, 

    IN  ULONG    NewDestinationAddress, 
    IN  USHORT   NewDestinationPort, 

    IN  ULONG    NewSourceAddress, 
    IN  USHORT   NewSourcePort
    )
/*++

Routine Description:

    Cancel a Redirect

Arguments:

    Protocol                - IP protocol of the session to be redirected eALG_TCP || eALG_UDP

    DestinationAddress      - destination endpoint of the session to be redirected
    DestinationPort         - "

    SourceAddress           - source endpoint of the session to be redirected
    SourcePort              - "

    NewDestinationAddress   - replacement destination endpoint for the session
    NewDestinationPort      - "

    NewSourceAddress        - replacement source endpoint for the session
    NewSourcePort           - "


Return Value:

    HRESULT                 - S_OK for success or and HRESULT error

Environment:

    The routine runs in the context of the ALG Manager and cant only be invoke by the ALG.EXE

--*/
{
    MYTRACE_ENTER("CNat::CancelRedirect");

    MYTRACE("Protocol Public %d, Internal %d", Protocol, ProtocolConvertToNT(Protocol));
    MYTRACE("Destination    %s:%d", MYTRACE_IP(DestinationAddress),     ntohs(DestinationPort));
    MYTRACE("Source         %s:%d", MYTRACE_IP(SourceAddress),          ntohs(SourcePort));
    MYTRACE("NewDestination %s:%d", MYTRACE_IP(NewDestinationAddress),  ntohs(NewDestinationPort));
    MYTRACE("NewSource      %s:%d", MYTRACE_IP(NewSourceAddress),       ntohs(NewSourcePort));

    ULONG Error = NatCancelRedirect(
        GetTranslatorHandle(),
        ProtocolConvertToNT(Protocol),
        DestinationAddress,
        DestinationPort,
        SourceAddress,
        SourcePort,
        NewDestinationAddress,
        NewDestinationPort,
        NewSourceAddress,
        NewSourcePort
        );

    if ( ERROR_SUCCESS != Error )
    {
        MYTRACE_ERROR("From NatCancelRedirect", Error);
        return HRESULT_FROM_WIN32(Error);
    }

    return S_OK;
}





STDMETHODIMP 
CNat::CreateDynamicRedirect(
    IN  ULONG       Flags, 
    IN  ULONG       nAdapterIndex,
    IN  UCHAR       Protocol, 

    IN  ULONG       DestinationAddress, 
    IN  USHORT      DestinationPort, 

    IN  ULONG       SourceAddress, 
    IN  USHORT      SourcePort, 

    IN  ULONG       NewDestinationAddress, 
    IN  USHORT      NewDestinationPort, 

    IN  ULONG       NewSourceAddress,
    IN  USHORT      NewSourcePort,

    OUT HANDLE_PTR* pDynamicRedirectHandle
    )
/*++

Routine Description:

    Cancel a dynamic Redirect, by seting up a dynamic redirection any time a adapter is created the redirection will be
    applied to that new adapter.

Arguments:

    Flags                   - Specifies options for the redirect
    nAdapterIndex           - Index of the IP adapter (Same as the index found using the cmd line "ROUTE PRINT")
    Protocol                - IP protocol of the session to be redirected

    DestinationAddress      - destination endpoint of the session to be redirected
    DestinationPort         - "

    SourceAddress           - source endpoint of the session to be redirected
    SourcePort              - "

    NewDestinationAddress   - replacement destination endpoint for the session
    NewDestinationPort      - "

    NewSourceAddress        - replacement source endpoint for the session
    NewSourcePort           - "

    pDynamicRedirectHandle  - This routine will populate this field with the handle (Cookie) for the purpose of canceling
                              this DynamicRedirect

Return Value:

    HRESULT                 - S_OK for success or and HRESULT error

Environment:

    The routine runs in the context of the ALG Manager and cant only be invoke by the ALG.EXE
    and is use via the public api CreatePrimaryControlChannel (See ALG.EXE)

--*/
{
    MYTRACE_ENTER("CNat::CreateDynamicRedirect");

    ASSERT(pDynamicRedirectHandle!=NULL);

#if defined(DBG) || defined(_DEBUG)

    MYTRACE("Flags          %d", Flags);


    MYTRACE("Protocol Public %d Internal %d", Protocol, ProtocolConvertToNT(Protocol));

    if ( Flags & NatRedirectFlagNoTimeout )
        MYTRACE("    NatRedirectFlagNoTimeout");

    if ( Flags & NatRedirectFlagUnidirectional )
        MYTRACE("    NatRedirectFlagUnidirectional");

    if ( Flags & NatRedirectFlagRestrictSource )
        MYTRACE("    NatRedirectFlagRestrictSource");

    if ( Flags & NatRedirectFlagPortRedirect )
        MYTRACE("    NatRedirectFlagPortRedirect");

    if ( Flags & NatRedirectFlagReceiveOnly )
        MYTRACE("    NatRedirectFlagReceiveOnly");

    if ( Flags & NatRedirectFlagLoopback )
        MYTRACE("    NatRedirectFlagLoopback");

    if ( Flags & NatRedirectFlagSendOnly )
        MYTRACE("    NatRedirectFlagSendOnly");

    if ( Flags & NatRedirectFlagRestrictAdapter )
        MYTRACE("    NatRedirectFlagRestrictAdapter");

    if ( Flags & NatRedirectFlagSourceRedirect )
        MYTRACE("    NatRedirectFlagSourceRedirect");


    MYTRACE("AdapterIndex   %d", nAdapterIndex);
    
    in_addr tmpAddr;
    tmpAddr.s_addr = DestinationAddress;
    MYTRACE("Destination    %s:%d", inet_ntoa(tmpAddr),    ntohs(DestinationPort));
    tmpAddr.s_addr = SourceAddress;
    MYTRACE("Source         %s:%d", inet_ntoa(tmpAddr),    ntohs(SourcePort));
    tmpAddr.s_addr = NewDestinationAddress;
    MYTRACE("NewDestination %s:%d", inet_ntoa(tmpAddr),    ntohs(NewDestinationPort));
    tmpAddr.s_addr = NewSourceAddress;
    MYTRACE("NewSource      %s:%d", inet_ntoa(tmpAddr),    ntohs(NewSourcePort));
#endif

    
    MYTRACE("About to call NatCreateDynamicFullRedirect");

    ULONG nRestrictSourceAddress = 0;

    if ( NatRedirectFlagRestrictSource & Flags )
    {
        MYTRACE("NatRedirectFlagRestrictSource flags is set");
        nRestrictSourceAddress = SourceAddress;
        SourceAddress = 0;
    }

    ULONG Error = NatCreateDynamicFullRedirect(
        Flags|NatRedirectFlagLoopback,
        ProtocolConvertToNT(Protocol),

        DestinationAddress,
        DestinationPort,

        SourceAddress,
        SourcePort,

        NewDestinationAddress,
        NewDestinationPort,

        NewSourceAddress,
        NewSourcePort,

        nRestrictSourceAddress,         //ULONG RestrictSourceAddress OPTIONAL,
        nAdapterIndex,                  //ULONG RestrictAdapterIndex OPTIONAL,
        0,                              //MinimumBacklog OPTIONAL,
        (PHANDLE)pDynamicRedirectHandle
        );

    if ( ERROR_SUCCESS != Error )
    {
        MYTRACE_ERROR("Failed NatCreateDynamicFullRedirect", Error);
        return HRESULT_FROM_WIN32(Error);
    }

    MYTRACE("Call to NatCreateDynamicFullRedirect worked");

    return S_OK;;
}




STDMETHODIMP 
CNat::CancelDynamicRedirect(
    IN  HANDLE_PTR DynamicRedirectHandle
    )
/*++

Routine Description:

    This routine is called to cancel the given dynamic redirect.
    by calling the NatApi version of this function

Arguments:

    DynamicRedirectHandle   - the handle to the dynamic redirect to be cancelled

Return Value:

    HRESULT                 - S_OK for success or and HRESULT error

--*/

{
    MYTRACE_ENTER("CNat::CancelDynamicRedirect");

    ULONG Error = NatCancelDynamicRedirect((PHANDLE)DynamicRedirectHandle);

    if ( ERROR_SUCCESS != Error )
    {
        MYTRACE_ERROR("Failed NatCancelDynamicRedirect", Error);
        return HRESULT_FROM_WIN32(Error);
    }

    return S_OK;
}




STDMETHODIMP 
CNat::GetBestSourceAddressForDestinationAddress(
    IN  ULONG       ulDestinationAddress, 
    IN  BOOL        fDemandDial, 
    OUT ULONG*      pulBestSrcAddress
    )
/*++

Routine Description:

    We create a temporary UDP socket, connect the socket to the
    actual client's IP address, extract the IP address to which
    the socket is implicitly bound by the TCP/IP driver, and
    discard the socket. This leaves us with the exact IP address
    that we need to use to contact the client.

Arguments:

    ulDestinationAddress, 
    fDemandDial, 
    pulBestSrcAddress


Return Value:

    HRESULT             - S_OK for success

Environment:

    ALG module will call this method to:

--*/

{
    MYTRACE_ENTER("CNat::GetBestSourceAddressForDestinationAddress");

    if ( !pulBestSrcAddress )
    {
        MYTRACE_ERROR("pulBestSrcAddress not supplied",0);
        return E_INVALIDARG;
    }


    SOCKADDR_IN SockAddr;

    SockAddr.sin_family         = AF_INET;
    SockAddr.sin_port           = 0;
    SockAddr.sin_addr.s_addr    = ulDestinationAddress;

    
    ULONG Length = sizeof(SockAddr);


    SOCKET UdpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    if  (   INVALID_SOCKET == UdpSocket
        ||  SOCKET_ERROR   == connect(UdpSocket, (PSOCKADDR)&SockAddr, sizeof(SockAddr))
        ||  SOCKET_ERROR   == getsockname(UdpSocket, (PSOCKADDR)&SockAddr, (int*)&Length)
        )   
    {
        ULONG nError = WSAGetLastError();

        if ( nError == WSAEHOSTUNREACH )
        {
            if ( fDemandDial )
                nError = RasAutoDialSharedConnection(); 

            if ( ERROR_SUCCESS != nError ) 
            {
                MYTRACE_ERROR(" RasAutoDialSharedConnection failed [%d]", nError);

                if ( UdpSocket != INVALID_SOCKET ) 
                { 
                    closesocket(UdpSocket); 
                }

                return HRESULT_FROM_WIN32(nError);
            }
        } 
        else 
        {
            MYTRACE_ERROR("error %d routing endpoint %d using UDP", nError);

            if (UdpSocket != INVALID_SOCKET) 
            { 
                closesocket(UdpSocket); 
            }

            return HRESULT_FROM_WIN32(nError);
        }
    }

    *pulBestSrcAddress = SockAddr.sin_addr.s_addr;

    closesocket(UdpSocket);

    return S_OK;
}



STDMETHODIMP CNat::LookupAdapterPortMapping(
    IN  ULONG   ulAdapterIndex,
    IN  UCHAR   Protocol,
    IN  ULONG   ulDestinationAddress,
    IN  USHORT  usDestinationPort,
    OUT ULONG*  pulRemapAddress,
    OUT USHORT* pusRemapPort
    )
/*++

Routine Description:

    Call NAT port maping to ge the real destination for the port
    This ofcourse is the use has set some maping in the SharedConnection or Firewalled adapter on the Service Tab.

Arguments:

    ulAdapterIndex          - Index of the IP adapter of the session.

    Protocol                - eALG_PROTOCOL_UDP, eALG_PROTOCOL_TCP
    DestinationAddress      - the edge public adapter address
    DestinationPort         - the edge public adapter port

    RemapAddres             - The address where that the user itended this port to go to (Private computer on the private lan)
    SourcePort              - Should be the same as the DestinationPort for future it may be different.


Return Value:

    HRESULT - S_OK if it worked or E_FAIL if no maping was found

--*/
{
    MYTRACE_ENTER("LookupAdapterPortMapping");
    MYTRACE("AdapterIndex %d Protocol %d DestAddress %s:%d", ulAdapterIndex, ProtocolConvertToNT(Protocol), MYTRACE_IP(ulDestinationAddress), ntohs(usDestinationPort));

    IP_NAT_PORT_MAPPING PortMapping;

    ULONG Error = NatLookupPortMappingAdapter(
                    ulAdapterIndex,
                    ProtocolConvertToNT(Protocol),
                    ulDestinationAddress,
                    usDestinationPort,
                    &PortMapping
                    );
                        
    if ( Error ) 
    {
        MYTRACE_ERROR("from NatLookupPortMappingAdapter", Error);
        return HRESULT_FROM_WIN32(Error);
    }

    *pulRemapAddress   = PortMapping.PrivateAddress;
    *pusRemapPort      = PortMapping.PrivatePort;

    return S_OK;
}




STDMETHODIMP CNat::GetOriginalDestinationInformation(
    IN  UCHAR   Protocol,

    IN  ULONG   ulDestinationAddress,
    IN  USHORT  usDestinationPort,

    IN  ULONG   ulSourceAddress,
    IN  USHORT  usSourcePort,

    OUT ULONG*  pulOriginalDestinationAddress,
    OUT USHORT* pusOriginalDestinationPort,

    OUT ULONG*  pulAdapterIndex
    )
/*++

Routine Description:

    Determine the original destination endpoint of a session that is redirected to.

Arguments:


    DestinationAddress      - destination endpoint of the session to be redirected
    DestinationPort         - "

    SourceAddress           - source endpoint of the session to be redirected
    SourcePort              - "

    NewDestinationAddress   - replacement destination endpoint for the session
    NewDestinationPort      - "

    NewSourceAddress        - replacement source endpoint for the session
    NewSourcePort           - "

    pulOriginalDestinationAddress   - Returns the original address of the destination (Where the caller realy wanted to go)
    pusOriginalDestinationPort      - Returns the original port of the destination

    pulAdapterIndex                 - Index of the IP adapter of the session.

Return Value:

    HRESULT - S_OK if it worked or E_FAIL

--*/
{
    MYTRACE_ENTER("CNat::GetOriginalDestinationInformation");
    MYTRACE("Destination  %s:%d", MYTRACE_IP(ulDestinationAddress), ntohs(usDestinationPort));
    MYTRACE("Address      %s:%d", MYTRACE_IP(ulSourceAddress),      ntohs(usSourcePort));

    ASSERT(pulOriginalDestinationAddress!=NULL);
    ASSERT(pusOriginalDestinationPort!=NULL);
    ASSERT(pulAdapterIndex!=NULL);


    IP_NAT_SESSION_MAPPING_KEY_EX  Information;
    ULONG   ulSizeOfInformation = sizeof(IP_NAT_SESSION_MAPPING_KEY_EX);

    ULONG Error = NatLookupAndQueryInformationSessionMapping(
        GetTranslatorHandle(),
        ProtocolConvertToNT(Protocol),

        ulDestinationAddress,
        usDestinationPort,

        ulSourceAddress,
        usSourcePort,

        &Information,
        &ulSizeOfInformation,
        NatKeySessionMappingExInformation
        );


    if ( ERROR_SUCCESS != Error )
    {
        MYTRACE_ERROR("Call to NatLookupAndQueryInformationMapping", Error);
        return HRESULT_FROM_WIN32(Error);
    }

    MYTRACE("Original Index %d Address:Port %s:%d", Information.AdapterIndex, MYTRACE_IP(Information.DestinationAddress), ntohs(Information.DestinationPort));

    *pulOriginalDestinationAddress  = Information.DestinationAddress;
    *pusOriginalDestinationPort     = Information.DestinationPort;
    *pulAdapterIndex                = Information.AdapterIndex;

    return S_OK;

}





STDMETHODIMP CNat::ReservePort(
    IN  USHORT      PortCount, 
    OUT PUSHORT     pReservedPortBase
    )
/*++

Routine Description:

    Call the into the NAP api to reserve the required port on behave of the ALG module.

Arguments:

    PortCount           -   Number of port to reserve
    pReservedPortBase   -   Starting number of the range of port reserved. example  ReserePort(3, &) would save 5000,5001,5002 and return 5000 as base

Return Value:

    HRESULT - S_OK if it worked or E_FAIL


Environment:

    Private interface between rmALG and ALG.EXE

    ALG expose a more simple interface to reserve at Port
    in turn it call this private interface that end up calling the more complex NatApi

--*/
{
    MYTRACE_ENTER("CNat::ReservePort");

    ASSERT(pReservedPortBase!=NULL);

    if ( !AlgPortReservationHandle )
        return E_FAIL;                  // AlgPortReservationHandle should already have been done


    ULONG Error = NatAcquirePortReservation(
        AlgPortReservationHandle,
        PortCount,
        pReservedPortBase
        );

    if ( ERROR_SUCCESS != Error )
    {
        MYTRACE_ERROR("from NatAcquirePortReservation", Error);
        return HRESULT_FROM_WIN32(Error);
    }

    MYTRACE("PortBase %d count %d", *pReservedPortBase, PortCount);

    return S_OK;
}





STDMETHODIMP CNat::ReleasePort(
    IN  USHORT  ReservedPortBase, 
    IN  USHORT  PortCount
    )
/*++

Routine Description:

    Private interface between rmALG and ALG.EXE

    ALG expose a more simple interface to reserve at Port
    in turn it call this private interface that end up calling the more complex NatApi

    This routine will call the Nat api to release the previously reserved ports

Arguments:

    PortCount           -   Number of port to reserve
    pReservedPortBase   -   Starting number of the range of port reserved. example  ReserePort(3, &) would save 5000,5001,5002 and return 5000 as base

Return Value:

    HRESULT - S_OK if it worked or E_FAIL

Environment:

    Private interface between rmALG and ALG.EXE

    ALG expose a more simple interface to reserve at Port
    in turn it call this private interface that end up calling the more complex NatApi

--*/
{

    MYTRACE_ENTER("CNat::ReleasePort");    

    if ( !AlgPortReservationHandle )
        return E_FAIL;                  // AlgPortReservationHandle should already have been done

    ULONG Error = NatReleasePortReservation(
        AlgPortReservationHandle,
        ReservedPortBase,
        PortCount
        );

    if ( ERROR_SUCCESS != Error )
    {
        MYTRACE_ERROR("from NatReleasePortReservation", Error);
        return HRESULT_FROM_WIN32(Error);
    }

    MYTRACE("PortBase=%d, Count=%d", ntohs(ReservedPortBase), PortCount);

    return S_OK;
}