/*
 * $Id: nemesis-printout.c,v 1.1.1.1 2003/10/31 21:29:37 jnathan Exp $
 *
 * THE NEMESIS PROJECT
 * Copyright (C) 2002, 2003 Jeff Nathan <jeff@snort.org>
 *
 * nemesis-functions.c (nemesis utility functions)
 *
 */

#if defined(HAVE_CONFIG_H)
    #include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(WIN32)
    #include <pcap.h>
#endif
#include <unistd.h>
#if defined(HAVE_LIMITS_H) || defined(WIN32)
    #include <limits.h>
#endif
#if defined(HAVE_ERRNO_H) || defined(WIN32)
    #include <errno.h>
#endif
#if defined(HAVE_NETINET_IN_H)
    #include <netinet/in.h>
#elif defined(WIN32)
    #include <winsock2.h>
    #include <process.h>
#endif
#include <libnet.h>
#include "nemesis.h"


/**
 * Dumps a packet payload in hex format
 *
 * @param buf pointer to allocated payload memory
 * @param len length of buffer to print in hex format
 *
 * @notes Contributed by Dragos Ruiu <dr@kyx.net>.  A very nice piece of code.
 *
 * @return void function
 */
void nemesis_hexdump(char *buf, u_int32_t len, int mode)
{
    int c, linemod;
    char *p, *l, *dump, *predump, *postdump, dumpbuf[40];

    l = &(buf[len - 1]);
    dump = dumpbuf;
    putchar('\n');

    switch (mode)
    {
        case HEX_RAW_DECODE:
            linemod = 26;
            predump = "   ";
            postdump = "";
            break;
        case HEX_ASCII_DECODE:  /* FALLTHROUGH */
        default:
            linemod = 16;
            predump = "   ";
            postdump = "  ";
            break;
    }

    STPUTS("[Hexdump]\n");
    for (p = buf; p <= l; p++)
    {
        *(dump++) = (isprint((int)*p) ? *p : '.');
        putchar((c = (*p & 0xF0) >> 4) < 10 ? c + '0' : c + '7');
        putchar((c = *p & 0x0F) < 10 ? c + '0' : c + '7');

        if (!(((p - buf) + 1) % linemod) || (p == l))
        {
            /* pad the last line */
            if (p == l)
            {
                while (((((p++) - buf) + 1) % linemod))
                {
                    STPUTS(predump);
                }
            }
            *dump = 0;
            STPUTS(postdump);
            if (mode == HEX_ASCII_DECODE)
                puts(dumpbuf);
            else
                putchar('\n');
            dump = dumpbuf;
        }
        else
            putchar(' ');
    }
    putchar('\n');
}


/**
 * Print the source and destination within the supplied ETHERhdr struct in 
 * ASCII form.
 *
 * @param eth pointer to an ETHERhdr struct
 *
 * @return void function
 */
void nemesis_printeth(ETHERhdr *eth)
{
    char *ethertype = "Unknown";

    switch (eth->ether_type)
    {
        case ETHERTYPE_PUP:
            ethertype = "PUP";
            break;
        case ETHERTYPE_IP:
            ethertype = "IP";
            break;
        case ETHERTYPE_ARP:
            ethertype = "ARP";
            break;
        case ETHERTYPE_REVARP:
            ethertype = "RARP";
            break;
        case ETHERTYPE_VLAN:
            ethertype = "802.1q";
            break;
        case ETHERTYPE_IPV6:
            ethertype = "IPv6";
            break;
        case 34915:
            ethertype = "PPOE discovery";
            break;
        case 34916:
            ethertype = "PPOE session";
            break;
        default:
            break;
    }

    printf("               [MAC] %02X:%02X:%02X:%02X:%02X:%02X > "
            "%02X:%02X:%02X:%02X:%02X:%02X\n", eth->ether_shost[0], 
            eth->ether_shost[1], eth->ether_shost[2], eth->ether_shost[3], 
            eth->ether_shost[4], eth->ether_shost[5], eth->ether_dhost[0],
            eth->ether_dhost[1], eth->ether_dhost[2], eth->ether_dhost[3],
            eth->ether_dhost[4], eth->ether_dhost[5]);
    printf("     [Ethernet type] %s (%#.4x)\n\n", ethertype, eth->ether_type); 
    return;
}


/**
 * Verbosely print portions of the ARP header in ASCII form.
 *
 * @param arp pointer to an ARPhdr struct
 *
 * @return void function
 */
void nemesis_printarp(ARPhdr *arp)
{
    char *src = NULL, *dst = NULL;
    char *opcode = "Unknown";

    switch (arp->ar_op)
    {
        case ARPOP_REQUEST:
            opcode = "Request";
            break;
        case ARPOP_REPLY:
            opcode = "Reply";
            break;
        case ARPOP_REVREQUEST:
            opcode = "Reverse request";
            break;
        case ARPOP_REVREPLY:
            opcode = "Reverse reply";
            break;
    }

    src = strdup(inet_ntoa(*(struct in_addr *)&arp->ar_spa));
    dst = strdup(inet_ntoa(*(struct in_addr *)&arp->ar_tpa));

    printf("  [Protocol addr:IP] %s > %s\n", src, dst);
    printf(" [Hardware addr:MAC] %02x:%02x:%02x:%02x:%02x:%02x > " 
            "%02X:%02X:%02X:%02X:%02X:%02X\n", arp->ar_sha[0], arp->ar_sha[1], 
            arp->ar_sha[2], arp->ar_sha[3], arp->ar_sha[4], arp->ar_sha[5], 
            arp->ar_tha[0], arp->ar_tha[1], arp->ar_tha[2], arp->ar_tha[3], 
            arp->ar_tha[4], arp->ar_tha[5]);

    printf("        [ARP opcode] %s\n", opcode);
    printf("  [ARP hardware fmt] %s (%hu)\n", "Ethernet", arp->ar_hrd);
    printf("  [ARP proto format] %s (%#.4x)\n", "IP", arp->ar_pro);
    printf("  [ARP protocol len] %d\n", arp->ar_hln);
    printf("  [ARP hardware len] %d\n\n", arp->ar_pln);

    if (src != NULL)
        free(src);
    if (dst != NULL)
        free(dst);

    return;
}


/**
 * Verbosely print portions of the IP header in ASCII form.
 *
 * @param ip pointer to an IPhdr struct
 *
 * @return void function
 */
void nemesis_printip(IPhdr *ip)
{
    char *protoname = "Unknown";
    char *src = NULL, *dst = NULL;

    src = strdup(inet_ntoa(ip->ip_src));
    dst = strdup(inet_ntoa(ip->ip_dst));

    printf("                [IP] %s > %s\n", src, dst);
    printf("             [IP ID] %hu\n", ip->ip_id);

    switch(ip->ip_p)
    {
        case 0:
            protoname = "IP";
            break;
        case 1:
            protoname = "ICMP";
            break;
        case 2:
            protoname = "IGMP";
            break;
        case 3:
            protoname = "GGP";
            break;
        case 4:
            protoname = "IP-ENCAP";
            break;
        case 5:
            protoname = "ST";
            break;
        case 6:
            protoname = "TCP";
            break;
        case 7:
            protoname = "UCL";
            break;
        case 8:
            protoname = "EGP";
            break;
        case 9:
            protoname = "IGP";
            break;
        case 10:
            protoname = "BBN-RCC-MON";
            break;
        case 11:
            protoname = "NVP-II";
            break;
        case 12:
            protoname = "PUP";
            break;
        case 13:
            protoname = "ARGUS";
            break;
        case 14:
            protoname = "EMCON";
            break;
        case 15:
            protoname = "XNET";
            break;
        case 16:
            protoname = "CHAOS";
            break;
        case 17:
            protoname = "UDP";
            break;
        case 18:
            protoname = "MUX";
            break;
        case 19:
            protoname = "DCN-MEAS";
            break;
        case 20:
            protoname = "HMP";
            break;
        case 21:
            protoname = "PRM";
            break;
        case 22:
            protoname = "XNS-IDP";
            break;
        case 23:
            protoname = "TRUNK-1";
            break;
        case 24:
            protoname = "TRUNK-2";
            break;
        case 25:
            protoname = "LEAF-1";
            break;
        case 26:
            protoname = "LEAF-2";
            break;
        case 27:
            protoname = "RDP";
            break;
        case 28:
            protoname = "IRTP";
            break;
        case 29:
            protoname = "ISO-TP4";
            break;
        case 30:
            protoname = "NETBLT";
            break;
        case 31:
            protoname = "MFE-NSP";
            break;
        case 32:
            protoname = "MERIT-INP";
            break;
        case 33:
            protoname = "SEP";
            break;
        case 34:
            protoname = "3PC";
            break;
        case 35:
            protoname = "IDPR";
            break;
        case 36:
            protoname = "XTP";
            break;
        case 37:
            protoname = "DDP";
            break;
        case 38:
            protoname = "IDPR-CMTP";
            break;
        case 39:
            protoname = "IDPR-CMTP";
            break;
        case 40:
            protoname = "IL";
            break;
        case 41:
            protoname = "IPv6";
            break;
        case 42:
            protoname = "SDRP";
            break;
        case 43:
            protoname = "SIP-SR";
            break;
        case 44:
            protoname = "SIP-FRAG";
            break;
        case 45:
            protoname = "IDRP";
            break;
        case 46:
            protoname = "RSVP";
            break;
        case 47:
            protoname = "GRE";
            break;
        case 48:
            protoname = "MHRP";
            break;
        case 49:
            protoname = "BNA";
            break;
        case 50:
            protoname = "IPSEC-ESP";
            break;
        case 51:
            protoname = "IPSEC-AH";
            break;
        case 52:
            protoname = "I-NLSP";
            break;
        case 53:
            protoname = "SWIPE";
            break;
        case 54:
            protoname = "NHRP";
            break;
        case 55:
            protoname = "MOBILEIP";
            break;
        case 57:
            protoname = "SKIP";
            break;
        case 58:
            protoname = "IPv6-ICMP";
            break;
        case 59:
            protoname = "IPv6-NoNxt";
            break;
        case 60:
            protoname = "IPv6-Opts";
            break;
        case 61:
            protoname = "any";
            break;
        case 62:
            protoname = "CFTP";
            break;
        case 63:
            protoname = "any";
            break;
        case 64:
            protoname = "SAT-EXPAK";
            break;
        case 65:
            protoname = "KRYPTOLAN";
            break;
        case 66:
            protoname = "RVD";
            break;
        case 67:
            protoname = "IPPC";
            break;
        case 68:
            protoname = "any";
            break;
        case 69:
            protoname = "SAT-MON";
            break;
        case 70:
            protoname = "VISA";
            break;
        case 71:
            protoname = "IPCV";
            break;
        case 72:
            protoname = "CPNX";
            break;
        case 73:
            protoname = "CPHB";
            break;
        case 74:
            protoname = "WSN";
            break;
        case 75:
            protoname = "PVP";
            break;
        case 76:
            protoname = "BR-SAT-MON";
            break;
        case 77:
            protoname = "SUN-ND";
            break;
        case 78:
            protoname = "WB-MON";
            break;
        case 79:
            protoname = "WB-EXPAK";
            break;
        case 80:
            protoname = "ISO-IP";
            break;
        case 81:
            protoname = "VMTP";
            break;
        case 82:
            protoname = "SECURE-VMTP";
            break;
        case 83:
            protoname = "VINES";
            break;
        case 84:
            protoname = "TTP";
            break;
        case 85:
            protoname = "NSFNET-IGP";
            break;
        case 86:
            protoname = "DGP";
            break;
        case 87:
            protoname = "TCF";
            break;
        case 88:
            protoname = "IGRP";
            break;
        case 89:
            protoname = "OSPFIGP";
            break;
        case 90:
            protoname = "Sprite-RPC";
            break;
        case 91:
            protoname = "LARP";
            break;
        case 92:
            protoname = "MTP";
            break;
        case 93:
            protoname = "AX.25";
            break;
        case 94:
            protoname = "IPIP";
            break;
        case 95:
            protoname = "MICP";
            break;
        case 96:
            protoname = "SCC-SP";
            break;
        case 97:
            protoname = "ETHERIP";
            break;
        case 98:
            protoname = "ENCAP";
            break;
        case 99:
            protoname = "any";
            break;
        case 100:
            protoname = "GMTP";
            break;
        case 103:
            protoname = "PIM";
            break;
        case 108:
            protoname = "IPComp";
            break;
        case 112:
            protoname = "VRRP";
            break;
        case 255:
            protoname = "Reserved";
            break;
    }
    printf("          [IP Proto] %s (%hu)\n", protoname, ip->ip_p);
    printf("            [IP TTL] %u\n", ip->ip_ttl);
    printf("            [IP TOS] 0x%.2x\n", ip->ip_tos);
    printf("    [IP Frag offset] 0x%.4x\n", ip->ip_off & IP_OFFMASK);
    STPUTS("     [IP Frag flags] ");
    if ((ip->ip_off & IP_RF) >> 15)
        STPUTS("RESERVED ");
    if ((ip->ip_off & IP_DF) >> 14)
        STPUTS("DF ");
    if ((ip->ip_off & IP_MF) >> 13)
        STPUTS("MF ");

    putchar('\n');

    if (src != NULL)
        free(src);
    if (dst != NULL)
        free(dst);

    return;
}


/**
 * Verbosely print portions of the TCP header in ASCII form.
 *
 * @param tcp TCPhdr struct
 *
 * @return void function
 */
void nemesis_printtcp(TCPhdr *tcp)
{
        printf("         [TCP Ports] %hu > %hu\n", tcp->th_sport, 
                tcp->th_dport);
        STPUTS("         [TCP Flags] ");
        if (tcp->th_flags & TH_SYN)
            STPUTS("SYN ");
        if (tcp->th_flags & TH_ACK)
            STPUTS("ACK ");
        if (tcp->th_flags & TH_RST)
            STPUTS("RST ");
        if (tcp->th_flags & TH_PUSH)
            STPUTS("PSH ");
        if (tcp->th_flags & TH_URG)
            STPUTS("URG ");
        if (tcp->th_flags & TH_FIN)
            STPUTS("FIN ");
        if (tcp->th_flags & TH_ECE)
            STPUTS("ECE ");
        if (tcp->th_flags & TH_CWR)
            STPUTS("CWR ");

        putchar('\n');
        printf("[TCP Urgent Pointer] %u\n", tcp->th_urp);
        printf("   [TCP Window Size] %u\n", tcp->th_win);
        if (tcp->th_flags & TH_ACK)
            printf("    [TCP Ack number] %lu\n", tcp->th_ack);
        if (tcp->th_flags & TH_SYN)
            printf("    [TCP Seq number] %lu\n", tcp->th_seq);

        putchar('\n');
        return;
}


/**
 * Verbosely print portions of the UDP header in ASCII form.
 *
 * @param udp UDPhdr struct
 *
 * @return void function
 */
void nemesis_printudp(UDPhdr *udp)
{
    printf("         [UDP Ports] %hu > %hu\n\n", udp->uh_sport, udp->uh_dport);
    return;
}


/**
 * Verbosely print portions of the ICMP header in ASCII form.
 *
 * @param icmp ICMPhdr struct
 * @param mode ICMP injection mode
 *
 * @return void function
 */
void nemesis_printicmp(ICMPhdr *icmp, int mode)
{
    char *icmptype = "Unknown";
    char *icmpcode = "Unknown";
    char *mask = NULL, *gateway = NULL;

    mask = strdup(inet_ntoa(*(struct in_addr *)&icmp->dun.mask));
    gateway = strdup(inet_ntoa(*(struct in_addr *)&icmp->hun.gateway));

    switch (icmp->icmp_type)
    {
        case 0:
            icmptype = "Echo Reply";
            if (icmp->icmp_code == 0)
                icmpcode = "Echo Reply";
            break;
        case 3:
            icmptype = "Destination Unreachable";
            switch (icmp->icmp_code)
            {
                case 0:
                    icmpcode = "Network Unreachable";
                    break;
                case 1:
                    icmpcode =  "Host Unreachable";
                    break;
                case 2:
                    icmpcode = "Protocol Unreachable";
                    break;
                case 3:
                    icmpcode = "Port Unreachable";
                    break;
                case 4:
                    icmpcode = "Fragmentation Needed";
                    break;
                case 5:
                    icmpcode = "Source Route Failed";
                    break;
                case 6:
                    icmpcode = "Destination Network Unknown";
                    break;
                case 7:
                    icmpcode = "Destination Host Unknown";
                    break;
                case 8:
                    icmpcode = "Source Host Isolated (obsolete)";
                    break;
                case 9:
                    icmpcode = "Destination Network Administratively "
                            "Prohibited";
                    break;
                case 10:
                    icmpcode = "Destination Host Administratively "
                            "Prohibited";
                    break;
                case 11:
                    icmpcode = "Network Unreachable For TOS";
                    break;
                case 12:
                    icmpcode = "Host Unreachable For TOS";
                    break;
                case 13: 
                    icmpcode = "Communication Administratively Prohibited "
                            "By Filtering";
                    break;
                case 14:
                    icmpcode = "Host Precedence Violation";
                    break;
                case 15:
                    icmpcode = "Precedence Cutoff In Effect";
                    break;
                default:
                    break;
                }
            break;
        case 4:
            icmptype = "Source Quench";
            if (icmp->icmp_code == 0)
                icmpcode = "Source Quench";
            break;
        case 5:
            icmptype = "Redirect";
            switch (icmp->icmp_code)
            {
                case 0:
                    icmpcode = "Redirect For Network";
                    break;
                case 1:
                    icmpcode = "Redirect For Host";
                    break;
                case 2:
                    icmpcode = "Redirect For TOS and Network";
                    break;
                case 3:
                    icmpcode = "Redirect For TOS and Host";
                    break;
                default:
                    break;
            }
            break;
        case 8:
            icmptype = "Echo Request";
            if (icmp->icmp_code == 0)
                icmpcode = "Echo Request";
            break;
        case 9:
            icmptype = "Router Advertisement";
            if (icmp->icmp_code == 0)
                icmpcode = "Router Advertisement";
            break;
        case 10:
            icmptype = "Router Solicitation";
            if (icmp->icmp_code == 0)
                icmpcode = "Router Solicitation";
            break;
        case 11:
            icmptype = "Time Exceeded";
            switch (icmp->icmp_code)
            {
                case 0:
                    icmpcode = "TTL = 0 During Transmit";
                    break;
                case 1:
                    icmpcode = "TTL = 0 During Reassembly";
                    break;
                default:
                    break;
            }
            break;
        case 12:
            icmptype = "Parameter Problem";
            switch (icmp->icmp_code)
            {
                case 0:
                    icmpcode = "IP Header Bad (catchall error)";
                    break;
                case 1:
                    icmpcode = "Required Option Missing";
                    break;
                default:
                    break;
            }
        case 13:
            icmptype = "Timestamp Request";
            if (icmp->icmp_code == 0)
                icmpcode = "Timestamp Request";
            break;
        case 14:
            icmpcode = "Timestamp Reply";
            if (icmp->icmp_code == 0)
                icmpcode = "Timestamp Reply";
            break;
        case 15:
            icmptype = "Information Request";
            if (icmp->icmp_code == 0)
                icmpcode = "Information Request";
            break;
        case 16:
            icmptype = "Information Reply";
            if (icmp->icmp_code == 0)
                icmpcode = "Information Reply";
            break;
        case 17:
            icmptype = "Address Mask Request";
            if (icmp->icmp_code == 0)
                icmpcode = "Address Mask Request";
            break;
        case 18:
            icmptype = "Address Mask Reply";
            if (icmp->icmp_code == 0)
                icmptype = "Address Mask Reply";
            break;
        default:
            icmptype = "Unknown";
            break;
    }
    printf("         [ICMP Type] %s\n", icmptype);
    printf("         [ICMP Code] %s\n", icmpcode);

    if (mode == ICMP_ECHO || mode == ICMP_MASKREQ || mode == ICMP_TSTAMP)
    {
        printf("           [ICMP ID] %hu\n", icmp->hun.echo.id);
        printf("   [ICMP Seq number] %hu\n", icmp->hun.echo.seq);
    }
    if (mode == ICMP_MASKREQ)
        printf(" [ICMP Address Mask] %s\n", mask);
    if (mode == ICMP_REDIRECT)
        printf(" [ICMP Pref Gateway] %s\n", gateway);

    putchar('\n');

    if (mask != NULL)
        free(mask);
    if (gateway != NULL)
        free(gateway);

    return;
}


/**
 * Verbosely print portions of the RIP header in ASCII form.
 *
 * @param rip pointer to a RIPhdr struct
 *
 * @return void function
 *
 */
void nemesis_printrip(RIPhdr *rip)
{
    char *cmd = "Unknown";
    char *family = "Unknown";
    char *addr = NULL, *mask = NULL, *hop = NULL;

    addr = strdup(inet_ntoa(*(struct in_addr *)&rip->addr));
    mask = strdup(inet_ntoa(*(struct in_addr *)&rip->mask));
    hop = strdup(inet_ntoa(*(struct in_addr *)&rip->next_hop));

    switch(rip->cmd)
    {
        case RIPCMD_REQUEST:
            cmd = "Request";
            break;
        case RIPCMD_RESPONSE:
            cmd = "Response";
            break;
        case RIPCMD_TRACEON:
            cmd = "Tracing on (obsolete)";
            break;
        case RIPCMD_TRACEOFF:
            cmd = "Tracing off (obsolete)";
            break;
        case RIPCMD_POLL:
            cmd = "Poll";
            break;
        case RIPCMD_POLLENTRY:
            cmd = "Poll entry";
            break;
        case RIPCMD_MAX:
            cmd = "Max";
            break;
        default:
            break;
    }
    printf("       [RIP Command] %s (%hu)\n", cmd, (u_int16_t)rip->cmd);
    printf("       [RIP Version] %hu\n", (u_int16_t)rip->ver);
    printf("[RIP Routing domain] %hu\n", (u_int16_t)rip->rd);
    if (rip->af == 2)
        family = "IP";

    printf("[RIP Address family] %s (%hu)\n", family, (u_int16_t)rip->af);
    printf("     [RIP Route tag] %hu\n", (u_int16_t)rip->rt);
    printf("       [RIP Address] %s\n", addr);
    printf("  [RIP Network mask] %s\n", mask);
    printf("      [RIP Next hop] %s\n", hop);
    printf("        [RIP Metric] %u\n", (u_int32_t)rip->metric);

    putchar('\n');

    if (addr != NULL)
        free(addr);
    if (mask != NULL)
        free(mask);
    if (hop != NULL)
        free(hop);

    return;
}


/**
 * Verbosely print portions of the OSPF header in ASCII form.
 *
 * @param opsf OSPfhdr struct
 *
 * @return void function
 */
void nemesis_printospf(OSPFhdr *ospf)
{
    char *type = "";
    char *auth_type = "Unknown";
    char *rtr_id = NULL, *area_id = NULL;

    rtr_id = strdup(inet_ntoa(*(struct in_addr *)&ospf->ospf_rtr_id.s_addr));
    area_id = strdup(inet_ntoa(*(struct in_addr *)&ospf->ospf_area_id.s_addr));

    switch(ospf->ospf_type)
    {
        case LIBNET_OSPF_HELLO:
            type = "Hello";
            break;
        case LIBNET_OSPF_DBD:
            type = "Database Description";
            break;
        case LIBNET_OSPF_LSR:
            type = "Link State Request";
            break;
        case LIBNET_OSPF_LSU:
            type = "Link State Update";
            break;
        case LIBNET_OSPF_LSA:
            type = "Link State Acknowledgement";
            break;
    }

    switch(ntohs(ospf->ospf_auth_type))
    {
        case LIBNET_OSPF_AUTH_NULL:
            auth_type = "None";
            break;
        case LIBNET_OSPF_AUTH_SIMPLE:
            auth_type = "Simple password";
            break;
        case LIBNET_OSPF_AUTH_MD5:
            auth_type = "MD5";
            break;
    }

    printf("         [OSPF Type] %s\n", type);
    printf("[OSPF src router ID] %s\n", rtr_id);
    printf("      [OSPF area ID] %s\n", area_id);
    printf("    [OSPF auth type] %s\n", auth_type);

    putchar('\n');

    if (type != NULL)
        free(type);
    if (rtr_id != NULL)
        free(rtr_id);
    if (area_id != NULL)
        free(area_id);
    if (auth_type != NULL)
        free(auth_type);

    return;
}


/**
 * Build the title string for each nemesis protocol builder.
 *
 * @param title the buffer containing the concatenated title
 * @param module the name of the protocol builder module
 * @param version release version
 *
 * @return void function
 */
void nemesis_maketitle(char *title, const char *module, const char *version)
{
    char tmptitle[TITLEBUFFSIZE], buildnum[13];


    strlcpy(tmptitle, module, sizeof(tmptitle));
    /* strlcat(char *dst, const char *src, size_t size) automatically uses 
     * size - strlen(dst) - 1 for size argument
     */
    strlcat(tmptitle, version, sizeof(tmptitle));
    snprintf(buildnum, sizeof(buildnum), " (Build %d)", BUILD);
    strlcat(tmptitle, buildnum, sizeof(tmptitle));

    memcpy(title, tmptitle, sizeof(tmptitle));
}


/**
 * Print the title string for each nemesis protocol builder.
 *
 * @param title the buffer containing the concatenated title
 *
 * @return void function
 */
void nemesis_printtitle(const char *title)
{
    putchar('\n');
    puts(title);
    putchar('\n');

    return;
}


/**
 * Print an error when an Ethernet device can't be opened for link-layer 
 * injection.
 *
 * @param errmsg the buffer containing the error message
 *
 * @return void function
 */
void nemesis_device_failure(int mode, const char *device)
{
    if (mode == INJECTION_RAW)
        fprintf(stderr, "ERROR: Unable to open raw socket for packet "
                "injection: %s.\n", errbuf);
    else if (mode == INJECTION_LINK && device != NULL && errbuf != NULL)
        fprintf(stderr, "ERROR: Unable to open layer 2 device '%s' for packet "
                "injection: %s.\n", device, errbuf);

#if !defined(WIN32)
    fprintf(stderr, "You may need root privileges to use nemesis.\n");
#else
    fprintf(stderr, "You may need Administrator privileges to use nemesis.\n");
#endif

    return;
}


syntax highlighted by Code2HTML, v. 0.9.1