diff --git a/configure.ac b/configure.ac index d33cc7d..f9727ab 100644 --- a/configure.ac +++ b/configure.ac @@ -34,7 +34,7 @@ AC_SUBST([LIBS]) # Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS([ctype.h errno.h arpa/inet.h netdb.h netinet/in.h netinet/in_systm.h limits.h sys/poll.h regex.h signal.h stdarg.h stdlib.h stdio.h string.h sys/param.h sys/socket.h sys/time.h unistd.h sys/utsname.h],,[AC_MSG_ERROR([missing required header (see above)])],) +AC_CHECK_HEADERS([ctype.h errno.h arpa/inet.h netdb.h netinet/in.h netinet/in_systm.h limits.h sys/poll.h regex.h signal.h stdarg.h stdlib.h stdio.h string.h sys/param.h sys/socket.h sys/time.h unistd.h sys/utsname.h iconv.h],,[AC_MSG_ERROR([missing required header (see above)])],) AC_CHECK_HEADERS([getopt.h syslog.h]) AC_HEADER_SYS_WAIT AC_HEADER_TIME @@ -49,7 +49,7 @@ AC_TYPE_SIZE_T # Checks for library functions. AC_FUNC_MALLOC AC_FUNC_SELECT_ARGTYPES -AC_CHECK_FUNCS([getchar gethostbyname gethostname getopt getpid gettimeofday memset ntohs regcomp select socket strchr strcmp strstr strtol uname],,[AC_MSG_ERROR([missing required function (see above)])]) +AC_CHECK_FUNCS([getchar gethostbyname gethostname getopt getpid gettimeofday memset ntohs regcomp select socket strchr strcmp strstr strtol uname iconv],,[AC_MSG_ERROR([missing required function (see above)])]) AC_CHECK_FUNCS([calloc getdomainname getopt_long inet_ntop strncasecmp strcasestr syslog]) PKG_PROG_PKG_CONFIG diff --git a/src/header_f.c b/src/header_f.c index 24e9fc7..b4c7285 100644 --- a/src/header_f.c +++ b/src/header_f.c @@ -25,8 +25,10 @@ /* add the given header(s) below the request line */ void insert_header(char *mes, char *header, int first) { - char *ins, *backup; - + char *ins, *backup, *hp, *dhp, *ehp; + int max_headers = 100, hcount = 0, c, i; + struct hline **hlist = malloc(sizeof(struct hline *) * max_headers); + if (first) { ins = strchr(mes, '\n'); if (ins == NULL) { @@ -43,6 +45,69 @@ void insert_header(char *mes, char *header, int first) { strncpy(ins, header, strlen(header)); strncpy(ins + strlen(header), backup, strlen(backup)+1); free(backup); + + // deduplicate headers + hp = strstr(mes, "\r\n"); + if (hp) + hp += 2; + + while (hp && *hp) { + if (hcount == max_headers) { + max_headers *= 2; + hlist = realloc(hlist, sizeof(struct hline *) * max_headers); + } + c = i = hcount++; + + hlist[i] = malloc(sizeof(struct hline)); + hlist[i]->begin = hp; + + ehp = strstr(hp, "\r\n"); + if (ehp) { + if (ehp == hp) { + hp = NULL; + continue; + } + *ehp = 0; + hlist[i]->len = ehp - hp + 2; + } + else + hlist[i]->len = hp - mes + strlen(mes); + + dhp = strchr(hp, ':'); + if (dhp) + hlist[i]->namelen = dhp - hp; + else + hlist[i]->namelen = 0; + + if (ehp) { + *ehp = 0x0D; + hp = ehp + 2; + } + else + hp = NULL; + + i = 0; + while (i < c) { + if ((hlist[i]->namelen == hlist[c]->namelen) && !strncmp(hlist[i]->begin, hlist[c]->begin, hlist[c]->namelen)) { + if (hp) { + memmove(hlist[c]->begin, hp, hp - mes + strlen(mes) + 1); + hp = hlist[c]->begin; + } + else + strcpy(hlist[c]->begin, "\r\n\0"); + + --hcount; + free(hlist[c]); + i = c; + } + else + ++i; + } + } + + for (i = 0; i < hcount; i++) + free(hlist[i]); + free(hlist); } /* add a Via Header Field in the message. */ diff --git a/src/header_f.h b/src/header_f.h index 382addd..8d516b1 100644 --- a/src/header_f.h +++ b/src/header_f.h @@ -20,6 +20,12 @@ #include "shoot.h" +struct hline +{ + char *begin; + int namelen, len; +}; + void insert_header(char *mes, char *header, int first); void add_via(char *mes); diff --git a/src/request.c b/src/request.c index 4143d2e..8d8ef49 100644 --- a/src/request.c +++ b/src/request.c @@ -198,12 +198,11 @@ void create_msg(int action, char *req_buff, char *repl_buff, char *username, int FROM_STR, fqdn, lport, c); } req_buff += strlen(req_buff); - if (mes_body) { - len = strlen(mes_body); - } - else { + if (mes_body) + len = mes_body->len; + else len = SIPSAK_MES_STR_LEN + strlen(username); - } + sprintf(req_buff, "%s%u\r\n", CON_LEN_STR, len); req_buff += strlen(req_buff); if (con_dis) { @@ -212,12 +211,7 @@ void create_msg(int action, char *req_buff, char *repl_buff, char *username, int } sprintf(req_buff, "\r\n"); req_buff += 2; - if (mes_body) { - sprintf(req_buff, - "%s", - mes_body); - } - else { + if (!mes_body) { sprintf(req_buff, "%s%s", SIPSAK_MES_STR, username); req_buff += strlen(req_buff) - 1; *(req_buff) = '.'; diff --git a/src/shoot.c b/src/shoot.c index 801ad4e..4037b01 100644 --- a/src/shoot.c +++ b/src/shoot.c @@ -1037,8 +1037,11 @@ void shoot(char *buf, int buff_size) /* prevents a strange error */ regcomp(&(regexps.authexp), "^SIP/[0-9]\\.[0-9] 40[17] ", REG_EXTENDED|REG_NOSUB|REG_ICASE); insert_auth(request, received); - if (verbose > 2) + if (verbose > 2) { printf("\nreceived:\n%s\n", received); + if (mes_body) + printf("\n"); + } new_transaction(request, response); continue; } /* if auth...*/ diff --git a/src/sipsak.c b/src/sipsak.c index 3b388c6..73aed93 100644 --- a/src/sipsak.c +++ b/src/sipsak.c @@ -55,6 +55,7 @@ #ifdef HAVE_SYSLOG_H # include #endif +#include #include "helper.h" #include "header_f.h" @@ -424,14 +425,24 @@ int main(int argc, char *argv[]) namebeg=str_to_int(0, optarg); break; case 'B': - mes_body=str_alloc(strlen(optarg) + 1); - strncpy(mes_body, optarg, strlen(optarg)); + { + iconv_t cd; + char *inbuf = optarg, *outbuf; + size_t inbytes = strlen(optarg), outbytes = inbytes * 2; + mes_body = malloc(sizeof(struct bytearray)); + mes_body->data = outbuf = str_alloc(outbytes); + mes_body->len = outbytes; + cd = iconv_open("UCS-2BE", "UTF-8"); // FIX: source charset should be picked from locale + iconv(cd, &inbuf, &inbytes, &outbuf, &outbytes); + iconv_close(cd); + mes_body->len -= outbytes; // some UTF-8 characters 1-byte long, some 2-byte long. All UCS-2 chars 2-byte long break; + } case 'c': backup=str_alloc(strlen(optarg)+1); strncpy(backup, optarg, strlen(optarg)); parse_uri(backup, &scheme, &user, &host, &port); - if (scheme == NULL) { + if (scheme == NULL) { fprintf(stderr, "error: missing scheme in From URI\n"); exit_code(2, __PRETTY_FUNCTION__, "missing scheme in From URI"); } @@ -464,9 +475,9 @@ int main(int argc, char *argv[]) strncpy(backup, optarg, strlen(optarg)); parse_uri(backup, &scheme, &user, &host, &port); if (scheme == NULL) { - fprintf(stderr, "error: REGISTER Contact uri doesn't not contain " - "sip:, sips:, *, or is not empty\n"); - exit_code(2, __PRETTY_FUNCTION__, "unsupported Contact for registration"); + fprintf(stderr, "error: REGISTER Contact uri doesn't not contain " + "sip:, sips:, *, or is not empty\n"); + exit_code(2, __PRETTY_FUNCTION__, "unsupported Contact for registration"); } /*else if (user == NULL) { fprintf(stderr, "error: missing username in Contact uri\n"); @@ -1056,7 +1067,7 @@ int main(int argc, char *argv[]) srand(time(0) ^ (getpid() + (getpid() << 15))); if (processes > 1) { - if (signal(SIGCHLD , sigchld_handler) == SIG_ERR ) { + if (signal(SIGCHLD , sigchld_handler) == SIG_ERR ) { fprintf(stderr, "error: Could not install SIGCHLD handler\n"); exit_code(2, __PRETTY_FUNCTION__, "failed to install SIGCHLD handler"); } @@ -1069,7 +1080,7 @@ int main(int argc, char *argv[]) } if (pid == 0){ - /* child */ + /* child */ upp = (nameend - namebeg + 1) / processes; namebeg = namebeg + upp * i; nameend = namebeg + upp; diff --git a/src/sipsak.h b/src/sipsak.h index 2f320a6..f840f0a 100644 --- a/src/sipsak.h +++ b/src/sipsak.h @@ -321,6 +321,11 @@ SSL* ssl; # endif #endif +struct bytearray { + char *data; + size_t len; +}; + /* lots of global variables. ugly but makes life easier. */ unsigned long address; unsigned int nonce_count, transport; @@ -331,10 +336,11 @@ int file_b, uri_b, trace, via_ins, usrloc, redirects, rand_rem, replace_b; 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; -char *mes_body, *con_dis, *auth_username, *from_uri, *headers, *authhash, *local_ip; +char *con_dis, *auth_username, *from_uri, *headers, *authhash, *local_ip; char fqdn[FQDN_SIZE]; char target_dot[INET_ADDRSTRLEN], source_dot[INET_ADDRSTRLEN]; char *request, *response, *received, *transport_str; regex_t* regex; +struct bytearray *mes_body; #endif diff --git a/src/transport.c b/src/transport.c index 3d4c4b0..7f287f7 100644 --- a/src/transport.c +++ b/src/transport.c @@ -679,23 +679,36 @@ void create_sockets(struct sipsak_con_data *cd) { void send_message(char* mes, struct sipsak_con_data *cd, struct sipsak_counter *sc, struct sipsak_sr_time *srt) { struct timezone tz; - int ret = -1; - + int ret = -1, len = strlen(mes), flen = len; + char *tmsg; + if (cd->dontsend == 0) { - if (verbose > 2) { - printf("\nrequest:\n%s", mes); + if (mes_body) + flen += mes_body->len; + + tmsg = str_alloc(flen); + memcpy(tmsg, mes, len); + + if (verbose > 2) + printf("\nrequest [%d bytes]:\n%s", flen, mes); + + if (mes_body) { + if (verbose > 2) + printf("\n"); + memcpy(tmsg + len, mes_body->data, mes_body->len); } + /* lets fire the request to the server and store when we did */ if (cd->csock == -1) { dbg("\nusing un-connected socket for sending\n"); - ret = sendto(cd->usock, mes, strlen(mes), 0, (struct sockaddr *) &(cd->adr), sizeof(struct sockaddr)); + ret = sendto(cd->usock, tmsg, flen, 0, (struct sockaddr *) &(cd->adr), sizeof(struct sockaddr)); } else { dbg("\nusing connected socket for sending\n"); #ifdef WITH_TLS_TRANSP if (transport == SIP_TLS_TRANSPORT) { # ifdef USE_GNUTLS - ret = gnutls_record_send(tls_session, mes, strlen(mes)); + ret = gnutls_record_send(tls_session, tmsg, flen); # else /* USE_GNUTLS */ # ifdef USE_OPENSSL # endif /* USE_OPENSSL */ @@ -703,11 +716,13 @@ void send_message(char* mes, struct sipsak_con_data *cd, } else { #endif /* TLS_TRANSP */ - ret = send(cd->csock, mes, strlen(mes), 0); + ret = send(cd->csock, tmsg, flen, 0); #ifdef WITH_TLS_TRANSP } #endif /* TLS_TRANSP */ } + free(tmsg); + (void)gettimeofday(&(srt->sendtime), &tz); if (ret==-1) { if (verbose) @@ -717,8 +732,8 @@ void send_message(char* mes, struct sipsak_con_data *cd, } #ifdef HAVE_INET_NTOP if (verbose > 2) { - printf("\nsend to: %s:%s:%i\n", transport_str, target_dot, rport); - } + printf("\nsend to: %s:%s:%i [%d bytes]\n", transport_str, target_dot, rport, ret); + } #endif sc->send_counter++; } @@ -936,6 +951,7 @@ int recv_message(char *buf, int size, int inv_trans, int ret = 0; int sock = 0; double tmp_delay; + char *ehp; #ifdef HAVE_INET_NTOP struct sockaddr_in peer_adr; socklen_t psize = sizeof(peer_adr); @@ -1042,6 +1058,11 @@ int recv_message(char *buf, int size, int inv_trans, #endif // RAW_SUPPORT if (ret > 0) { *(buf+ ret) = '\0'; + + // Cut off binary message body + if (mes_body && (ehp = strstr(buf, "\r\n\r\n"))) + ehp[4] = 0; + if (transport != SIP_UDP_TRANSPORT) { if (verbose > 0) printf("\nchecking message for completeness...\n"); @@ -1083,8 +1104,8 @@ int recv_message(char *buf, int size, int inv_trans, } #ifdef HAVE_INET_NTOP if ((verbose > 2) && (getpeername(sock, (struct sockaddr *)&peer_adr, &psize) == 0) && (inet_ntop(peer_adr.sin_family, &peer_adr.sin_addr, &source_dot[0], INET_ADDRSTRLEN) != NULL)) { - printf("\nreceived from: %s:%s:%i\n", transport_str, - source_dot, ntohs(peer_adr.sin_port)); + printf("\nreceived from: %s:%s:%i [%d bytes]\n", transport_str, + source_dot, ntohs(peer_adr.sin_port), ret); } else if (verbose > 1 && trace == 0 && usrloc == 0) printf(":\n");