zeek/aux/rst/rst.c

380 lines
9.3 KiB
C

/* $Id: rst.c 7073 2010-09-13 00:45:02Z vern $ */
/* Derived from traceroute, which has the following copyright:
*
* Copyright (c) 1999, 2002
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static const char copyright[] =
"@(#) Copyright (c) 1999, 2002\nThe Regents of the University of California. All rights reserved.\n";
static const char rcsid[] =
"@(#) $Id: rst.c 7073 2010-09-13 00:45:02Z vern $ (LBL)";
#endif
/* need this due to linux's funny idea of a tcphdr */
#if defined(__linux__)
#define _BSD_SOURCE
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "../../config.h"
/* Forwards */
void gripe(const char *, const char *);
void pgripe(const char *);
u_short in_cksum(register u_short *, register int);
int ones_complement_checksum(const void *, int, u_int32_t);
int tcp_checksum(const struct ip *, const struct tcphdr *, int);
void send_pkt(int, struct in_addr, int, u_int32_t, struct in_addr,
int, u_int32_t, int, int, int, int, const char *);
void terminate(int, const char *, int, u_int32_t, const char *,
int, u_int32_t, int, int, int, int, const char *);
void usage(void);
int main(int, char **);
const char *prog_name;
void gripe(const char *fmt, const char *arg)
{
fprintf(stderr, "%s: ", prog_name);
fprintf(stderr, fmt, arg);
fprintf(stderr, "\n");
}
void pgripe(const char *msg)
{
fprintf(stderr, "%s: %s (%s)\n", prog_name, msg, strerror(errno));
exit(1);
}
/*
* Checksum routine for Internet Protocol family headers (C Version)
*/
u_short
in_cksum(register u_short *addr, register int len)
{
register int nleft = len;
register u_short *w = addr;
register u_short answer;
register int sum = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum),
* we add sequential 16 bit words to it, and at the end, fold
* back all the carry bits from the top 16 bits into the lower
* 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if (nleft == 1)
sum += *(u_char *)w;
/*
* add back carry outs from top 16 bits to low 16 bits
*/
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return (answer);
}
// - adapted from tcpdump
// Returns the ones-complement checksum of a chunk of b short-aligned bytes.
int ones_complement_checksum(const void *p, int b, u_int32_t sum)
{
const u_short *sp = (u_short *) p; // better be aligned!
b /= 2; // convert to count of short's
/* No need for endian conversions. */
while ( --b >= 0 )
sum += *sp++;
while ( sum > 0xffff )
sum = (sum & 0xffff) + (sum >> 16);
return sum;
}
int tcp_checksum(const struct ip *ip, const struct tcphdr *tp, int len)
{
int tcp_len = tp->th_off * 4 + len;
u_int32_t sum, addl_pseudo;
if ( len % 2 == 1 )
// Add in pad byte.
sum = htons(((const u_char*) tp)[tcp_len - 1] << 8);
else
sum = 0;
sum = ones_complement_checksum((void*) &ip->ip_src.s_addr, 4, sum);
sum = ones_complement_checksum((void*) &ip->ip_dst.s_addr, 4, sum);
addl_pseudo = (htons(IPPROTO_TCP) << 16) | htons((unsigned short) tcp_len);
sum = ones_complement_checksum((void*) &addl_pseudo, 4, sum);
sum = ones_complement_checksum((void*) tp, tcp_len, sum);
return sum;
}
void send_pkt(int s, struct in_addr from, int from_port, u_int32_t from_seq,
struct in_addr to, int to_port, u_int32_t to_seq,
int size, int redundancy, int delay, int flags,
const char *inject)
{
int cc;
int pktlen = 40 + size;
const int max_injection_size = 4096;
char *pkt = malloc(pktlen + max_injection_size + 1024 /* slop */);
struct ip *ip = (struct ip *) pkt;
struct tcphdr *tcp = (struct tcphdr *) &pkt[20];
if ( ! pkt )
pgripe("couldn't malloc memory");
if ( inject && *inject ) {
size = strlen(inject);
if ( size > max_injection_size )
gripe("injection text too large%s", "");
pktlen = 40 + size;
}
memset(pkt, 0, pktlen);
ip->ip_v = IPVERSION;
ip->ip_len = pktlen; /* on FreeBSD, don't use htons(); YMMV */
ip->ip_off = 0;
ip->ip_src = from;
ip->ip_dst = to;
ip->ip_hl = 5;
ip->ip_p = IPPROTO_TCP;
ip->ip_ttl = 255;
ip->ip_id = 0;
ip->ip_sum = in_cksum((u_short *) ip, sizeof(*ip));
if (ip->ip_sum == 0)
ip->ip_sum = 0xffff;
tcp->th_sport = htons(from_port);
tcp->th_dport = htons(to_port);
tcp->th_seq = htonl(from_seq);
tcp->th_ack = htonl(to_seq);
tcp->th_off = 5;
tcp->th_flags = flags;
tcp->th_win = 0;
tcp->th_urp = 0;
tcp->th_sum = 0;
if ( inject && *inject ) {
char *payload = &pkt[40];
strcpy(payload, inject);
} else if ( size > 0 )
{
const char *fill_string =
(inject && *inject) ? inject : "BRO-RST\n";
char *payload = &pkt[40];
int n = strlen(fill_string);
int i;
for ( i = size; i > n + 1; i -= n )
{
strcpy(payload, fill_string);
payload += n;
}
for ( ; i > 0; --i )
*(payload++) = '\n';
}
tcp->th_sum = ~tcp_checksum(ip, tcp, size);
while ( redundancy-- > 0 )
{
cc = send(s, (char *) ip, pktlen, 0);
if (cc < 0 || cc != pktlen)
pgripe("problem in sendto()");
usleep(delay * 1000);
}
free(pkt);
}
void terminate(int s, const char *from_addr, int from_port, u_int32_t from_seq,
const char *to_addr, int to_port, u_int32_t to_seq,
int num, int redundancy, int stride, int delay,
const char *inject)
{
struct sockaddr where_from, where_to;
struct sockaddr_in *from = (struct sockaddr_in *) &where_from;
struct sockaddr_in *to = (struct sockaddr_in *) &where_to;
memset(from, 0, sizeof(*from));
memset(to, 0, sizeof(*to));
#ifdef SIN_LEN
from->sin_len = to->sin_len = sizeof(*to);
#endif /* SIN_LEN */
from->sin_family = to->sin_family = AF_INET;
if ( inet_aton(from_addr, (struct in_addr *) &from->sin_addr) == 0 )
gripe("bad from address %s", from_addr);
if ( inet_aton(to_addr, (struct in_addr *) &to->sin_addr) == 0 )
gripe("bad to address %s", to_addr);
if ( connect(s, &where_to, sizeof(where_to)) < 0 )
pgripe("can't connect");
while ( num-- > 0 )
{
send_pkt(s, from->sin_addr, from_port, from_seq,
to->sin_addr, to_port, to_seq, 0, redundancy, delay,
(*inject ? 0 : TH_RST) | TH_ACK, inject);
if ( num > 0 && stride > 1 )
send_pkt(s, from->sin_addr, from_port, from_seq,
to->sin_addr, to_port, to_seq, stride,
redundancy, delay, TH_ACK, inject);
from_seq += stride;
}
}
void usage()
{
fprintf(stderr, "%s [-R] [-I text-to-inject] [-d delay-msec] [-n num] [-r redundancy] [-s stride] from_addr from_port from_seq to_addr to_port to_seq\n", prog_name);
exit(0);
}
int main(int argc, char **argv)
{
extern char* optarg;
extern int optind, opterr;
const char *from_addr, *to_addr;
char inject[8192];
int from_port, to_port;
u_int32_t from_seq, to_seq;
int delay = 0.0;
int redundancy = 1;
int num = 1;
int stride = 1;
int reverse = 0;
int s;
int on = 1;
int op;
prog_name = argv[0];
opterr = 0;
inject[0] = 0;
while ( (op = getopt(argc, argv, "RI:d:n:r:s:")) != EOF )
switch ( op ) {
case 'R':
reverse = 1;
break;
case 'I':
{
char *ap = optarg;
char *ip;
for ( ip = inject; *ap; ++ip, ++ap ) {
if ( ap[0] == '\\' && ap[1] == 'n' )
*ip = '\n', ++ap;
else
*ip = *ap;
}
}
break;
case 'd':
delay = atoi(optarg);
break;
case 'n':
num = atoi(optarg);
break;
case 'r':
redundancy = atoi(optarg);
break;
case 's':
stride = atoi(optarg);
break;
default:
usage();
break;
}
if ( argc - optind != 6 )
usage();
s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if ( s < 0 )
pgripe("couldn't create raw socket");
setuid(getuid());
if (setsockopt(s, 0, IP_HDRINCL, (char *) &on, sizeof(on)) < 0)
pgripe("can't turn on IP_HDRINCL");
from_addr = argv[optind++];
from_port = atoi(argv[optind++]);
from_seq = strtoul(argv[optind++], 0, 10);
to_addr = argv[optind++];
to_port = atoi(argv[optind++]);
to_seq = strtoul(argv[optind++], 0, 10);
if ( reverse )
terminate(s, to_addr, to_port, to_seq,
from_addr, from_port, from_seq,
num, redundancy, stride, delay, inject);
else
terminate(s, from_addr, from_port, from_seq,
to_addr, to_port, to_seq,
num, redundancy, stride, delay, inject);
return 0;
}