root/src/bin/debug/strace/NetworkTypes.cpp
/*
 * Copyright 2007, Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Hugo Santos <hugosantos@gmail.com>
 *              Ingo Weinhold <bonefish@cs.tu-berlin.de>
 */

#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/un.h>

#include "Context.h"
#include "MemoryReader.h"
#include "TypeHandler.h"


template<typename Type>
static bool
obtain_pointer_data(Context &context, Type *data, void *address, uint32 what)
{
        if (address == NULL || !context.GetContents(what))
                return false;

        int32 bytesRead;

        status_t err = context.Reader().Read(address, data, sizeof(Type), bytesRead);
        if (err != B_OK || bytesRead < (int32)sizeof(Type))
                return false;

        return true;
}


static string
format_number(uint32 value)
{
        char tmp[32];
        snprintf(tmp, sizeof(tmp), "%u", (unsigned int)value);
        return tmp;
}


static string
format_iovecs(Context &context, const iovec *iov, int iovlen)
{
        if (iov == NULL && iovlen == 0)
                return "(empty)";

        iovec vecs[iovlen];
        int32 bytesRead;

        string r = "[";
        status_t err = context.Reader().Read((void*)iov, vecs, sizeof(vecs), bytesRead);
        if (err != B_OK) {
                r += context.FormatPointer(iov);
                r += ", " + context.FormatSigned(iovlen);
        } else {
                for (int i = 0; i < iovlen; i++) {
                        if (i > 0)
                                r += ", ";
                        if (i >= 8) {
                                r += "...";
                                break;
                        }
                        r += "{iov_base=" + context.FormatPointer(vecs[i].iov_base);
                        r += ", iov_len=" + context.FormatUnsigned(vecs[i].iov_len);
                        r += "}";
                }
        }
        return r + "]";
}


template<>
string
TypeHandlerImpl<iovec *>::GetParameterValue(Context &context, Parameter *param,
        const void *address)
{
        Parameter *size = context.GetNextSibling(param);
        return format_iovecs(context, (const iovec*)*(void **)address,
                context.ReadValue<size_t>(size));
}


template<>
string
TypeHandlerImpl<iovec *>::GetReturnValue(Context &context, uint64 value)
{
        return context.FormatPointer((void *)value);
}


static string
format_ltype(Context &context, int ltype)
{
        if (context.GetContents(Context::ENUMERATIONS)) {
#define LTYPE(type) \
                case type: \
                        return #type

                switch (ltype) {
                        LTYPE(F_RDLCK);
                        LTYPE(F_UNLCK);
                        LTYPE(F_WRLCK);
                }
        }

        return context.FormatSigned(ltype);
}


static string
format_lwhence(Context &context, int lwhence)
{
        if (context.GetContents(Context::ENUMERATIONS)) {
#define LWHENCE(whence) \
                case whence: \
                        return #whence

                switch (lwhence) {
                        LWHENCE(SEEK_SET);
                        LWHENCE(SEEK_CUR);
                        LWHENCE(SEEK_END);
                        LWHENCE(SEEK_DATA);
                        LWHENCE(SEEK_HOLE);
                }
        }

        return context.FormatSigned(lwhence);
}



static string
format_pointer(Context &context, flock *lock)
{
        string r;

        r = "l_type=" + format_ltype(context, lock->l_type) + ", ";
        r += "l_whence=" + format_lwhence(context, lock->l_whence) + ", ";
        r += "l_start=" + context.FormatSigned(lock->l_start) + ", ";
        r += "l_len=" + context.FormatSigned(lock->l_len);

        return r;
}



template<typename Type>
static string
format_pointer_value(Context &context, void *address)
{
        Type data;

        if (obtain_pointer_data(context, &data, address, Context::COMPLEX_STRUCTS))
                return "{" + format_pointer(context, &data) + "}";

        return context.FormatPointer(address);
}


static string
get_ipv4_address(in_addr *addr)
{
        char tmp[32];
        snprintf(tmp, sizeof(tmp), "%u.%u.%u.%u",
                 (unsigned int)(htonl(addr->s_addr) >> 24) & 0xff,
                 (unsigned int)(htonl(addr->s_addr) >> 16) & 0xff,
                 (unsigned int)(htonl(addr->s_addr) >>  8) & 0xff,
                 (unsigned int)(htonl(addr->s_addr) >>  0) & 0xff);
        return tmp;
}


static string
format_socket_family(Context &context, int family)
{
        if (context.GetContents(Context::ENUMERATIONS)) {
#define SOCKET_FAMILY(family) \
                case family: \
                        return #family

                switch (family) {
                        SOCKET_FAMILY(AF_UNSPEC);
                        SOCKET_FAMILY(AF_INET);
                        SOCKET_FAMILY(AF_APPLETALK);
                        SOCKET_FAMILY(AF_ROUTE);
                        SOCKET_FAMILY(AF_LINK);
                        SOCKET_FAMILY(AF_INET6);
                        SOCKET_FAMILY(AF_LOCAL);
                }
        }

        return "family = " + context.FormatSigned(family);
}


#if 0
static string
format_socket_type(Context &context, int type)
{
        if (context.GetContents(Context::ENUMERATIONS)) {
                switch (type) {
                        case SOCK_RAW:
                                return "SOCK_RAW";
                        case SOCK_DGRAM:
                                return "SOCK_DGRAM";
                        case SOCK_STREAM:
                                return "SOCK_STREAM";
                }
        }

        return "type = " + context.FormatSigned(type);
}


static string
format_socket_protocol(Context &context, int protocol)
{
        if (context.GetContents(Context::ENUMERATIONS)) {
                switch (protocol) {
                        case IPPROTO_IP:
                                return "IPPROTO_IP";
                        case IPPROTO_RAW:
                                return "IPPROTO_RAW";
                        case IPPROTO_ICMP:
                                return "IPPROTO_ICMP";
                        case IPPROTO_UDP:
                                return "IPPROTO_UDP";
                        case IPPROTO_TCP:
                                return "IPPROTO_TCP";
                }
        }

        return "protocol = " + context.FormatSigned(protocol);
}
#endif


static string
format_pointer(Context &context, sockaddr *saddr)
{
        string r;

        r = format_socket_family(context, saddr->sa_family) + ", ";

        switch (saddr->sa_family) {
                case AF_INET:
                {
                        sockaddr_in *sin = (sockaddr_in *)saddr;
                        r += get_ipv4_address(&sin->sin_addr);
                        r += "/";
                        r += format_number(ntohs(sin->sin_port));
                        break;
                }
                case AF_UNIX:
                {
                        sockaddr_un *sun = (sockaddr_un *)saddr;
                        r += "path = \"" + string(sun->sun_path) + "\"";
                        break;
                }
                default:
                        r += "...";
                        break;
        }

        return r;
}


static string
read_sockaddr(Context &context, Parameter *param, void *address)
{
        param = context.GetNextSibling(param);
        if (param == NULL)
                return context.FormatPointer(address);

        socklen_t addrlen = context.ReadValue<socklen_t>(param);

        sockaddr_storage data;

        if (addrlen > sizeof(data))
                return context.FormatPointer(address);

        int32 bytesRead;
        status_t err = context.Reader().Read(address, &data, addrlen, bytesRead);
        if (err != B_OK)
                return context.FormatPointer(address);

        return "{" + format_pointer(context, (sockaddr *)&data) + "}";
}


template<>
string
TypeHandlerImpl<sockaddr *>::GetParameterValue(Context &context,
        Parameter *param, const void *address)
{
        void *data = *(void **)address;
        if (data != NULL && context.GetContents(Context::SIMPLE_STRUCTS))
                return read_sockaddr(context, param, data);
        return context.FormatPointer(data);
}


template<>
string
TypeHandlerImpl<sockaddr *>::GetReturnValue(Context &context, uint64 value)
{
        return context.FormatPointer((void *)value);
}


static string
format_pointer(Context &context, msghdr *h)
{
        string r;

        r  =   "name = " + format_pointer_value<sockaddr>(context, h->msg_name);
        r += ", name_len = " + context.FormatUnsigned(h->msg_namelen);
        r += ", iov = " + format_iovecs(context, h->msg_iov, h->msg_iovlen);
        if (h->msg_control != NULL || h->msg_controllen != 0) {
                r += ", control = " + context.FormatPointer(h->msg_control);
                r += ", control_len = " + context.FormatUnsigned(h->msg_controllen);
        }
        r += ", flags = " + context.FormatFlags(h->msg_flags);

        return r;
}


static string
format_pointer(Context &context, ifreq *ifr)
{
        return string(ifr->ifr_name) + ", ...";
}


static string
format_pointer(Context &context, ifconf *conf)
{
        unsigned char buffer[sizeof(ifreq) * 8];
        int maxData = sizeof(buffer);
        int32 bytesRead;

        if (conf->ifc_len < maxData)
                maxData = conf->ifc_len;

        string r = "len = " + context.FormatSigned(conf->ifc_len);

        if (conf->ifc_len == 0)
                return r;

        status_t err = context.Reader().Read(conf->ifc_buf, buffer,
                                             maxData, bytesRead);
        if (err != B_OK)
                return r + ", buf = " + context.FormatPointer(conf->ifc_buf);

        r += ", [";

        uint8 *current = buffer, *bufferEnd = buffer + bytesRead;
        for (int i = 0; i < 8; i++) {
                if ((bufferEnd - current) < (IF_NAMESIZE + 1))
                        break;

                ifreq *ifr = (ifreq *)current;
                int size = IF_NAMESIZE + ifr->ifr_addr.sa_len;

                if ((bufferEnd - current) < size)
                        break;

                if (i > 0)
                        r += ", ";

                r += "{" + string(ifr->ifr_name) + ", {"
                         + format_pointer(context, &ifr->ifr_addr) + "}}";

                current += size;
        }

        if (current < bufferEnd)
                r += ", ...";

        return r + "]";
}


static string
format_pointer(Context &context, siginfo_t *info)
{
        string r;

        switch (info->si_code) {
                case CLD_EXITED:
                        r = "WIFEXITED(s) && WEXITSTATUS(s) == " + context.FormatUnsigned(info->si_status & 0xff);
                        break;
        }
        return r;
}



template<typename Type>
class SpecializedPointerTypeHandler : public TypeHandler {
        string GetParameterValue(Context &context, Parameter *,
                const void *address)
        {
                return format_pointer_value<Type>(context, *(void **)address);
        }

        string GetReturnValue(Context &context, uint64 value)
        {
                return format_pointer_value<Type>(context, (void *)value);
        }
};

#define POINTER_TYPE(name, type) \
        TypeHandler *create_##name##_type_handler() \
        { \
                return new SpecializedPointerTypeHandler<type>(); \
        }

DEFINE_TYPE(iovec_ptr, iovec *);
POINTER_TYPE(flock_ptr, flock);
POINTER_TYPE(ifconf_ptr, ifconf);
POINTER_TYPE(ifreq_ptr, ifreq);
POINTER_TYPE(siginfo_t_ptr, siginfo_t);
POINTER_TYPE(msghdr_ptr, msghdr);
DEFINE_TYPE(sockaddr_ptr, sockaddr *);