/*
 *  tcp_packet.c
 *
 *  $Id: tcp_packet.c,v 1.1.1.1 1999/10/27 09:54:38 fukusima Exp $
 *
 *  Copyright (C) 1998, 1999  Masaki Fukushima
 */

#include "ruby_pcap.h"
#include <limits.h>

#define TCP_HDR(pkt)    ((struct tcphdr *)LAYER4_HDR(pkt))
#define TCP_DATA(pkt)   ((u_char *)LAYER5_HDR(pkt))
#define TCP_DATALEN(pkt) (ntohs(IP_HDR(pkt)->ip_len) - \
    (IP_HDR(pkt)->ip_hl + TCP_HDR(pkt)->th_off) * 4)

VALUE cTCPPacket;

#define CheckTruncateTcp(pkt, need) \
    CheckTruncate(pkt, pkt->hdr.layer4_off, need, "truncated TCP")

VALUE
setup_tcp_packet(pkt, tl_len)
     struct packet_object *pkt;
     int tl_len;
{
    VALUE class;

    DEBUG_PRINT("setup_tcp_packet");

    class = cTCPPacket;
    if (tl_len > 20) {
        int hl = TCP_HDR(pkt)->th_off * 4;
        int layer5_len = tl_len - hl;
        if (layer5_len > 0) {
            pkt->hdr.layer5_off = pkt->hdr.layer4_off + hl;
            /* upper layer */
        }
    }
    return class;
}

#define TCPP_METHOD(func, need, val) \
static VALUE\
(func)(self)\
     VALUE self;\
{\
    struct packet_object *pkt;\
    struct tcphdr *tcp;\
    DEBUG_PRINT(#func);\
    GetPacket(self, pkt);\
    CheckTruncateTcp(pkt, (need));\
    tcp = TCP_HDR(pkt);\
    return (val);\
}

TCPP_METHOD(tcpp_sport,   2, INT2FIX(ntohs(tcp->th_sport)))
TCPP_METHOD(tcpp_dport,   4, INT2FIX(ntohs(tcp->th_dport)))
TCPP_METHOD(tcpp_seq,     8, UINT32_2_NUM(ntohl(tcp->th_seq)))
TCPP_METHOD(tcpp_acknum, 12, UINT32_2_NUM(ntohl(tcp->th_ack)))
TCPP_METHOD(tcpp_off,    13, INT2FIX(tcp->th_off))
TCPP_METHOD(tcpp_flags,  14, INT2FIX(tcp->th_flags))
TCPP_METHOD(tcpp_win,    16, INT2FIX(ntohs(tcp->th_win)))
TCPP_METHOD(tcpp_sum,    18, INT2FIX(ntohs(tcp->th_sum)))
TCPP_METHOD(tcpp_urp,    20, INT2FIX(ntohs(tcp->th_urp)))

#define TCPP_FLAG(func, flag) \
    TCPP_METHOD(func, 14, (tcp->th_flags & flag) ? Qtrue : Qfalse)
TCPP_FLAG(tcpp_fin, TH_FIN)
TCPP_FLAG(tcpp_syn, TH_SYN)
TCPP_FLAG(tcpp_rst, TH_RST)
TCPP_FLAG(tcpp_psh, TH_PUSH)
TCPP_FLAG(tcpp_ack, TH_ACK)
TCPP_FLAG(tcpp_urg, TH_URG)

static VALUE
tcpp_data(self)
     VALUE self;
{
    struct packet_object *pkt;
    VALUE v_len;
    int len;

    DEBUG_PRINT("tcpp_data");
    GetPacket(self, pkt);

    if (pkt->hdr.layer5_off == OFF_NONEXIST) return Qnil;

    len = MIN(Caplen(pkt, pkt->hdr.layer5_off), TCP_DATALEN(pkt));
    if (len < 1) return Qnil;
    return rb_str_new(TCP_DATA(pkt), len);
}

void
Init_tcp_packet(void)
{
    DEBUG_PRINT("Init_tcp_packet");

    /* define class TcpPacket */
    cTCPPacket = rb_define_class_under(mPcap, "TCPPacket", cIPPacket);

    rb_define_method(cTCPPacket, "tcp_sport", tcpp_sport, 0);
    rb_define_method(cTCPPacket, "sport", tcpp_sport, 0);
    rb_define_method(cTCPPacket, "tcp_dport", tcpp_dport, 0);
    rb_define_method(cTCPPacket, "dport", tcpp_dport, 0);
    rb_define_method(cTCPPacket, "tcp_seq", tcpp_seq, 0);
    rb_define_method(cTCPPacket, "tcp_ack", tcpp_acknum, 0);
    rb_define_method(cTCPPacket, "tcp_off", tcpp_off, 0);
    rb_define_method(cTCPPacket, "tcp_hlen", tcpp_off, 0);
    rb_define_method(cTCPPacket, "tcp_flags", tcpp_flags, 0);
    rb_define_method(cTCPPacket, "tcp_win", tcpp_win, 0);
    rb_define_method(cTCPPacket, "tcp_sum", tcpp_sum, 0);
    rb_define_method(cTCPPacket, "tcp_urp", tcpp_urp, 0);
    rb_define_method(cTCPPacket, "tcp_fin?", tcpp_fin, 0);
    rb_define_method(cTCPPacket, "tcp_syn?", tcpp_syn, 0);
    rb_define_method(cTCPPacket, "tcp_rst?", tcpp_rst, 0);
    rb_define_method(cTCPPacket, "tcp_psh?", tcpp_psh, 0);
    rb_define_method(cTCPPacket, "tcp_ack?", tcpp_ack, 0);
    rb_define_method(cTCPPacket, "tcp_urg?", tcpp_urg, 0);
    rb_define_method(cTCPPacket, "tcp_data", tcpp_data, 0);
}
