root/src/bin/debug/strace/network.cpp
/*
 * Copyright 2023-2025, Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 */


#include <netinet/in.h>
#include <sys/socket.h>

#include "strace.h"
#include "Syscall.h"
#include "TypeHandler.h"


struct enum_info {
        int index;
        const char *name;
};

#define ENUM_INFO_ENTRY(name) \
        { name, #name }


#define FLAG_INFO_ENTRY(name) \
        { name, #name }


static const FlagsTypeHandler::FlagInfo kRecvFlagInfos[] = {
        FLAG_INFO_ENTRY(MSG_PEEK),
        FLAG_INFO_ENTRY(MSG_OOB),
        FLAG_INFO_ENTRY(MSG_DONTROUTE),
        FLAG_INFO_ENTRY(MSG_EOR),
        FLAG_INFO_ENTRY(MSG_TRUNC),
        FLAG_INFO_ENTRY(MSG_CTRUNC),
        FLAG_INFO_ENTRY(MSG_WAITALL),
        FLAG_INFO_ENTRY(MSG_DONTWAIT),
        FLAG_INFO_ENTRY(MSG_BCAST),
        FLAG_INFO_ENTRY(MSG_MCAST),
        FLAG_INFO_ENTRY(MSG_EOF),
        FLAG_INFO_ENTRY(MSG_NOSIGNAL),
        FLAG_INFO_ENTRY(MSG_CMSG_CLOEXEC),
        FLAG_INFO_ENTRY(MSG_CMSG_CLOFORK),
        { 0, NULL }
};


static const enum_info kSocketFamily[] = {
        ENUM_INFO_ENTRY(AF_UNSPEC),
        ENUM_INFO_ENTRY(AF_INET),
        ENUM_INFO_ENTRY(AF_APPLETALK),
        ENUM_INFO_ENTRY(AF_ROUTE),
        ENUM_INFO_ENTRY(AF_LINK),
        ENUM_INFO_ENTRY(AF_INET6),
        ENUM_INFO_ENTRY(AF_DLI),
        ENUM_INFO_ENTRY(AF_IPX),
        ENUM_INFO_ENTRY(AF_NOTIFY),
        ENUM_INFO_ENTRY(AF_UNIX),
        ENUM_INFO_ENTRY(AF_BLUETOOTH),

        { 0, NULL }
};


static const enum_info kSocketType[] = {
        ENUM_INFO_ENTRY(SOCK_STREAM),
        ENUM_INFO_ENTRY(SOCK_DGRAM),
        ENUM_INFO_ENTRY(SOCK_RAW),
        ENUM_INFO_ENTRY(SOCK_SEQPACKET),
        ENUM_INFO_ENTRY(SOCK_MISC),

        { 0, NULL }
};


static const enum_info kShutdownHow[] = {
        ENUM_INFO_ENTRY(SHUT_RD),
        ENUM_INFO_ENTRY(SHUT_WR),
        ENUM_INFO_ENTRY(SHUT_RDWR),

        { 0, NULL }
};


static const FlagsTypeHandler::FlagInfo kSocketFlagInfos[] = {
        FLAG_INFO_ENTRY(SOCK_NONBLOCK),
        FLAG_INFO_ENTRY(SOCK_CLOEXEC),
        FLAG_INFO_ENTRY(SOCK_CLOFORK),

        { 0, NULL }
};


static const enum_info kProtocolLevels[] = {
        ENUM_INFO_ENTRY(SOL_SOCKET),
        ENUM_INFO_ENTRY(IPPROTO_IP),
        ENUM_INFO_ENTRY(IPPROTO_IPV6),

        { 0, NULL }
};


struct socket_option_info {
        int level;
        int option;
        const char *name;
        TypeHandler *handler;
        int length;
};


#define SOCKET_OPTION_INFO_ENTRY(level, option) \
        { level, option, #option, NULL, 0 }

#define SOCKET_OPTION_INFO_ENTRY_TYPE(level, option, type) \
        { level, option, #option, TypeHandlerFactory<type *>::Create(), sizeof(type) }

static const socket_option_info kSocketOptions[] = {
        SOCKET_OPTION_INFO_ENTRY(SOL_SOCKET, SO_ACCEPTCONN),
        SOCKET_OPTION_INFO_ENTRY_TYPE(SOL_SOCKET, SO_BROADCAST, int32),
        SOCKET_OPTION_INFO_ENTRY_TYPE(SOL_SOCKET, SO_DEBUG, int32),
        SOCKET_OPTION_INFO_ENTRY_TYPE(SOL_SOCKET, SO_DONTROUTE, int32),
        SOCKET_OPTION_INFO_ENTRY_TYPE(SOL_SOCKET, SO_KEEPALIVE, int32),
        SOCKET_OPTION_INFO_ENTRY_TYPE(SOL_SOCKET, SO_OOBINLINE, int32),
        SOCKET_OPTION_INFO_ENTRY_TYPE(SOL_SOCKET, SO_REUSEADDR, int32),
        SOCKET_OPTION_INFO_ENTRY_TYPE(SOL_SOCKET, SO_REUSEPORT, int32),
        SOCKET_OPTION_INFO_ENTRY_TYPE(SOL_SOCKET, SO_USELOOPBACK, int32),
        SOCKET_OPTION_INFO_ENTRY(SOL_SOCKET, SO_LINGER),
        SOCKET_OPTION_INFO_ENTRY_TYPE(SOL_SOCKET, SO_SNDBUF, uint32),
        SOCKET_OPTION_INFO_ENTRY(SOL_SOCKET, SO_SNDLOWAT),
        SOCKET_OPTION_INFO_ENTRY(SOL_SOCKET, SO_SNDTIMEO),
        SOCKET_OPTION_INFO_ENTRY_TYPE(SOL_SOCKET, SO_RCVBUF, uint32),
        SOCKET_OPTION_INFO_ENTRY(SOL_SOCKET, SO_RCVLOWAT),
        SOCKET_OPTION_INFO_ENTRY(SOL_SOCKET, SO_RCVTIMEO),
        SOCKET_OPTION_INFO_ENTRY(SOL_SOCKET, SO_ERROR),
        SOCKET_OPTION_INFO_ENTRY(SOL_SOCKET, SO_TYPE),
        SOCKET_OPTION_INFO_ENTRY_TYPE(SOL_SOCKET, SO_NONBLOCK, int32),
        SOCKET_OPTION_INFO_ENTRY(SOL_SOCKET, SO_BINDTODEVICE),
        SOCKET_OPTION_INFO_ENTRY_TYPE(SOL_SOCKET, SO_PEERCRED, struct ucred),

        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, IP_OPTIONS),
        SOCKET_OPTION_INFO_ENTRY_TYPE(IPPROTO_IP, IP_HDRINCL, int),
        SOCKET_OPTION_INFO_ENTRY_TYPE(IPPROTO_IP, IP_TOS, int),
        SOCKET_OPTION_INFO_ENTRY_TYPE(IPPROTO_IP, IP_TTL, int),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, IP_RECVOPTS),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, IP_RECVRETOPTS),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, IP_RECVDSTADDR),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, IP_RETOPTS),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, IP_MULTICAST_IF),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, IP_MULTICAST_TTL),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, IP_MULTICAST_LOOP),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, IP_ADD_MEMBERSHIP),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, IP_DROP_MEMBERSHIP),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, IP_BLOCK_SOURCE),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, IP_UNBLOCK_SOURCE),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, MCAST_JOIN_GROUP),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, MCAST_BLOCK_SOURCE),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, MCAST_UNBLOCK_SOURCE),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, MCAST_LEAVE_GROUP),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, MCAST_JOIN_SOURCE_GROUP),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, MCAST_LEAVE_SOURCE_GROUP),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IP, IP_DONTFRAG),

        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IPV6, IPV6_MULTICAST_IF),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IPV6, IPV6_MULTICAST_HOPS),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IPV6, IPV6_MULTICAST_LOOP),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IPV6, IPV6_UNICAST_HOPS),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IPV6, IPV6_JOIN_GROUP),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IPV6, IPV6_LEAVE_GROUP),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IPV6, IPV6_V6ONLY),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IPV6, IPV6_PKTINFO),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IPV6, IPV6_RECVPKTINFO),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IPV6, IPV6_HOPLIMIT),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IPV6, IPV6_RECVHOPLIMIT),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IPV6, IPV6_HOPOPTS),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IPV6, IPV6_DSTOPTS),
        SOCKET_OPTION_INFO_ENTRY(IPPROTO_IPV6, IPV6_RTHDR),

        { -1, -1, NULL, NULL }
};


static FlagsTypeHandler::FlagsList kRecvFlags;
static EnumTypeHandler::EnumMap kSocketFamilyMap;
static EnumTypeHandler::EnumMap kSocketTypeMap;
static EnumTypeHandler::EnumMap kShutdownHowMap;
static FlagsTypeHandler::FlagsList kSocketFlags;
static EnumTypeHandler::EnumMap kProtocolLevelMap;
static TypeHandlerSelector::SelectMap kLevelTypeHandlers;
static EnumTypeHandler::EnumMap kSocketLevelOptionMap;
static EnumTypeHandler::EnumMap kIPProtoLevelOptionMap;
static EnumTypeHandler::EnumMap kIPv6ProtoLevelOptionMap;
static TypeHandlerSelector::SelectMap kSocketLevelOptionTypeHandlers;
static TypeHandlerSelector::SelectMap kIPProtoLevelOptionTypeHandlers;
static TypeHandlerSelector::SelectMap kIPv6ProtoLevelOptionTypeHandlers;
static TypeHandlerSelector::SelectMap kLevelOptionTypeHandlers;


void
patch_network()
{
        for (int i = 0; kRecvFlagInfos[i].name != NULL; i++) {
                kRecvFlags.push_back(kRecvFlagInfos[i]);
        }
        for (int i = 0; kSocketFamily[i].name != NULL; i++) {
                kSocketFamilyMap[kSocketFamily[i].index] = kSocketFamily[i].name;
        }
        for (int i = 0; kSocketType[i].name != NULL; i++) {
                kSocketTypeMap[kSocketType[i].index] = kSocketType[i].name;
        }
        for (int i = 0; kShutdownHow[i].name != NULL; i++) {
                kShutdownHowMap[kShutdownHow[i].index] = kShutdownHow[i].name;
        }
        for (int i = 0; kSocketFlagInfos[i].name != NULL; i++) {
                kSocketFlags.push_back(kSocketFlagInfos[i]);
        }
        for (int i = 0; kProtocolLevels[i].name != NULL; i++) {
                kProtocolLevelMap[kProtocolLevels[i].index] = kProtocolLevels[i].name;
        }
        for (int i = 0; kSocketOptions[i].name != NULL; i++) {
                EnumTypeHandler::EnumMap* map = NULL;
                TypeHandlerSelector::SelectMap* selectMap = NULL;
                if (kSocketOptions[i].level == SOL_SOCKET) {
                        map = &kSocketLevelOptionMap;
                        selectMap = &kSocketLevelOptionTypeHandlers;
                } else if (kSocketOptions[i].level == IPPROTO_IP) {
                        map = &kIPProtoLevelOptionMap;
                        selectMap = &kIPProtoLevelOptionTypeHandlers;
                } else if (kSocketOptions[i].level == IPPROTO_IPV6) {
                        map = &kIPv6ProtoLevelOptionMap;
                        selectMap = &kIPv6ProtoLevelOptionTypeHandlers;
                }
                if (map != NULL) {
                        (*map)[kSocketOptions[i].option] = kSocketOptions[i].name;
                        if (kSocketOptions[i].handler == NULL)
                                continue;
                        (*selectMap)[kSocketOptions[i].option] = kSocketOptions[i].handler;
                }
        }
        kLevelTypeHandlers[SOL_SOCKET] = new EnumTypeHandler(kSocketLevelOptionMap);
        kLevelOptionTypeHandlers[SOL_SOCKET] = new TypeHandlerSelector(
                kSocketLevelOptionTypeHandlers, 2, TypeHandlerFactory<void *>::Create());
        kLevelOptionTypeHandlers[IPPROTO_IP] = new TypeHandlerSelector(
                kIPProtoLevelOptionTypeHandlers, 2, TypeHandlerFactory<void *>::Create());
        kLevelOptionTypeHandlers[IPPROTO_IPV6] = new TypeHandlerSelector(
                kIPv6ProtoLevelOptionTypeHandlers, 2, TypeHandlerFactory<void *>::Create());

        Syscall *recv = get_syscall("_kern_recv");
        recv->GetParameter("flags")->SetHandler(new FlagsTypeHandler(kRecvFlags));
        Syscall *recvfrom = get_syscall("_kern_recvfrom");
        recvfrom->GetParameter("flags")->SetHandler(new FlagsTypeHandler(kRecvFlags));
        Syscall *recvmsg = get_syscall("_kern_recvmsg");
        recvmsg->GetParameter("flags")->SetHandler(new FlagsTypeHandler(kRecvFlags));
        Syscall *send = get_syscall("_kern_send");
        send->GetParameter("flags")->SetHandler(new FlagsTypeHandler(kRecvFlags));
        Syscall *sendmsg = get_syscall("_kern_sendmsg");
        sendmsg->GetParameter("flags")->SetHandler(new FlagsTypeHandler(kRecvFlags));
        Syscall *sendto = get_syscall("_kern_sendto");
        sendto->GetParameter("flags")->SetHandler(new FlagsTypeHandler(kRecvFlags));

        Syscall *socket = get_syscall("_kern_socket");
        socket->GetParameter("family")->SetHandler(
                new EnumTypeHandler(kSocketFamilyMap));
        socket->GetParameter("type")->SetHandler(
                new EnumFlagsTypeHandler(kSocketTypeMap, kSocketFlags));

        Syscall *shutdown = get_syscall("_kern_shutdown_socket");
        shutdown->GetParameter("how")->SetHandler(
                new EnumTypeHandler(kShutdownHowMap));

        Syscall *socketPair = get_syscall("_kern_socketpair");
        socketPair->ParameterAt(3)->SetOut(true);
        socketPair->ParameterAt(3)->SetCount(2);
        socketPair->GetParameter("family")->SetHandler(
                new EnumTypeHandler(kSocketFamilyMap));
        socketPair->GetParameter("type")->SetHandler(
                new EnumFlagsTypeHandler(kSocketTypeMap, kSocketFlags));

        Syscall *accept = get_syscall("_kern_accept");
        accept->GetParameter("flags")->SetHandler(new FlagsTypeHandler(kSocketFlags));

        Syscall *getsockopt = get_syscall("_kern_getsockopt");
        getsockopt->GetParameter("level")->SetHandler(new EnumTypeHandler(kProtocolLevelMap));
        getsockopt->GetParameter("option")->SetHandler(
                new TypeHandlerSelector(kLevelTypeHandlers, 1, TypeHandlerFactory<void *>::Create()));
        getsockopt->GetParameter("value")->SetHandler(
                new TypeHandlerSelector(kLevelOptionTypeHandlers, 1, TypeHandlerFactory<void *>::Create()));
        getsockopt->GetParameter("value")->SetOut(true);
        Syscall *setsockopt = get_syscall("_kern_setsockopt");
        setsockopt->GetParameter("level")->SetHandler(new EnumTypeHandler(kProtocolLevelMap));
        setsockopt->GetParameter("option")->SetHandler(
                new TypeHandlerSelector(kLevelTypeHandlers, 1, TypeHandlerFactory<void *>::Create()));
        setsockopt->GetParameter("value")->SetHandler(
                new TypeHandlerSelector(kLevelOptionTypeHandlers, 1, TypeHandlerFactory<void *>::Create()));

}