# HG changeset patch # User Mychaela Falconia # Date 1709536819 28800 # Node ID 35c0d9f03c0ac8ee3059a4d264d955e725531560 beginning with sipout-test-voice, a copy of sip-manual-out from themwi-system-sw diff -r 000000000000 -r 35c0d9f03c0a .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,5 @@ +syntax: regexp + +\.[oa]$ + +^test-voice/sipout-test-voice$ diff -r 000000000000 -r 35c0d9f03c0a include/rtp_alloc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/rtp_alloc.h Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,20 @@ +/* + * This header file defines the ad hoc control interface + * to themwi-rtp-mgr over a dedicated local socket. + */ + +struct rtp_alloc_req { + uint32_t transact_ref; + uint32_t ep_type; +}; + +struct rtp_alloc_resp { + uint32_t transact_ref; + uint32_t res; + struct sockaddr_storage gsm_addr; + struct sockaddr_storage pstn_addr; +}; + +#define RTP_ALLOC_OK 0 +#define RTP_ALLOC_ERR_PARAM 1 +#define RTP_ALLOC_ERR_RSRC 2 diff -r 000000000000 -r 35c0d9f03c0a include/rtp_defs.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/rtp_defs.h Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,17 @@ +/* + * This header file holds some definitions for RTP, as this protocol + * functions in our GSM and PSTN environment. + */ + +#define RTP_PACKET_HDR_SIZE 12 +#define RTP_PACKET_SIZE_PSTN 172 +#define RTP_MAX_PAYLOAD 160 + +struct rtp_packet { + uint8_t v_p_x_cc; + uint8_t m_pt; + uint16_t seq; + uint32_t tstamp; + uint32_t ssrc; + uint8_t payload[RTP_MAX_PAYLOAD]; +}; diff -r 000000000000 -r 35c0d9f03c0a include/tmgw_const.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/tmgw_const.h Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,37 @@ +/* + * This header file defines some constants for themwi-mgw, + * used in the implementation, in the control interface and + * throughout the architecture. + * + * Some of these constants (specifically those dealing with + * RTP endpoint types) are now also used in themwi-rtp-mgr + * and other RTP-touching tools beyond the original themwi-mgw + * design. + */ + +#define TMGW_EP_TYPE_GSM_ONLY 1 +#define TMGW_EP_TYPE_PSTN_ONLY 2 +#define TMGW_EP_TYPE_GATEWAY 3 + +/* backward compatibility, from themwi-mgw perspective */ +#define TMGW_EP_TYPE_DUMMY_GSM TMGW_EP_TYPE_GSM_ONLY +#define TMGW_EP_TYPE_DUMMY_PSTN TMGW_EP_TYPE_PSTN_ONLY + +#define TMGW_EP_HAS_GSM_SOCK 1 +#define TMGW_EP_HAS_PSTN_SOCK 2 + +#define TMGW_FWD_MODE_INACTIVE 0 +#define TMGW_FWD_MODE_RECVONLY 1 +#define TMGW_FWD_MODE_SENDONLY 2 +#define TMGW_FWD_MODE_SENDRECV 3 + +#define TMGW_FWD_ENABLE_PSTN2GSM 1 +#define TMGW_FWD_ENABLE_GSM2PSTN 2 + +#define GSM_TCHF_FRAME 0x0300 +#define GSM_TCHF_FRAME_EFR 0x0301 +#define GSM_TCHH_FRAME 0x0302 +#define GSM_TCH_FRAME_AMR 0x0303 + +#define PSTN_CODEC_PCMU 0 +#define PSTN_CODEC_PCMA 8 diff -r 000000000000 -r 35c0d9f03c0a librtpalloc/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librtpalloc/Makefile Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,13 @@ +CC= gcc +CFLAGS= -O2 +OBJS= rtp_alloc_simple.o rtpmgr_resp.o +LIB= librtpalloc.a + +all: ${LIB} + +${LIB}: ${OBJS} + ar rcu $@ ${OBJS} + ranlib $@ + +clean: + rm -f *.[oa] errs diff -r 000000000000 -r 35c0d9f03c0a librtpalloc/rtp_alloc_simple.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librtpalloc/rtp_alloc_simple.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,124 @@ +/* + * The library function implemented in this C module provides a + * simple interface for obtaining a single RTP endpoint from + * themwi-rtp-mgr. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/tmgw_const.h" +#include "../include/rtp_alloc.h" +#include "rtpmgr_resp.h" +#include "rtp_alloc_simple.h" + +static char ctrl_socket_pathname[] = "/var/gsm/rtp_alloc_socket"; + +static void +close_fds(resp) + struct rtp_alloc_resp_wrap *resp; +{ + unsigned n; + + for (n = 0; n < resp->num_fd; n++) + close(resp->fd_buf[n]); +} + +rtp_alloc_simple(ep_type, out) + int ep_type; + struct rtp_alloc_simple *out; +{ + struct sockaddr_un sa; + unsigned sa_len; + int ctrl_fd, rc; + struct rtp_alloc_req req; + struct rtp_alloc_resp_wrap resp; + unsigned expect_num_fd; + + switch (ep_type) { + case TMGW_EP_TYPE_GSM_ONLY: + case TMGW_EP_TYPE_PSTN_ONLY: + expect_num_fd = 2; + break; + case TMGW_EP_TYPE_GATEWAY: + expect_num_fd = 4; + break; + default: + fprintf(stderr, + "rtp_alloc_simple() error: unknown EP type %d\n", + ep_type); + return(-1); + } + ctrl_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (ctrl_fd < 0) { + perror("socket(AF_UNIX, SOCK_SEQPACKET, 0)"); + return(-1); + } + fill_sockaddr_un(ctrl_socket_pathname, &sa, &sa_len); + rc = connect(ctrl_fd, (struct sockaddr *) &sa, sa_len); + if (rc < 0) { + perror(ctrl_socket_pathname); + close(ctrl_fd); + return(-1); + } + bzero(&req, sizeof req); + req.ep_type = ep_type; + rc = send(ctrl_fd, &req, sizeof req, 0); + if (rc < 0) { + perror("send to RTP allocator socket"); + close(ctrl_fd); + return(-1); + } + rc = collect_rtpmgr_resp(ctrl_fd, 0, &resp); + if (rc < 0) { + perror("recvmsg from RTP allocator socket"); + close(ctrl_fd); + return(-1); + } + close(ctrl_fd); + if (resp.resp_len != sizeof(struct rtp_alloc_resp)) { + fprintf(stderr, +"error: response packet from themwi-rtp-mgr has wrong length (%u bytes)\n", + resp.resp_len); + close_fds(&resp); + return(-1); + } + if (resp.resp.res != RTP_ALLOC_OK) { + fprintf(stderr, "themwi-rtp-mgr returned error %u\n", + resp.resp.res); + close_fds(&resp); + return(-1); + } + if (resp.num_fd != expect_num_fd) { + fprintf(stderr, +"error: themwi-rtp-mgr returned %u descriptors instead of expected %u\n", + resp.num_fd, expect_num_fd); + close_fds(&resp); + return(-1); + } + switch (ep_type) { + case TMGW_EP_TYPE_GSM_ONLY: + out->gsm_rtp_fd = resp.fd_buf[0]; + out->gsm_rtcp_fd = resp.fd_buf[1]; + break; + case TMGW_EP_TYPE_PSTN_ONLY: + out->pstn_rtp_fd = resp.fd_buf[0]; + out->pstn_rtcp_fd = resp.fd_buf[1]; + break; + case TMGW_EP_TYPE_GATEWAY: + out->gsm_rtp_fd = resp.fd_buf[0]; + out->gsm_rtcp_fd = resp.fd_buf[1]; + out->pstn_rtp_fd = resp.fd_buf[2]; + out->pstn_rtcp_fd = resp.fd_buf[3]; + } + bcopy(&resp.resp.gsm_addr, &out->gsm_addr, + sizeof(struct sockaddr_storage)); + bcopy(&resp.resp.pstn_addr, &out->pstn_addr, + sizeof(struct sockaddr_storage)); + return(0); +} diff -r 000000000000 -r 35c0d9f03c0a librtpalloc/rtp_alloc_simple.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librtpalloc/rtp_alloc_simple.h Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,13 @@ +/* + * This header file defines the library interface for "simple" + * RTP endpoint allocation. + */ + +struct rtp_alloc_simple { + struct sockaddr_storage gsm_addr; + int gsm_rtp_fd; + int gsm_rtcp_fd; + struct sockaddr_storage pstn_addr; + int pstn_rtp_fd; + int pstn_rtcp_fd; +}; diff -r 000000000000 -r 35c0d9f03c0a librtpalloc/rtpmgr_resp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librtpalloc/rtpmgr_resp.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,66 @@ +/* + * Here we implement the collect_rtpmgr_resp() function, + * which is a wrapper around the mess of recvmsg + * with file descriptor passing. + */ + +#include +#include +#include +#include +#include +#include "../include/rtp_alloc.h" +#include "rtpmgr_resp.h" + +collect_rtpmgr_resp(ctrl_fd, recv_flags, out) + struct rtp_alloc_resp_wrap *out; +{ + int rc; + struct iovec iov; + struct msghdr msg; + union { + char buf[CMSG_SPACE(sizeof(int) * 4)]; + struct cmsghdr align; + } cmsgu; + struct cmsghdr *cmsg; + + iov.iov_base = &out->resp; + iov.iov_len = sizeof(struct rtp_alloc_resp); + bzero(&msg, sizeof msg); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgu.buf; + msg.msg_controllen = CMSG_SPACE(sizeof(int) * 4); + rc = recvmsg(ctrl_fd, &msg, recv_flags); + if (rc < 0) + return rc; + out->resp_len = rc; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) + break; + } + if (cmsg) { + switch (cmsg->cmsg_len) { + case CMSG_LEN(sizeof(int)): + out->num_fd = 1; + break; + case CMSG_LEN(sizeof(int) * 2): + out->num_fd = 2; + break; + case CMSG_LEN(sizeof(int) * 3): + out->num_fd = 3; + break; + case CMSG_LEN(sizeof(int) * 4): + out->num_fd = 4; + break; + default: + out->num_fd = 0; + } + if (out->num_fd) + bcopy(CMSG_DATA(cmsg), out->fd_buf, + sizeof(int) * out->num_fd); + } else + out->num_fd = 0; + return 0; +} diff -r 000000000000 -r 35c0d9f03c0a librtpalloc/rtpmgr_resp.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/librtpalloc/rtpmgr_resp.h Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,12 @@ +/* + * The structure defined in this header file is returned by the + * collect_rtpmgr_resp() function, which is a wrapper around + * the mess of recvmsg with file descriptor passing. + */ + +struct rtp_alloc_resp_wrap { + struct rtp_alloc_resp resp; + unsigned resp_len; + int fd_buf[4]; + unsigned num_fd; +}; diff -r 000000000000 -r 35c0d9f03c0a libsip/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/Makefile Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,14 @@ +CC= gcc +CFLAGS= -O2 +OBJS= get_header.o grok_from.o out_msg.o primary_parse.o req_supp.o \ + resp_ident.o sdp_gen.o sdp_parse.o to_tag.o uas_basic.o uri_utils.o +LIB= libsip.a + +all: ${LIB} + +${LIB}: ${OBJS} + ar rcu $@ ${OBJS} + ranlib $@ + +clean: + rm -f *.[oa] errs diff -r 000000000000 -r 35c0d9f03c0a libsip/get_header.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/get_header.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,39 @@ +/* + * In this module we implement functions for retrieving individual + * header fields from struct sip_pkt_rx. + */ + +#include +#include +#include "parse.h" + +char * +get_single_header(msg, name, altname, dupp) + struct sip_pkt_rx *msg; + char *name, *altname; + int *dupp; +{ + unsigned n; + char *ret; + + for (n = 0; n < msg->num_hdr_fields; n++) { + if (!strcasecmp(msg->hdr_fields[n].field_name, name)) + break; + if (altname && + !strcasecmp(msg->hdr_fields[n].field_name, altname)) + break; + } + if (n >= msg->num_hdr_fields) + return 0; + ret = msg->hdr_fields[n].field_value; + if (!dupp) + return ret; + for (n++; n < msg->num_hdr_fields; n++) { + if (!strcasecmp(msg->hdr_fields[n].field_name, name)) + *dupp = 1; + if (altname && + !strcasecmp(msg->hdr_fields[n].field_name, altname)) + *dupp = 1; + } + return ret; +} diff -r 000000000000 -r 35c0d9f03c0a libsip/grok_from.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/grok_from.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,103 @@ +/* + * Here we implement further parsing of the From header, + * beyond the preliminary step of uas_get_basic_headers(). + */ + +#include +#include +#include +#include "grok_from.h" + +grok_from_header(from_hdr, gfr) + char *from_hdr; + struct grok_from *gfr; +{ + char *cp = from_hdr, *bp; + int bracketed; + + if (*cp == '<' || !strncasecmp(cp, "sip:", 4)) { + gfr->cnam = 0; + gfr->cnam_len = 0; + gfr->cnam_quoted = 0; + } else { + if (*cp == '"') { + cp++; + gfr->cnam = cp; + for (;;) { + if (!*cp) + return(-1); + if (*cp == '"') + break; + if (*cp++ == '\\') { + if (!*cp) + return(-1); + cp++; + } + } + gfr->cnam_len = cp - gfr->cnam; + gfr->cnam_quoted = 1; + cp++; + while (isspace(*cp)) + cp++; + if (*cp != '<') + return(-1); + } else { + gfr->cnam = cp; + for (;;) { + if (!*cp) + return(-1); + if (*cp == '<') + break; + cp++; + } + for (bp = cp; bp > gfr->cnam && isspace(bp[-1]); bp--) + ; + gfr->cnam_len = bp - gfr->cnam; + gfr->cnam_quoted = 0; + } + } + if (*cp == '<') { + cp++; + bracketed = 1; + } else + bracketed = 0; + gfr->uri = cp; + if (strncasecmp(cp, "sip:", 4)) + return(-1); + cp += 4; + gfr->user = cp; + for (;;) { + if (!*cp || *cp == '<' || *cp == '>') + return(-1); + if (*cp == '@') + break; + cp++; + } + if (cp == gfr->user) + return(-1); + gfr->user_len = cp - gfr->user; + cp++; + for (;;) { + switch (*cp) { + case '\0': + case ';': + if (bracketed) + return(-1); + else + break; + case '>': + if (bracketed) + break; + else + return(-1); + case '<': + return(-1); + default: + cp++; + continue; + } + break; + } + gfr->uri_len = cp - gfr->uri; + return(0); +} diff -r 000000000000 -r 35c0d9f03c0a libsip/grok_from.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/grok_from.h Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,14 @@ +/* + * Here we define the structure used to capture the result + * of From header field parsing. + */ + +struct grok_from { + char *uri; + unsigned uri_len; + char *user; + unsigned user_len; + char *cnam; + unsigned cnam_len; + int cnam_quoted; +}; diff -r 000000000000 -r 35c0d9f03c0a libsip/out_msg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/out_msg.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,88 @@ +/* + * Functions for constructing outgoing SIP messages. + */ + +#include +#include +#include + +#include "out_msg.h" + +start_request_out_msg(msg, method, uri) + struct sip_msg_out *msg; + char *method, *uri; +{ + unsigned len; + + len = strlen(method) + strlen(uri) + (2 + 7 + 2); + if (len + 2 > MAX_SIP_TX_PACKET) + return(-1); + sprintf(msg->buf, "%s %s SIP/2.0\r\n", method, uri); + msg->msg_len = len; + return(0); +} + +start_request_out_msg_urilen(msg, method, uri, uri_len) + struct sip_msg_out *msg; + char *method, *uri; + unsigned uri_len; +{ + unsigned len; + + len = strlen(method) + uri_len + (2 + 7 + 2); + if (len + 2 > MAX_SIP_TX_PACKET) + return(-1); + sprintf(msg->buf, "%s %.*s SIP/2.0\r\n", method, uri_len, uri); + msg->msg_len = len; + return(0); +} + +start_response_out_msg(msg, status) + struct sip_msg_out *msg; + char *status; +{ + unsigned len; + + len = strlen(status) + (8 + 2); + if (len + 2 > MAX_SIP_TX_PACKET) + return(-1); + sprintf(msg->buf, "SIP/2.0 %s\r\n", status); + msg->msg_len = len; + return(0); +} + +out_msg_add_header(msg, name, value) + struct sip_msg_out *msg; + char *name, *value; +{ + unsigned len; + + len = strlen(name) + strlen(value) + 4; + if (msg->msg_len + len + 2 > MAX_SIP_TX_PACKET) + return(-1); + sprintf(msg->buf + msg->msg_len, "%s: %s\r\n", name, value); + msg->msg_len += len; + return(0); +} + +out_msg_finish(msg) + struct sip_msg_out *msg; +{ + msg->buf[msg->msg_len++] = '\r'; + msg->buf[msg->msg_len++] = '\n'; + return(0); +} + +out_msg_finish_body(msg, body, bodylen) + struct sip_msg_out *msg; + char *body; + unsigned bodylen; +{ + msg->buf[msg->msg_len++] = '\r'; + msg->buf[msg->msg_len++] = '\n'; + if (msg->msg_len + bodylen > MAX_SIP_TX_PACKET) + return(-1); + bcopy(body, msg->buf + msg->msg_len, bodylen); + msg->msg_len += bodylen; + return(0); +} diff -r 000000000000 -r 35c0d9f03c0a libsip/out_msg.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/out_msg.h Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,11 @@ +/* + * Here we define the structure we are going to use for constructing + * outgoing SIP messages (UDP packets). + */ + +#define MAX_SIP_TX_PACKET 1472 + +struct sip_msg_out { + char buf[MAX_SIP_TX_PACKET]; + unsigned msg_len; +}; diff -r 000000000000 -r 35c0d9f03c0a libsip/parse.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/parse.h Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,33 @@ +/* + * Here we define the structure we are going to use for receiving + * and parsing SIP UDP packets. + */ + +#define MAX_SIP_RX_PACKET 3072 +#define MAX_HEADER_FIELDS 64 + +struct sip_parse_hdr { + char *field_name; + char *field_value; +}; + +struct sip_pkt_rx { + /* recvfrom on UDP socket, input to parser */ + char pkt_buffer[MAX_SIP_RX_PACKET]; + unsigned pkt_length; + /* filled by parser */ + int parse_msgtype; + char *req_method; + char *req_uri; + unsigned status_code; + char *status_str; + /* header fields */ + struct sip_parse_hdr hdr_fields[MAX_HEADER_FIELDS]; + unsigned num_hdr_fields; + /* optional message body */ + char *msg_body; + unsigned msg_body_len; +}; + +#define SIP_MSG_TYPE_REQ 1 +#define SIP_MSG_TYPE_RESP 2 diff -r 000000000000 -r 35c0d9f03c0a libsip/primary_parse.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/primary_parse.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,165 @@ +/* + * In this module we are going to implement the first stage of + * parsing for incoming SIP UDP packets, using struct sip_pkt_rx + * defined in parse.h. + */ + +#include +#include +#include +#include +#include "parse.h" + +static +find_crlf(ptr, msg_end, endp, nextp) + char *ptr, *msg_end, **endp, **nextp; +{ + for (;;) { + if (ptr >= msg_end) + return(0); + switch (*ptr) { + case '\0': + return(0); + case '\n': + *endp = ptr; + *nextp = ptr + 1; + return(1); + case '\r': + *endp = ptr; + ptr++; + if (ptr >= msg_end) + return(0); + if (*ptr != '\n') + return(0); + *nextp = ptr + 1; + return(1); + } + ptr++; + } +} + +static +try_status_line(msg) + struct sip_pkt_rx *msg; +{ + if (strncasecmp(msg->pkt_buffer, "SIP/2.0 ", 8)) + return(0); + if (!isdigit(msg->pkt_buffer[8])) + return(0); + if (!isdigit(msg->pkt_buffer[9])) + return(0); + if (!isdigit(msg->pkt_buffer[10])) + return(0); + if (msg->pkt_buffer[11] != ' ') + return(0); + msg->status_code = atoi(msg->pkt_buffer + 8); + msg->status_str = msg->pkt_buffer + 8; + return(1); +} + +static +try_request_line(msg) + struct sip_pkt_rx *msg; +{ + char *cp; + + cp = msg->pkt_buffer; + if (!isupper(*cp)) + return(0); + while (isalnum(*cp)) + cp++; + if (*cp != ' ') + return(0); + msg->req_method = msg->pkt_buffer; + *cp++ = '\0'; + msg->req_uri = cp; + while (*cp && !isspace(*cp)) + cp++; + if (*cp != ' ') + return(0); + *cp++ = '\0'; + if (strcasecmp(cp, "SIP/2.0")) + return(0); + else + return(1); +} + +static void +trim_trailing_spaces(sp) + char *sp; +{ + char *ep; + + ep = index(sp, '\0'); + while (ep > sp && isspace(ep[-1])) + ep--; + *ep = '\0'; +} + +parse_incoming_sip_msg(msg) + struct sip_pkt_rx *msg; +{ + char *msg_end = msg->pkt_buffer + msg->pkt_length; + char *cp, *endp, *nextp, *sp; + unsigned hdr_cnt; + int rc; + + /* begin by isolating the Request-Line or Status-Line */ + rc = find_crlf(msg->pkt_buffer, msg_end, &endp, &nextp); + if (!rc) + return(-1); + *endp = '\0'; + if (try_status_line(msg)) + msg->parse_msgtype = SIP_MSG_TYPE_RESP; + else if (try_request_line(msg)) + msg->parse_msgtype = SIP_MSG_TYPE_REQ; + else + return(-1); + /* now preparse header fields */ + cp = nextp; + for (hdr_cnt = 0; ; ) { + rc = find_crlf(cp, msg_end, &endp, &nextp); + if (!rc) + return(-1); + if (endp == cp) /* final CRLF? */ + break; + if (!isalpha(*cp)) + return(-1); + if (hdr_cnt >= MAX_HEADER_FIELDS) + return(-2); + msg->hdr_fields[hdr_cnt].field_name = cp; + while (cp < endp && (isalnum(*cp) || *cp == '-')) + cp++; + if (cp >= endp) + return(-1); + if (*cp == ':') + *cp++ = '\0'; + else if (isspace(*cp)) { + *cp++ = '\0'; + while (cp < endp && isspace(*cp)) + cp++; + if (cp >= endp) + return(-1); + if (*cp++ != ':') + return(-1); + } else + return(-1); + sp = cp; + cp = nextp; + while (cp < msg_end && (*cp == ' ' || *cp == '\t')) { + rc = find_crlf(cp, msg_end, &endp, &nextp); + if (!rc) + return(-1); + cp = nextp; + } + *endp = '\0'; + while (isspace(*sp)) + sp++; + trim_trailing_spaces(sp); + msg->hdr_fields[hdr_cnt++].field_value = sp; + } + msg->num_hdr_fields = hdr_cnt; + msg->msg_body = nextp; + msg->msg_body_len = msg_end - nextp; + return(0); +} diff -r 000000000000 -r 35c0d9f03c0a libsip/req_supp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/req_supp.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,77 @@ +/* + * Here we parse Require and Supported headers in a SIP request, + * checking whether our supported extensions are also supported + * or required by the client, and catching any client requirements + * which we don't support. + */ + +#include +#include +#include +#include "parse.h" +#include "req_supp.h" + +parse_require_supported(msg, lsupp, nlsupp, unsupp) + struct sip_pkt_rx *msg; + struct supported_ext *lsupp; + unsigned nlsupp; + char **unsupp; +{ + struct sip_parse_hdr *hdr, *endhdr; + char *cp, *np; + unsigned n; + + endhdr = msg->hdr_fields + msg->num_hdr_fields; + /* check Require first */ + for (hdr = msg->hdr_fields; hdr < endhdr; hdr++) { + if (strcasecmp(hdr->field_name, "Require")) + continue; + cp = hdr->field_value; + for (;;) { + while (isspace(*cp) || *cp == ',') + cp++; + if (!*cp) + break; + np = cp; + while (*cp && !isspace(*cp) && *cp != ',') + cp++; + if (*cp) + *cp++ = '\0'; + for (n = 0; n < nlsupp; n++) { + if (!strcasecmp(np, lsupp[n].name)) { + *lsupp[n].req_flag = 1; + break; + } else { + if (unsupp) + *unsupp = np; + return(-1); + } + } + } + } + /* now check Supported */ + for (hdr = msg->hdr_fields; hdr < endhdr; hdr++) { + if (strcasecmp(hdr->field_name, "Supported") && + strcasecmp(hdr->field_name, "k")) + continue; + cp = hdr->field_value; + for (;;) { + while (isspace(*cp) || *cp == ',') + cp++; + if (!*cp) + break; + np = cp; + while (*cp && !isspace(*cp) && *cp != ',') + cp++; + if (*cp) + *cp++ = '\0'; + for (n = 0; n < nlsupp; n++) { + if (!strcasecmp(np, lsupp[n].name)) { + *lsupp[n].sup_flag = 1; + break; + } + } + } + } + return(0); +} diff -r 000000000000 -r 35c0d9f03c0a libsip/req_supp.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/req_supp.h Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,9 @@ +/* + * Data structure for parsing Require and Supported header fields + */ + +struct supported_ext { + char *name; + int *req_flag; + int *sup_flag; +}; diff -r 000000000000 -r 35c0d9f03c0a libsip/resp_ident.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/resp_ident.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,52 @@ +/* + * In this module we implement an essential step in the process + * of handling incoming SIP response messages: extracting Call-ID + * and CSeq headers and preparsing the latter, providing key info + * for matching these incoming responses with call state and + * with outstanding UAC requests. + */ + +#include +#include +#include +#include +#include +#include "parse.h" +#include "resp_ident.h" + +extern char *get_single_header(); + +sip_resp_extract_ident(msg, id) + struct sip_pkt_rx *msg; + struct sip_resp_ident *id; +{ + char *hval, *cp; + int dup_flag = 0; + + hval = get_single_header(msg, "Call-ID", "i", &dup_flag); + if (!hval || dup_flag) { + id->error_field = "Call-ID"; + return(-1); + } + id->call_id = hval; + hval = get_single_header(msg, "CSeq", (char *) 0, &dup_flag); + if (!hval || dup_flag) { +bad_cseq: id->error_field = "CSeq"; + return(-1); + } + if (!isdigit(*hval)) + goto bad_cseq; + id->cseq_num = strtoul(hval, &cp, 10); + if (!isspace(*cp)) + goto bad_cseq; + while (isspace(*cp)) + cp++; + if (!isupper(*cp)) + goto bad_cseq; + id->cseq_method = cp; + while (isalnum(*cp)) + cp++; + if (*cp) + goto bad_cseq; + return(0); +} diff -r 000000000000 -r 35c0d9f03c0a libsip/resp_ident.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/resp_ident.h Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,13 @@ +/* + * Here we define struct sip_resp_ident, a structure that captures + * Call-ID and CSeq headers from initial parsing of SIP responses, + * allowing these responses to be matched with call state and + * with UAC requests that elicited them. + */ + +struct sip_resp_ident { + char *call_id; + unsigned cseq_num; + char *cseq_method; + char *error_field; +}; diff -r 000000000000 -r 35c0d9f03c0a libsip/sdp.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/sdp.h Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,24 @@ +/* + * This header file defines structures to be used for SDP + * parsing and generation. + */ + +struct sdp_parse { + struct in_addr ip_addr; + unsigned audio_port; + unsigned codec_mask; +}; + +struct sdp_gen { + unsigned session_id; + unsigned version; + struct in_addr owner_ip; + struct in_addr conn_ip; + unsigned conn_port; + unsigned codec_mask; +}; + +#define SDP_CODEC_MASK_PCMU 1 +#define SDP_CODEC_MASK_PCMA 2 +#define SDP_CODEC_MASK_BOTH 3 +#define SDP_CODEC_MASK_PCMA_PREF 4 diff -r 000000000000 -r 35c0d9f03c0a libsip/sdp_gen.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/sdp_gen.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,81 @@ +/* + * Here we implement generation of outgoing SDP descriptions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdp.h" +#include "out_msg.h" + +format_sdp_in_buffer(gen, buf, lenptr) + struct sdp_gen *gen; + char *buf; + unsigned *lenptr; +{ + char *dp, *codec_list; + + dp = buf; + strcpy(dp, "v=0\r\n"); + dp += 5; + sprintf(dp, "o=- %u %u IN IP4 %s\r\n", gen->session_id, gen->version, + inet_ntoa(gen->owner_ip)); + dp = index(dp, '\0'); + strcpy(dp, "s=-\r\n"); + dp += 5; + sprintf(dp, "c=IN IP4 %s\r\n", inet_ntoa(gen->conn_ip)); + dp = index(dp, '\0'); + strcpy(dp, "t=0 0\r\n"); + dp += 7; + switch (gen->codec_mask) { + case SDP_CODEC_MASK_PCMU: + codec_list = "0"; + break; + case SDP_CODEC_MASK_PCMA: + codec_list = "8"; + break; + case SDP_CODEC_MASK_BOTH: + codec_list = "0 8"; + break; + case SDP_CODEC_MASK_BOTH | SDP_CODEC_MASK_PCMA_PREF: + codec_list = "8 0"; + break; + default: + return(-2); + } + sprintf(dp, "m=audio %u RTP/AVP %s\r\n", gen->conn_port, codec_list); + dp = index(dp, '\0'); + if (gen->codec_mask & SDP_CODEC_MASK_PCMU) { + strcpy(dp, "a=rtpmap:0 PCMU/8000\r\n"); + dp += 22; + } + if (gen->codec_mask & SDP_CODEC_MASK_PCMA) { + strcpy(dp, "a=rtpmap:8 PCMA/8000\r\n"); + dp += 22; + } + strcpy(dp, "a=sendrecv\r\n"); + dp += 12; + strcpy(dp, "a=ptime:20\r\n"); + dp += 12; + *lenptr = (dp - buf); + return(0); +} + +out_msg_finish_sdp(msg, gen) + struct sip_msg_out *msg; + struct sdp_gen *gen; +{ + char sdp_buf[256]; + unsigned sdp_len; + int rc; + + rc = format_sdp_in_buffer(gen, sdp_buf, &sdp_len); + if (rc < 0) + return rc; + return out_msg_finish_body(msg, sdp_buf, sdp_len); +} diff -r 000000000000 -r 35c0d9f03c0a libsip/sdp_parse.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/sdp_parse.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,104 @@ +/* + * Here we implement the function that parses received SDP descriptions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/tmgw_const.h" +#include "sdp.h" + +parse_incoming_sdp(body, bodylen, dist) + char *body; + unsigned bodylen; + struct sdp_parse *dist; +{ + char *cp, *endp; + int got_c = 0, got_m = 0, prefer_pcma = 0; + unsigned codec; + + cp = body; + endp = body + bodylen; + while (cp < endp) { + if (!islower(cp[0]) || cp[1] != '=') + return(-1); + switch (cp[0]) { + case 'c': + if (got_c) + return(-1); + got_c = 1; + if (strncmp(cp + 2, "IN IP4 ", 7)) + return(-1); + cp += 9; + dist->ip_addr.s_addr = inet_addr(cp); + if (dist->ip_addr.s_addr == INADDR_NONE) + return(-1); + break; + case 'm': + if (got_m) + return(-1); + got_m = 1; + if (strncmp(cp + 2, "audio ", 6)) + return(-1); + cp += 8; + if (!isdigit(*cp)) + return(-1); + dist->audio_port = strtoul(cp, &cp, 10); + if (strncmp(cp, " RTP/AVP", 8)) + return(-1); + cp += 8; + dist->codec_mask = 0; + for (;;) { + if (*cp == '\r' || *cp == '\n') + break; + if (*cp++ != ' ') + return(-1); + codec = strtoul(cp, &cp, 10); + switch (codec) { + case PSTN_CODEC_PCMU: + dist->codec_mask |= SDP_CODEC_MASK_PCMU; + break; + case PSTN_CODEC_PCMA: + if (!dist->codec_mask) + prefer_pcma = 1; + dist->codec_mask |= SDP_CODEC_MASK_PCMA; + break; + } + } + if (dist->codec_mask == SDP_CODEC_MASK_BOTH && + prefer_pcma) + dist->codec_mask |= SDP_CODEC_MASK_PCMA_PREF; + break; + default: + cp += 2; + } + for (;;) { + if (cp >= endp) + return(-1); + if (!*cp) + return(-1); + if (*cp == '\n') { + cp++; + break; + } + if (*cp == '\r') { + cp++; + if (cp >= endp) + return(-1); + if (*cp++ != '\n') + return(-1); + break; + } + cp++; + } + } + if (got_c && got_m) + return(0); + else + return(-1); +} diff -r 000000000000 -r 35c0d9f03c0a libsip/to_tag.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/to_tag.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,76 @@ +/* + * The function implemented in this module extracts the tag + * from the To header of a SIP destination server's response. + */ + +#include +#include +#include +#include +#include +#include "parse.h" + +extern char *get_single_header(); + +char * +extract_to_tag(msg, expect_uri) + struct sip_pkt_rx *msg; + char *expect_uri; +{ + char *cp, *tag; + int bracketed, c; + unsigned expect_uri_len; + + cp = get_single_header(msg, "To", "t", (int *) 0); + if (!cp) + return 0; + if (*cp == '<') { + cp++; + bracketed = 1; + } else + bracketed = 0; + expect_uri_len = strlen(expect_uri); + if (strncasecmp(cp, expect_uri, expect_uri_len)) + return 0; + cp += expect_uri_len; + if (bracketed) { + if (*cp++ != '>') + return 0; + } + while (isspace(*cp)) + cp++; + if (*cp++ != ';') + return 0; + while (isspace(*cp)) + cp++; + if (strncasecmp(cp, "tag", 3)) + return 0; + cp += 3; + while (isspace(*cp)) + cp++; + if (*cp++ != '=') + return 0; + while (isspace(*cp)) + cp++; + tag = cp; + for (; c = *cp; cp++) { + switch (c) { + case '-': + case '.': + case '!': + case '%': + case '*': + case '_': + case '+': + case '`': + case '\'': + case '~': + continue; + default: + if (isalnum(c)) + continue; + return 0; + } + } + return tag; +} diff -r 000000000000 -r 35c0d9f03c0a libsip/uas_basic.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/uas_basic.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,88 @@ +/* + * Here we implement some essential UAS functions. + */ + +#include +#include +#include +#include +#include +#include "parse.h" +#include "uas_basic.h" +#include "out_msg.h" + +extern char *get_single_header(); + +uas_get_basic_headers(msg, ess) + struct sip_pkt_rx *msg; + struct uas_parse_hdrs *ess; +{ + char *hval, *cp; + int dup_flag = 0; + + hval = get_single_header(msg, "Call-ID", "i", &dup_flag); + if (!hval || dup_flag) { + ess->error_field = "Call-ID"; + return(-1); + } + ess->call_id = hval; + hval = get_single_header(msg, "CSeq", (char *) 0, &dup_flag); + if (!hval || dup_flag) { +bad_cseq: ess->error_field = "CSeq"; + return(-1); + } + if (!isdigit(*hval)) + goto bad_cseq; + ess->cseq_num = strtoul(hval, &cp, 10); + if (*cp) { + if (!isspace(*cp)) + goto bad_cseq; + while (isspace(*cp)) + cp++; + if (strcmp(cp, msg->req_method)) + goto bad_cseq; + } + hval = get_single_header(msg, "From", "f", &dup_flag); + if (!hval || dup_flag) { + ess->error_field = "From"; + return(-1); + } + ess->from = hval; + hval = get_single_header(msg, "To", "t", &dup_flag); + if (!hval || dup_flag) { + ess->error_field = "To"; + return(-1); + } + ess->to = hval; + hval = get_single_header(msg, "Via", "v", &dup_flag); + if (!hval || dup_flag) { + ess->error_field = "Via"; + return(-1); + } + ess->via = hval; + return(0); +} + +add_resp_basic_headers(msg, ess, req_method) + struct sip_msg_out *msg; + struct uas_parse_hdrs *ess; + char *req_method; +{ + char cseq_str[80]; + int rc; + + rc = out_msg_add_header(msg, "From", ess->from); + if (rc < 0) + return rc; + rc = out_msg_add_header(msg, "To", ess->to); + if (rc < 0) + return rc; + rc = out_msg_add_header(msg, "Call-ID", ess->call_id); + if (rc < 0) + return rc; + sprintf(cseq_str, "%u %.64s", ess->cseq_num, req_method); + rc = out_msg_add_header(msg, "CSeq", cseq_str); + if (rc < 0) + return rc; + return out_msg_add_header(msg, "Via", ess->via); +} diff -r 000000000000 -r 35c0d9f03c0a libsip/uas_basic.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/uas_basic.h Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,14 @@ +/* + * Here we define struct uas_parse_hdrs, a structure that captures + * the 5 essential header fields that must be present in every + * received request in order for us to be able to respond to it. + */ + +struct uas_parse_hdrs { + char *from; + char *to; + char *call_id; + unsigned cseq_num; + char *via; + char *error_field; +}; diff -r 000000000000 -r 35c0d9f03c0a libsip/uri_utils.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libsip/uri_utils.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,32 @@ +/* + * Some utility functions for working with SIP URIs. + */ + +#include +#include + +user_from_sip_uri(uri, outbuf, maxlen) + char *uri, *outbuf; + unsigned maxlen; +{ + char *cp, *dp; + unsigned n; + + if (strncasecmp(uri, "sip:", 4)) + return(-1); + cp = uri + 4; + dp = outbuf; + n = 0; + for (;;) { + if (!*cp) + return(-1); + if (*cp == '@') + break; + if (n >= maxlen) + return(-2); + *dp++ = *cp++; + n++; + } + *dp = '\0'; + return(0); +} diff -r 000000000000 -r 35c0d9f03c0a libutil/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libutil/Makefile Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,13 @@ +CC= gcc +CFLAGS= -O2 +OBJS= bitfunc.o crc8gen.o sockinit.o tfo_msg_enc.o +LIB= libutil.a + +all: ${LIB} + +${LIB}: ${OBJS} + ar rcu $@ ${OBJS} + ranlib $@ + +clean: + rm -f *.[oa] errs diff -r 000000000000 -r 35c0d9f03c0a libutil/bitfunc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libutil/bitfunc.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,14 @@ +/* + * Some bit manipulation functions. + */ + +#include +#include "osmo_bits.h" + +void uint16_to_bits(uint16_t word, ubit_t *out, unsigned nbits) +{ + unsigned n; + + for (n = 0; n < nbits; n++) + out[n] = (word >> (nbits-n-1)) & 1; +} diff -r 000000000000 -r 35c0d9f03c0a libutil/crc8gen.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libutil/crc8gen.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,115 @@ +/*! \file crc8gen.c + * Osmocom generic CRC routines (for max 8 bits poly). */ +/* + * Copyright (C) 2011 Sylvain Munaut + * + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/*! \addtogroup crc + * @{ + * Osmocom generic CRC routines (for max 8 bits poly). + * + * \file crc8gen.c.tpl */ + +#include +#include "osmo_bits.h" + +/*! Compute the CRC value of a given array of hard-bits + * \param[in] code The CRC code description to apply + * \param[in] in Array of hard bits + * \param[in] len Length of the array of hard bits + * \returns The CRC value + */ +uint8_t +osmo_crc8gen_compute_bits(const struct osmo_crc8gen_code *code, + const ubit_t *in, int len) +{ + const uint8_t poly = code->poly; + uint8_t crc = code->init; + int i, n = code->bits-1; + + for (i=0; ibits) - 1; + } + + crc ^= code->remainder; + + return crc; +} + + +/*! Checks the CRC value of a given array of hard-bits + * \param[in] code The CRC code description to apply + * \param[in] in Array of hard bits + * \param[in] len Length of the array of hard bits + * \param[in] crc_bits Array of hard bits with the alleged CRC + * \returns 0 if CRC matches. 1 in case of error. + * + * The crc_bits array must have a length of code->len + */ +int +osmo_crc8gen_check_bits(const struct osmo_crc8gen_code *code, + const ubit_t *in, int len, const ubit_t *crc_bits) +{ + uint8_t crc; + int i; + + crc = osmo_crc8gen_compute_bits(code, in, len); + + for (i=0; ibits; i++) + if (crc_bits[i] ^ ((crc >> (code->bits-i-1)) & 1)) + return 1; + + return 0; +} + + +/*! Computes and writes the CRC value of a given array of bits + * \param[in] code The CRC code description to apply + * \param[in] in Array of hard bits + * \param[in] len Length of the array of hard bits + * \param[in] crc_bits Array of hard bits to write the computed CRC to + * + * The crc_bits array must have a length of code->len + */ +void +osmo_crc8gen_set_bits(const struct osmo_crc8gen_code *code, + const ubit_t *in, int len, ubit_t *crc_bits) +{ + uint8_t crc; + int i; + + crc = osmo_crc8gen_compute_bits(code, in, len); + + for (i=0; ibits; i++) + crc_bits[i] = ((crc >> (code->bits-i-1)) & 1); +} + +/*! @} */ + +/* vim: set syntax=c: */ diff -r 000000000000 -r 35c0d9f03c0a libutil/osmo_bits.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libutil/osmo_bits.h Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,35 @@ +/* + * This include file has been put together from Osmocom (specifically + * libosmocore) header files, containing definitions for bit vector + * manipulation and CRC functions. + */ + +/* from bits.h */ + +/*! unpacked bit (0 or 1): 1 bit per byte */ +typedef uint8_t ubit_t; + +/* from crc8gen.h */ + +/*! structure describing a given CRC code of max 8 bits */ +struct osmo_crc8gen_code { + int bits; /*!< Actual number of bits of the CRC */ + uint8_t poly; /*!< Polynom (normal representation, MSB omitted */ + uint8_t init; /*!< Initialization value of the CRC state */ + uint8_t remainder; /*!< Remainder of the CRC (final XOR) */ +}; + +uint8_t osmo_crc8gen_compute_bits(const struct osmo_crc8gen_code *code, + const ubit_t *in, int len); +int osmo_crc8gen_check_bits(const struct osmo_crc8gen_code *code, + const ubit_t *in, int len, const ubit_t *crc_bits); +void osmo_crc8gen_set_bits(const struct osmo_crc8gen_code *code, + const ubit_t *in, int len, ubit_t *crc_bits); + +/* + * Themyscira-added (not Osmocom-originating) functions + * that use the ubit_t defined type that (to our knowledge) + * was invented by Osmocom. + */ + +void uint16_to_bits(uint16_t word, ubit_t *out, unsigned nbits); diff -r 000000000000 -r 35c0d9f03c0a libutil/sockinit.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libutil/sockinit.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,40 @@ +/* + * This library module implements a function that helps initialize + * sockaddr for bind or connect operations on UNIX domain sockets. + * + * Back when I programmed under 4.3BSD UNIX, this operation was simple + * and straightforward - but under "modern" Unixes, it appears to be + * a complex affair, given the messy code (originally copied from + * Osmocom) that appears in FreeCalypso host tools for the rvinterf + * local socket interface. Hence I am factoring that mess out into + * its own library function this time around. + */ + +#include +#include +#include +#include +#include + +void +fill_sockaddr_un(pathname, sunp, lenp) + char *pathname; + struct sockaddr_un *sunp; + unsigned *lenp; +{ + /* local socket binding voodoo copied from osmocon */ + sunp->sun_family = AF_UNIX; + strncpy(sunp->sun_path, pathname, sizeof(sunp->sun_path)); + sunp->sun_path[sizeof(sunp->sun_path) - 1] = '\0'; + /* we use the same magic that X11 uses in Xtranssock.c for + * calculating the proper length of the sockaddr */ +#if defined(BSD44SOCKETS) || defined(__UNIXWARE__) + sunp->sun_len = strlen(sunp->sun_path); +#endif +#if defined(BSD44SOCKETS) || defined(SUN_LEN) + *lenp = SUN_LEN(sunp); +#else + *lenp = strlen(sunp->sun_path) + + offsetof(struct sockaddr_un, sun_path) + 1; +#endif +} diff -r 000000000000 -r 35c0d9f03c0a libutil/tfo_msg_enc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libutil/tfo_msg_enc.c Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,33 @@ +/* + * The function implemented in this module encodes a GSM 08.62 Extension_Block. + */ + +#include +#include "osmo_bits.h" + +/* + * EFR TRAU parity + * + * g(x) = x^3 + x^1 + 1 + */ +static const struct osmo_crc8gen_code gsm0860_efr_crc3 = { + .bits = 3, + .poly = 0x3, + .init = 0x0, + .remainder = 0x7, +}; + +void +encode_tfo_ext_words(bits_2_10, bits_12_15, ex, out) + unsigned bits_2_10, bits_12_15, ex; + uint16_t *out; +{ + ubit_t crc_in[13]; + uint8_t crc; + + uint16_to_bits(bits_2_10, crc_in, 9); + uint16_to_bits(bits_12_15, crc_in + 9, 4); + crc = osmo_crc8gen_compute_bits(&gsm0860_efr_crc3, crc_in, 13); + out[0] = bits_2_10; + out[1] = (bits_12_15 << 5) | (crc << 2) | ex; +} diff -r 000000000000 -r 35c0d9f03c0a test-voice/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-voice/Makefile Sun Mar 03 23:20:19 2024 -0800 @@ -0,0 +1,18 @@ +CC= gcc +CFLAGS= -O2 +PROG= sipout-test-voice +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 000000000000 -r 35c0d9f03c0a test-voice/bye_in.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-voice/bye_in.c Sun Mar 03 23:20: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 000000000000 -r 35c0d9f03c0a test-voice/disc_cmd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-voice/disc_cmd.c Sun Mar 03 23:20: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 000000000000 -r 35c0d9f03c0a test-voice/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-voice/main.c Sun Mar 03 23:20: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 000000000000 -r 35c0d9f03c0a test-voice/readconf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-voice/readconf.c Sun Mar 03 23:20: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 000000000000 -r 35c0d9f03c0a test-voice/reinvite.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-voice/reinvite.c Sun Mar 03 23:20: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 000000000000 -r 35c0d9f03c0a test-voice/rtp_rx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-voice/rtp_rx.c Sun Mar 03 23:20: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 000000000000 -r 35c0d9f03c0a test-voice/rtp_tx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-voice/rtp_tx.c Sun Mar 03 23:20: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 000000000000 -r 35c0d9f03c0a test-voice/sdp_in.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-voice/sdp_in.c Sun Mar 03 23:20: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 000000000000 -r 35c0d9f03c0a test-voice/sip_log.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-voice/sip_log.c Sun Mar 03 23:20: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 000000000000 -r 35c0d9f03c0a test-voice/sip_udp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-voice/sip_udp.c Sun Mar 03 23:20: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 000000000000 -r 35c0d9f03c0a test-voice/uac.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-voice/uac.c Sun Mar 03 23:20: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 000000000000 -r 35c0d9f03c0a test-voice/uas.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-voice/uas.c Sun Mar 03 23:20: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 000000000000 -r 35c0d9f03c0a test-voice/user_cmd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-voice/user_cmd.c Sun Mar 03 23:20: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"); +}