From c696006923ffbf8c82620e41a0db3e7d5bb957de Mon Sep 17 00:00:00 2001 From: Mark Spencer Date: Thu, 11 May 2006 14:56:52 +0000 Subject: Go ahead and merge STUN mods for RTP in preparation for some STUN support in SIP. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@26920 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- rtp.c | 316 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 307 insertions(+), 9 deletions(-) (limited to 'rtp.c') diff --git a/rtp.c b/rtp.c index 208e58287..05f4cdee1 100644 --- a/rtp.c +++ b/rtp.c @@ -68,6 +68,7 @@ static int dtmftimeout = DEFAULT_DTMF_TIMEOUT; static int rtpstart = 0; /*!< First port for RTP sessions (set in rtp.conf) */ static int rtpend = 0; /*!< Last port for RTP sessions (set in rtp.conf) */ static int rtpdebug = 0; /*!< Are we debugging? */ +static int stundebug = 0; /*!< Are we debugging stun? */ static struct sockaddr_in rtpdebugaddr; /*!< Debug packets to/from this host */ #ifdef SO_NO_CHECK static int nochecksums = 0; @@ -143,6 +144,260 @@ struct ast_rtcp { struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */ }; + +typedef struct { unsigned int id[4]; } __attribute__((packed)) stun_trans_id; + +/* XXX Maybe stun belongs in another file if it ever has use outside of RTP */ +struct stun_header { + unsigned short msgtype; + unsigned short msglen; + stun_trans_id id; + unsigned char ies[0]; +} __attribute__((packed)); + +struct stun_attr { + unsigned short attr; + unsigned short len; + unsigned char value[0]; +} __attribute__((packed)); + +struct stun_addr { + unsigned char unused; + unsigned char family; + unsigned short port; + unsigned int addr; +} __attribute__((packed)); + +#define STUN_IGNORE (0) +#define STUN_ACCEPT (1) + +#define STUN_BINDREQ 0x0001 +#define STUN_BINDRESP 0x0101 +#define STUN_BINDERR 0x0111 +#define STUN_SECREQ 0x0002 +#define STUN_SECRESP 0x0102 +#define STUN_SECERR 0x0112 + +#define STUN_MAPPED_ADDRESS 0x0001 +#define STUN_RESPONSE_ADDRESS 0x0002 +#define STUN_CHANGE_REQUEST 0x0003 +#define STUN_SOURCE_ADDRESS 0x0004 +#define STUN_CHANGED_ADDRESS 0x0005 +#define STUN_USERNAME 0x0006 +#define STUN_PASSWORD 0x0007 +#define STUN_MESSAGE_INTEGRITY 0x0008 +#define STUN_ERROR_CODE 0x0009 +#define STUN_UNKNOWN_ATTRIBUTES 0x000a +#define STUN_REFLECTED_FROM 0x000b + +static const char *stun_msg2str(int msg) +{ + switch(msg) { + case STUN_BINDREQ: + return "Binding Request"; + case STUN_BINDRESP: + return "Binding Response"; + case STUN_BINDERR: + return "Binding Error Response"; + case STUN_SECREQ: + return "Shared Secret Request"; + case STUN_SECRESP: + return "Shared Secret Response"; + case STUN_SECERR: + return "Shared Secret Error Response"; + } + return "Non-RFC3489 Message"; +} + +static const char *stun_attr2str(int msg) +{ + switch(msg) { + case STUN_MAPPED_ADDRESS: + return "Mapped Address"; + case STUN_RESPONSE_ADDRESS: + return "Response Address"; + case STUN_CHANGE_REQUEST: + return "Change Request"; + case STUN_SOURCE_ADDRESS: + return "Source Address"; + case STUN_CHANGED_ADDRESS: + return "Changed Address"; + case STUN_USERNAME: + return "Username"; + case STUN_PASSWORD: + return "Password"; + case STUN_MESSAGE_INTEGRITY: + return "Message Integrity"; + case STUN_ERROR_CODE: + return "Error Code"; + case STUN_UNKNOWN_ATTRIBUTES: + return "Unknown Attributes"; + case STUN_REFLECTED_FROM: + return "Reflected From"; + } + return "Non-RFC3489 Attribute"; +} + +struct stun_state { + unsigned char *username; + unsigned char *password; +}; + +static int stun_process_attr(struct stun_state *state, struct stun_attr *attr) +{ + if (stundebug) + ast_verbose("Found STUN Attribute %s (%04x), length %d\n", + stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len)); + switch(ntohs(attr->attr)) { + case STUN_USERNAME: + state->username = (unsigned char *)(attr->value); + break; + case STUN_PASSWORD: + state->password = (unsigned char *)(attr->value); + break; + default: + if (stundebug) + ast_verbose("Ignoring STUN attribute %s (%04x), length %d\n", + stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len)); + } + return 0; +} + +static void append_attr_string(struct stun_attr **attr, int attrval, const char *s, int *len, int *left) +{ + int size = sizeof(**attr) + strlen(s); + if (*left > size) { + (*attr)->attr = htons(attrval); + (*attr)->len = htons(strlen(s)); + memcpy((*attr)->value, s, strlen(s)); + (*attr) = (struct stun_attr *)((*attr)->value + strlen(s)); + *len += size; + *left -= size; + } +} + +static void append_attr_address(struct stun_attr **attr, int attrval, struct sockaddr_in *sin, int *len, int *left) +{ + int size = sizeof(**attr) + 8; + struct stun_addr *addr; + if (*left > size) { + (*attr)->attr = htons(attrval); + (*attr)->len = htons(8); + addr = (struct stun_addr *)((*attr)->value); + addr->unused = 0; + addr->family = 0x01; + addr->port = sin->sin_port; + addr->addr = sin->sin_addr.s_addr; + (*attr) = (struct stun_attr *)((*attr)->value + 8); + *len += size; + *left -= size; + } +} + +static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp) +{ + return sendto(s, resp, ntohs(resp->msglen) + sizeof(*resp), 0, dst, sizeof(*dst)); +} + +static void stun_req_id(struct stun_header *req) +{ + int x; + for (x=0;x<4;x++) + req->id.id[x] = ast_random(); +} + +void ast_rtp_stun_request(struct ast_rtp *rtp, struct sockaddr_in *suggestion, const char *username) +{ + struct stun_header *req; + unsigned char reqdata[1024]; + int reqlen, reqleft; + struct stun_attr *attr; + + req = (struct stun_header *)reqdata; + stun_req_id(req); + reqlen = 0; + reqleft = sizeof(reqdata) - sizeof(struct stun_header); + req->msgtype = 0; + req->msglen = 0; + attr = (struct stun_attr *)req->ies; + if (username) + append_attr_string(&attr, STUN_USERNAME, username, &reqlen, &reqleft); + req->msglen = htons(reqlen); + req->msgtype = htons(STUN_BINDREQ); + stun_send(rtp->s, suggestion, req); +} + +static int stun_handle_packet(int s, struct sockaddr_in *src, unsigned char *data, int len) +{ + struct stun_header *resp, *hdr = (struct stun_header *)data; + struct stun_attr *attr; + struct stun_state st; + int ret = STUN_IGNORE; + unsigned char respdata[1024]; + int resplen, respleft; + + if (len < sizeof(struct stun_header)) { + ast_log(LOG_DEBUG, "Runt STUN packet (only %d, wanting at least %d)\n", len, sizeof(struct stun_header)); + return -1; + } + if (stundebug) + ast_verbose("STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), ntohs(hdr->msgtype), ntohs(hdr->msglen)); + if (ntohs(hdr->msglen) > len - sizeof(struct stun_header)) { + ast_log(LOG_DEBUG, "Scrambled STUN packet length (got %d, expecting %d)\n", ntohs(hdr->msglen), len - sizeof(struct stun_header)); + } else + len = ntohs(hdr->msglen); + data += sizeof(struct stun_header); + memset(&st, 0, sizeof(st)); + while(len) { + if (len < sizeof(struct stun_attr)) { + ast_log(LOG_DEBUG, "Runt Attribute (got %d, expecting %d)\n", len, sizeof(struct stun_attr)); + break; + } + attr = (struct stun_attr *)data; + if (ntohs(attr->len) > len) { + ast_log(LOG_DEBUG, "Inconsistant Attribute (length %d exceeds remaining msg len %d)\n", ntohs(attr->len), len); + break; + } + if (stun_process_attr(&st, attr)) { + ast_log(LOG_DEBUG, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr)); + break; + } + /* Clear attribute in case previous entry was a string */ + attr->attr = 0; + data += ntohs(attr->len) + sizeof(struct stun_attr); + len -= ntohs(attr->len) + sizeof(struct stun_attr); + } + /* Null terminate any string */ + *data = '\0'; + resp = (struct stun_header *)respdata; + resplen = 0; + respleft = sizeof(respdata) - sizeof(struct stun_header); + resp->id = hdr->id; + resp->msgtype = 0; + resp->msglen = 0; + attr = (struct stun_attr *)resp->ies; + if (!len) { + switch(ntohs(hdr->msgtype)) { + case STUN_BINDREQ: + if (stundebug) + ast_verbose("STUN Bind Request, username: %s\n", + st.username ? (const char *)st.username : ""); + if (st.username) + append_attr_string(&attr, STUN_USERNAME, st.username, &resplen, &respleft); + append_attr_address(&attr, STUN_MAPPED_ADDRESS, src, &resplen, &respleft); + resp->msglen = htons(resplen); + resp->msgtype = htons(STUN_BINDRESP); + stun_send(s, src, resp); + ret = STUN_ACCEPT; + break; + default: + if (stundebug) + ast_verbose("Dunno what to do with STUN message %04x (%s)\n", ntohs(hdr->msgtype), stun_msg2str(ntohs(hdr->msgtype))); + } + } + return ret; +} + /*! \brief List of current sessions */ static AST_LIST_HEAD_STATIC(protos, ast_rtp_protocol); @@ -442,7 +697,6 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0, (struct sockaddr *)&sin, &len); - rtpheader = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET); if (res < 0) { if (errno != EAGAIN) @@ -456,6 +710,21 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) return &ast_null_frame; } + /* Get fields */ + seqno = ntohl(rtpheader[0]); + + /* Check RTP version */ + version = (seqno & 0xC0000000) >> 30; + if (!version) { + if ((stun_handle_packet(rtp->s, &sin, rtp->rawdata + AST_FRIENDLY_OFFSET, res) == STUN_ACCEPT) && + (!rtp->them.sin_port && !rtp->them.sin_addr.s_addr)) { + memcpy(&rtp->them, &sin, sizeof(rtp->them)); + } + return &ast_null_frame; + } + + if (version != 2) + return &ast_null_frame; /* Ignore if the other side hasn't been given an address yet. */ if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port) @@ -473,14 +742,6 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) } } - /* Get fields */ - seqno = ntohl(rtpheader[0]); - - /* Check RTP version */ - version = (seqno & 0xC0000000) >> 30; - if (version != 2) - return &ast_null_frame; - payloadtype = (seqno & 0x7f0000) >> 16; padding = seqno & (1 << 29); mark = seqno & (1 << 23); @@ -1912,6 +2173,26 @@ static int rtp_no_debug(int fd, int argc, char *argv[]) return RESULT_SUCCESS; } +static int stun_do_debug(int fd, int argc, char *argv[]) +{ + if(argc != 2) { + return RESULT_SHOWUSAGE; + } + stundebug = 1; + ast_cli(fd, "STUN Debugging Enabled\n"); + return RESULT_SUCCESS; +} + +static int stun_no_debug(int fd, int argc, char *argv[]) +{ + if(argc !=3) + return RESULT_SHOWUSAGE; + stundebug = 0; + ast_cli(fd,"STUN Debugging Disabled\n"); + return RESULT_SUCCESS; +} + + static char debug_usage[] = "Usage: rtp debug [ip host[:port]]\n" " Enable dumping of all RTP packets to and from host.\n"; @@ -1920,6 +2201,15 @@ static char no_debug_usage[] = "Usage: rtp no debug\n" " Disable all RTP debugging\n"; +static char stun_debug_usage[] = + "Usage: stun debug\n" + " Enable STUN (Simple Traversal of UDP through NATs) debugging\n"; + +static char stun_no_debug_usage[] = + "Usage: stun no debug\n" + " Disable STUN debugging\n"; + + static struct ast_cli_entry cli_debug_ip = {{ "rtp", "debug", "ip", NULL } , rtp_do_debug, "Enable RTP debugging on IP", debug_usage }; @@ -1929,6 +2219,12 @@ static struct ast_cli_entry cli_debug = static struct ast_cli_entry cli_no_debug = {{ "rtp", "no", "debug", NULL } , rtp_no_debug, "Disable RTP debugging", no_debug_usage }; +static struct ast_cli_entry cli_stun_debug = +{{ "stun", "debug", NULL } , stun_do_debug, "Enable STUN debugging", stun_debug_usage }; + +static struct ast_cli_entry cli_stun_no_debug = +{{ "stun", "no", "debug", NULL } , stun_no_debug, "Disable STUN debugging", stun_no_debug_usage }; + int ast_rtp_reload(void) { struct ast_config *cfg; @@ -1990,5 +2286,7 @@ void ast_rtp_init(void) ast_cli_register(&cli_debug); ast_cli_register(&cli_debug_ip); ast_cli_register(&cli_no_debug); + ast_cli_register(&cli_stun_debug); + ast_cli_register(&cli_stun_no_debug); ast_rtp_reload(); } -- cgit v1.2.3