1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2006 Lennart Poettering 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as published 8 by the Free Software Foundation; either version 2.1 of the License, 9 or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <stdlib.h> 25#include <sys/types.h> 26#include <sys/socket.h> 27#include <netinet/in.h> 28#include <errno.h> 29#include <string.h> 30#include <unistd.h> 31#include <sys/ioctl.h> 32 33#ifdef HAVE_SYS_FILIO_H 34#include <sys/filio.h> 35#endif 36 37#ifdef HAVE_SYS_UIO_H 38#include <sys/uio.h> 39#endif 40 41#include <pulse/xmalloc.h> 42 43#include <pulsecore/core-error.h> 44#include <pulsecore/core-util.h> 45#include <pulsecore/log.h> 46#include <pulsecore/macro.h> 47#include <pulsecore/arpa-inet.h> 48 49#include "sap.h" 50#include "sdp.h" 51 52#define MIME_TYPE "application/sdp" 53 54pa_sap_context* pa_sap_context_init_send(pa_sap_context *c, int fd, char *sdp_data) { 55 pa_assert(c); 56 pa_assert(fd >= 0); 57 pa_assert(sdp_data); 58 59 c->fd = fd; 60 c->sdp_data = sdp_data; 61 c->msg_id_hash = (uint16_t) (rand()*rand()); 62 63 return c; 64} 65 66void pa_sap_context_destroy(pa_sap_context *c) { 67 pa_assert(c); 68 69 pa_close(c->fd); 70 pa_xfree(c->sdp_data); 71} 72 73int pa_sap_send(pa_sap_context *c, bool goodbye) { 74 uint32_t header; 75 struct sockaddr_storage sa_buf; 76 struct sockaddr *sa = (struct sockaddr*) &sa_buf; 77 socklen_t salen = sizeof(sa_buf); 78 struct iovec iov[4]; 79 struct msghdr m; 80 ssize_t k; 81 82 if (getsockname(c->fd, sa, &salen) < 0) { 83 pa_log("getsockname() failed: %s\n", pa_cstrerror(errno)); 84 return -1; 85 } 86 87#ifdef HAVE_IPV6 88 pa_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6); 89#else 90 pa_assert(sa->sa_family == AF_INET); 91#endif 92 93 header = htonl(((uint32_t) 1 << 29) | 94#ifdef HAVE_IPV6 95 (sa->sa_family == AF_INET6 ? (uint32_t) 1 << 28 : 0) | 96#endif 97 (goodbye ? (uint32_t) 1 << 26 : 0) | 98 (c->msg_id_hash)); 99 100 iov[0].iov_base = &header; 101 iov[0].iov_len = sizeof(header); 102 103 if (sa->sa_family == AF_INET) { 104 iov[1].iov_base = (void*) &((struct sockaddr_in*) sa)->sin_addr; 105 iov[1].iov_len = 4U; 106#ifdef HAVE_IPV6 107 } else { 108 iov[1].iov_base = (void*) &((struct sockaddr_in6*) sa)->sin6_addr; 109 iov[1].iov_len = 16U; 110#endif 111 } 112 113 iov[2].iov_base = (char*) MIME_TYPE; 114 iov[2].iov_len = sizeof(MIME_TYPE); 115 116 iov[3].iov_base = c->sdp_data; 117 iov[3].iov_len = strlen(c->sdp_data); 118 119 m.msg_name = NULL; 120 m.msg_namelen = 0; 121 m.msg_iov = iov; 122 m.msg_iovlen = 4; 123 m.msg_control = NULL; 124 m.msg_controllen = 0; 125 m.msg_flags = 0; 126 127 if ((k = sendmsg(c->fd, &m, MSG_DONTWAIT)) < 0) 128 pa_log_warn("sendmsg() failed: %s\n", pa_cstrerror(errno)); 129 130 return (int) k; 131} 132 133pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd) { 134 pa_assert(c); 135 pa_assert(fd >= 0); 136 137 c->fd = fd; 138 c->sdp_data = NULL; 139 return c; 140} 141 142int pa_sap_recv(pa_sap_context *c, bool *goodbye) { 143 struct msghdr m; 144 struct iovec iov; 145 int size; 146 char *buf = NULL, *e; 147 uint32_t header; 148 unsigned six, ac, k; 149 ssize_t r; 150 151 pa_assert(c); 152 pa_assert(goodbye); 153 154 if (ioctl(c->fd, FIONREAD, &size) < 0) { 155 pa_log_warn("FIONREAD failed: %s", pa_cstrerror(errno)); 156 goto fail; 157 } 158 159 buf = pa_xnew(char, (unsigned) size+1); 160 buf[size] = 0; 161 162 iov.iov_base = buf; 163 iov.iov_len = (size_t) size; 164 165 m.msg_name = NULL; 166 m.msg_namelen = 0; 167 m.msg_iov = &iov; 168 m.msg_iovlen = 1; 169 m.msg_control = NULL; 170 m.msg_controllen = 0; 171 m.msg_flags = 0; 172 173 if ((r = recvmsg(c->fd, &m, 0)) != size) { 174 pa_log_warn("recvmsg() failed: %s", r < 0 ? pa_cstrerror(errno) : "size mismatch"); 175 goto fail; 176 } 177 178 if (size < 4) { 179 pa_log_warn("SAP packet too short."); 180 goto fail; 181 } 182 183 memcpy(&header, buf, sizeof(uint32_t)); 184 header = ntohl(header); 185 186 if (header >> 29 != 1) { 187 pa_log_warn("Unsupported SAP version."); 188 goto fail; 189 } 190 191 if ((header >> 25) & 1) { 192 pa_log_warn("Encrypted SAP not supported."); 193 goto fail; 194 } 195 196 if ((header >> 24) & 1) { 197 pa_log_warn("Compressed SAP not supported."); 198 goto fail; 199 } 200 201 six = (header >> 28) & 1U; 202 ac = (header >> 16) & 0xFFU; 203 204 k = 4 + (six ? 16U : 4U) + ac*4U; 205 if ((unsigned) size < k) { 206 pa_log_warn("SAP packet too short (AD)."); 207 goto fail; 208 } 209 210 e = buf + k; 211 size -= (int) k; 212 213 if ((unsigned) size >= sizeof(MIME_TYPE) && pa_streq(e, MIME_TYPE)) { 214 e += sizeof(MIME_TYPE); 215 size -= (int) sizeof(MIME_TYPE); 216 } else if ((unsigned) size < sizeof(PA_SDP_HEADER)-1 || strncmp(e, PA_SDP_HEADER, sizeof(PA_SDP_HEADER)-1)) { 217 pa_log_warn("Invalid SDP header."); 218 goto fail; 219 } 220 221 if (c->sdp_data) 222 pa_xfree(c->sdp_data); 223 224 c->sdp_data = pa_xstrndup(e, (unsigned) size); 225 pa_xfree(buf); 226 227 *goodbye = !!((header >> 26) & 1); 228 229 return 0; 230 231fail: 232 pa_xfree(buf); 233 234 return -1; 235} 236