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 <time.h> 25#include <stdlib.h> 26#include <sys/types.h> 27#include <sys/socket.h> 28#include <netinet/in.h> 29#include <string.h> 30 31#include <pulse/xmalloc.h> 32#include <pulse/util.h> 33 34#include <pulsecore/core-util.h> 35#include <pulsecore/log.h> 36#include <pulsecore/macro.h> 37#include <pulsecore/arpa-inet.h> 38 39#include "sdp.h" 40#include "rtp.h" 41 42char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, uint16_t port, uint8_t payload, const pa_sample_spec *ss, bool enable_opus) { 43 uint32_t ntp; 44 uint32_t rate, channels; 45 char buf_src[64], buf_dst[64], un[64]; 46 const char *u, *f; 47 48 pa_assert(src); 49 pa_assert(dst); 50 51#ifdef HAVE_IPV6 52 pa_assert(af == AF_INET || af == AF_INET6); 53#else 54 pa_assert(af == AF_INET); 55#endif 56 57 if (enable_opus) { 58 f = "OPUS"; 59 rate = 48000; 60 channels = 2; 61 } else { 62 pa_assert_se(f = pa_rtp_format_to_string(ss->format)); 63 rate = ss->rate; 64 channels = ss->channels; 65 } 66 67 if (!(u = pa_get_user_name(un, sizeof(un)))) 68 u = "-"; 69 70 ntp = (uint32_t) time(NULL) + 2208988800U; 71 72 pa_assert_se(inet_ntop(af, src, buf_src, sizeof(buf_src))); 73 pa_assert_se(inet_ntop(af, dst, buf_dst, sizeof(buf_dst))); 74 75 return pa_sprintf_malloc( 76 PA_SDP_HEADER 77 "o=%s %lu 0 IN %s %s\n" 78 "s=%s\n" 79 "c=IN %s %s\n" 80 "t=%lu 0\n" 81 "a=recvonly\n" 82 "m=audio %u RTP/AVP %i\n" 83 "a=rtpmap:%i %s/%u/%u\n" 84 "a=type:broadcast\n", 85 u, (unsigned long) ntp, af == AF_INET ? "IP4" : "IP6", buf_src, 86 name, 87 af == AF_INET ? "IP4" : "IP6", buf_dst, 88 (unsigned long) ntp, 89 port, payload, 90 payload, f, rate, channels); 91} 92 93static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) { 94 unsigned rate, channels; 95 pa_assert(ss); 96 pa_assert(c); 97 98 if (pa_startswith(c, "L16/")) { 99 ss->format = PA_SAMPLE_S16BE; 100 c += 4; 101 } else if (pa_startswith(c, "OPUS/")) { 102 ss->format = PA_SAMPLE_S16LE; 103 c += 5; 104 } else 105 return NULL; 106 107 if (sscanf(c, "%u/%u", &rate, &channels) == 2) { 108 ss->rate = (uint32_t) rate; 109 ss->channels = (uint8_t) channels; 110 } else if (sscanf(c, "%u", &rate) == 2) { 111 ss->rate = (uint32_t) rate; 112 ss->channels = 1; 113 } else 114 return NULL; 115 116 if (!pa_sample_spec_valid(ss)) 117 return NULL; 118 119 return ss; 120} 121 122pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) { 123 uint16_t port = 0; 124 bool ss_valid = false; 125 126 pa_assert(t); 127 pa_assert(i); 128 129 i->origin = i->session_name = NULL; 130 i->salen = 0; 131 i->payload = 255; 132 i->enable_opus = false; 133 134 if (!pa_startswith(t, PA_SDP_HEADER)) { 135 pa_log("Failed to parse SDP data: invalid header."); 136 goto fail; 137 } 138 139 t += sizeof(PA_SDP_HEADER)-1; 140 141 while (*t) { 142 size_t l; 143 144 l = strcspn(t, "\n"); 145 146 if (l <= 2) { 147 pa_log("Failed to parse SDP data: line too short: >%s<.", t); 148 goto fail; 149 } 150 151 if (pa_startswith(t, "o=")) 152 i->origin = pa_xstrndup(t+2, l-2); 153 else if (pa_startswith(t, "s=")) 154 i->session_name = pa_xstrndup(t+2, l-2); 155 else if (pa_startswith(t, "c=IN IP4 ")) { 156 char a[64]; 157 size_t k; 158 159 k = l-8 > sizeof(a) ? sizeof(a) : l-8; 160 161 pa_strlcpy(a, t+9, k); 162 a[strcspn(a, "/")] = 0; 163 164 if (inet_pton(AF_INET, a, &((struct sockaddr_in*) &i->sa)->sin_addr) <= 0) { 165 pa_log("Failed to parse SDP data: bad address: >%s<.", a); 166 goto fail; 167 } 168 169 ((struct sockaddr_in*) &i->sa)->sin_family = AF_INET; 170 ((struct sockaddr_in*) &i->sa)->sin_port = 0; 171 i->salen = sizeof(struct sockaddr_in); 172#ifdef HAVE_IPV6 173 } else if (pa_startswith(t, "c=IN IP6 ")) { 174 char a[64]; 175 size_t k; 176 177 k = l-8 > sizeof(a) ? sizeof(a) : l-8; 178 179 pa_strlcpy(a, t+9, k); 180 a[strcspn(a, "/")] = 0; 181 182 if (inet_pton(AF_INET6, a, &((struct sockaddr_in6*) &i->sa)->sin6_addr) <= 0) { 183 pa_log("Failed to parse SDP data: bad address: >%s<.", a); 184 goto fail; 185 } 186 187 ((struct sockaddr_in6*) &i->sa)->sin6_family = AF_INET6; 188 ((struct sockaddr_in6*) &i->sa)->sin6_port = 0; 189 i->salen = sizeof(struct sockaddr_in6); 190#endif 191 } else if (pa_startswith(t, "m=audio ")) { 192 193 if (i->payload > 127) { 194 int _port, _payload; 195 196 if (sscanf(t+8, "%i RTP/AVP %i", &_port, &_payload) == 2) { 197 198 if (_port <= 0 || _port > 0xFFFF) { 199 pa_log("Failed to parse SDP data: invalid port %i.", _port); 200 goto fail; 201 } 202 203 if (_payload < 0 || _payload > 127) { 204 pa_log("Failed to parse SDP data: invalid payload %i.", _payload); 205 goto fail; 206 } 207 208 port = (uint16_t) _port; 209 i->payload = (uint8_t) _payload; 210 211 if (pa_rtp_sample_spec_from_payload(i->payload, &i->sample_spec)) 212 ss_valid = true; 213 } 214 } 215 } else if (pa_startswith(t, "a=rtpmap:")) { 216 217 if (i->payload <= 127) { 218 char c[64]; 219 int _payload; 220 int len; 221 222 if (sscanf(t + 9, "%i %n", &_payload, &len) == 1) { 223 if (_payload < 0 || _payload > 127) { 224 pa_log("Failed to parse SDP data: invalid payload %i.", _payload); 225 goto fail; 226 } 227 if (_payload == i->payload) { 228 strncpy(c, t + 9 + len, 63); 229 c[63] = 0; 230 c[strcspn(c, "\n")] = 0; 231 232 if (parse_sdp_sample_spec(&i->sample_spec, c)) 233 ss_valid = true; 234 235 if (pa_startswith(c, "OPUS/")) 236 i->enable_opus = true; 237 } 238 } 239 } 240 } 241 242 t += l; 243 244 if (*t == '\n') 245 t++; 246 } 247 248 if (!i->origin || (!is_goodbye && (!i->salen || i->payload > 127 || !ss_valid || port == 0))) { 249 pa_log("Failed to parse SDP data: missing data."); 250 goto fail; 251 } 252 253 if (((struct sockaddr*) &i->sa)->sa_family == AF_INET) 254 ((struct sockaddr_in*) &i->sa)->sin_port = htons(port); 255 else 256 ((struct sockaddr_in6*) &i->sa)->sin6_port = htons(port); 257 258 return i; 259 260fail: 261 pa_xfree(i->origin); 262 pa_xfree(i->session_name); 263 264 return NULL; 265} 266 267void pa_sdp_info_destroy(pa_sdp_info *i) { 268 pa_assert(i); 269 270 pa_xfree(i->origin); 271 pa_xfree(i->session_name); 272} 273