From f7b3c5ae54e10d9b2de32b946862b2ede65a298d Mon Sep 17 00:00:00 2001 From: jiri Date: Thu, 1 Dec 2016 08:27:36 +0100 Subject: [PATCH] x-option for constant monitoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit introduce new monitoring mode that keeps sipsak running until failureThis new -X mode keeps sending messages until a failure occurs. A negative answer is not considered a failure. This is helpful when you want to monitor a SIP device and do not want to restart sipsak all over again — it keeps running until the device fails. In this mode, the RFC3261 retransmission timers are ignored — they are constant. Use it like this: keep trying every 1/4 sec and return with a failure if 5 retries are not sufficient: sipsak -s sip:foo@bar.com -X -Z 250 -D 5 I know it is not a best practice but I did several unnecessary changes too — the code was simply too widely nested and I had a too narrow monitor — I hope that’s fine. If not I can submit just the minimum necessary changes instead. --- shoot.c | 335 ++++++++++++++++++++++++++-------------------------- sipsak.1 | 16 ++- sipsak.c | 27 ++++- sipsak.h | 4 + transport.c | 54 +++++---- 5 files changed, 244 insertions(+), 192 deletions(-) diff --git a/shoot.c b/shoot.c index c22c92f..c2a2620 100644 --- a/shoot.c +++ b/shoot.c @@ -170,7 +170,7 @@ void trace_reply() set_maxforw(req, namebeg); return; } - else if (regexec(&(regexps.proexp), rec, 0, 0, 0) == REG_NOERROR) { + if (regexec(&(regexps.proexp), rec, 0, 0, 0) == REG_NOERROR) { /* we received a provisional response */ printf("%i: ", namebeg); if (verbose > 2) { @@ -186,36 +186,37 @@ void trace_reply() cdata.dontsend=1; return; } - else { - /* anything else then 483 or provisional will - be treated as final */ - printf("%i: ", namebeg); - warning_extract(rec); - printf("(%.3f ms) ", deltaT(&(timers.sendtime), &(timers.recvtime))); - print_message_line(rec); - if ((contact = STRCASESTR(rec, CONT_STR)) != NULL || - (contact = STRCASESTR(rec, CONT_SHORT_STR)) != NULL) { - if (*contact == '\n') { - contact++; - } - printf("\t"); - print_message_line(contact); - } - else { - printf("\twithout Contact header\n"); - } - if (regexec(&(regexps.okexp), rec, 0, 0, 0) == REG_NOERROR) { - on_success(rec); - } else { - log_message(req); - exit_code(1, __PRETTY_FUNCTION__, "received final non-2xx reply"); + /* anything else then 483 or provisional will be treated as final */ + printf("%i: ", namebeg); + warning_extract(rec); + printf("(%.3f ms) ", deltaT(&(timers.sendtime), &(timers.recvtime))); + print_message_line(rec); + if ((contact = STRCASESTR(rec, CONT_STR)) != NULL || + (contact = STRCASESTR(rec, CONT_SHORT_STR)) != NULL) { + if (*contact == '\n') { + contact++; } + printf("\t"); + print_message_line(contact); + } + else { + printf("\twithout Contact header\n"); + } + if (regexec(&(regexps.okexp), rec, 0, 0, 0) == REG_NOERROR) { + on_success(rec); + } else { + log_message(req); + exit_code(1, __PRETTY_FUNCTION__, "received final non-2xx reply"); } } /* takes care of replies in the default mode */ void handle_default() { + + int is200; + struct timespec waitabit; + /* in the normal send and reply case anything other then 1xx will be treated as final response*/ if (regexec(&(regexps.proexp), rec, 0, 0, 0) == REG_NOERROR) { @@ -235,57 +236,67 @@ void handle_default() printf(" provisional received; still" " waiting for a final response\n"); } - if (inv_trans) { - delays.retryAfter = timer_final; - } - else { - delays.retryAfter = timer_t2; - } + delays.retryAfter = inv_trans ? timer_final : timer_t2; cdata.dontsend = 1; return; } - else { - if (verbose > 1) { - printf("%s\n\n", rec); - printf("** reply received "); - if ((counters.send_counter == 1) || (STRNCASECMP(req, ACK_STR, ACK_STR_LEN) == 0)){ - printf("after %.3f ms **\n", deltaT(&(timers.firstsendt), &(timers.recvtime))); - } - else { - printf("%.3f ms after first send\n and " - "%.3f ms after last send **\n", deltaT(&(timers.firstsendt), &(timers.recvtime)), - deltaT(&(timers.sendtime), &(timers.recvtime))); - } - printf(" "); - print_message_line(rec); - printf(" final received\n"); + if (verbose > 1) { + printf("%s\n\n", rec); + printf("** reply received "); + if ((counters.send_counter == 1) || (STRNCASECMP(req, ACK_STR, ACK_STR_LEN) == 0)){ + printf("after %.3f ms **\n", deltaT(&(timers.firstsendt), &(timers.recvtime))); } - else if (verbose>0) { - printf("%s\n", rec); + else { + printf("%.3f ms after first send\n and " + "%.3f ms after last send **\n", deltaT(&(timers.firstsendt), &(timers.recvtime)), + deltaT(&(timers.sendtime), &(timers.recvtime))); } - if (timing > 0) { - timing--; - if (timing == 0) { - if (counters.run == 0) { - counters.run++; - } - printf("%.3f/%.3f/%.3f ms\n", delays.small_delay, delays.all_delay / counters.run, delays.big_delay); - } - else { + printf(" "); + print_message_line(rec); + printf(" final received\n"); + } + else if (verbose>0) { + printf("%s\n", rec); + } + if (timing > 0) { + timing--; + if (timing == 0) { + if (counters.run == 0) { counters.run++; - new_transaction(req, rep); - delays.retryAfter = timer_t1; } + printf("%.3f/%.3f/%.3f ms\n", delays.small_delay, delays.all_delay / counters.run, delays.big_delay); } - if (timing == 0) { - if (regexec(&(regexps.okexp), rec, 0, 0, 0) == REG_NOERROR) { - on_success(rec); - } - else { - log_message(req); - exit_code(1, __PRETTY_FUNCTION__, "received final non-2xx reply"); + else { + counters.run++; + new_transaction(req, rep); + delays.retryAfter = timer_t1; + } + } + if (timing == 0) { + is200 = regexec(&(regexps.okexp), rec, 0, 0, 0) == REG_NOERROR; + if (monitor_mode) { + if (verbose > 1 ) { + printf("final reply received (200: %s), next run\n", is200?"YES":"NO"); } + + /* wait till the next transaction for the retransmission interval */ + waitabit.tv_sec = timer_t1 / 1000; + waitabit.tv_nsec = (timer_t1 % 1000) * 1000000; + nanosleep(&waitabit, 0); + + /* initiate the next transactio */; + counters.run++; + new_transaction(req,rep); + delays.retryAfter = timer_t1; + + return; + } + if (is200) { + on_success(rec); + return; } + log_message(req); + exit_code(1, __PRETTY_FUNCTION__, "received final non-2xx reply"); } } @@ -342,12 +353,7 @@ void handle_usrloc() print_message_line(rec); printf("ignoring provisional response\n\n"); } - if (inv_trans) { - delays.retryAfter = timer_final; - } - else { - delays.retryAfter = timer_t2; - } + delays.retryAfter = inv_trans ? timer_final : timer_t2; cdata.dontsend = 1; } else { @@ -980,101 +986,7 @@ void shoot(char *buf, int buff_size) send_message(req, &cdata, &counters, &timers); /* in flood we are only interested in sending so skip the rest */ - if (flood == 0) { - ret = recv_message(rec, BUFSIZE, inv_trans, &delays, &timers, - &counters, &cdata, ®exps); - if(ret > 0) - { - if (usrlocstep == INV_OK_RECV) { - swap_ptr(&rep, &req); - } - /* send ACK for non-provisional reply on INVITE */ - if ((STRNCASECMP(req, "INVITE", 6)==0) && - (regexec(&(regexps.replyexp), rec, 0, 0, 0) == REG_NOERROR) && - (regexec(&(regexps.proexp), rec, 0, 0, 0) == REG_NOMATCH)) { - build_ack(req, rec, rep, ®exps); - cdata.dontsend = 0; - inv_trans = 0; - /* lets fire the ACK to the server */ - send_message(rep, &cdata, &counters, &timers); - inv_trans = 1; - } - /* check for old CSeq => ignore retransmission */ - cseqtmp = cseq(rec); - if ((0 < cseqtmp) && (cseqtmp < cseq_counter)) { - if (verbose>0) { - printf("ignoring retransmission\n"); - } - counters.retrans_r_c++; - cdata.dontsend = 1; - continue; - } - else if (regexec(&(regexps.authexp), rec, 0, 0, 0) == REG_NOERROR) { - if (!username && !auth_username) { - if (timing > 0) { - timing--; - if (timing == 0) { - if (counters.run == 0) { - counters.run++; - } - printf("%.3f/%.3f/%.3f ms\n", delays.small_delay, delays.all_delay / counters.run, delays.big_delay); - exit_code(0, __PRETTY_FUNCTION__, NULL); - } - counters.run++; - new_transaction(req, rep); - delays.retryAfter = timer_t1; - continue; - } - fprintf(stderr, "%s\nerror: received 40[17] but cannot " - "authentication without a username or auth username\n", rec); - log_message(req); - exit_code(2, __PRETTY_FUNCTION__, "missing username for authentication"); - } - /* prevents a strange error */ - regcomp(&(regexps.authexp), "^SIP/[0-9]\\.[0-9] 40[17] ", REG_EXTENDED|REG_NOSUB|REG_ICASE); - insert_auth(req, rec); - if (verbose > 2) - printf("\nreceived:\n%s\n", rec); - new_transaction(req, rep); - continue; - } /* if auth...*/ - /* lets see if received a redirect */ - if (redirects == 1 && regexec(&(regexps.redexp), rec, 0, 0, 0) == REG_NOERROR) { - handle_3xx(&(cdata.adr)); - } /* if redircts... */ - else if (trace == 1) { - trace_reply(); - } /* if trace ... */ - else if (usrloc == 1||invite == 1||message == 1) { - handle_usrloc(); - } - else if (randtrash == 1) { - handle_randtrash(); - } - else { - handle_default(); - } /* redirect, auth, and modes */ - } /* ret > 0 */ - else if (ret == -1) { // we did not got anything back, send again - /* no re-transmission on reliable transports */ - if (transport != SIP_UDP_TRANSPORT) { - cdata.dontsend = 1; - } - continue; - } - else if (ret == -2) { // we received non-matching ICMP - cdata.dontsend = 1; - continue; - } - else { - if (usrloc == 1) { - printf("failed\n"); - } - perror("socket error"); - exit_code(3, __PRETTY_FUNCTION__, "internal socket error"); - } - } /* !flood */ - else { + if (flood) { if (counters.send_counter == 1) { memcpy(&(timers.firstsendt), &(timers.sendtime), sizeof(struct timeval)); } @@ -1089,7 +1001,98 @@ void shoot(char *buf, int buff_size) namebeg++; cseq_counter++; create_msg(REQ_FLOOD, req, NULL, usern, cseq_counter); + continue; + } + ret = recv_message(rec, BUFSIZE, inv_trans, &delays, &timers, + &counters, &cdata, ®exps); + + if (ret == -1) { // we did not got anything back, send again + /* no re-transmission on reliable transports */ + if (transport != SIP_UDP_TRANSPORT) { + cdata.dontsend = 1; + } + continue; } + if (ret == -2) { // we received non-matching ICMP + cdata.dontsend = 1; + continue; + } + if (ret <=0 ) { + if (usrloc == 1) { + printf("failed\n"); + } + perror("socket error"); + exit_code(3, __PRETTY_FUNCTION__, "internal socket error"); + } + if (usrlocstep == INV_OK_RECV) { + swap_ptr(&rep, &req); + } + /* send ACK for non-provisional reply on INVITE */ + if ((STRNCASECMP(req, "INVITE", 6)==0) && + (regexec(&(regexps.replyexp), rec, 0, 0, 0) == REG_NOERROR) && + (regexec(&(regexps.proexp), rec, 0, 0, 0) == REG_NOMATCH)) { + build_ack(req, rec, rep, ®exps); + cdata.dontsend = 0; + inv_trans = 0; + /* lets fire the ACK to the server */ + send_message(rep, &cdata, &counters, &timers); + inv_trans = 1; + } + /* check for old CSeq => ignore retransmission */ + cseqtmp = cseq(rec); + if ((0 < cseqtmp) && (cseqtmp < cseq_counter)) { + if (verbose>0) { + printf("ignoring retransmission\n"); + } + counters.retrans_r_c++; + cdata.dontsend = 1; + continue; + } + else if (regexec(&(regexps.authexp), rec, 0, 0, 0) == REG_NOERROR) { + if (!username && !auth_username) { + if (timing > 0) { + timing--; + if (timing == 0) { + if (counters.run == 0) { + counters.run++; + } + printf("%.3f/%.3f/%.3f ms\n", delays.small_delay, delays.all_delay / counters.run, delays.big_delay); + exit_code(0, __PRETTY_FUNCTION__, NULL); + } + counters.run++; + new_transaction(req, rep); + delays.retryAfter = timer_t1; + continue; + } + fprintf(stderr, "%s\nerror: received 40[17] but cannot " + "authentication without a username or auth username\n", rec); + log_message(req); + exit_code(2, __PRETTY_FUNCTION__, "missing username for authentication"); + } + /* prevents a strange error */ + regcomp(&(regexps.authexp), "^SIP/[0-9]\\.[0-9] 40[17] ", REG_EXTENDED|REG_NOSUB|REG_ICASE); + insert_auth(req, rec); + if (verbose > 2) + printf("\nreceived:\n%s\n", rec); + new_transaction(req, rep); + continue; + } /* if auth...*/ + /* lets see if received a redirect */ + if (redirects == 1 && regexec(&(regexps.redexp), rec, 0, 0, 0) == REG_NOERROR) { + handle_3xx(&(cdata.adr)); + } /* if redircts... */ + else if (trace == 1) { + trace_reply(); + } /* if trace ... */ + else if (usrloc == 1||invite == 1||message == 1) { + handle_usrloc(); + } + else if (randtrash == 1) { + handle_randtrash(); + } + else { + handle_default(); + } /* redirect, auth, and modes */ } /* while 1 */ /* this should never happen any more... */ diff --git a/sipsak.1 b/sipsak.1 index 540ed9a..adf81ac 100644 --- a/sipsak.1 +++ b/sipsak.1 @@ -5,7 +5,7 @@ .SH NAME sipsak \- a utility for various tests on sip servers and user agents .SH SYNOPSIS -.B sipsak [-dFGhiILnNMRSTUVvwz] [-a +.B sipsak [-dFGhiILnNMRSTUVvwzX] [-a .I PASSWORD .B ] [-b .I NUMBER @@ -108,6 +108,10 @@ parser. Stress mode for SIP servers. .B sipsak keeps sending requests to a SIP server at high pace. +. IP "- monitor mode (-X)" +Monitoring mode for SIP servers. +.B sipsak +keeps sending messages as long as the server keeps responding. .PP If libruli (http://www.nongnu.org/ruli/) or c-ares @@ -525,6 +529,12 @@ success was above the given number. .IP "-x, --expires NUMBER" Sets the value of the Expires header to the given number. +.IP "-X, --monitor-mode" +Activates the monitoring mode. +.B sipsak +keeps sending messages as long as the target keeps responding. Useful to find +out unresponsive destination. + .IP "-z, --remove-bindings" Activates the randomly removing of old bindings in the usrloc mode. How many per cent of the bindings will be removed, is determined by the @@ -537,6 +547,7 @@ length of the gaps between two retransmissions of a request on a unreliable transport. Default value is 500 if not changed via the configure option --enable-timeout. + .SH RETURN VALUES The return value 0 means that a 200 was received. 1 means something else then 1xx or 2xx was received. @@ -567,6 +578,9 @@ inserts forwarding from work to home for one hour. .IP "sipsak -f bye.sip -g '!FTAG!345.af23!TTAG!1208.12!' -s sip:myproxy" reads the file bye.sip, replaces $FTAG$ with 345.af23 and $TTAG$ with 1208.12 and finally send this message to myproxy +.IP "sipsak -s sip:foo@bar.com -X -Z 250 -D 5" +pings in a loop up to five times a SIP URI in 250 ms interval until an +erroc occurs. .SH LIMITATIONS / NOT IMPLEMENTED Many servers may decide NOT to include SIP "Warning" header fields. diff --git a/sipsak.c b/sipsak.c index b412a4d..4aaec21 100644 --- a/sipsak.c +++ b/sipsak.c @@ -78,6 +78,7 @@ void print_version() { printf( " shoot : sipsak [-f FILE] [-L] -s SIPURI\n" " trace : sipsak -T -s SIPURI\n" + " monitor: sipsak -X [-Z timeout ] -s SIPURI\n\n" " usrloc : sipsak -U [-I|M] [-b NUMBER] [-e NUMBER] [-x NUMBER] [-z NUMBER] -s SIPURI\n" " usrloc : sipsak -I|M [-b NUMBER] [-e NUMBER] -s SIPURI\n" " usrloc : sipsak -U [-C SIPURI] [-x NUMBER] -s SIPURI\n" @@ -104,6 +105,7 @@ void print_long_help() { " --sip-uri=SIPURI the destination server uri in form\n" " sip:[user@]servername[:port]\n" " --traceroute activates the traceroute mode\n" + " --monitor activates the monitor mode\n" ); printf(" --usrloc-mode activates the usrloc mode\n" " --invite-mode simulates a successful calls with itself\n" @@ -184,6 +186,7 @@ void print_help() { " -s SIPURI the destination server uri in form\n" " sip:[user@]servername[:port]\n" " -T activates the traceroute mode\n" + " -X activates the monitor mode\n" " -U activates the usrloc mode\n" " -I simulates a successful calls with itself\n" " -M sends messages to itself\n" @@ -266,6 +269,7 @@ int main(int argc, char *argv[]) {"filename", 1, 0, 'f'}, {"sip-uri", 1, 0, 's'}, {"traceroute-mode", 0, 0, 'T'}, + {"monitor-mode", 0, 0, 'X'}, {"usrloc-mode", 0, 0, 'U'}, {"invite-mode", 0, 0, 'I'}, {"message-mode", 0, 0, 'M'}, @@ -322,6 +326,7 @@ int main(int argc, char *argv[]) warning_ext=rand_rem=nonce_count=replace_b=invite=message=sysl = 0; sleep_ms=empty_contact=nagios_warn=timing=outbound_proxy=symmetric = 0; namebeg=nameend=maxforw= -1; + monitor_mode=0; #ifdef OLDSTYLE_FQDN numeric = 0; #else @@ -361,9 +366,9 @@ int main(int argc, char *argv[]) /* lots of command line switches to handle*/ #ifdef HAVE_GETOPT_LONG - while ((c=getopt_long(argc, argv, "a:A:b:B:c:C:dD:e:E:f:Fg:GhH:iIj:J:k:K:l:Lm:MnNo:O:p:P:q:r:Rs:St:Tu:UvVwW:x:z:Z:", l_opts, &option_index)) != EOF){ + while ((c=getopt_long(argc, argv, OPTIONS_STRING, l_opts, &option_index)) != EOF){ #else - while ((c=getopt(argc, argv, "a:A:b:B:c:C:dD:e:E:f:Fg:GhH:iIj:J:k:K:l:Lm:MnNo:O:p:P:q:r:Rs:St:Tu:UvVwW:x:z:Z:")) != EOF){ + while ((c=getopt(argc, argv, OPTIONS_STRING)) != EOF){ #endif switch(c){ #ifdef HAVE_GETOPT_LONG @@ -754,6 +759,9 @@ int main(int argc, char *argv[]) case 'T': trace=1; break; + case 'X': + monitor_mode=1; + break; case 'U': usrloc=1; break; @@ -873,6 +881,21 @@ int main(int argc, char *argv[]) insert_header(buff, headers, 1); } /* lots of conditions to check */ + if (monitor_mode) { + if (usrloc || flood || randtrash ) { + fprintf(stderr, "error: monitor can't be combined with usrloc, random or " + "flood\n"); + exit_code(2, __PRETTY_FUNCTION__, "monitor mode can't be combined with other modes"); + } + if (!uri_b) { + fprintf(stderr, "error: for monitor mode a SIPURI is really needed\n"); + exit_code(2, __PRETTY_FUNCTION__, "missing URI for trace mode"); + } + if (!via_ins){ + fprintf(stderr, "warning: Via-Line is needed for tracing. Ignoring -i\n"); + via_ins=1; + } + } if (trace) { if (usrloc || flood || randtrash) { fprintf(stderr, "error: trace can't be combined with usrloc, random or " diff --git a/sipsak.h b/sipsak.h index 3a1319c..f8d1b4f 100644 --- a/sipsak.h +++ b/sipsak.h @@ -293,6 +293,9 @@ #define USRLOC_EXP_DEF 15 #define FLOOD_METH "OPTIONS" + +#define OPTIONS_STRING "a:A:b:B:c:C:dD:e:E:f:Fg:GhH:iIj:J:k:K:l:Lm:MnNo:O:p:P:q:r:Rs:St:Tu:XUvVwW:x:z:Z:" + #define SIPSAK_HASHLEN_MD5 16 #define SIPSAK_HASHHEXLEN_MD5 2 * SIPSAK_HASHLEN_MD5 #ifdef HAVE_OPENSSL_SHA1 @@ -328,6 +331,7 @@ int sleep_ms, processes, cseq_counter; int verbose, nameend, namebeg, expires_t, flood, warning_ext, invite, message; int maxforw, lport, rport, randtrash, trashchar, numeric, symmetric; int file_b, uri_b, trace, via_ins, usrloc, redirects, rand_rem, replace_b; +int monitor_mode; int empty_contact, nagios_warn, fix_crlf, timing, outbound_proxy; int timer_t1, timer_t2, timer_final, sysl; char *username, *domainname, *password, *replace_str, *hostname, *contact_uri; diff --git a/transport.c b/transport.c index 79d9210..ba7aae2 100644 --- a/transport.c +++ b/transport.c @@ -765,6 +765,19 @@ void check_socket_error(int socket, int size) { } } + +/* set retry time according to RFC3261 */ +void reset_timer(struct sipsak_delay *sd) +{ + if (monitor_mode) { + /* don't change timer -- keep runing at constant pace */ + } else if ((inv_trans) || (sd->retryAfter *2 < timer_t2)) { + sd->retryAfter = sd->retryAfter * 2; + } else { + sd->retryAfter = timer_t2; + } +} + int check_for_message(char *recv, int size, struct sipsak_con_data *cd, struct sipsak_sr_time *srt, struct sipsak_counter *count, struct sipsak_delay *sd) { @@ -800,8 +813,13 @@ int check_for_message(char *recv, int size, struct sipsak_con_data *cd, if (count->send_counter==1) { memcpy(&(srt->firstsendt), &(srt->sendtime), sizeof(struct timeval)); } - if (sd->retryAfter == timer_t1) { - memcpy(&(srt->starttime), &(srt->sendtime), sizeof(struct timeval)); + if (monitor_mode) { + if (count->send_counter==1) + memcpy(&(srt->starttime), &(srt->sendtime), sizeof(struct timeval)); + } else { + if (sd->retryAfter == timer_t1) { + memcpy(&(srt->starttime), &(srt->sendtime), sizeof(struct timeval)); + } } if (ret == 0) { @@ -847,29 +865,19 @@ int check_for_message(char *recv, int size, struct sipsak_con_data *cd, log_message(req); exit_code(3, __PRETTY_FUNCTION__, "timeout (no final response)"); } - else { - timing--; - count->run++; - sd->all_delay += senddiff; - sd->big_delay = senddiff; - new_transaction(req, rep); - sd->retryAfter = timer_t1; - if (timing == 0) { - printf("%.3f/%.3f/%.3f ms\n", sd->small_delay, sd->all_delay / count->run, sd->big_delay); - log_message(req); - exit_code(3, __PRETTY_FUNCTION__, "timeout (no final response)"); - } - } - } - else { - /* set retry time according to RFC3261 */ - if ((inv_trans) || (sd->retryAfter *2 < timer_t2)) { - sd->retryAfter = sd->retryAfter * 2; - } - else { - sd->retryAfter = timer_t2; + timing--; + count->run++; + sd->all_delay += senddiff; + sd->big_delay = senddiff; + new_transaction(req, rep); + sd->retryAfter = timer_t1; + if (timing == 0) { + printf("%.3f/%.3f/%.3f ms\n", sd->small_delay, sd->all_delay / count->run, sd->big_delay); + log_message(req); + exit_code(3, __PRETTY_FUNCTION__, "timeout (no final response)"); } } + else reset_timer(sd); (count->retrans_s_c)++; if (srt->delaytime.tv_sec == 0) memcpy(&(srt->delaytime), &(srt->sendtime), sizeof(struct timeval));