/* $Id: network.c,v 1.13 2005/06/20 00:18:58 jfontain Exp $ */

/* to create the loadable library, use: cc -shared -o libnetwork.so.1.20 -O2 -fPIC -Wall network.c */
/* with stubs: cc -shared -o libnetwork.so.1.20 -O2 -fPIC -Wall -DUSE_TCL_STUBS network.c /usr/lib/libtclstub8.3.a */
/* pkgIndex.tcl: package ifneeded network 1.20 [list load [file join $dir libnetwork.so.1.20]] */

#include <tcl.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/route.h>

static int hostFromAddress(ClientData clientData, Tcl_Interp *interpreter, int numberOfArguments, Tcl_Obj * CONST arguments[])
{
    struct hostent *hostEntry;
    struct in_addr address;

    if(numberOfArguments != 2){
        Tcl_WrongNumArgs(interpreter, 1, arguments, "address");
        return TCL_ERROR;
    }
    if(inet_aton(Tcl_GetStringFromObj(arguments[1], 0), &address) == 0){
        Tcl_SetObjResult(interpreter, Tcl_NewStringObj("invalid address", -1));
        return TCL_ERROR;
    }
    hostEntry = gethostbyaddr((char *)&address, sizeof(address), AF_INET);
    if(hostEntry == 0){
        Tcl_SetObjResult(interpreter, Tcl_NewStringObj((char *)hstrerror(h_errno), -1));     /* core takes care of freeing object */
        return TCL_ERROR;
    }
    Tcl_SetObjResult(interpreter, Tcl_NewStringObj(hostEntry->h_name, -1));                  /* core takes care of freeing object */
    return TCL_OK;
}

static int addressFromHost(ClientData clientData, Tcl_Interp *interpreter, int numberOfArguments, Tcl_Obj * CONST arguments[])
{
    struct hostent *hostEntry;
    struct in_addr address;

    if(numberOfArguments != 2){
        Tcl_WrongNumArgs(interpreter, 1, arguments, "host");
        return TCL_ERROR;
    }
    hostEntry = gethostbyname(Tcl_GetStringFromObj(arguments[1], 0));
    if(hostEntry == 0){
        Tcl_SetObjResult(interpreter, Tcl_NewStringObj((char *)hstrerror(h_errno), -1));     /* core takes care of freeing object */
        return TCL_ERROR;
    }
    memcpy(&address, hostEntry->h_addr, sizeof address);
    Tcl_SetObjResult(interpreter, Tcl_NewStringObj(inet_ntoa(address), -1));                 /* core takes care of freeing object */
    return TCL_OK;
}

static int networkFromAddress(ClientData clientData, Tcl_Interp *interpreter, int numberOfArguments, Tcl_Obj * CONST arguments[])
{
    unsigned long network;
    struct netent *networkEntry;

    if(numberOfArguments != 2){
        Tcl_WrongNumArgs(interpreter, 1, arguments, "address");
        return TCL_ERROR;
    }
    network = inet_network(Tcl_GetStringFromObj(arguments[1], 0));
    if(network < 0) {
        Tcl_SetObjResult(interpreter, Tcl_NewStringObj("invalid address", -1));
        return TCL_ERROR;
    }
    networkEntry = getnetbyaddr(network, AF_INET);
    if(networkEntry == 0){
        Tcl_SetObjResult(interpreter, Tcl_NewStringObj((char *)hstrerror(h_errno), -1));     /* core takes care of freeing object */
        return TCL_ERROR;
    }
    Tcl_SetObjResult(interpreter, Tcl_NewStringObj(networkEntry->n_name, -1));               /* core takes care of freeing object */
    return TCL_OK;
}

static int routeLetterFlags(ClientData clientData, Tcl_Interp *interpreter, int numberOfArguments, Tcl_Obj * CONST arguments[])
{                         /* sole argument is bit encoded value from /proc/net/route, as documented in /usr/include/linux/route.h */
    int bits;
    char letters[33];                                                                             /* 32 bits plus zero terminator */
    char *letter;

    if(numberOfArguments != 2){
        Tcl_WrongNumArgs(interpreter, 1, arguments, "integer");
        return TCL_ERROR;
    }
    if(Tcl_GetIntFromObj(interpreter, arguments[1], &bits) == TCL_ERROR)
        return TCL_ERROR;
    letter = letters;                                                                      /* return flags in alphabetical order: */
    if(bits & RTF_DYNAMIC){*(letter++) = 'D';}
    if(bits & RTF_GATEWAY){*(letter++) = 'G';}
    if(bits & RTF_HOST){*(letter++) = 'H';}
    if(bits & RTF_MODIFIED){*(letter++) = 'M';}
#ifdef RTF_REINSTATE
    if(bits & RTF_REINSTATE){*(letter++) = 'R';}
#endif
    if(bits & RTF_UP){*(letter++) = 'U';}
    if(bits & RTF_REJECT){*(letter++) = '!';}
    Tcl_SetObjResult(interpreter, Tcl_NewStringObj(letters, letter - letters));              /* core takes care of freeing object */
    return TCL_OK;
}


EXTERN int Network_Init(Tcl_Interp *interpreter)                                      /* create all commands in network namespace */
{
#ifdef USE_TCL_STUBS
    if (Tcl_InitStubs(interpreter, "8.1", 0) == NULL) {
        return TCL_ERROR;
    }
#endif
    Tcl_CreateObjCommand(interpreter, "::network::hostfromaddress", hostFromAddress, 0, 0);
    Tcl_CreateObjCommand(interpreter, "::network::addressfromhost", addressFromHost, 0, 0);
    Tcl_CreateObjCommand(interpreter, "::network::networkfromaddress", networkFromAddress, 0, 0);
    Tcl_CreateObjCommand(interpreter, "::network::routeletterflags", routeLetterFlags, 0, 0);
    return TCL_OK;                                      /* the package provide command is invoked in the related network.tcl file */
}

EXTERN int Network_SafeInit(Tcl_Interp *interpreter)
{
    return Network_Init(interpreter);                                                                    /* all commands are safe */
}
