/*
 * Mini Binl Server
 * Copyright (c) 2005-2007 Gianluigi Tiesi <sherpya@netfarm.it>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this software; if not, write to the
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* Compilation on linux: gcc binlsrv.c -o binlsrv
 * Compilation on mingw: gcc binlsrv.c -o binlsrv -lws2_32
 * Compilation on msvc : cl.exe binlsrv.c ws2_32.lib
 */

#ifdef _MSC_VER
#define _CRT_SECURE_NO_DEPRECATE
#endif

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#ifndef PORT
#define PORT 4011
#endif

#ifndef NICFILE
#define NICFILE "nics.txt"
#endif

#ifndef __LITTLE_ENDIAN
#define __LITTLE_ENDIAN 1234
#endif
#ifndef __BIG_ENDIAN
#define __BIG_ENDIAN    4321
#endif

#ifdef _WIN32
#include <winsock2.h>
#define __BYTE_ORDER __LITTLE_ENDIAN
#define inline __inline
typedef unsigned __int32 uint32_t;
typedef unsigned __int16 uint16_t;
#else
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> /* inet_ntoa */
#include <errno.h>
#include <inttypes.h>
#define INVALID_SOCKET      -1
#define SOCKET_ERROR        -1
#define closesocket         close
#define WSAGetLastError()   errno
#define WSACleanup()
#endif

#ifndef __BYTE_ORDER
#if defined(_BIG_ENDIAN)
#define __BYTE_ORDER __BIG_ENDIAN
#elif defined(_LITTLE_ENDIAN)
#define __BYTE_ORDER __LITTLE_ENDIAN
#endif
#endif

#if __BYTE_ORDER == __LITTLE_ENDIAN
#define SWAB32(x) x
#define SWAB16(x) x
#elif __BYTE_ORDER == __BIG_ENDIAN
#define SWAB32(x) (((uint32_t)(x) >> 24) | \
                  (((uint32_t)(x) >> 8) & 0xff00) | \
                  (((uint32_t)(x) << 8) & 0xff0000) | \
                  ((uint32_t)(x) << 24))
#define SWAB16(x) (((uint16_t)(x) >> 8) | (((uint16_t)(x) & 0xff) << 8))
#else
#error "Unknown byte order"
#endif

#define PKT_NCQ             0x51434e81  /* Network Card Query */
#define PKT_NCR             0x52434e82  /* Network Card Reply */

#define PKT_RQU             0x55515281  /* OSC File request */
#define PKT_RSU             0x55535282  /* OSC File reply */

#define PKT_NEG             0x47454e81  /* NTLM Negotiate */
#define PKT_CHL             0x4c484382  /* NTLM Sending Challenge */
#define PKT_AUT             0x54554181  /* NTLM Autorize */
#define PKT_RES             0x53455282  /* NTLM Auth reply */

#define PKT_REQ             0x51455281  /* Unknown */
#define PKT_RSP             0x50535282  /* Unknown */

#define PKT_OFF             0x46464f81  /* Reboot to new pxe rom */
#define PKT_UNR             0x524e5582  /* Session expired */

#define NCR_OK              0x00000000
#define NCR_KO              0xc000000d

const char ris_params[] = "Description\0" "2\0" "Ris NIC Card\0"
                          "Characteristics\0" "1\0" "132\0"
                          "BusType\0" "1\0" "5\0";

typedef struct _DRIVER
{
    uint16_t vid, pid;
    char driver[256];
    char service[256];
} DRIVER;

static int m_socket = INVALID_SOCKET;

static void cleanup(int signum)
{
    printf("Shutting down...\n");
    closesocket(m_socket);
    WSACleanup();
    exit(0);
}

#ifdef _WIN32
static void stop_console_handler(void)
{
    SetConsoleCtrlHandler((PHANDLER_ROUTINE) cleanup, FALSE);
}
#endif

const char *type2str(uint32_t type)
{
    switch (type)
    {
        case PKT_NCQ:
            return "[NCQ] Network Card Query";
        case PKT_NCR:
            return "[NCR] Network Card Reply";
        case PKT_RQU:
            return "[RQU] OSC File request";
        case PKT_RSU:
            return "[RSU] OSC File reply";
        case PKT_NEG:
            return "[NEG] NTLM Negotiate";
        case PKT_CHL:
            return "[CHL] NTLM Sending Challenge";
        case PKT_AUT:
            return "[AUT] NTLM Autorize";
        case PKT_RES:
            return "[RES] NTLM Auth reply";
        case PKT_REQ:
            return "[REQ] Unknown";
        case PKT_RSP:
            return "[REQ] Unknown";
        case PKT_OFF:
            return "[OFF] Reboot to new pxe rom";
        case PKT_UNR:
            return "[UNR] Session expired";
        default:
            return "[XXX] Unknown";
    }
}

unsigned char get_string(FILE *fd, char *dest, size_t size)
{
    int i = 0;
    unsigned char c = 0;

    while (i < size)
    {
        if(fread(&c, 1, sizeof(c), fd) != sizeof(c)) break;
        if(isspace(c)) break;
        dest[i++] = c;
    }
    dest[i] = 0;
    return c;
}

static inline void skipspaces(FILE *fd)
{
    unsigned char c = 0;
    while(!feof(fd) && !isspace(c))
        if(fread(&c, 1, sizeof(c), fd) != sizeof(c)) break;
}

static inline void eol(FILE *fd)
{
    unsigned char c = 0;
    while(!feof(fd) && (c != '\n') && (c != '\r'))
        if(fread(&c, 1, sizeof(c), fd) != sizeof(c)) break;
}

int find_drv(uint16_t cvid, uint16_t cpid, DRIVER *drv)
{
    uint32_t vid, pid;
    char buffer[1024];
    int found = 0;

    FILE *fd = fopen(NICFILE, "r");
    if (!fd)
    {
         printf("Problems opening nic file\n");
         return 0;
    }

    while (1)
    {
        if (fread(buffer, 1, 4, fd) != 4) break;
        buffer[4] = 0;
        sscanf(buffer, "%x", &vid);

        skipspaces(fd);

        if (fread(buffer, 1, 4, fd) != 4) break;
        buffer[4] = 0;
        sscanf(buffer, "%x", &pid);

        skipspaces(fd);

        if (!isspace(get_string(fd, drv->driver, sizeof(drv->driver))))
            skipspaces(fd);

        if (!isspace(get_string(fd, drv->service, sizeof(drv->service))))
            eol(fd);

        drv->vid = vid;
        drv->pid = pid;

        printf("Checking vs 0x%x - 0x%x: %s - ", vid, pid, drv->driver);

        if ((SWAB16(cvid) == vid) && (SWAB16(cpid) == pid))
        {
            found = 1;
            printf("Matched\n");
            break;
        }
        printf("No Match\n");
    }

    fclose(fd);
    return found;
}

void dump_packet(char *packet, size_t size, const char *filename)
{
    FILE *fd = fopen(filename, "wb");
    if (!fd) return;

    fwrite(packet, 1, size, fd);
    fclose(fd);
}

size_t ascii_to_utf16le(const char *src, char *dest, size_t offset)
{
    size_t ulen = 0, i = 0;
    size_t len = strlen(src);

    for (i = 0; i < len; i++)
    {
        dest[offset+ulen] = src[i];
        ulen += 2;
    }
    return ulen;
}

int main(int argc, char *argv[])
{
    DRIVER drv;
    struct sockaddr_in local, from;
    char buffer[1024];
    char packet[1024];
    uint32_t type = 0, value = 0, res = NCR_OK;
    uint16_t vid = 0, pid = 0;
    uint32_t fromlen = 0, offset = 0, retval = 0;

#ifdef _WIN32
    WSADATA wsaData;
    SetConsoleCtrlHandler((PHANDLER_ROUTINE) cleanup, TRUE);
    atexit(stop_console_handler);
    if (WSAStartup(MAKEWORD(2,2), &wsaData) != NO_ERROR)
    {
        fprintf(stderr, "Error at WSAStartup()\n");
        return -1;
    }
#else
    signal(SIGINT, cleanup);
#endif

    m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    if (m_socket == INVALID_SOCKET)
    {
        fprintf(stderr, "Error at socket(): %d\n", WSAGetLastError());
        WSACleanup();
        return -1;
    }

    local.sin_family = AF_INET;
    local.sin_addr.s_addr = INADDR_ANY;
    local.sin_port = htons(PORT);

    if (bind(m_socket, (struct sockaddr *) &local, sizeof(local)) == SOCKET_ERROR)
    {
        fprintf(stderr, "bind() failed with error %d\n", WSAGetLastError());
        WSACleanup();
        return -1;
    }

    printf("Mini Binl Server - Copyright (c) 2005-2007 Gianluigi Tiesi\n");
    printf("This program is free software\n");
    printf("Listening on port %d\n", PORT);

    while (1)
    {
        fromlen = sizeof(from);
        retval = recvfrom(m_socket, buffer, sizeof(buffer), 0, (struct sockaddr *) &from, &fromlen);
        printf("Received datagram from %s\n", inet_ntoa(from.sin_addr));

        if (retval < 0)
        {
            fprintf(stderr, "recv() failed: error %d\n", WSAGetLastError());
            continue;
        }

        if (retval == 0)
        {
            printf("Client closed connection\n");
            continue;
        }

        memcpy(&type, buffer, sizeof(type));
        printf("Received %d bytes, packet %s\n", retval, type2str(SWAB32(type)));

        if (SWAB32(type) != PKT_NCQ)
        {
            printf("Only NCQ packets are supported\n");
            continue;
        }

        memcpy(&vid, &buffer[0x24], sizeof(vid));
        memcpy(&pid, &buffer[0x26], sizeof(pid));
        printf("Vendor id 0x%x - Product id 0x%x\n", SWAB16(vid), SWAB16(pid));

        offset = 0;
        memset(packet, 0, sizeof(packet));
        type = SWAB32(PKT_NCR);
        memcpy(packet, &type, sizeof(type));
        offset += sizeof(type);

        if (find_drv(vid, pid, &drv))
        {
            size_t ulen = 0;
            res = SWAB32(NCR_OK);
            offset += 0x4; /* Packet len will be filled later */

            memcpy(&packet[offset], &res, sizeof(res));
            offset += sizeof(res);

            value = SWAB32(0x2); /* Type */
            memcpy(&packet[offset], &value, sizeof(value));
            offset += sizeof(value);

            value = SWAB32(0x24); /* Base offset */
            memcpy(&packet[offset], &value, sizeof(value));
            offset += sizeof(value);

            offset += 0x8; /* Driver name offset / Service name offset */

            value = SWAB32(sizeof(ris_params)); /* Parameters list length in chars */
            memcpy(&packet[offset], &value, sizeof(value));
            offset += sizeof(value);

            offset += 0x4; /* Parameters list offset */

            printf("Found Driver is %s - Service is %s\n", drv.driver, drv.service);
            sprintf(buffer, "PCI\\VEN_%04X&DEV_%04X", drv.vid, drv.pid);

            ulen = ascii_to_utf16le(buffer, packet, offset);
            offset += ulen + 2; /* PCI\VEN_XXXX&DEV_YYYY */

            /* We can fill Driver name offset */
            value = SWAB32(offset);
            memcpy(&packet[0x14], &value, sizeof(value));

            ulen = ascii_to_utf16le(drv.driver, packet, offset);
            offset += ulen + 2; /* Driver name */

            /* We can fill Service name offset */
            value = SWAB32(offset);
            memcpy(&packet[0x18], &value, sizeof(value));

            ulen = ascii_to_utf16le(drv.service, packet, offset);
            offset += ulen + 2; /* Service name */

            /* We can fill Parameters list offset */
            value = SWAB32(offset);
            memcpy(&packet[0x20], &value, sizeof(value));

            /* And now params */
            memcpy(&packet[offset], ris_params, sizeof(ris_params));
            offset += sizeof(ris_params) + 2;

            /* Packet Len */
            value = SWAB32(offset);
            memcpy(&packet[0x4], &value, sizeof(value));

            printf("Driver found - Sending NCR OK\n");
            retval = sendto(m_socket, packet, offset, 0, (struct sockaddr *) &from, fromlen);
            if (retval < 0) fprintf(stderr, "send() failed: error %d\n", WSAGetLastError());
        }
        else
        {
            res = SWAB32(NCR_KO);
            value = SWAB32(offset);
            memcpy(&packet[offset], &value, sizeof(value));
            offset += sizeof(offset);
            memcpy(&packet[offset], &res, sizeof(res));
            offset += sizeof(res);
            printf("Driver not found - Sending NCR Fail\n");
            retval = sendto(m_socket, packet, offset, 0, (struct sockaddr *) &from, fromlen);
            if (retval == SOCKET_ERROR) fprintf(stderr, "send() failed: error %d\n", WSAGetLastError());
        }
    }

    /* The server never reaches this point */
    closesocket(m_socket);
    WSACleanup();
    return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1