# HG changeset patch # User Mychaela Falconia # Date 1709807629 28800 # Node ID 3c5734b88c20469a1d009d7f6c98c1dd5b070e0d # Parent ff535725e01f7ff2f6b84d7d99ed3625db571162 sipout-test-v22 put together diff -r ff535725e01f -r 3c5734b88c20 .hgignore --- a/.hgignore Thu Mar 07 00:05:44 2024 -0800 +++ b/.hgignore Thu Mar 07 02:33:49 2024 -0800 @@ -3,5 +3,6 @@ \.[oa]$ ^test-fsk/sipout-test-fsk$ +^test-v22/sipout-test-v22$ ^test-voice/sipout-test-voice$ ^tone-detect/g711-tone-detect$ diff -r ff535725e01f -r 3c5734b88c20 Makefile --- a/Makefile Thu Mar 07 00:05:44 2024 -0800 +++ b/Makefile Thu Mar 07 02:33:49 2024 -0800 @@ -1,13 +1,14 @@ CC= gcc CFLAGS= -O2 -PROGDIR=test-fsk test-voice tone-detect +PROGDIR=test-fsk test-v22 test-voice tone-detect LIBDIR= librtpalloc libsip libutil SUBDIR= ${PROGDIR} ${LIBDIR} all: ${SUBDIR} test-fsk: librtpalloc libsip libutil +test-v22: librtpalloc libsip libutil test-voice: librtpalloc libsip libutil ${SUBDIR}: FRC diff -r ff535725e01f -r 3c5734b88c20 test-v22/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-v22/Makefile Thu Mar 07 02:33:49 2024 -0800 @@ -0,0 +1,18 @@ +CC= gcc +CFLAGS= -O2 +PROG= sipout-test-v22 +OBJS= bye_in.o disc_cmd.o main.o modem_func.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} -lspandsp -lm + +install: + install -c -m 755 ${PROG} ${INSTBIN} + +clean: + rm -f *.o ${PROG} errs diff -r ff535725e01f -r 3c5734b88c20 test-v22/bye_in.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-v22/bye_in.c Thu Mar 07 02:33:49 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 ff535725e01f -r 3c5734b88c20 test-v22/disc_cmd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-v22/disc_cmd.c Thu Mar 07 02:33:49 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 ff535725e01f -r 3c5734b88c20 test-v22/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-v22/main.c Thu Mar 07 02:33:49 2024 -0800 @@ -0,0 +1,207 @@ +/* + * 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; +int v22_bitrate; + +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 bps\n", + argv[0]); + exit(1); + } + } + if (argc != optind + 4) + 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); + v22_bitrate = atoi(argv[optind+3]); + if (v22_bitrate != 1200 && v22_bitrate != 2400) { + fprintf(stderr, "error: bps argument must be 1200 or 2400\n"); + exit(1); + } + init_modem_func(); + 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 ff535725e01f -r 3c5734b88c20 test-v22/modem_func.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-v22/modem_func.c Thu Mar 07 02:33:49 2024 -0800 @@ -0,0 +1,122 @@ +/* + * In this module we implement our interface to SpanDSP V.22 engine. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../include/pstn_defs.h" + +extern int v22_bitrate; + +#define MAX_TEXT_LINE 80 + +static v22bis_state_t *modem_state; +static async_rx_state_t *async_state; +static u_char rx_line_buf[MAX_TEXT_LINE]; +static unsigned rx_buf_fill; + +static void +safe_print_char(c) +{ + if (c >= ' ' && c <= '~') { + putchar(c); + return; + } + switch (c) { + case '\t': + putchar(c); + return; + case '\n': + return; + case '\r': + putchar('\\'); + putchar('r'); + return; + case '\b': + putchar('\\'); + putchar('b'); + return; + case '\f': + putchar('\\'); + putchar('f'); + return; + } + printf("\\x%02X", c); +} + +static void +print_rx_line() +{ + u_char *dp, *endp; + int c; + + fputs("MRx:\t", stdout); + dp = rx_line_buf; + endp = rx_line_buf + rx_buf_fill; + while (dp < endp) { + c = *dp++; + safe_print_char(c); + } + if (c != '\n') + putchar('\\'); + putchar('\n'); +} + +static void +byte_rx_func(user_data, byte) + void *user_data; + int byte; +{ + if (byte < 0) { + printf("Modem state change: %s\n", signal_status_to_str(byte)); + return; + } + rx_line_buf[rx_buf_fill++] = byte; + if (byte == '\n' || rx_buf_fill >= MAX_TEXT_LINE) { + print_rx_line(); + rx_buf_fill = 0; + } +} + +static int +supply_bit() +{ + return 1; +} + +void +init_modem_func() +{ + async_state = async_rx_init(NULL, 8, ASYNC_PARITY_NONE, 1, true, + byte_rx_func, NULL); + if (!async_state) { + fprintf(stderr, "error: async_rx_init() failed!\n"); + exit(1); + } + modem_state = v22bis_init(NULL, v22_bitrate, V22BIS_GUARD_TONE_NONE, + true, supply_bit, NULL, async_rx_put_bit, + async_state); + if (!modem_state) { + fprintf(stderr, "error: v22bis_init() failed!\n"); + exit(1); + } +} + +void +process_rx_frame(samples) + int16_t *samples; +{ + v22bis_rx(modem_state, samples, FRAME_20MS); +} + +void +fill_tx_frame(samples) + int16_t *samples; +{ + v22bis_tx(modem_state, samples, FRAME_20MS); +} diff -r ff535725e01f -r 3c5734b88c20 test-v22/readconf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-v22/readconf.c Thu Mar 07 02:33:49 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 ff535725e01f -r 3c5734b88c20 test-v22/reinvite.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-v22/reinvite.c Thu Mar 07 02:33:49 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 ff535725e01f -r 3c5734b88c20 test-v22/rtp_rx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-v22/rtp_rx.c Thu Mar 07 02:33:49 2024 -0800 @@ -0,0 +1,148 @@ +/* + * 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 "../include/pstn_defs.h" +#include "../librtpalloc/rtp_alloc_simple.h" + +extern const uint16_t pcmu_decode_table[256]; +extern const uint16_t pcma_decode_table[256]; + +extern int rtp_out_enable; /* misusing the flag :-( */ + +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; + +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; +} + +void +rtp_rx_select() +{ + struct rtp_packet pkt; + struct sockaddr_in sin_from; + socklen_t addrlen; + int16_t seq_delta; + int32_t ts_delta; + const uint16_t *pcm_dec_table; + int16_t pcm_samples[FRAME_20MS]; + unsigned n; + int rc; + + 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: + pcm_dec_table = pcmu_decode_table; + break; + case PSTN_CODEC_PCMA: + pcm_dec_table = pcma_decode_table; + 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; + } + } 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; + } + } else if (ts_delta != 160) { + if (!rtp_ts_brk_flag) { + printf("Rx RTP stream tstamp break\n"); + rtp_ts_brk_flag = 1; + } + } + } + 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; + } + /* ignore early RTP during ringing, before answer supervision */ + if (!rtp_out_enable) + return; + /* feed samples to modem Rx */ + for (n = 0; n < FRAME_20MS; n++) + pcm_samples[n] = pcm_dec_table[pkt.payload[n]]; + process_rx_frame(pcm_samples); +} + +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 ff535725e01f -r 3c5734b88c20 test-v22/rtp_tx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-v22/rtp_tx.c Thu Mar 07 02:33:49 2024 -0800 @@ -0,0 +1,65 @@ +/* + * In this module we implement outgoing RTP stream generation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/tmgw_const.h" +#include "../include/rtp_defs.h" +#include "../include/pstn_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 g711_state_t *g711_enc_state; + +void +assign_rtpout_ssrc() +{ + rtp_ssrc = cur_event_time.tv_sec ^ cur_event_time.tv_usec ^ getpid(); +} + +void +init_pcm_tx() +{ + g711_enc_state = g711_init(NULL, pcma_selected ? G711_ALAW : G711_ULAW); + if (!g711_enc_state) { + fprintf(stderr, "error: g711_init() failed!\n"); + exit(1); + } +} + +void +generate_rtp_packet() +{ + struct rtp_packet pkt; + socklen_t addrlen; + int16_t linear[FRAME_20MS]; + + 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 += FRAME_20MS; + pkt.ssrc = rtp_ssrc; + fill_tx_frame(linear); + g711_encode(g711_enc_state, pkt.payload, linear, FRAME_20MS); + addrlen = sizeof(struct sockaddr_in); + sendto(rtp_udp_fd, &pkt, RTP_PACKET_SIZE_PSTN, 0, + (struct sockaddr *) &rtp_remote_addr, addrlen); +} diff -r ff535725e01f -r 3c5734b88c20 test-v22/sdp_in.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-v22/sdp_in.c Thu Mar 07 02:33:49 2024 -0800 @@ -0,0 +1,85 @@ +/* + * 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_tx(); +} diff -r ff535725e01f -r 3c5734b88c20 test-v22/sip_log.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-v22/sip_log.c Thu Mar 07 02:33:49 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 ff535725e01f -r 3c5734b88c20 test-v22/sip_udp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-v22/sip_udp.c Thu Mar 07 02:33:49 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 ff535725e01f -r 3c5734b88c20 test-v22/uac.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-v22/uac.c Thu Mar 07 02:33:49 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 ff535725e01f -r 3c5734b88c20 test-v22/uas.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-v22/uas.c Thu Mar 07 02:33:49 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 ff535725e01f -r 3c5734b88c20 test-v22/user_cmd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-v22/user_cmd.c Thu Mar 07 02:33:49 2024 -0800 @@ -0,0 +1,58 @@ +/* + * In this module we implement stdin command handling, supporting + * user-initiated CANCEL and BYE. + */ + +#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); +} + +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 + fprintf(stderr, "error: non-understood stdin command\n"); +}