# HG changeset patch # User Mychaela Falconia # Date 1709614999 28800 # Node ID 26383ed8b79fa83b5c576890db579c9394ff0479 # Parent 38c4d09882f6df63b61e478c27f7c41cba5b77c6 test-fsk: starting as a copy of test-voice diff -r 38c4d09882f6 -r 26383ed8b79f .hgignore --- a/.hgignore Sun Mar 03 23:25:15 2024 -0800 +++ b/.hgignore Mon Mar 04 21:03:19 2024 -0800 @@ -2,4 +2,5 @@ \.[oa]$ +^test-fsk/sipout-test-fsk$ ^test-voice/sipout-test-voice$ diff -r 38c4d09882f6 -r 26383ed8b79f Makefile --- a/Makefile Sun Mar 03 23:25:15 2024 -0800 +++ b/Makefile Mon Mar 04 21:03:19 2024 -0800 @@ -1,12 +1,13 @@ CC= gcc CFLAGS= -O2 -PROGDIR=test-voice +PROGDIR=test-fsk test-voice LIBDIR= librtpalloc libsip libutil SUBDIR= ${PROGDIR} ${LIBDIR} all: ${SUBDIR} +test-fsk: librtpalloc libsip libutil test-voice: librtpalloc libsip libutil ${SUBDIR}: FRC diff -r 38c4d09882f6 -r 26383ed8b79f test-fsk/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-fsk/Makefile Mon Mar 04 21:03:19 2024 -0800 @@ -0,0 +1,18 @@ +CC= gcc +CFLAGS= -O2 +PROG= sipout-test-fsk +OBJS= bye_in.o disc_cmd.o main.o readconf.o reinvite.o rtp_rx.o rtp_tx.o \ + sdp_in.o sip_log.o sip_udp.o uac.o uas.o user_cmd.o +LIBS= ../libsip/libsip.a ../librtpalloc/librtpalloc.a ../libutil/libutil.a +INSTBIN=/opt/themwi/bin + +all: ${PROG} + +${PROG}: ${OBJS} ${LIBS} + ${CC} ${CFLAGS} -o $@ ${OBJS} ${LIBS} + +install: + install -c -m 755 ${PROG} ${INSTBIN} + +clean: + rm -f *.o ${PROG} errs diff -r 38c4d09882f6 -r 26383ed8b79f test-fsk/bye_in.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-fsk/bye_in.c Mon Mar 04 21:03:19 2024 -0800 @@ -0,0 +1,70 @@ +/* + * Here we handle incoming BYE requests in the UAS role. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../libsip/parse.h" +#include "../libsip/uas_basic.h" +#include "../libsip/out_msg.h" + +extern char call_id[]; +extern int rtp_out_enable; + +static void +bye_correct_call(req, ess, sin) + struct sip_pkt_rx *req; + struct uas_parse_hdrs *ess; + struct sockaddr_in *sin; +{ + struct sip_msg_out resp; + int rc; + + printf("Received BYE for our call, responding with 200\n"); + rtp_out_enable = 0; + start_response_out_msg(&resp, "200 OK"); + rc = add_resp_basic_headers(&resp, ess, req->req_method); + if (rc < 0) { + fprintf(stderr, "sending 200 response: msg length exceeded\n"); + return; + } + out_msg_finish(&resp); + sip_tx_packet(&resp, sin); +} + +static void +bye_unknown_call(req, ess, sin) + struct sip_pkt_rx *req; + struct uas_parse_hdrs *ess; + struct sockaddr_in *sin; +{ + struct sip_msg_out resp; + int rc; + + printf("Received BYE for unknown call, responding with 481\n"); + start_response_out_msg(&resp, "481 Call-ID not found"); + rc = add_resp_basic_headers(&resp, ess, req->req_method); + if (rc < 0) { + fprintf(stderr, "sending 481 response: msg length exceeded\n"); + return; + } + out_msg_finish(&resp); + sip_tx_packet(&resp, sin); +} + +void +handle_bye_req(req, ess, sin) + struct sip_pkt_rx *req; + struct uas_parse_hdrs *ess; + struct sockaddr_in *sin; +{ + if (!strcmp(ess->call_id, call_id)) + bye_correct_call(req, ess, sin); + else + bye_unknown_call(req, ess, sin); +} diff -r 38c4d09882f6 -r 26383ed8b79f test-fsk/disc_cmd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-fsk/disc_cmd.c Mon Mar 04 21:03:19 2024 -0800 @@ -0,0 +1,55 @@ +/* + * In this module we implement user-driven sending of CANCEL and BYE + * disconnection requests. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../libsip/out_msg.h" + +extern struct sockaddr_in sip_dest_sin; +extern char to_uri[]; +extern int rtp_out_enable; + +send_cancel_req() +{ + struct sip_msg_out msg; + int rc; + + rtp_out_enable = 0; + rc = start_request_out_msg(&msg, "CANCEL", to_uri); + if (rc < 0) { +msg_size_err: fprintf(stderr, "composing CANCEL message: size error\n"); + return(-1); + } + rc = add_req_boilerplate(&msg, "1 CANCEL", 0); + if (rc < 0) + goto msg_size_err; + out_msg_finish(&msg); + sip_tx_packet(&msg, &sip_dest_sin); + return(0); +} + +send_bye_req() +{ + struct sip_msg_out msg; + int rc; + + rtp_out_enable = 0; + rc = start_request_out_msg(&msg, "BYE", to_uri); + if (rc < 0) { +msg_size_err: fprintf(stderr, "composing BYE message: size error\n"); + return(-1); + } + rc = add_req_boilerplate(&msg, "2 BYE", 1); + if (rc < 0) + goto msg_size_err; + out_msg_finish(&msg); + sip_tx_packet(&msg, &sip_dest_sin); + return(0); +} diff -r 38c4d09882f6 -r 26383ed8b79f test-fsk/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-fsk/main.c Mon Mar 04 21:03:19 2024 -0800 @@ -0,0 +1,200 @@ +/* + * This is the main module for sip-manual-out test program. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../libsip/out_msg.h" +#include "../libsip/sdp.h" + +extern int sip_socket; +extern struct in_addr sip_bind_ip, sip_dest_ip; +extern unsigned sip_bind_port, sip_dest_port; +extern char sip_dest_domain[]; +extern struct sockaddr_in rtp_local_addr; +extern int rtp_udp_fd, rtcp_udp_fd; +extern int rtp_out_enable; + +struct sockaddr_in sip_dest_sin; +char from_uri[128], to_uri[128], call_id[128]; +struct timeval cur_event_time; +unsigned max_forwards = 70; +int declare_100rel_supp; +int pcma_codec_pref, pcma_codec_force; + +send_invite_req() +{ + struct sip_msg_out msg; + struct sdp_gen sdp; + int rc; + + rc = start_request_out_msg(&msg, "INVITE", to_uri); + if (rc < 0) { +msg_size_err: fprintf(stderr, "composing INVITE req: msg size error\n"); + exit(1); + } + rc = add_req_boilerplate(&msg, "1 INVITE", 0); + if (rc < 0) + goto msg_size_err; + rc = add_contact_header(&msg); + if (rc < 0) + goto msg_size_err; + if (declare_100rel_supp) { + rc = out_msg_add_header(&msg, "Supported", "100rel"); + if (rc < 0) + goto msg_size_err; + } + rc = out_msg_add_header(&msg, "Content-Type", "application/sdp"); + if (rc < 0) + goto msg_size_err; + bzero(&sdp, sizeof sdp); + sdp.conn_ip = rtp_local_addr.sin_addr; + sdp.conn_port = ntohs(rtp_local_addr.sin_port); + if (pcma_codec_force) + sdp.codec_mask = SDP_CODEC_MASK_PCMA; + else { + sdp.codec_mask = SDP_CODEC_MASK_BOTH; + if (pcma_codec_pref) + sdp.codec_mask |= SDP_CODEC_MASK_PCMA_PREF; + } + sdp.session_id = sdp.conn_port << 16; + sdp.owner_ip = sip_bind_ip; + rc = out_msg_finish_sdp(&msg, &sdp); + if (rc < 0) + goto msg_size_err; + sip_tx_packet(&msg, &sip_dest_sin); +} + +static void +preliminary_proc(argc, argv) + char **argv; +{ + extern int optind; + extern char *optarg; + char *logfile; + int opt, rc; + + logfile = 0; + while ((opt = getopt(argc, argv, "aAl:m:r")) != EOF) { + switch (opt) { + case 'a': + pcma_codec_pref = 1; + continue; + case 'A': + pcma_codec_force = 1; + continue; + case 'l': + logfile = optarg; + continue; + case 'm': + max_forwards = atoi(optarg); + continue; + case 'r': + declare_100rel_supp = 1; + continue; + default: + usage: + fprintf(stderr, + "usage: %s [options] dest-conf from-num to-num\n", + argv[0]); + exit(1); + } + } + if (argc != optind + 3) + goto usage; + read_config_file(argv[optind]); + open_sip_udp_socket(); + obtain_rtp_endp(); + sip_dest_sin.sin_family = AF_INET; + sip_dest_sin.sin_addr = sip_dest_ip; + sip_dest_sin.sin_port = htons(sip_dest_port); + sprintf(from_uri, ";tag=out%u", argv[optind+1], + inet_ntoa(sip_bind_ip), ntohs(rtp_local_addr.sin_port)); + sprintf(to_uri, "sip:%s@%s", argv[optind+2], sip_dest_domain); + if (logfile) { + rc = open_sip_log_file(logfile); + if (rc < 0) + exit(1); /* error msg already printed */ + } +} + +main(argc, argv) + char **argv; +{ + fd_set fds; + struct timeval next_rtp_out, timeout; + int rc, max_fd, rtp_out_running; + + preliminary_proc(argc, argv); + gettimeofday(&cur_event_time, 0); + sprintf(call_id, "%08u_%u@%s", + (unsigned)(cur_event_time.tv_sec % 100000000), + ntohs(rtp_local_addr.sin_port), inet_ntoa(sip_bind_ip)); + send_invite_req(); + /* main select loop */ + max_fd = sip_socket; + if (rtp_udp_fd > max_fd) + max_fd = rtp_udp_fd; + if (rtcp_udp_fd > max_fd) + max_fd = rtcp_udp_fd; + rtp_out_running = 0; + for (;;) { + FD_ZERO(&fds); + FD_SET(0, &fds); + FD_SET(sip_socket, &fds); + FD_SET(rtp_udp_fd, &fds); + FD_SET(rtcp_udp_fd, &fds); + if (rtp_out_enable) { + if (!rtp_out_running) { + printf("Starting RTP output\n"); + bcopy(&cur_event_time, &next_rtp_out, + sizeof(struct timeval)); + rtp_out_running = 1; + } + if (timercmp(&cur_event_time, &next_rtp_out, <)) + timersub(&next_rtp_out, &cur_event_time, + &timeout); + else + timerclear(&timeout); + rc = select(max_fd+1, &fds, 0, 0, &timeout); + } else { + if (rtp_out_running) { + printf("Stopping RTP output\n"); + rtp_out_running = 0; + } + rc = select(max_fd+1, &fds, 0, 0, 0); + } + if (rc < 0) { + if (errno == EINTR) + continue; + perror("select"); + exit(1); + } + gettimeofday(&cur_event_time, 0); + if (FD_ISSET(0, &fds)) + select_stdin(); + if (FD_ISSET(sip_socket, &fds)) + sip_socket_select(); + if (FD_ISSET(rtp_udp_fd, &fds)) + rtp_rx_select(); + if (FD_ISSET(rtcp_udp_fd, &fds)) + rtcp_rx_select(); + if (rtp_out_running && (rc == 0)) { + generate_rtp_packet(); + next_rtp_out.tv_usec += 20000; + if (next_rtp_out.tv_usec >= 1000000) { + next_rtp_out.tv_sec++; + next_rtp_out.tv_usec -= 1000000; + } + } + } +} diff -r 38c4d09882f6 -r 26383ed8b79f test-fsk/readconf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-fsk/readconf.c Mon Mar 04 21:03:19 2024 -0800 @@ -0,0 +1,159 @@ +/* + * In this module we implement the reading of destination configuration + * for sip-manual-out. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct in_addr sip_bind_ip, sip_dest_ip; +unsigned sip_bind_port, sip_dest_port = 5060; +char sip_dest_domain[64]; + +struct parse_state { + char *filename; + int lineno; + int set_mask; +}; + +static void +handle_ip(st, kw, var, arg) + struct parse_state *st; + char *kw, *arg; + struct in_addr *var; +{ + var->s_addr = inet_addr(arg); + if (var->s_addr == INADDR_NONE) { + fprintf(stderr, + "%s line %d: invalid IP address argument \"%s\"\n", + st->filename, st->lineno, arg); + exit(1); + } +} + +static void +handle_num(st, kw, var, arg) + struct parse_state *st; + char *kw, *arg; + unsigned *var; +{ + char *endp; + + *var = strtoul(arg, &endp, 10); + if (*endp) { + fprintf(stderr, "%s line %d: invalid numeric argument \"%s\"\n", + st->filename, st->lineno, arg); + exit(1); + } +} + +static void +handle_str(st, kw, var, arg) + struct parse_state *st; + char *kw, *arg, *var; +{ + strcpy(var, arg); +} + +static void +process_line(st, line) + struct parse_state *st; + char *line; +{ + char *cp, *np, *arg; + void (*handler)(), *var; + int set_id; + + if (!index(line, '\n')) { + fprintf(stderr, "%s line %d: too long or missing newline\n", + st->filename, st->lineno); + exit(1); + } + for (cp = line; isspace(*cp); cp++) + ; + if (*cp == '\0' || *cp == '#') + return; + for (np = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + if (!strcmp(np, "bind-ip")) { + handler = handle_ip; + var = &sip_bind_ip; + set_id = 1; + } else if (!strcmp(np, "bind-port")) { + handler = handle_num; + var = &sip_bind_port; + set_id = 2; + } else if (!strcmp(np, "dest-ip")) { + handler = handle_ip; + var = &sip_dest_ip; + set_id = 4; + } else if (!strcmp(np, "dest-port")) { + handler = handle_num; + var = &sip_dest_port; + set_id = 0; + } else if (!strcmp(np, "dest-domain")) { + handler = handle_str; + var = sip_dest_domain; + set_id = 8; + } else { + fprintf(stderr, "%s line %d: non-understood keyword \"%s\"\n", + st->filename, st->lineno, np); + exit(1); + } + if (st->set_mask & set_id) { + fprintf(stderr, "%s line %d: duplicate %s setting\n", + st->filename, st->lineno, np); + exit(1); + } + while (isspace(*cp)) + cp++; + if (*cp == '\0' || *cp == '#') { +inv_syntax: fprintf(stderr, + "%s line %d: %s setting requires one argument\n", + st->filename, st->lineno, np); + exit(1); + } + for (arg = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + while (isspace(*cp)) + cp++; + if (*cp != '\0' && *cp != '#') + goto inv_syntax; + handler(st, np, var, arg); + st->set_mask |= set_id; +} + +read_config_file(filename) + char *filename; +{ + FILE *inf; + struct parse_state pst; + char linebuf[256]; + + inf = fopen(filename, "r"); + if (!inf) { + perror(filename); + exit(1); + } + pst.set_mask = 0; + pst.filename = filename; + for (pst.lineno = 1; fgets(linebuf, sizeof linebuf, inf); pst.lineno++) + process_line(&pst, linebuf); + fclose(inf); + if (pst.set_mask != 15) { + fprintf(stderr, "error: %s did not set all required settings\n", + filename); + exit(1); + } +} diff -r 38c4d09882f6 -r 26383ed8b79f test-fsk/reinvite.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-fsk/reinvite.c Mon Mar 04 21:03:19 2024 -0800 @@ -0,0 +1,70 @@ +/* + * Here we handle incoming INVITE requests in the UAS role: even though + * we are strictly outbound, BulkVS servers will send us periodic + * re-INVITEs as keepalives, and we have to play along. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../libsip/parse.h" +#include "../libsip/uas_basic.h" +#include "../libsip/out_msg.h" + +extern char call_id[]; + +static void +invite_correct_call(req, ess, sin) + struct sip_pkt_rx *req; + struct uas_parse_hdrs *ess; + struct sockaddr_in *sin; +{ + struct sip_msg_out resp; + int rc; + + printf("Received re-INVITE for our call, responding with 200\n"); + start_response_out_msg(&resp, "200 OK"); + rc = add_resp_basic_headers(&resp, ess, req->req_method); + if (rc < 0) { + fprintf(stderr, "sending 200 response: msg length exceeded\n"); + return; + } + out_msg_finish(&resp); + sip_tx_packet(&resp, sin); +} + +static void +invite_unknown_call(req, ess, sin) + struct sip_pkt_rx *req; + struct uas_parse_hdrs *ess; + struct sockaddr_in *sin; +{ + struct sip_msg_out resp; + int rc; + + printf("Received INVITE for unknown call, responding with 405\n"); + start_response_out_msg(&resp, "405 This gateway is outbound only"); + rc = add_resp_basic_headers(&resp, ess, req->req_method); + if (rc < 0) { + fprintf(stderr, "sending 405 response: msg length exceeded\n"); + return; + } + out_msg_finish(&resp); + sip_tx_packet(&resp, sin); +} + +void +handle_invite_req(req, ess, sin) + struct sip_pkt_rx *req; + struct uas_parse_hdrs *ess; + struct sockaddr_in *sin; +{ + if (!strcmp(ess->call_id, call_id)) + invite_correct_call(req, ess, sin); + else + invite_unknown_call(req, ess, sin); +} diff -r 38c4d09882f6 -r 26383ed8b79f test-fsk/rtp_rx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-fsk/rtp_rx.c Mon Mar 04 21:03:19 2024 -0800 @@ -0,0 +1,270 @@ +/* + * In this module we implement our RTP handling: obtaining a PSTN-side + * RTP endpoint from themwi-rtp-mgr, then handling read select on RTP + * and RTCP UDP sockets. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/tmgw_const.h" +#include "../include/rtp_defs.h" +#include "../librtpalloc/rtp_alloc_simple.h" + +struct sockaddr_in rtp_local_addr; +int rtp_udp_fd, rtcp_udp_fd; + +static int rtp_start_flag, rtp_bad_flag, rtp_ssrc_chg_flag; +static int rtp_seq_brk_flag, rtp_seq_zero_flag, rtp_seq_neg_flag; +static int rtp_ts_brk_flag; +static uint32_t rtp_ssrc; +static uint32_t rtp_last_ts; +static uint16_t rtp_last_seq; +static int got_some_rtcp; + +static const uint8_t hdr_pattern[20] = {0, 1, 0, 1, 0, 1, 1, 0, 1, 0, + 0, 1, 1, 0, 1, 0, 1, 0, 0, 1}; + +static uint8_t is_hunt_buf[320]; +static int is_state; +static unsigned is_offset, is_bit_count, is_hunt_fill; +static uint32_t is_rx_word; + +static void +reset_is_hunt() +{ + is_state = 0; + is_hunt_fill = 0; +} + +static void +is_rx_hunt(input_pos) + unsigned input_pos; +{ + unsigned offset, n; + + for (offset = 0; offset < 16; offset++) { + for (n = 0; n < 20; n++) + if ((is_hunt_buf[offset + n*16] & 1) != hdr_pattern[n]) + break; + if (n == 20) + break; + } + if (n != 20) + return; + printf("Found IS_Header, last bit offset %u\n", + input_pos * 16 + offset); + is_offset = offset; + is_state = 1; + is_bit_count = 0; + is_rx_word = 0; + is_hunt_fill = 0; +} + +static void +is_process_cmd() +{ + int cont; + + printf("IS_Command: 0x%03X", is_rx_word); + switch (is_rx_word) { + case 0x05D: + printf(" (REQ)\n"); + cont = 1; + break; + case 0x0BA: + printf(" (ACK)\n"); + cont = 1; + break; + case 0x0E7: + printf(" (IPE)\n"); + cont = 1; + break; + case 0x129: + printf(" (FILL)\n"); + cont = 0; + break; + case 0x174: + printf(" (DUP)\n"); + cont = 0; + break; + case 0x193: + printf(" (SYL)\n"); + cont = 0; + break; + default: + printf(" (bad)\n"); + cont = 0; + } + if (cont) { + is_state = 2; + is_bit_count = 0; + is_rx_word = 0; + } else + is_state = 0; +} + +static void +is_process_ext() +{ + printf("IS_Extension: 0x%05X", is_rx_word); + if (is_rx_word & 0x80200) { + printf(" (bad sync)\n"); + is_state = 0; + return; + } + switch (is_rx_word & 3) { + case 0: + printf(" (final)\n"); + is_state = 0; + return; + case 3: + printf(" (continue)\n"); + is_state = 2; + is_bit_count = 0; + is_rx_word = 0; + return; + default: + printf(" (bad EX)\n"); + is_state = 0; + } +} + +static void +is_rx_process(input, input_pos) + uint8_t *input; + unsigned input_pos; +{ + unsigned new_bit; + + memmove(is_hunt_buf, is_hunt_buf + 16, 304); + memcpy(is_hunt_buf + 304, input, 16); + if (!is_state) { + if (is_hunt_fill < 20) + is_hunt_fill++; + if (is_hunt_fill == 20) + is_rx_hunt(input_pos); + return; + } + new_bit = input[is_offset] & 1; + is_rx_word <<= 1; + is_rx_word |= new_bit; + is_bit_count++; + if (is_state == 1 && is_bit_count == 10) + is_process_cmd(); + else if (is_state == 2 && is_bit_count == 20) + is_process_ext(); +} + +void +obtain_rtp_endp() +{ + int rc; + struct rtp_alloc_simple res; + + rc = rtp_alloc_simple(TMGW_EP_TYPE_PSTN_ONLY, &res); + if (rc < 0) + exit(1); /* error msg already printed */ + bcopy(&res.pstn_addr, &rtp_local_addr, sizeof(struct sockaddr_in)); + rtp_udp_fd = res.pstn_rtp_fd; + rtcp_udp_fd = res.pstn_rtcp_fd; + reset_is_hunt(); +} + +void +rtp_rx_select() +{ + struct rtp_packet pkt; + struct sockaddr_in sin_from; + socklen_t addrlen; + int16_t seq_delta; + int32_t ts_delta; + int rc; + unsigned is_chunk; + + addrlen = sizeof(struct sockaddr_in); + rc = recvfrom(rtp_udp_fd, &pkt, sizeof pkt, 0, + (struct sockaddr *) &sin_from, &addrlen); + if (rc < 0) + return; + if (rc != RTP_PACKET_SIZE_PSTN) { +bad_rtp_pkt: if (!rtp_bad_flag) { + printf("Got a bad RTP packet\n"); + rtp_bad_flag = 1; + } + return; + } + if (pkt.v_p_x_cc != 0x80) + goto bad_rtp_pkt; + switch (pkt.m_pt & 0x7F) { + case PSTN_CODEC_PCMU: + case PSTN_CODEC_PCMA: + break; + default: + goto bad_rtp_pkt; + } + if (rtp_start_flag && pkt.ssrc != rtp_ssrc) { + if (!rtp_ssrc_chg_flag) { + printf("Rx RTP stream changed SSRC\n"); + rtp_ssrc_chg_flag = 1; + } + reset_is_hunt(); + } else if (rtp_start_flag) { + seq_delta = ntohs(pkt.seq) - rtp_last_seq; + ts_delta = ntohl(pkt.tstamp) - rtp_last_ts; + if (seq_delta == 0) { + if (!rtp_seq_zero_flag) { + printf("Rx RTP seq zero increment\n"); + rtp_seq_zero_flag = 1; + } + return; + } + if (seq_delta < 0) { + if (!rtp_seq_neg_flag) { + printf("Rx RTP seq negative increment\n"); + rtp_seq_neg_flag = 1; + } + return; + } + if (seq_delta != 1) { + if (!rtp_seq_brk_flag) { + printf("Rx RTP stream seq break\n"); + rtp_seq_brk_flag = 1; + } + reset_is_hunt(); + } else if (ts_delta != 160) { + if (!rtp_ts_brk_flag) { + printf("Rx RTP stream tstamp break\n"); + rtp_ts_brk_flag = 1; + } + reset_is_hunt(); + } + } + rtp_ssrc = pkt.ssrc; + rtp_last_ts = ntohl(pkt.tstamp); + rtp_last_seq = ntohs(pkt.seq); + if (!rtp_start_flag) { + printf("Rx RTP stream begins with seq=%u ts=%u\n", + rtp_last_seq, rtp_last_ts); + rtp_start_flag = 1; + } + for (is_chunk = 0; is_chunk < 10; is_chunk++) + is_rx_process(pkt.payload + is_chunk * 16, is_chunk); +} + +void +rtcp_rx_select() +{ + u_char buf[512]; + + recv(rtcp_udp_fd, buf, sizeof buf, 0); + if (!got_some_rtcp) { + printf("Got some RTCP\n"); + got_some_rtcp = 1; + } +} diff -r 38c4d09882f6 -r 26383ed8b79f test-fsk/rtp_tx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-fsk/rtp_tx.c Mon Mar 04 21:03:19 2024 -0800 @@ -0,0 +1,132 @@ +/* + * In this module we implement outgoing RTP stream generation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/tmgw_const.h" +#include "../include/rtp_defs.h" +#include "../libutil/osmo_bits.h" + +extern struct sockaddr_in rtp_local_addr, rtp_remote_addr; +extern int rtp_udp_fd, rtcp_udp_fd, pcma_selected; +extern struct timeval cur_event_time; + +static uint32_t rtp_ssrc; +static uint32_t rtp_out_ts; +static uint16_t rtp_out_seq; + +static uint8_t pcm_fill_octet; + +static uint16_t tfo_fill_buf[9], tfo_req_buf[7]; +static uint16_t *is_out_ptr; +static unsigned is_out_count; +static int tfo_stop_req; + +void +assign_rtpout_ssrc() +{ + rtp_ssrc = cur_event_time.tv_sec ^ cur_event_time.tv_usec ^ getpid(); +} + +void +init_pcm_fill_octet() +{ + if (pcma_selected) + pcm_fill_octet = 0xD5; + else + pcm_fill_octet = 0xFF; +} + +void +prepare_tfo_fill() +{ + tfo_fill_buf[0] = 0x15A; + tfo_fill_buf[1] = 0x1A9; + tfo_fill_buf[2] = 0x129; + tfo_fill_buf[3] = 0x15A; + tfo_fill_buf[4] = 0x1A9; + tfo_fill_buf[5] = 0x129; + tfo_fill_buf[6] = 0x15A; + tfo_fill_buf[7] = 0x1A9; + tfo_fill_buf[8] = 0x129; +} + +static void +insert_is_msg(payload, word) + uint8_t *payload; + uint16_t word; +{ + ubit_t is_bits[10]; + uint8_t *bytep; + unsigned n; + + uint16_to_bits(word, is_bits, 10); + for (n = 0; n < 10; n++) { + bytep = payload + n * 16; + *bytep &= 0xFE; + *bytep |= is_bits[n]; + } +} + +void +generate_rtp_packet() +{ + struct rtp_packet pkt; + socklen_t addrlen; + + pkt.v_p_x_cc = 0x80; + pkt.m_pt = pcma_selected ? PSTN_CODEC_PCMA : PSTN_CODEC_PCMU; + pkt.seq = htons(rtp_out_seq++); + pkt.tstamp = htonl(rtp_out_ts); + rtp_out_ts += 160; + pkt.ssrc = rtp_ssrc; + memset(pkt.payload, pcm_fill_octet, RTP_MAX_PAYLOAD); + if (is_out_count) { + insert_is_msg(pkt.payload, *is_out_ptr++); + is_out_count--; + if (!is_out_count && !tfo_stop_req) { + is_out_ptr = tfo_req_buf; + is_out_count = 7; + } + } + addrlen = sizeof(struct sockaddr_in); + sendto(rtp_udp_fd, &pkt, RTP_PACKET_SIZE_PSTN, 0, + (struct sockaddr *) &rtp_remote_addr, addrlen); +} + +void +set_pcm_fill_octet(oct) + unsigned oct; +{ + pcm_fill_octet = oct; +} + +void +send_tfo_req(sig, codec) + unsigned sig, codec; +{ + tfo_req_buf[0] = 0x15A; + tfo_req_buf[1] = 0x1A9; + tfo_req_buf[2] = 0x05D; + tfo_req_buf[3] = 0x14E; + tfo_req_buf[4] = 0x14B; + encode_tfo_ext_words(sig, codec, 0, tfo_req_buf + 5); + is_out_ptr = tfo_fill_buf; + is_out_count = 9; + tfo_stop_req = 0; +} + +void +stop_tfo_out() +{ + tfo_stop_req = 1; +} diff -r 38c4d09882f6 -r 26383ed8b79f test-fsk/sdp_in.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-fsk/sdp_in.c Mon Mar 04 21:03:19 2024 -0800 @@ -0,0 +1,86 @@ +/* + * In this module we handle SDP responses to our INVITE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../libsip/parse.h" +#include "../libsip/sdp.h" + +extern char *get_single_header(); +extern char *extract_to_tag(); + +struct sockaddr_in rtp_remote_addr; +int got_sdp_answer, pcma_selected, rtp_out_enable; + +static +check_sdp_present(msg) + struct sip_pkt_rx *msg; +{ + char *hval; + + if (!msg->msg_body_len) + return 0; + hval = get_single_header(msg, "Content-Type", "c", (int *) 0); + if (!hval) + return 0; + if (!strcasecmp(hval, "application/sdp")) + return 1; + else + return 0; +} + +void +extract_resp_sdp(msg) + struct sip_pkt_rx *msg; +{ + struct sdp_parse sdp_parse; + int rc; + + if (!check_sdp_present(msg)) + return; + rc = parse_incoming_sdp(msg->msg_body, msg->msg_body_len, &sdp_parse); + if (rc < 0) { + printf("SDP parse error: %d\n", rc); + return; + } + switch (sdp_parse.codec_mask) { + case SDP_CODEC_MASK_PCMU: + case SDP_CODEC_MASK_BOTH: + pcma_selected = 0; + break; + case SDP_CODEC_MASK_PCMA: + case SDP_CODEC_MASK_BOTH | SDP_CODEC_MASK_PCMA_PREF: + pcma_selected = 1; + break; + default: + printf("SDP error: no supported codec\n"); + return; + } + printf("SDP response: IP %s port %u codec %s\n", + inet_ntoa(sdp_parse.ip_addr), sdp_parse.audio_port, + pcma_selected ? "PCMA" : "PCMU"); + rtp_remote_addr.sin_family = AF_INET; + rtp_remote_addr.sin_addr = sdp_parse.ip_addr; + rtp_remote_addr.sin_port = htons(sdp_parse.audio_port); + got_sdp_answer = 1; +} + +void +invite_200_rtpout() +{ + if (!got_sdp_answer) { + printf("INVITE response has no SDP!\n"); + return; + } + rtp_out_enable = 1; + assign_rtpout_ssrc(); + init_pcm_fill_octet(); + prepare_tfo_fill(); +} diff -r 38c4d09882f6 -r 26383ed8b79f test-fsk/sip_log.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-fsk/sip_log.c Mon Mar 04 21:03:19 2024 -0800 @@ -0,0 +1,68 @@ +/* + * In this module we implement debug logging of SIP Rx & Tx messages. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct timeval cur_event_time; + +static FILE *logfile; + +open_sip_log_file(filename) + char *filename; +{ + logfile = fopen(filename, "a"); + if (!logfile) { + perror(filename); + return(-1); + } + return(0); +} + +static void +log_common(msg, msglen, sin, dir) + char *msg, *dir; + unsigned msglen; + struct sockaddr_in *sin; +{ + unsigned sec, ms; + + sec = cur_event_time.tv_sec % 86400; + ms = cur_event_time.tv_usec / 1000; + fprintf(logfile, "Msg %s %s:%u %u bytes %02u:%02u:%02u.%03u\n", dir, + inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), msglen, + sec / 3600, (sec / 60) % 60, sec % 60, ms); + fwrite(msg, 1, msglen, logfile); + putc('\n', logfile); + fflush(logfile); +} + +void +log_sip_msg_rx(msg, msglen, sin) + char *msg; + unsigned msglen; + struct sockaddr_in *sin; +{ + if (!logfile) + return; + log_common(msg, msglen, sin, "from"); +} + +void +log_sip_msg_tx(msg, msglen, sin) + char *msg; + unsigned msglen; + struct sockaddr_in *sin; +{ + if (!logfile) + return; + log_common(msg, msglen, sin, "to"); +} diff -r 38c4d09882f6 -r 26383ed8b79f test-fsk/sip_udp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-fsk/sip_udp.c Mon Mar 04 21:03:19 2024 -0800 @@ -0,0 +1,83 @@ +/* + * In this module we implement our UDP socket for SIP, + * and the associated lowest-level protocol handling. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../libsip/parse.h" +#include "../libsip/out_msg.h" + +extern struct in_addr sip_bind_ip; +extern unsigned sip_bind_port; + +int sip_socket; + +open_sip_udp_socket() +{ + struct sockaddr_in sin; + int rc; + + sip_socket = socket(AF_INET, SOCK_DGRAM, 0); + if (sip_socket < 0) { + perror("socket(AF_INET, SOCK_DGRAM, 0)"); + exit(1); + } + sin.sin_family = AF_INET; + sin.sin_addr = sip_bind_ip; + sin.sin_port = htons(sip_bind_port); + rc = bind(sip_socket, (struct sockaddr *) &sin, sizeof sin); + if (rc < 0) { + perror("bind of SIP UDP socket"); + exit(1); + } + return(0); +} + +void +sip_socket_select() +{ + struct sip_pkt_rx pkt; + struct sockaddr_in sin; + socklen_t addrlen; + int rc; + + addrlen = sizeof sin; + rc = recvfrom(sip_socket, pkt.pkt_buffer, MAX_SIP_RX_PACKET, 0, + (struct sockaddr *) &sin, &addrlen); + if (rc <= 0) { + perror("recvfrom"); + return; + } + pkt.pkt_length = rc; + log_sip_msg_rx(pkt.pkt_buffer, pkt.pkt_length, &sin); + rc = parse_incoming_sip_msg(&pkt); + if (rc < 0) { + printf("Incoming SIP UDP message parse error %d\n", rc); + return; + } + /* dispatch good-so-far SIP message */ + if (pkt.parse_msgtype == SIP_MSG_TYPE_REQ) + process_sip_request(&pkt, &sin); + else if (pkt.parse_msgtype == SIP_MSG_TYPE_RESP) + process_sip_response(&pkt, &sin); +} + +void +sip_tx_packet(msg, sin) + struct sip_msg_out *msg; + struct sockaddr_in *sin; +{ + socklen_t addrlen; + + addrlen = sizeof(struct sockaddr_in); + sendto(sip_socket, msg->buf, msg->msg_len, 0, (struct sockaddr *) sin, + addrlen); + log_sip_msg_tx(msg->buf, msg->msg_len, sin); +} diff -r 38c4d09882f6 -r 26383ed8b79f test-fsk/uac.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-fsk/uac.c Mon Mar 04 21:03:19 2024 -0800 @@ -0,0 +1,142 @@ +/* + * Here we implement processing of SIP responses to the requests we sent out. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../libsip/parse.h" +#include "../libsip/resp_ident.h" +#include "../libsip/out_msg.h" + +#define MAX_TO_TAG 63 + +extern char *get_single_header(); +extern char *extract_to_tag(); + +extern struct in_addr sip_bind_ip; +extern unsigned sip_bind_port; +extern char call_id[], from_uri[], to_uri[]; +extern unsigned max_forwards; + +char to_tag[MAX_TO_TAG+1]; + +add_req_boilerplate(msg, cseq, add_to_tag) + struct sip_msg_out *msg; + char *cseq; +{ + char strbuf[256]; + int rc; + + sprintf(strbuf, "SIP/2.0/UDP %s:%u", + inet_ntoa(sip_bind_ip), sip_bind_port); + rc = out_msg_add_header(msg, "Via", strbuf); + if (rc < 0) + return rc; + rc = out_msg_add_header(msg, "From", from_uri); + if (rc < 0) + return rc; + if (add_to_tag && to_tag[0]) { + sprintf(strbuf, "<%s>;tag=%s", to_uri, to_tag); + rc = out_msg_add_header(msg, "To", strbuf); + } else + rc = out_msg_add_header(msg, "To", to_uri); + if (rc < 0) + return rc; + rc = out_msg_add_header(msg, "Call-ID", call_id); + if (rc < 0) + return rc; + rc = out_msg_add_header(msg, "CSeq", cseq); + if (rc < 0) + return rc; + sprintf(strbuf, "%u", max_forwards); + return out_msg_add_header(msg, "Max-Forwards", strbuf); +} + +add_contact_header(msg) + struct sip_msg_out *msg; +{ + char strbuf[80]; + + sprintf(strbuf, "", + inet_ntoa(sip_bind_ip), sip_bind_port); + return out_msg_add_header(msg, "Contact", strbuf); +} + +static void +send_ack(sin) + struct sockaddr_in *sin; +{ + struct sip_msg_out msg; + int rc; + + rc = start_request_out_msg(&msg, "ACK", to_uri); + if (rc < 0) { +msg_size_err: fprintf(stderr, "composing ACK message: size error\n"); + return; + } + rc = add_req_boilerplate(&msg, "1 ACK", 1); + if (rc < 0) + goto msg_size_err; + out_msg_finish(&msg); + sip_tx_packet(&msg, sin); +} + +static void +handle_invite_response(msg, sin) + struct sip_pkt_rx *msg; + struct sockaddr_in *sin; +{ + char *tag; + + printf("Response to INVITE: %s\n", msg->status_str); + tag = extract_to_tag(msg, to_uri); + if (tag) { + printf("To tag: %s\n", tag); + if (strlen(tag) <= MAX_TO_TAG) + strcpy(to_tag, tag); + else + printf("To tag exceeds length limit!\n"); + } + extract_resp_sdp(msg); + if (msg->status_code >= 200) { + printf("Sending ACK\n"); + send_ack(sin); + if (msg->status_code <= 299) + invite_200_rtpout(); + } +} + +void +process_sip_response(msg, sin) + struct sip_pkt_rx *msg; + struct sockaddr_in *sin; +{ + struct sip_resp_ident rid; + int rc; + + rc = sip_resp_extract_ident(msg, &rid); + if (rc < 0) { + printf("SIP %03u response: bad or missing %s header\n", + msg->status_code, rid.error_field); + return; + } + if (strcmp(rid.call_id, call_id)) { + printf("Got SIP response with wrong Call-ID\n"); + return; + } + if (rid.cseq_num == 1 && !strcmp(rid.cseq_method, "INVITE")) + handle_invite_response(msg, sin); + else if (rid.cseq_num == 1 && !strcmp(rid.cseq_method, "CANCEL")) + printf("Response to CANCEL: %s\n", msg->status_str); + else if (rid.cseq_num == 2 && !strcmp(rid.cseq_method, "BYE")) + printf("Response to BYE: %s\n", msg->status_str); + else + printf("Got SIP resp for our Call-ID with unknown CSeq %u %s\n", + rid.cseq_num, rid.cseq_method); +} diff -r 38c4d09882f6 -r 26383ed8b79f test-fsk/uas.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-fsk/uas.c Mon Mar 04 21:03:19 2024 -0800 @@ -0,0 +1,63 @@ +/* + * UAS for sip-manual-out. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../libsip/parse.h" +#include "../libsip/uas_basic.h" +#include "../libsip/out_msg.h" + +static void +unsupported_method(req, ess, sin) + struct sip_pkt_rx *req; + struct uas_parse_hdrs *ess; + struct sockaddr_in *sin; +{ + struct sip_msg_out resp; + int rc; + + printf("SIP %.16s request: unsupported method\n", req->req_method); + start_response_out_msg(&resp, "501 Method not supported"); + rc = add_resp_basic_headers(&resp, ess, req->req_method); + if (rc < 0) { +too_long: fprintf(stderr, + "sending 501 error: response length exceeded\n"); + return; + } + rc = out_msg_add_header(&resp, "Allow", "INVITE,ACK,BYE"); + if (rc < 0) + goto too_long; + out_msg_finish(&resp); + sip_tx_packet(&resp, sin); +} + +void +process_sip_request(msg, sin) + struct sip_pkt_rx *msg; + struct sockaddr_in *sin; +{ + struct uas_parse_hdrs ess; + int rc; + + rc = uas_get_basic_headers(msg, &ess); + if (rc < 0) { + printf("SIP %.16s request: bad or missing %s header\n", + msg->req_method, ess.error_field); + return; + } + /* dispatch by method */ + if (!strcmp(msg->req_method, "INVITE")) + handle_invite_req(msg, &ess, sin); + else if (!strcmp(msg->req_method, "ACK")) + printf("Received ACK request, swallowing it\n"); + else if (!strcmp(msg->req_method, "BYE")) + handle_bye_req(msg, &ess, sin); + else + unsupported_method(msg, &ess, sin); +} diff -r 38c4d09882f6 -r 26383ed8b79f test-fsk/user_cmd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-fsk/user_cmd.c Mon Mar 04 21:03:19 2024 -0800 @@ -0,0 +1,102 @@ +/* + * In this module we implement stdin command handling, supporting + * user-initiated CANCEL and BYE as well as TFO testing commands. + */ + +#include +#include +#include +#include +#include + +static void +pcm_fill_cmd(arg) + char *arg; +{ + char *cp; + unsigned octet; + + for (cp = arg; isspace(*cp); cp++) + ; + if (!isxdigit(*cp)) { +inv_syntax: fprintf(stderr, "error: pcm-fill command invalid syntax\n"); + return; + } + octet = strtoul(cp, &cp, 16); + if (*cp) + goto inv_syntax; + if (octet > 0xFF) { + fprintf(stderr, + "error: pcm-fill octet argument out of range\n"); + return; + } + set_pcm_fill_octet(octet); +} + +static void +tfo_req_cmd(args) + char *args; +{ + char *cp; + unsigned sig, codec; + + for (cp = args; isspace(*cp); cp++) + ; + if (!isdigit(*cp)) { +inv_syntax: fprintf(stderr, "error: tfo-req command invalid syntax\n"); + return; + } + sig = strtoul(cp, &cp, 0); + if (!isspace(*cp)) + goto inv_syntax; + if (sig > 0xFF) { + fprintf(stderr, "error: Sig argument out of range\n"); + return; + } + while (isspace(*cp)) + cp++; + if (!isdigit(*cp)) + goto inv_syntax; + codec = strtoul(cp, &cp, 0); + if (*cp && !isspace(*cp)) + goto inv_syntax; + if (codec > 14) { + fprintf(stderr, "error: Codec_Type argument out of range\n"); + return; + } + while (isspace(*cp)) + cp++; + if (*cp) + goto inv_syntax; + send_tfo_req(sig, codec); +} + +void +select_stdin() +{ + char buf[256], *cp; + + fgets(buf, sizeof buf, stdin); + cp = index(buf, '\n'); + if (cp) { + while (cp > buf && isspace(cp[-1])) + cp--; + *cp = '\0'; + } + for (cp = buf; isspace(*cp); cp++) + ; + if (!*cp) + return; + if (!strcmp(cp, "b") || !strcasecmp(cp, "bye")) + send_bye_req(); + else if (!strcmp(cp, "c") || !strcasecmp(cp, "cancel")) + send_cancel_req(); + else if (!strncmp(cp, "pcm-fill", 8) && isspace(cp[8])) + pcm_fill_cmd(cp + 9); + else if (!strncmp(cp, "tfo-req", 7) && isspace(cp[7])) + tfo_req_cmd(cp + 8); + else if (!strcmp(cp, "tfo-stop")) + stop_tfo_out(); + else + fprintf(stderr, "error: non-understood stdin command\n"); +}