#!/usr/bin/python3 -sP
"""Customizable TCP fuzzer."""

from __future__ import print_function

import re
import os
import sys
import argparse
import socket
from time import sleep


# -------------------------------------------------------------------------------------------------
# GLOBALS
# -------------------------------------------------------------------------------------------------

DEF_CHAR = "A"
DEF_PREFIX = ""
DEF_SUFFIX = ""
DEF_INIT_MULTIPLIER = 100
DEF_ROUND_MULTIPLIER = 100
DEF_TIMEOUT = 30.0
DEF_DELAY = 1.0
DEF_CRLF = False

# 10,000 characters from patter_create.rb
PATTERN = (
    "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9"
    "Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9"
    "Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9"
    "Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9"
    "Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9"
    "Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9"
    "As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9"
    "Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9"
    "Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9"
    "Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9"
    "Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9"
    "Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9"
    "Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9"
    "Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9"
    "Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9"
    "Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9"
    "Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9"
    "Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9"
    "Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9"
    "Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9"
    "Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9"
    "Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9"
    "Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9"
    "Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9"
    "Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9"
    "Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9"
    "Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9"
    "Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9"
    "Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9"
    "Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9"
    "Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9"
    "Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9"
    "Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9"
    "Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9Dw0Dw1Dw2Dw3Dw4Dw5Dw6Dw7Dw8Dw9Dx0Dx1Dx2Dx3Dx4Dx5Dx6Dx7Dx8Dx9"
    "Dy0Dy1Dy2Dy3Dy4Dy5Dy6Dy7Dy8Dy9Dz0Dz1Dz2Dz3Dz4Dz5Dz6Dz7Dz8Dz9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9"
    "Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9"
    "Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Eg0Eg1Eg2Eg3Eg4Eg5Eg6Eg7Eg8Eg9"
    "Eh0Eh1Eh2Eh3Eh4Eh5Eh6Eh7Eh8Eh9Ei0Ei1Ei2Ei3Ei4Ei5Ei6Ei7Ei8Ei9Ej0Ej1Ej2Ej3Ej4Ej5Ej6Ej7Ej8Ej9"
    "Ek0Ek1Ek2Ek3Ek4Ek5Ek6Ek7Ek8Ek9El0El1El2El3El4El5El6El7El8El9Em0Em1Em2Em3Em4Em5Em6Em7Em8Em9"
    "En0En1En2En3En4En5En6En7En8En9Eo0Eo1Eo2Eo3Eo4Eo5Eo6Eo7Eo8Eo9Ep0Ep1Ep2Ep3Ep4Ep5Ep6Ep7Ep8Ep9"
    "Eq0Eq1Eq2Eq3Eq4Eq5Eq6Eq7Eq8Eq9Er0Er1Er2Er3Er4Er5Er6Er7Er8Er9Es0Es1Es2Es3Es4Es5Es6Es7Es8Es9"
    "Et0Et1Et2Et3Et4Et5Et6Et7Et8Et9Eu0Eu1Eu2Eu3Eu4Eu5Eu6Eu7Eu8Eu9Ev0Ev1Ev2Ev3Ev4Ev5Ev6Ev7Ev8Ev9"
    "Ew0Ew1Ew2Ew3Ew4Ew5Ew6Ew7Ew8Ew9Ex0Ex1Ex2Ex3Ex4Ex5Ex6Ex7Ex8Ex9Ey0Ey1Ey2Ey3Ey4Ey5Ey6Ey7Ey8Ey9"
    "Ez0Ez1Ez2Ez3Ez4Ez5Ez6Ez7Ez8Ez9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9"
    "Fc0Fc1Fc2Fc3Fc4Fc5Fc6Fc7Fc8Fc9Fd0Fd1Fd2Fd3Fd4Fd5Fd6Fd7Fd8Fd9Fe0Fe1Fe2Fe3Fe4Fe5Fe6Fe7Fe8Fe9"
    "Ff0Ff1Ff2Ff3Ff4Ff5Ff6Ff7Ff8Ff9Fg0Fg1Fg2Fg3Fg4Fg5Fg6Fg7Fg8Fg9Fh0Fh1Fh2Fh3Fh4Fh5Fh6Fh7Fh8Fh9"
    "Fi0Fi1Fi2Fi3Fi4Fi5Fi6Fi7Fi8Fi9Fj0Fj1Fj2Fj3Fj4Fj5Fj6Fj7Fj8Fj9Fk0Fk1Fk2Fk3Fk4Fk5Fk6Fk7Fk8Fk9"
    "Fl0Fl1Fl2Fl3Fl4Fl5Fl6Fl7Fl8Fl9Fm0Fm1Fm2Fm3Fm4Fm5Fm6Fm7Fm8Fm9Fn0Fn1Fn2Fn3Fn4Fn5Fn6Fn7Fn8Fn9"
    "Fo0Fo1Fo2Fo3Fo4Fo5Fo6Fo7Fo8Fo9Fp0Fp1Fp2Fp3Fp4Fp5Fp6Fp7Fp8Fp9Fq0Fq1Fq2Fq3Fq4Fq5Fq6Fq7Fq8Fq9"
    "Fr0Fr1Fr2Fr3Fr4Fr5Fr6Fr7Fr8Fr9Fs0Fs1Fs2Fs3Fs4Fs5Fs6Fs7Fs8Fs9Ft0Ft1Ft2Ft3Ft4Ft5Ft6Ft7Ft8Ft9"
    "Fu0Fu1Fu2Fu3Fu4Fu5Fu6Fu7Fu8Fu9Fv0Fv1Fv2Fv3Fv4Fv5Fv6Fv7Fv8Fv9Fw0Fw1Fw2Fw3Fw4Fw5Fw6Fw7Fw8Fw9"
    "Fx0Fx1Fx2Fx3Fx4Fx5Fx6Fx7Fx8Fx9Fy0Fy1Fy2Fy3Fy4Fy5Fy6Fy7Fy8Fy9Fz0Fz1Fz2Fz3Fz4Fz5Fz6Fz7Fz8Fz9"
    "Ga0Ga1Ga2Ga3Ga4Ga5Ga6Ga7Ga8Ga9Gb0Gb1Gb2Gb3Gb4Gb5Gb6Gb7Gb8Gb9Gc0Gc1Gc2Gc3Gc4Gc5Gc6Gc7Gc8Gc9"
    "Gd0Gd1Gd2Gd3Gd4Gd5Gd6Gd7Gd8Gd9Ge0Ge1Ge2Ge3Ge4Ge5Ge6Ge7Ge8Ge9Gf0Gf1Gf2Gf3Gf4Gf5Gf6Gf7Gf8Gf9"
    "Gg0Gg1Gg2Gg3Gg4Gg5Gg6Gg7Gg8Gg9Gh0Gh1Gh2Gh3Gh4Gh5Gh6Gh7Gh8Gh9Gi0Gi1Gi2Gi3Gi4Gi5Gi6Gi7Gi8Gi9"
    "Gj0Gj1Gj2Gj3Gj4Gj5Gj6Gj7Gj8Gj9Gk0Gk1Gk2Gk3Gk4Gk5Gk6Gk7Gk8Gk9Gl0Gl1Gl2Gl3Gl4Gl5Gl6Gl7Gl8Gl9"
    "Gm0Gm1Gm2Gm3Gm4Gm5Gm6Gm7Gm8Gm9Gn0Gn1Gn2Gn3Gn4Gn5Gn6Gn7Gn8Gn9Go0Go1Go2Go3Go4Go5Go6Go7Go8Go9"
    "Gp0Gp1Gp2Gp3Gp4Gp5Gp6Gp7Gp8Gp9Gq0Gq1Gq2Gq3Gq4Gq5Gq6Gq7Gq8Gq9Gr0Gr1Gr2Gr3Gr4Gr5Gr6Gr7Gr8Gr9"
    "Gs0Gs1Gs2Gs3Gs4Gs5Gs6Gs7Gs8Gs9Gt0Gt1Gt2Gt3Gt4Gt5Gt6Gt7Gt8Gt9Gu0Gu1Gu2Gu3Gu4Gu5Gu6Gu7Gu8Gu9"
    "Gv0Gv1Gv2Gv3Gv4Gv5Gv6Gv7Gv8Gv9Gw0Gw1Gw2Gw3Gw4Gw5Gw6Gw7Gw8Gw9Gx0Gx1Gx2Gx3Gx4Gx5Gx6Gx7Gx8Gx9"
    "Gy0Gy1Gy2Gy3Gy4Gy5Gy6Gy7Gy8Gy9Gz0Gz1Gz2Gz3Gz4Gz5Gz6Gz7Gz8Gz9Ha0Ha1Ha2Ha3Ha4Ha5Ha6Ha7Ha8Ha9"
    "Hb0Hb1Hb2Hb3Hb4Hb5Hb6Hb7Hb8Hb9Hc0Hc1Hc2Hc3Hc4Hc5Hc6Hc7Hc8Hc9Hd0Hd1Hd2Hd3Hd4Hd5Hd6Hd7Hd8Hd9"
    "He0He1He2He3He4He5He6He7He8He9Hf0Hf1Hf2Hf3Hf4Hf5Hf6Hf7Hf8Hf9Hg0Hg1Hg2Hg3Hg4Hg5Hg6Hg7Hg8Hg9"
    "Hh0Hh1Hh2Hh3Hh4Hh5Hh6Hh7Hh8Hh9Hi0Hi1Hi2Hi3Hi4Hi5Hi6Hi7Hi8Hi9Hj0Hj1Hj2Hj3Hj4Hj5Hj6Hj7Hj8Hj9"
    "Hk0Hk1Hk2Hk3Hk4Hk5Hk6Hk7Hk8Hk9Hl0Hl1Hl2Hl3Hl4Hl5Hl6Hl7Hl8Hl9Hm0Hm1Hm2Hm3Hm4Hm5Hm6Hm7Hm8Hm9"
    "Hn0Hn1Hn2Hn3Hn4Hn5Hn6Hn7Hn8Hn9Ho0Ho1Ho2Ho3Ho4Ho5Ho6Ho7Ho8Ho9Hp0Hp1Hp2Hp3Hp4Hp5Hp6Hp7Hp8Hp9"
    "Hq0Hq1Hq2Hq3Hq4Hq5Hq6Hq7Hq8Hq9Hr0Hr1Hr2Hr3Hr4Hr5Hr6Hr7Hr8Hr9Hs0Hs1Hs2Hs3Hs4Hs5Hs6Hs7Hs8Hs9"
    "Ht0Ht1Ht2Ht3Ht4Ht5Ht6Ht7Ht8Ht9Hu0Hu1Hu2Hu3Hu4Hu5Hu6Hu7Hu8Hu9Hv0Hv1Hv2Hv3Hv4Hv5Hv6Hv7Hv8Hv9"
    "Hw0Hw1Hw2Hw3Hw4Hw5Hw6Hw7Hw8Hw9Hx0Hx1Hx2Hx3Hx4Hx5Hx6Hx7Hx8Hx9Hy0Hy1Hy2Hy3Hy4Hy5Hy6Hy7Hy8Hy9"
    "Hz0Hz1Hz2Hz3Hz4Hz5Hz6Hz7Hz8Hz9Ia0Ia1Ia2Ia3Ia4Ia5Ia6Ia7Ia8Ia9Ib0Ib1Ib2Ib3Ib4Ib5Ib6Ib7Ib8Ib9"
    "Ic0Ic1Ic2Ic3Ic4Ic5Ic6Ic7Ic8Ic9Id0Id1Id2Id3Id4Id5Id6Id7Id8Id9Ie0Ie1Ie2Ie3Ie4Ie5Ie6Ie7Ie8Ie9"
    "If0If1If2If3If4If5If6If7If8If9Ig0Ig1Ig2Ig3Ig4Ig5Ig6Ig7Ig8Ig9Ih0Ih1Ih2Ih3Ih4Ih5Ih6Ih7Ih8Ih9"
    "Ii0Ii1Ii2Ii3Ii4Ii5Ii6Ii7Ii8Ii9Ij0Ij1Ij2Ij3Ij4Ij5Ij6Ij7Ij8Ij9Ik0Ik1Ik2Ik3Ik4Ik5Ik6Ik7Ik8Ik9"
    "Il0Il1Il2Il3Il4Il5Il6Il7Il8Il9Im0Im1Im2Im3Im4Im5Im6Im7Im8Im9In0In1In2In3In4In5In6In7In8In9"
    "Io0Io1Io2Io3Io4Io5Io6Io7Io8Io9Ip0Ip1Ip2Ip3Ip4Ip5Ip6Ip7Ip8Ip9Iq0Iq1Iq2Iq3Iq4Iq5Iq6Iq7Iq8Iq9"
    "Ir0Ir1Ir2Ir3Ir4Ir5Ir6Ir7Ir8Ir9Is0Is1Is2Is3Is4Is5Is6Is7Is8Is9It0It1It2It3It4It5It6It7It8It9"
    "Iu0Iu1Iu2Iu3Iu4Iu5Iu6Iu7Iu8Iu9Iv0Iv1Iv2Iv3Iv4Iv5Iv6Iv7Iv8Iv9Iw0Iw1Iw2Iw3Iw4Iw5Iw6Iw7Iw8Iw9"
    "Ix0Ix1Ix2Ix3Ix4Ix5Ix6Ix7Ix8Ix9Iy0Iy1Iy2Iy3Iy4Iy5Iy6Iy7Iy8Iy9Iz0Iz1Iz2Iz3Iz4Iz5Iz6Iz7Iz8Iz9"
    "Ja0Ja1Ja2Ja3Ja4Ja5Ja6Ja7Ja8Ja9Jb0Jb1Jb2Jb3Jb4Jb5Jb6Jb7Jb8Jb9Jc0Jc1Jc2Jc3Jc4Jc5Jc6Jc7Jc8Jc9"
    "Jd0Jd1Jd2Jd3Jd4Jd5Jd6Jd7Jd8Jd9Je0Je1Je2Je3Je4Je5Je6Je7Je8Je9Jf0Jf1Jf2Jf3Jf4Jf5Jf6Jf7Jf8Jf9"
    "Jg0Jg1Jg2Jg3Jg4Jg5Jg6Jg7Jg8Jg9Jh0Jh1Jh2Jh3Jh4Jh5Jh6Jh7Jh8Jh9Ji0Ji1Ji2Ji3Ji4Ji5Ji6Ji7Ji8Ji9"
    "Jj0Jj1Jj2Jj3Jj4Jj5Jj6Jj7Jj8Jj9Jk0Jk1Jk2Jk3Jk4Jk5Jk6Jk7Jk8Jk9Jl0Jl1Jl2Jl3Jl4Jl5Jl6Jl7Jl8Jl9"
    "Jm0Jm1Jm2Jm3Jm4Jm5Jm6Jm7Jm8Jm9Jn0Jn1Jn2Jn3Jn4Jn5Jn6Jn7Jn8Jn9Jo0Jo1Jo2Jo3Jo4Jo5Jo6Jo7Jo8Jo9"
    "Jp0Jp1Jp2Jp3Jp4Jp5Jp6Jp7Jp8Jp9Jq0Jq1Jq2Jq3Jq4Jq5Jq6Jq7Jq8Jq9Jr0Jr1Jr2Jr3Jr4Jr5Jr6Jr7Jr8Jr9"
    "Js0Js1Js2Js3Js4Js5Js6Js7Js8Js9Jt0Jt1Jt2Jt3Jt4Jt5Jt6Jt7Jt8Jt9Ju0Ju1Ju2Ju3Ju4Ju5Ju6Ju7Ju8Ju9"
    "Jv0Jv1Jv2Jv3Jv4Jv5Jv6Jv7Jv8Jv9Jw0Jw1Jw2Jw3Jw4Jw5Jw6Jw7Jw8Jw9Jx0Jx1Jx2Jx3Jx4Jx5Jx6Jx7Jx8Jx9"
    "Jy0Jy1Jy2Jy3Jy4Jy5Jy6Jy7Jy8Jy9Jz0Jz1Jz2Jz3Jz4Jz5Jz6Jz7Jz8Jz9Ka0Ka1Ka2Ka3Ka4Ka5Ka6Ka7Ka8Ka9"
    "Kb0Kb1Kb2Kb3Kb4Kb5Kb6Kb7Kb8Kb9Kc0Kc1Kc2Kc3Kc4Kc5Kc6Kc7Kc8Kc9Kd0Kd1Kd2Kd3Kd4Kd5Kd6Kd7Kd8Kd9"
    "Ke0Ke1Ke2Ke3Ke4Ke5Ke6Ke7Ke8Ke9Kf0Kf1Kf2Kf3Kf4Kf5Kf6Kf7Kf8Kf9Kg0Kg1Kg2Kg3Kg4Kg5Kg6Kg7Kg8Kg9"
    "Kh0Kh1Kh2Kh3Kh4Kh5Kh6Kh7Kh8Kh9Ki0Ki1Ki2Ki3Ki4Ki5Ki6Ki7Ki8Ki9Kj0Kj1Kj2Kj3Kj4Kj5Kj6Kj7Kj8Kj9"
    "Kk0Kk1Kk2Kk3Kk4Kk5Kk6Kk7Kk8Kk9Kl0Kl1Kl2Kl3Kl4Kl5Kl6Kl7Kl8Kl9Km0Km1Km2Km3Km4Km5Km6Km7Km8Km9"
    "Kn0Kn1Kn2Kn3Kn4Kn5Kn6Kn7Kn8Kn9Ko0Ko1Ko2Ko3Ko4Ko5Ko6Ko7Ko8Ko9Kp0Kp1Kp2Kp3Kp4Kp5Kp6Kp7Kp8Kp9"
    "Kq0Kq1Kq2Kq3Kq4Kq5Kq6Kq7Kq8Kq9Kr0Kr1Kr2Kr3Kr4Kr5Kr6Kr7Kr8Kr9Ks0Ks1Ks2Ks3Ks4Ks5Ks6Ks7Ks8Ks9"
    "Kt0Kt1Kt2Kt3Kt4Kt5Kt6Kt7Kt8Kt9Ku0Ku1Ku2Ku3Ku4Ku5Ku6Ku7Ku8Ku9Kv0Kv1Kv2Kv3Kv4Kv5Kv6Kv7Kv8Kv9"
    "Kw0Kw1Kw2Kw3Kw4Kw5Kw6Kw7Kw8Kw9Kx0Kx1Kx2Kx3Kx4Kx5Kx6Kx7Kx8Kx9Ky0Ky1Ky2Ky3Ky4Ky5Ky6Ky7Ky8Ky9"
    "Kz0Kz1Kz2Kz3Kz4Kz5Kz6Kz7Kz8Kz9La0La1La2La3La4La5La6La7La8La9Lb0Lb1Lb2Lb3Lb4Lb5Lb6Lb7Lb8Lb9"
    "Lc0Lc1Lc2Lc3Lc4Lc5Lc6Lc7Lc8Lc9Ld0Ld1Ld2Ld3Ld4Ld5Ld6Ld7Ld8Ld9Le0Le1Le2Le3Le4Le5Le6Le7Le8Le9"
    "Lf0Lf1Lf2Lf3Lf4Lf5Lf6Lf7Lf8Lf9Lg0Lg1Lg2Lg3Lg4Lg5Lg6Lg7Lg8Lg9Lh0Lh1Lh2Lh3Lh4Lh5Lh6Lh7Lh8Lh9"
    "Li0Li1Li2Li3Li4Li5Li6Li7Li8Li9Lj0Lj1Lj2Lj3Lj4Lj5Lj6Lj7Lj8Lj9Lk0Lk1Lk2Lk3Lk4Lk5Lk6Lk7Lk8Lk9"
    "Ll0Ll1Ll2Ll3Ll4Ll5Ll6Ll7Ll8Ll9Lm0Lm1Lm2Lm3Lm4Lm5Lm6Lm7Lm8Lm9Ln0Ln1Ln2Ln3Ln4Ln5Ln6Ln7Ln8Ln9"
    "Lo0Lo1Lo2Lo3Lo4Lo5Lo6Lo7Lo8Lo9Lp0Lp1Lp2Lp3Lp4Lp5Lp6Lp7Lp8Lp9Lq0Lq1Lq2Lq3Lq4Lq5Lq6Lq7Lq8Lq9"
    "Lr0Lr1Lr2Lr3Lr4Lr5Lr6Lr7Lr8Lr9Ls0Ls1Ls2Ls3Ls4Ls5Ls6Ls7Ls8Ls9Lt0Lt1Lt2Lt3Lt4Lt5Lt6Lt7Lt8Lt9"
    "Lu0Lu1Lu2Lu3Lu4Lu5Lu6Lu7Lu8Lu9Lv0Lv1Lv2Lv3Lv4Lv5Lv6Lv7Lv8Lv9Lw0Lw1Lw2Lw3Lw4Lw5Lw6Lw7Lw8Lw9"
    "Lx0Lx1Lx2Lx3Lx4Lx5Lx6Lx7Lx8Lx9Ly0Ly1Ly2Ly3Ly4Ly5Ly6Ly7Ly8Ly9Lz0Lz1Lz2Lz3Lz4Lz5Lz6Lz7Lz8Lz9"
    "Ma0Ma1Ma2Ma3Ma4Ma5Ma6Ma7Ma8Ma9Mb0Mb1Mb2Mb3Mb4Mb5Mb6Mb7Mb8Mb9Mc0Mc1Mc2Mc3Mc4Mc5Mc6Mc7Mc8Mc9"
    "Md0Md1Md2Md3Md4Md5Md6Md7Md8Md9Me0Me1Me2Me3Me4Me5Me6Me7Me8Me9Mf0Mf1Mf2Mf3Mf4Mf5Mf6Mf7Mf8Mf9"
    "Mg0Mg1Mg2Mg3Mg4Mg5Mg6Mg7Mg8Mg9Mh0Mh1Mh2Mh3Mh4Mh5Mh6Mh7Mh8Mh9Mi0Mi1Mi2Mi3Mi4Mi5Mi6Mi7Mi8Mi9"
    "Mj0Mj1Mj2Mj3Mj4Mj5Mj6Mj7Mj8Mj9Mk0Mk1Mk2Mk3Mk4Mk5Mk6Mk7Mk8Mk9Ml0Ml1Ml2Ml3Ml4Ml5Ml6Ml7Ml8Ml9"
    "Mm0Mm1Mm2Mm3Mm4Mm5Mm6Mm7Mm8Mm9Mn0Mn1Mn2Mn3Mn4Mn5Mn6Mn7Mn8Mn9Mo0Mo1Mo2Mo3Mo4Mo5Mo6Mo7Mo8Mo9"
    "Mp0Mp1Mp2Mp3Mp4Mp5Mp6Mp7Mp8Mp9Mq0Mq1Mq2Mq3Mq4Mq5Mq6Mq7Mq8Mq9Mr0Mr1Mr2Mr3Mr4Mr5Mr6Mr7Mr8Mr9"
    "Ms0Ms1Ms2Ms3Ms4Ms5Ms6Ms7Ms8Ms9Mt0Mt1Mt2Mt3Mt4Mt5Mt6Mt7Mt8Mt9Mu0Mu1Mu2Mu3Mu4Mu5Mu6Mu7Mu8Mu9"
    "Mv0Mv1Mv2M"
)


# -------------------------------------------------------------------------------------------------
# HELPER FUNCTIONS
# -------------------------------------------------------------------------------------------------


def str2b(data):
    """Unescape P2/P3 and convert to bytes if Python3."""
    # Python2: Unescape control chars
    try:
        return data.decode("string_escape")
    except AttributeError:
        pass
    except UnicodeDecodeError:
        pass
    # Python3: Unescape control chars and convert to byte
    try:
        return data.encode("utf-8").decode("unicode-escape").encode("latin1")
    except UnicodeDecodeError:
        pass


def b2str(data):
    """Convert bytes into string type."""
    try:
        return data.decode("utf-8")
    except UnicodeDecodeError:
        pass
    try:
        return data.decode("utf-8-sig")
    except UnicodeDecodeError:
        pass
    try:
        return data.decode("ascii")
    except UnicodeDecodeError:
        return data.decode("latin-1")


def print_crashlog(prefix, suffix, char, buff):
    payload = prefix + buff + suffix
    print('\nRemote service (most likely) crashed at %s bytes of "%s"' % (str(len(buff)), char))
    print("Payload sent:\n%s" % (payload))


# -------------------------------------------------------------------------------------------------
# GENERATE FUNCTIONS
# -------------------------------------------------------------------------------------------------


def _script_pattern(host, port, prefix, suffix, length, init, exit):
    """Generate pattern overflow triaging scripts."""

    # Can we pre-fill data from patter_create.rb?
    if length <= len(PATTERN):
        pattern_val = "("
        pattern_desc = "  # {} bytes from pattern_create.rb".format(length)
        for idx, item in enumerate(PATTERN):
            # Only create the length we require
            if idx == length:
                break
            if idx % 90 == 0:
                if idx == 0:
                    pattern_val += '\n    "'
                else:
                    pattern_val += '"\n    "'
            pattern_val += item
        pattern_val += '"\n)'
    # Nope, current offset too long, do it yourself
    else:
        pattern_val = '""'
        pattern_desc = "     # Add output from 'pattern_create.rb -l " + str(length) + "'"

    code_head = '''#!/usr/bin/env python
"""fuzza autogenerated."""

from __future__ import print_function
import socket

def str2b(data):
    """Unescape P2/P3 and convert to bytes if Python3."""
    # Python2: Unescape control chars
    try:
        return data.decode('string_escape')
    except AttributeError:
        pass
    except UnicodeDecodeError:
        pass
    # Python3: Unescape control chars and convert to byte
    try:
        return data.encode("utf-8").decode('unicode-escape').encode("latin1")
    except UnicodeDecodeError:
        pass

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

pattern = {}{}

print('Trying to send %s bytes unique chars...' % (str(len(pattern))))
try:
    s.connect(('{}', {}))
'''.format(
        pattern_val, pattern_desc, host, port
    )

    code_init = ""
    if init is not None:
        for comm in init.split(","):
            d_send, d_expect = comm.split(":")
            if len(d_send) > 0:
                code_init += "    s.send(str2b('{}'))\n".format(d_send)
            if len(d_expect) > 0:
                code_init += "    s.recv(1024)\n"

    payload = "    s.send(str2b('{}' + pattern + '{}'))\n".format(prefix, suffix)

    code_exit = ""
    if exit is not None:
        for comm in exit.split(","):
            d_send, d_expect = comm.split(":")
            if len(d_send) > 0:
                code_exit += "    s.send(str2b('{}'))\n".format(d_send)
            if len(d_expect) > 0:
                code_exit += "    s.recv(1024)\n"
    code_tail = """    print('done')
except:
    print('Could not connect')
s.close()
"""
    return code_head + code_init + payload + code_exit + code_tail


def _script_badchars(host, port, char, prefix, suffix, length, init, exit):
    """Generate badchar overflow triaging scripts."""
    code_head = '''#!/usr/bin/env python
"""fuzza autogenerated."""

from __future__ import print_function
import socket

def str2b(data):
    """Unescape P2/P3 and convert to bytes if Python3."""
    # Python2: Unescape control chars
    try:
        return data.decode('string_escape')
    except AttributeError:
        pass
    except UnicodeDecodeError:
        pass
    # Python3: Unescape control chars and convert to byte
    try:
        return data.encode("utf-8").decode('unicode-escape').encode("latin1")
    except UnicodeDecodeError:
        pass

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

len_overflow = {}      # Use pattern_create.rb and pattern_offset.rb to find exact offset
eip          = "B"*4     # Ignore for badchar detection
badchars = (
    "\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\\x10"
    "\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f\\x20"
    "\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30"
    "\\x31\\x32\\x33\\x34\\x35\\x36\\x37\\x38\\x39\\x3a\\x3b\\x3c\\x3d\\x3e\\x3f\\x40"
    "\\x41\\x42\\x43\\x44\\x45\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f\\x50"
    "\\x51\\x52\\x53\\x54\\x55\\x56\\x57\\x58\\x59\\x5a\\x5b\\x5c\\x5d\\x5e\\x5f\\x60"
    "\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x69\\x6a\\x6b\\x6c\\x6d\\x6e\\x6f\\x70"
    "\\x71\\x72\\x73\\x74\\x75\\x76\\x77\\x78\\x79\\x7a\\x7b\\x7c\\x7d\\x7e\\x7f\\x80"
    "\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90"
    "\\x91\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0"
    "\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\\xb0"
    "\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd\\xbe\\xbf\\xc0"
    "\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\\xd0"
    "\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0"
    "\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0"
    "\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff"
)

buffer = "{}"*len_overflow + eip + badchars

print('Trying to send %s bytes buffer...' % (str(len(buffer))))
try:
    s.connect(('{}', {}))
'''.format(
        length, char, host, port
    )

    code_init = ""
    if init is not None:
        for comm in init.split(","):
            d_send, d_expect = comm.split(":")
            if len(d_send) > 0:
                code_init += "    s.send(str2b('{}'))\n".format(d_send)
            if len(d_expect) > 0:
                code_init += "    s.recv(1024)\n"

    payload = "    s.send(str2b('{}' + buffer + '{}'))\n".format(prefix, suffix)

    code_exit = ""
    if exit is not None:
        for comm in exit.split(","):
            d_send, d_expect = comm.split(":")
            if len(d_send) > 0:
                code_exit += "    s.send(str2b('{}'))\n".format(d_send)
            if len(d_expect) > 0:
                code_exit += "    s.recv(1024)\n"
    code_tail = """    print('done')
except:
    print('Could not connect')
s.close()
"""
    return code_head + code_init + payload + code_exit + code_tail


def _script_attack(host, port, char, prefix, suffix, length, init, exit):
    """Generate attack overflow triaging scripts."""
    code_head = '''#!/usr/bin/env python
"""fuzza autogenerated."""

from __future__ import print_function
import socket

def str2b(data):
    """Unescape P2/P3 and convert to bytes if Python3."""
    # Python2: Unescape control chars
    try:
        return data.decode('string_escape')
    except AttributeError:
        pass
    except UnicodeDecodeError:
        pass
    # Python3: Unescape control chars and convert to byte
    try:
        return data.encode("utf-8").decode('unicode-escape').encode("latin1")
    except UnicodeDecodeError:
        pass

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

len_total    = {}                # Start at len_overflow and try out how much can be overwritten
len_overflow = {}                # Use pattern_create.rb and pattern_offset.rb to find exact offset
len_nop_sled = 0                   # Add x bytes of nops before shellcode for shellcode decoding
eip          = "\\x42\\x42\\x42\\x42"  # Change this (Keep in mind to put address in reverse order)
shellcode    = ""

padding = "C"*(len_total - len_overflow - len(str(eip)) - len_nop_sled - len(shellcode))
buffer  = "{}"*len_overflow + eip + "\\x90"*len_nop_sled + shellcode + padding

print('Trying to send %s bytes buffer...' % (str(len(buffer))))
try:
    s.connect(('{}', {}))
'''.format(
        length, length - 4, char, host, port
    )

    code_init = ""
    if init is not None:
        for comm in init.split(","):
            d_send, d_expect = comm.split(":")
            if len(d_send) > 0:
                code_init += "    s.send(str2b('{}'))\n".format(d_send)
            if len(d_expect) > 0:
                code_init += "    s.recv(1024)\n"

    payload = "    s.send(str2b('{}' + buffer + '{}'))\n".format(prefix, suffix)

    code_exit = ""
    if exit is not None:
        for comm in exit.split(","):
            d_send, d_expect = comm.split(":")
            if len(d_send) > 0:
                code_exit += "    s.send(str2b('{}'))\n".format(d_send)
            if len(d_expect) > 0:
                code_exit += "    s.recv(1024)\n"
    code_tail = """    print('done')
except:
    print('Could not connect')
s.close()
"""
    return code_head + code_init + payload + code_exit + code_tail


def generate(directory, host, port, char, prefix, suffix, length, init, exit):
    """Generate overflow triaging scripts."""

    attack = _script_attack(host, port, char, prefix, suffix, length, init, exit)
    pattern = _script_pattern(host, port, prefix, suffix, length, init, exit)
    badchars = _script_badchars(host, port, char, prefix, suffix, length, init, exit)

    fp = open(os.path.join(directory, "attack.py"), "w")
    fp.write(attack)
    fp.close()

    fp = open(os.path.join(directory, "pattern.py"), "w")
    fp.write(pattern)
    fp.close()

    fp = open(os.path.join(directory, "badchars.py"), "w")
    fp.write(badchars)
    fp.close()


# -------------------------------------------------------------------------------------------------
# NETWORK FUNCTIONS
# -------------------------------------------------------------------------------------------------


def connect(host, port):
    """Connect to remote host."""
    # Create socket
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    except socket.error as msg:
        return (None, msg)
    # Get remote IP
    try:
        addr = socket.gethostbyname(host)
    except socket.gaierror as msg:
        s.close()
        return (None, msg)
    # Connect
    try:
        s.connect((addr, port))
    except socket.error as msg:
        s.close()
        return (None, msg)

    return (s, None)


def send(s, data):
    """Send data to socket."""
    try:
        s.send(str2b(data))
    except socket.error as msg:
        s.close()
        return (False, msg)

    return (True, None)


def receive(s, timeout, bufsize=1024):
    """Read one newline terminated line from a connected socket."""
    data = ""
    size = len(data)
    s.settimeout(timeout)

    while True:
        try:
            data += b2str(s.recv(bufsize))
        except socket.error as err:
            return (False, err)
        if not data:
            return (False, "upstream connection is gone while receiving")
        # Newline terminates the read request
        if data.endswith("\n"):
            break
        if data.endswith("\r"):
            break
        # Sometimes a newline is missing at the end
        # If this round has the same data length as previous, we're done
        if size == len(data):
            break
        size = len(data)
    # Remove trailing newlines
    data = data.rstrip("\r\n")
    data = data.rstrip("\n")
    data = data.rstrip("\r")
    return (True, data)


# -------------------------------------------------------------------------------------------------
# ARGS
# -------------------------------------------------------------------------------------------------


def _args_check_dir(value):
    """Check argument for valid directory."""
    if not os.path.isdir(str(value)):
        raise argparse.ArgumentTypeError('Directory "%s" does not exist.' % value)
    return value


def _args_check_init(value):
    """Check argument for valid init value."""
    for comm in value.split(","):
        if comm.find(":") == -1:
            raise argparse.ArgumentTypeError('"%s" is an invalid init value.' % value)
    return value


def _args_check_port(value):
    """Check argument for valid port number."""
    min_port = 1
    max_port = 65535

    try:
        intvalue = int(value)
    except ValueError:
        raise argparse.ArgumentTypeError('"%s" is an invalid port number.' % value)

    if intvalue < min_port or intvalue > max_port:
        raise argparse.ArgumentTypeError('"%s" is an invalid port number.' % value)
    return intvalue


def get_args():
    """Retrieve command line arguments."""
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        usage="""%(prog)s [options] host port
       fuzza --help
       fuzza --version
""",
        description="""Customizable TCP fuzzing tool to test for remote buffer overflows.

It works in two different modes: normal and generate. Normal mode will send your payload
to a remote endpoint and increase the payload size each round in order to try to crash the
service. The generate mode however will generate three easy to use python scripts to
further triage any potential buffer overflow manually.
""",
        epilog="""\
example:\n
  The following example illustrates how to use the initial communication by:
      1. Expecting the POP3 server banner
      2. Sending 'USER bob'
      3. Expecting a welcome message
  Additionally before sending the fuzzing characters, it is prepended with 'PASS ',
  so that the actuall fuzzing can be done on the password:
     1. Prefix payload with 'PASS '
     2. Send payload
  Lastly in order to also close the connection the '-e' opton is used
  (which works exactly as '-i') in order to send data after the payload.
     1. Expect any response from password payload
     2. Terminate the connection via QUIT
     3. Do not expect a follow up response
  $ fuzza -i ':.*POP3.*,USER bob\\r\\n:.*welcome.*' -e ':.*,QUIT:' -p 'PASS ' -s '\\r\\n'

Visit https://github.com/cytopia/fuzza for more examples.
""",
    )
    parser.add_argument(
        "-v",
        "--version",
        action="version",
        version="%(prog)s 0.6.0 by cytopia",
        help="Show version information,",
    )
    parser.add_argument(
        "-c",
        "--char",
        metavar="char",
        required=False,
        default=DEF_CHAR,
        type=str,
        help='Buffer character to send as payload. Default: "' + DEF_CHAR + '"',
    )
    parser.add_argument(
        "-p",
        "--prefix",
        metavar="str",
        required=False,
        default=DEF_PREFIX,
        type=str,
        help="Prefix string to prepend to buffer. Empty by default.",
    )
    parser.add_argument(
        "-s",
        "--suffix",
        metavar="str",
        required=False,
        default=DEF_SUFFIX,
        type=str,
        help="Suffix string to append to buffer. Empty by default.",
    )
    parser.add_argument(
        "-l",
        "--length",
        metavar="int",
        required=False,
        default=DEF_INIT_MULTIPLIER,
        type=int,
        help="""Initial length to concat buffer string with x*char.
When using the '-g' option to generate reproducible attack scripts set this
to the value at which the crash occured in order to pre-populate the
generated scripts. """
        + "Default: "
        + str(DEF_INIT_MULTIPLIER),
    )
    parser.add_argument(
        "-m",
        "--multiply",
        metavar="int",
        required=False,
        default=DEF_ROUND_MULTIPLIER,
        type=int,
        help="""Round multiplier to concat buffer string with x*char every round.
"""
        + "Default: "
        + str(DEF_ROUND_MULTIPLIER),
    )
    parser.add_argument(
        "-i",
        "--init",
        metavar="str",
        required=False,
        default=None,
        type=_args_check_init,
        help="""If specified, initializes communication before sending the payload in the
form '<send>:<expect>,<send>:<expect>,...'. Where <send> is the data to be
sent to the server and <expect> is the answer to be received from the
server.  Either one of <send> or <expect> can be omitted if you expect
something without having sent data yet or need to send something for which
there will not be an answer. Multiple <send>:<expect> are supported and
must be separated by a comma.
Regex supported for <expect> part.""",
    )
    parser.add_argument(
        "-e",
        "--exit",
        metavar="str",
        required=False,
        default=None,
        type=_args_check_init,
        help="""If specified, finalizes communication after sending the payload in the
form '<send>:<expect>,<send>:<expect>,...'. Where <send> is the data to be
sent to the server and <expect> is the answer to be received from the
server.  Either one of <send> or <expect> can be omitted if you expect
something without having sent data yet or need to send something for
which there will not be an answer. Multiple <send>:<expect> are supported
and must be separated by a comma.
Regex supported for <expect> part.""",
    )
    parser.add_argument(
        "-t",
        "--timeout",
        metavar="float",
        required=False,
        default=DEF_TIMEOUT,
        type=float,
        help="""Timeout in sec for receiving data before declaring the endpoint as crashed.
Default: """
        + str(DEF_TIMEOUT),
    )
    parser.add_argument(
        "-d",
        "--delay",
        metavar="float",
        required=False,
        default=DEF_DELAY,
        type=float,
        help="Delay in seconds between each round. Default: " + str(DEF_DELAY),
    )
    parser.add_argument(
        "-g",
        "--generate",
        metavar="dir",
        required=False,
        default=None,
        type=_args_check_dir,
        help="""Generate custom python scripts based on your command line arguments
to reproduce and triage the overflow. Requires a directory to be
specified where to save the scripts to.""",
    )
    parser.add_argument("host", type=str, help="IP or hostname to connect to.")
    parser.add_argument("port", type=_args_check_port, help="Port to connect to.")
    return parser.parse_args()


# -------------------------------------------------------------------------------------------------
# MAIN ENTRYPOINT
# -------------------------------------------------------------------------------------------------


def main():
    """Start the program."""
    args = get_args()

    char = args.char
    imulti = args.length
    rmulti = args.multiply
    prefix = args.prefix
    suffix = args.suffix
    timeout = args.timeout
    delay = args.delay
    gen = args.generate

    multiplier = imulti
    buff = char * multiplier

    # Generate triage scripts
    if gen:
        generate(gen, args.host, args.port, char, prefix, suffix, args.length, args.init, args.exit)
        return

    # Fuzz
    while True:
        print("------------------------------------------------------------")
        print("%s * %s" % (char, str(multiplier)))
        print("------------------------------------------------------------")

        # Connect
        s, err = connect(args.host, args.port)
        if s is None:
            if multiplier == imulti:
                print(err, file=sys.stderr)
                sys.exit(1)
            else:
                print_crashlog(prefix, suffix, char, buff)
                sys.exit(0)

        # Initial communication
        if args.init is not None:
            for comm in args.init.split(","):
                d_send, d_expect = comm.split(":")
                # Send data?
                if len(d_send) > 0:
                    print('Init Sending:    "\033[91m%s\033[00m"' % (d_send))
                    succ, err = send(s, d_send)
                    if not succ:
                        if multiplier == imulti:
                            print(err, file=sys.stderr)
                            sys.exit(1)
                        else:
                            print_crashlog(prefix, suffix, char, buff)
                            sys.exit(0)
                # Expect data?
                if len(d_expect) > 0:
                    print('Init Awaiting:   "%s"' % (d_expect))
                    succ, d_recv = receive(s, timeout, 1024)
                    if not succ:
                        if multiplier == imulti:
                            print(d_recv, file=sys.stderr)
                            sys.exit(1)
                        else:
                            print_crashlog(prefix, suffix, char, buff)
                            sys.exit(0)
                    print('Init Received:   "\033[92m%s\033[00m"' % (d_recv))
                    if d_expect != d_recv and not bool(re.search(d_expect, d_recv)):
                        print_crashlog(prefix, suffix, char, buff)
                        sys.exit(0)

        # Send payload
        print(
            "Sending Payload: "
            + '"{r}{p}{d}" {r}+{d} "{r}{c}{d}"{r}*{m}{d} {r}+{d} "{r}{s}{d}"'.format(
                r="\033[91m", d="\033[00m", p=prefix, c=char, m=multiplier, s=suffix
            )
        )
        buff = char * multiplier
        payload = prefix + buff + suffix
        succ, err = send(s, payload)
        if not succ:
            if multiplier == imulti:
                print(err, file=sys.stderr)
                sys.exit(1)
            else:
                print_crashlog(prefix, suffix, char, buff)
                sys.exit(0)

        # Exit communication
        if args.exit is not None:
            for comm in args.exit.split(","):
                d_send, d_expect = comm.split(":")
                # Send data?
                if len(d_send) > 0:
                    print('Exit Sending:    "\033[91m%s\033[00m"' % (d_send))
                    succ, err = send(s, d_send)
                    if not succ:
                        if multiplier == imulti:
                            print(err, file=sys.stderr)
                            sys.exit(1)
                        else:
                            print_crashlog(prefix, suffix, char, buff)
                            sys.exit(0)
                # Expect data?
                if len(d_expect) > 0:
                    print('Exit Awaiting:   "%s"' % (d_expect))
                    succ, d_recv = receive(s, timeout, 1024)
                    if not succ:
                        print_crashlog(prefix, suffix, char, buff)
                        sys.exit(0)
                    print('Exit Received:   "\033[92m%s\033[00m"' % (d_recv))
                    if d_expect != d_recv and not bool(re.search(d_expect, d_recv)):
                        print_crashlog(prefix, suffix, char, buff)
                        sys.exit(0)

        s.close()
        sleep(delay)
        multiplier = multiplier + rmulti


if __name__ == "__main__":
    # Catch Ctrl+c and exit without error message
    try:
        main()
    except KeyboardInterrupt:
        print()
        sys.exit(1)
