/* TODO: act proper if interface is promisc (only reply to packets send to local host */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define BLEN 16384 #define PORT 80 const char * raw_payload = "HTTP/1.0 301 Not enough crypto to process request\r\n" "Location: %s\r\n" "Connection: close\r\n" "\r\n" "bye bye"; unsigned chksum(unsigned short *buf, int length) { unsigned sum = 0; for (; length > 1; length -= 2) sum += *buf++; return sum; } unsigned short ipchksum(struct iphdr *ip) { unsigned sum = chksum((unsigned short *) ip, sizeof(struct iphdr)); sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); return ~sum; } unsigned short tcpchksum(struct iphdr *ip, struct tcphdr *tcp, int length) { unsigned sum = chksum((unsigned short*) &(ip->saddr), sizeof(ip->saddr)) + chksum((unsigned short*) &(ip->daddr), sizeof(ip->daddr)) + ((length + ip->protocol) << 8) + chksum((unsigned short*) tcp, length); if (length % 2) sum += *(((char*) tcp) + length - 1); sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); return ~sum; } int main(int argc, char **argv) { if (argc != 2) { printf("Usage: %s target-url\n", argv[0]); return 1; } static unsigned ack_src; /* prepare input buffer */ char *ibuf = malloc(BLEN); assert(ibuf != NULL); struct ethhdr *i_eth = (struct ethhdr*) ibuf; struct iphdr *i_ip = (struct iphdr*) (i_eth + 1); struct tcphdr *i_tcp = (struct tcphdr*) (i_ip + 1); char *i_pld = (char*) (i_tcp + 1); /* prepare output buffer */ const size_t os = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct tcphdr) + strlen(raw_payload) + strlen(argv[1]) - 1; char *obuf = malloc(os); assert(obuf != NULL); struct ethhdr *o_eth = (struct ethhdr*) obuf; struct iphdr *o_ip = (struct iphdr*) (o_eth + 1); struct tcphdr *o_tcp = (struct tcphdr*) (o_ip + 1); char *o_pld = (char*) (o_tcp + 1); /* setup sock */ int fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)); assert(fd >= 0); /* retrieve interface idx (for send()) */ int ifidx; { struct ifreq req; const char *if_name = "eth0"; strcpy((char*) &(req.ifr_name), if_name); assert(ioctl(fd, SIOCGIFINDEX, &req) >= 0); ifidx = req.ifr_ifindex; printf("device index of %s is %d\n", if_name, ifidx); } /* prefill static output */ o_eth->h_proto = htons(ETH_P_IP); o_ip->ihl = 5; o_ip->version = 4; o_ip->tos = 0; o_ip->id = 0; o_ip->frag_off = 0; o_ip->ttl = 255; o_ip->protocol = 6; o_tcp->source = htons(PORT); o_tcp->res1 = 0; o_tcp->doff = 5; o_tcp->ack = 1; o_tcp->rst = 0; o_tcp->psh = 1; o_tcp->urg = 0; o_tcp->ece = 0; o_tcp->cwr = 0; o_tcp->urg_ptr = 0; o_tcp->window = htons(4); /* first 4 byte are harmless (and we won't ack them) */ snprintf(o_pld, strlen(raw_payload) + strlen(argv[1]) - 1, raw_payload, argv[1]); struct sockaddr_ll sall; sall.sll_family = AF_PACKET; sall.sll_ifindex = ifidx; sall.sll_halen = 6; memcpy(&(sall.sll_addr), &(o_eth->h_dest), sall.sll_halen); sall.sll_protocol = sall.sll_hatype = sall.sll_pkttype = 0; /* read packets forever */ while (1) { int is = recv(fd, ibuf, BLEN, 0); if ((is >= sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct tcphdr)) && // packet long enough? (ntohs(i_eth->h_proto) == ETH_P_IP) && // ip protocol? (i_ip->ihl == 5) && // no IP options (i_ip->protocol == 6) && // tcp protocol? (ntohs(i_tcp->dest) == PORT)) { // port 80? int size = 0; /* eth & IP are common for all responses */ memcpy(&(o_eth->h_dest), &(i_eth->h_source), ETH_ALEN); memcpy(&(o_eth->h_source), &(i_eth->h_dest), ETH_ALEN); o_ip->check = 0; memcpy(&(o_ip->saddr), &(i_ip->daddr), sizeof(o_ip->saddr)); memcpy(&(o_ip->daddr), &(i_ip->saddr), sizeof(o_ip->daddr)); o_tcp->dest = i_tcp->source; o_tcp->check = 0; if (i_tcp->syn && !i_tcp->ack) { /* first packet of a handshake? */ printf("syn\n"); size = os - strlen(o_pld); o_tcp->seq = i_tcp->seq; o_tcp->ack_seq = htonl(ntohl(i_tcp->seq) + 1); o_tcp->fin = 0; //1; o_tcp->syn = 1; }else if (i_tcp->ack) { printf("ack\n"); size = os; o_tcp->seq = htonl(ntohl(i_tcp->ack_seq)); o_tcp->ack_seq = htonl(ntohl(i_tcp->ack_seq)); o_tcp->fin = 1; o_tcp->syn = 0; } if (size > 0) { o_ip->tot_len = htons(size - sizeof(struct ethhdr)); o_ip->check = ipchksum(o_ip); o_tcp->check = tcpchksum(o_ip, o_tcp, size - sizeof(struct ethhdr) - sizeof(struct iphdr)); assert(sendto(fd, o_eth, size, 0, (struct sockaddr*) &sall, sizeof(struct sockaddr_ll)) == size); } } } close(fd); }