/*** This file is part of PulseAudio. Copyright 2006 Lennart Poettering PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. PulseAudio 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 Lesser General Public License along with PulseAudio; if not, see . ***/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_FILIO_H #include #endif #ifdef HAVE_SYS_UIO_H #include #endif #include #include #include #include #include #include #include "sap.h" #include "sdp.h" #define MIME_TYPE "application/sdp" pa_sap_context* pa_sap_context_init_send(pa_sap_context *c, int fd, char *sdp_data) { pa_assert(c); pa_assert(fd >= 0); pa_assert(sdp_data); c->fd = fd; c->sdp_data = sdp_data; c->msg_id_hash = (uint16_t) (rand()*rand()); return c; } void pa_sap_context_destroy(pa_sap_context *c) { pa_assert(c); pa_close(c->fd); pa_xfree(c->sdp_data); } int pa_sap_send(pa_sap_context *c, bool goodbye) { uint32_t header; struct sockaddr_storage sa_buf; struct sockaddr *sa = (struct sockaddr*) &sa_buf; socklen_t salen = sizeof(sa_buf); struct iovec iov[4]; struct msghdr m; ssize_t k; if (getsockname(c->fd, sa, &salen) < 0) { pa_log("getsockname() failed: %s\n", pa_cstrerror(errno)); return -1; } #ifdef HAVE_IPV6 pa_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); #else pa_assert(sa->sa_family == AF_INET); #endif header = htonl(((uint32_t) 1 << 29) | #ifdef HAVE_IPV6 (sa->sa_family == AF_INET6 ? (uint32_t) 1 << 28 : 0) | #endif (goodbye ? (uint32_t) 1 << 26 : 0) | (c->msg_id_hash)); iov[0].iov_base = &header; iov[0].iov_len = sizeof(header); if (sa->sa_family == AF_INET) { iov[1].iov_base = (void*) &((struct sockaddr_in*) sa)->sin_addr; iov[1].iov_len = 4U; #ifdef HAVE_IPV6 } else { iov[1].iov_base = (void*) &((struct sockaddr_in6*) sa)->sin6_addr; iov[1].iov_len = 16U; #endif } iov[2].iov_base = (char*) MIME_TYPE; iov[2].iov_len = sizeof(MIME_TYPE); iov[3].iov_base = c->sdp_data; iov[3].iov_len = strlen(c->sdp_data); m.msg_name = NULL; m.msg_namelen = 0; m.msg_iov = iov; m.msg_iovlen = 4; m.msg_control = NULL; m.msg_controllen = 0; m.msg_flags = 0; if ((k = sendmsg(c->fd, &m, MSG_DONTWAIT)) < 0) pa_log_warn("sendmsg() failed: %s\n", pa_cstrerror(errno)); return (int) k; } pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd) { pa_assert(c); pa_assert(fd >= 0); c->fd = fd; c->sdp_data = NULL; return c; } int pa_sap_recv(pa_sap_context *c, bool *goodbye) { struct msghdr m; struct iovec iov; int size; char *buf = NULL, *e; uint32_t header; unsigned six, ac, k; ssize_t r; pa_assert(c); pa_assert(goodbye); if (ioctl(c->fd, FIONREAD, &size) < 0) { pa_log_warn("FIONREAD failed: %s", pa_cstrerror(errno)); goto fail; } buf = pa_xnew(char, (unsigned) size+1); buf[size] = 0; iov.iov_base = buf; iov.iov_len = (size_t) size; m.msg_name = NULL; m.msg_namelen = 0; m.msg_iov = &iov; m.msg_iovlen = 1; m.msg_control = NULL; m.msg_controllen = 0; m.msg_flags = 0; if ((r = recvmsg(c->fd, &m, 0)) != size) { pa_log_warn("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch"); goto fail; } if (size < 4) { pa_log_warn("SAP packet too short."); goto fail; } memcpy(&header, buf, sizeof(uint32_t)); header = ntohl(header); if (header >> 29 != 1) { pa_log_warn("Unsupported SAP version."); goto fail; } if ((header >> 25) & 1) { pa_log_warn("Encrypted SAP not supported."); goto fail; } if ((header >> 24) & 1) { pa_log_warn("Compressed SAP not supported."); goto fail; } six = (header >> 28) & 1U; ac = (header >> 16) & 0xFFU; k = 4 + (six ? 16U : 4U) + ac*4U; if ((unsigned) size < k) { pa_log_warn("SAP packet too short (AD)."); goto fail; } e = buf + k; size -= (int) k; if ((unsigned) size >= sizeof(MIME_TYPE) && pa_streq(e, MIME_TYPE)) { e += sizeof(MIME_TYPE); size -= (int) sizeof(MIME_TYPE); } else if ((unsigned) size < sizeof(PA_SDP_HEADER)-1 || strncmp(e, PA_SDP_HEADER, sizeof(PA_SDP_HEADER)-1)) { pa_log_warn("Invalid SDP header."); goto fail; } if (c->sdp_data) pa_xfree(c->sdp_data); c->sdp_data = pa_xstrndup(e, (unsigned) size); pa_xfree(buf); *goodbye = !!((header >> 26) & 1); return 0; fail: pa_xfree(buf); return -1; }