/*++

Copyright (c) 2000, Microsoft Corporation

Module Name:

    rtchange.c

Abstract:

    This module contains a program demonstrating the use of the TCP/IP driver's
    route-change notification facilities.

Author:

    Abolade Gbadegesin (aboladeg)   15-April-2000

Revision History:

--*/

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <ntddip.h>
#include <ipinfo.h>

char*
ntoa(
    ULONG IpAddress
    )
{
    return inet_ntoa(*(struct in_addr*)&IpAddress);
}

NTSTATUS
NotifyRouteChange(
    HANDLE FileHandle,
    ULONG IoControlCode,
    ULONG NotifyIpAddress,
    BOOLEAN OutputRequired
    )
{
    HANDLE EventHandle;
    PVOID InputBuffer;
    ULONG InputBufferLength;
    IO_STATUS_BLOCK IoStatus;
    IPNotifyData NotifyData = {0};
    IPRouteNotifyOutput NotifyOutput = {0};
    PVOID OutputBuffer;
    ULONG OutputBufferLength;
    NTSTATUS Status;

    if (NotifyIpAddress == INADDR_NONE) {
        InputBuffer = NULL;
        InputBufferLength = 0;
    } else if (NotifyIpAddress == INADDR_ANY) {
        NotifyData.Add = 0;
        InputBuffer = &NotifyData;
        InputBufferLength = sizeof(NotifyData);
    } else {
        NotifyData.Add = NotifyIpAddress;
        InputBuffer = &NotifyData;
        InputBufferLength = sizeof(NotifyData);
    }

    if (OutputRequired) {
        OutputBuffer = &NotifyOutput;
        OutputBufferLength = sizeof(NotifyOutput);
    } else {
        OutputBuffer = NULL;
        OutputBufferLength = 0;
    }

    Status =
        NtCreateEvent(
            &EventHandle, EVENT_ALL_ACCESS, NULL,
            SynchronizationEvent, FALSE
            );
    if (!NT_SUCCESS(Status)) {
        printf("NtCreateEvent=%x\n", Status);
    } else {

        Status =
            NtDeviceIoControlFile(
                FileHandle,
                EventHandle,
                NULL,
                NULL,
                &IoStatus,
                IOCTL_IP_RTCHANGE_NOTIFY_REQUEST,
                InputBuffer,
                InputBufferLength,
                OutputBuffer,
                OutputBufferLength
                );
        if (Status == STATUS_PENDING) {
            printf("NtDeviceIoControlFile=%x, waiting\n", Status);
            NtWaitForSingleObject(EventHandle, FALSE, NULL);
            Status = IoStatus.Status;
        }
        printf("NtDeviceIoControlFile=%x\n", Status);
        NtClose(EventHandle);

        if (NT_SUCCESS(Status) && OutputRequired) {
            printf("\tDestination:  %s\n", ntoa(NotifyOutput.irno_dest));
            printf("\tMask:         %s\n", ntoa(NotifyOutput.irno_mask));
            printf("\tNext-hop:     %s\n", ntoa(NotifyOutput.irno_nexthop));
            printf("\tProtocol:     %d\n", NotifyOutput.irno_proto);
            printf("\tIndex:        %d\n", NotifyOutput.irno_ifindex);
        }
    }
    return Status;
}

typedef struct {
    IO_STATUS_BLOCK IoStatus;
    IPRouteNotifyOutput NotifyOutput;
} ROUTE_NOTIFY_CONTEXT, *PROUTE_NOTIFY_CONTEXT;

VOID NTAPI
NotifyRouteCompletionRoutine(
    PVOID Context,
    PIO_STATUS_BLOCK IoStatus,
    ULONG Reserved
    )
{
    PROUTE_NOTIFY_CONTEXT NotifyContext = (PROUTE_NOTIFY_CONTEXT)Context;
    printf("NotifyRouteCompletionRoutine(%p, %x)\n", Context, IoStatus->Status);
    if (NT_SUCCESS(IoStatus->Status)) {
        PIPRouteNotifyOutput NotifyOutput = &NotifyContext->NotifyOutput;
        printf("\tDestination:  %s\n", ntoa(NotifyOutput->irno_dest));
        printf("\tMask:         %s\n", ntoa(NotifyOutput->irno_mask));
        printf("\tNext-hop:     %s\n", ntoa(NotifyOutput->irno_nexthop));
        printf("\tProtocol:     %d\n", NotifyOutput->irno_proto);
        printf("\tIndex:        %d\n", NotifyOutput->irno_ifindex);
    }
}

ULONG
QueueNotifyRouteChange(
    HANDLE FileHandle,
    IPNotifyVersion Version,
    ULONG NotificationCount
    )
{
    ULONG i;
    PVOID InputBuffer;
    ULONG InputBufferLength;
    PROUTE_NOTIFY_CONTEXT NotifyContext;
    IPNotifyData NotifyData = {0};
    PVOID OutputBuffer;
    ULONG OutputBufferLength;
    NTSTATUS Status;

    NotifyData.Version = Version;
    NotifyData.Add = 0;
    InputBuffer = &NotifyData;
    InputBufferLength = sizeof(NotifyData);

    for (i = 0; i < NotificationCount; i++) {
        NotifyContext = (PROUTE_NOTIFY_CONTEXT)malloc(sizeof(*NotifyContext));
        if (!NotifyContext) {
            printf("QueueNotifyRouteChange: malloc=<null>\n");
            break;
        } else {
            printf("QueueNotifyRouteChange: queuing %p\n", NotifyContext);
            ZeroMemory(NotifyContext, sizeof(*NotifyContext));
            Status =
                NtDeviceIoControlFile(
                    FileHandle,
                    NULL,
                    NotifyRouteCompletionRoutine,
                    NotifyContext,
                    &NotifyContext->IoStatus,
                    IOCTL_IP_RTCHANGE_NOTIFY_REQUEST,
                    InputBuffer,
                    InputBufferLength,
                    &NotifyContext->NotifyOutput,
                    sizeof(NotifyContext->NotifyOutput)
                    );
            printf("NtDeviceIoControlFile=%x\n", Status);
        }
    }

    return i;
}

int __cdecl
main(
    int argc,
    char* argv[]
    )
{
    HANDLE FileHandle;
    IO_STATUS_BLOCK IoStatus;
    OBJECT_ATTRIBUTES ObjectAttributes;
    NTSTATUS Status;
    UNICODE_STRING UnicodeString;

    //
    // Open a handle to the IP device-object.
    //

    RtlInitUnicodeString(&UnicodeString, DD_IP_DEVICE_NAME);
    InitializeObjectAttributes(
        &ObjectAttributes,
        &UnicodeString,
        OBJ_CASE_INSENSITIVE,
        NULL,
        NULL
        );
    Status =
        NtCreateFile(
            &FileHandle,
            GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,
            &ObjectAttributes,
            &IoStatus,
            NULL,
            0,
            FILE_SHARE_READ|FILE_SHARE_WRITE,
            FILE_OPEN_IF,
            0,
            NULL,
            0
            );
    if (!NT_SUCCESS(Status)) {
        printf("NtCreateFile=%x\n", Status);
        return 0;
    }

    //
    // Continually prompt for instructions until interrupted.
    //

    for (;;) {
        ULONG Selection;
        BOOLEAN OutputRequired = TRUE;
        IPNotifyVersion Version = IPNotifySynchronization;

        printf("Simple route-change notification:\n");
        printf("\t1.  Submit NULL route-change request [no output]\n");
        printf("\t2.  Submit NULL route-change request\n");
        printf("\t3.  Submit general route-change request [no output]\n");
        printf("\t4.  Submit general route-change request\n");
        printf("\t5.  Submit specific route-change request [no output]\n");
        printf("\t6.  Submit specific route-change request\n");

        printf("Extended route-change notification:\n");
        printf("\t7.  Submit NULL route-change request [no output]\n");
        printf("\t8.  Submit NULL route-change request\n");
        printf("\t9.  Submit general route-change request [no output]\n");
        printf("\t10. Submit general route-change request\n");
        printf("\t11. Submit specific route-change request [no output]\n");
        printf("\t12. Submit specific route-change request\n");

        printf("\t13. Submit multiple general route-change requests\n");
        printf("\t    using 'notification' semantics.\n");
        printf("\t14. Submit multiple general route-change requests\n");
        printf("\t    using 'synchronization' semantics.\n");

        printf("\nEnter selection: ");
        if (!scanf("%d", &Selection)) {
            break;
        }
        switch(Selection) {
            case 1:
                OutputRequired = FALSE;
            case 2: {
                Status =
                    NotifyRouteChange(
                        FileHandle, IOCTL_IP_RTCHANGE_NOTIFY_REQUEST,
                        INADDR_NONE, OutputRequired
                        );
                printf("NotifyRouteChange=%x\n", Status);
                break;
            }
            case 3:
                OutputRequired = FALSE;
            case 4: {
                Status =
                    NotifyRouteChange(
                        FileHandle, IOCTL_IP_RTCHANGE_NOTIFY_REQUEST,
                        INADDR_ANY, OutputRequired
                        );
                printf("NotifyRouteChange=%x\n", Status);
                break;
            }
            case 5:
                OutputRequired = FALSE;
            case 6: {
                UCHAR Destination[16];

                printf("Enter destination: ");
                if (!scanf("%s", Destination)) {
                    break;
                }
                Status =
                    NotifyRouteChange(
                        FileHandle, IOCTL_IP_RTCHANGE_NOTIFY_REQUEST,
                        inet_addr(Destination), OutputRequired
                        );
                printf("NotifyRouteChange=%x\n", Status);
                break;
            }
            case 7:
                OutputRequired = FALSE;
            case 8: {
                Status =
                    NotifyRouteChange(
                        FileHandle, IOCTL_IP_RTCHANGE_NOTIFY_REQUEST_EX,
                        INADDR_NONE, OutputRequired
                        );
                printf("NotifyRouteChange=%x\n", Status);
                break;
            }
            case 9:
                OutputRequired = FALSE;
            case 10: {
                Status =
                    NotifyRouteChange(
                        FileHandle, IOCTL_IP_RTCHANGE_NOTIFY_REQUEST_EX,
                        INADDR_ANY, OutputRequired
                        );
                printf("NotifyRouteChange=%x\n", Status);
                break;
            }
            case 11:
                OutputRequired = FALSE;
            case 12: {
                UCHAR Destination[16];

                printf("Enter destination: ");
                if (!scanf("%s", Destination)) {
                    break;
                }
                Status =
                    NotifyRouteChange(
                        FileHandle, IOCTL_IP_RTCHANGE_NOTIFY_REQUEST_EX,
                        inet_addr(Destination), OutputRequired
                        );
                printf("NotifyRouteChange=%x\n", Status);
                break;
            }

            case 13:
                Version = IPNotifyNotification;
            case 14: {
                LARGE_INTEGER Timeout;
                ULONG NotificationCount = 0;

                printf("Enter number of requests to queue: ");
                if (!scanf("%d", &NotificationCount)) {
                    break;
                }

                NotificationCount =
                    QueueNotifyRouteChange(
                        FileHandle, Version, NotificationCount
                        );

                for (; NotificationCount; --NotificationCount) {
                    Timeout.LowPart = 0;
                    Timeout.HighPart = MINLONG;
                    NtDelayExecution(TRUE, &Timeout);
                }

                break;
            }
        }
    }
    
    NtClose(FileHandle);
    return 0;
}