xref: /third_party/pulseaudio/src/modules/rtp/sap.c (revision 53a5a1b3)
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