]> git.sesse.net Git - ffmpeg/blob - libavformat/sctp.c
http: Reduce scope of a variable in parse_content_encoding()
[ffmpeg] / libavformat / sctp.c
1 /*
2  * SCTP protocol
3  * Copyright (c) 2012 Luca Barbato
4  *
5  * This file is part of Libav.
6  *
7  * Libav is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * Libav is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with Libav; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21
22 /**
23  * @file
24  *
25  * sctp url_protocol
26  *
27  * url syntax: sctp://host:port[?option=val...]
28  * option: 'listen'        : listen for an incoming connection
29  *         'max_streams=n' : set the maximum number of streams
30  *         'reuse=1'       : enable reusing the socket [TBD]
31  *
32  * by setting the maximum number of streams the protocol will use the
33  * first two bytes of the incoming/outgoing buffer to store the
34  * stream number of the packet being read/written.
35  * @see sctp_read
36  * @see sctp_write
37  */
38
39
40 #include <netinet/in.h>
41 #include <netinet/sctp.h>
42
43 #include "config.h"
44
45 #if HAVE_POLL_H
46 #include <poll.h>
47 #endif
48
49 #include "libavutil/intreadwrite.h"
50 #include "libavutil/parseutils.h"
51 #include "avformat.h"
52 #include "internal.h"
53 #include "network.h"
54 #include "os_support.h"
55 #include "url.h"
56
57 /*
58  * The sctp_recvmsg and sctp_sendmsg functions are part of the user
59  * library that offers support for the SCTP kernel Implementation.
60  * To avoid build-time clashes the functions sport an ff_-prefix here.
61  * The main purpose of this code is to provide the SCTP Socket API
62  * mappings for user applications to interface with SCTP in the kernel.
63  *
64  * This implementation is based on the Socket API Extensions for SCTP
65  * defined in <draft-ietf-tsvwg-sctpsocket-10.txt>
66  *
67  * Copyright (c) 2003 International Business Machines, Corp.
68  *
69  * Written or modified by:
70  *  Ryan Layer <rmlayer@us.ibm.com>
71  */
72
73 static int ff_sctp_recvmsg(int s, void *msg, size_t len, struct sockaddr *from,
74                            socklen_t *fromlen, struct sctp_sndrcvinfo *sinfo,
75                            int *msg_flags)
76 {
77     int recvb;
78     struct iovec iov;
79     char incmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
80     struct msghdr inmsg  = { 0 };
81     struct cmsghdr *cmsg = NULL;
82
83     iov.iov_base = msg;
84     iov.iov_len  = len;
85
86     inmsg.msg_name       = from;
87     inmsg.msg_namelen    = fromlen ? *fromlen : 0;
88     inmsg.msg_iov        = &iov;
89     inmsg.msg_iovlen     = 1;
90     inmsg.msg_control    = incmsg;
91     inmsg.msg_controllen = sizeof(incmsg);
92
93     if ((recvb = recvmsg(s, &inmsg, msg_flags ? *msg_flags : 0)) < 0)
94         return recvb;
95
96     if (fromlen)
97         *fromlen   = inmsg.msg_namelen;
98     if (msg_flags)
99         *msg_flags = inmsg.msg_flags;
100
101     for (cmsg = CMSG_FIRSTHDR(&inmsg); cmsg != NULL;
102          cmsg = CMSG_NXTHDR(&inmsg, cmsg)) {
103         if ((IPPROTO_SCTP == cmsg->cmsg_level) &&
104             (SCTP_SNDRCV  == cmsg->cmsg_type))
105             break;
106     }
107
108     /* Copy sinfo. */
109     if (cmsg)
110         memcpy(sinfo, CMSG_DATA(cmsg), sizeof(struct sctp_sndrcvinfo));
111
112     return recvb;
113 }
114
115 static int ff_sctp_send(int s, const void *msg, size_t len,
116                         const struct sctp_sndrcvinfo *sinfo, int flags)
117 {
118     struct msghdr outmsg;
119     struct iovec iov;
120
121     outmsg.msg_name       = NULL;
122     outmsg.msg_namelen    = 0;
123     outmsg.msg_iov        = &iov;
124     iov.iov_base          = msg;
125     iov.iov_len           = len;
126     outmsg.msg_iovlen     = 1;
127     outmsg.msg_controllen = 0;
128
129     if (sinfo) {
130         char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
131         struct cmsghdr *cmsg;
132
133         outmsg.msg_control    = outcmsg;
134         outmsg.msg_controllen = sizeof(outcmsg);
135         outmsg.msg_flags      = 0;
136
137         cmsg             = CMSG_FIRSTHDR(&outmsg);
138         cmsg->cmsg_level = IPPROTO_SCTP;
139         cmsg->cmsg_type  = SCTP_SNDRCV;
140         cmsg->cmsg_len   = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
141
142         outmsg.msg_controllen = cmsg->cmsg_len;
143         memcpy(CMSG_DATA(cmsg), sinfo, sizeof(struct sctp_sndrcvinfo));
144     }
145
146     return sendmsg(s, &outmsg, flags);
147 }
148
149 typedef struct SCTPContext {
150     int fd;
151     int max_streams;
152     struct sockaddr_storage dest_addr;
153     socklen_t dest_addr_len;
154 } SCTPContext;
155
156 static int sctp_open(URLContext *h, const char *uri, int flags)
157 {
158     struct addrinfo *ai, *cur_ai;
159     struct addrinfo hints             = { 0 };
160     struct sctp_event_subscribe event = { 0 };
161     struct sctp_initmsg initparams    = { 0 };
162     int port;
163     int fd         = -1;
164     SCTPContext *s = h->priv_data;
165     const char *p;
166     char buf[256];
167     int ret, listen_socket = 0;
168     char hostname[1024], proto[1024], path[1024];
169     char portstr[10];
170
171     av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
172                  &port, path, sizeof(path), uri);
173     if (strcmp(proto, "sctp"))
174         return AVERROR(EINVAL);
175     if (port <= 0 || port >= 65536) {
176         av_log(s, AV_LOG_ERROR, "Port missing in uri\n");
177         return AVERROR(EINVAL);
178     }
179
180     s->max_streams = 0;
181     p = strchr(uri, '?');
182     if (p) {
183         if (av_find_info_tag(buf, sizeof(buf), "listen", p))
184             listen_socket = 1;
185         if (av_find_info_tag(buf, sizeof(buf), "max_streams", p))
186             s->max_streams = strtol(buf, NULL, 10);
187     }
188
189     hints.ai_family   = AF_UNSPEC;
190     hints.ai_socktype = SOCK_STREAM;
191     snprintf(portstr, sizeof(portstr), "%d", port);
192     ret = getaddrinfo(hostname, portstr, &hints, &ai);
193     if (ret) {
194         av_log(h, AV_LOG_ERROR, "Failed to resolve hostname %s: %s\n",
195                hostname, gai_strerror(ret));
196         return AVERROR(EIO);
197     }
198
199     cur_ai = ai;
200
201     fd = ff_socket(cur_ai->ai_family, SOCK_STREAM, IPPROTO_SCTP);
202     if (fd < 0)
203         goto fail;
204
205     s->dest_addr_len = sizeof(s->dest_addr);
206
207     if (listen_socket) {
208         int fd1;
209         ret = bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen);
210         listen(fd, 100);
211         fd1 = accept(fd, NULL, NULL);
212         closesocket(fd);
213         fd  = fd1;
214     } else
215         ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen);
216
217     ff_socket_nonblock(fd, 1);
218
219     event.sctp_data_io_event = 1;
220     /* TODO: Subscribe to more event types and handle them */
221
222     if (setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &event,
223                    sizeof(event)) != 0) {
224         av_log(h, AV_LOG_ERROR,
225                "SCTP ERROR: Unable to subscribe to events\n");
226         goto fail;
227     }
228
229     if (s->max_streams) {
230         initparams.sinit_max_instreams = s->max_streams;
231         initparams.sinit_num_ostreams  = s->max_streams;
232         if (setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &initparams,
233                        sizeof(initparams)) < 0)
234             av_log(h, AV_LOG_ERROR,
235                    "SCTP ERROR: Unable to initialize socket max streams %d\n",
236                    s->max_streams);
237     }
238
239     h->priv_data   = s;
240     h->is_streamed = 1;
241     s->fd          = fd;
242     freeaddrinfo(ai);
243     return 0;
244
245 fail:
246     ret = AVERROR(EIO);
247     freeaddrinfo(ai);
248     return ret;
249 }
250
251 static int sctp_wait_fd(int fd, int write)
252 {
253     int ev          = write ? POLLOUT : POLLIN;
254     struct pollfd p = { .fd = fd, .events = ev, .revents = 0 };
255     int ret;
256
257     ret = poll(&p, 1, 100);
258     return ret < 0 ? ff_neterrno() : p.revents & ev ? 0 : AVERROR(EAGAIN);
259 }
260
261 static int sctp_read(URLContext *h, uint8_t *buf, int size)
262 {
263     SCTPContext *s = h->priv_data;
264     int ret;
265
266     if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
267         ret = sctp_wait_fd(s->fd, 0);
268         if (ret < 0)
269             return ret;
270     }
271
272     if (s->max_streams) {
273         /*StreamId is introduced as a 2byte code into the stream*/
274         struct sctp_sndrcvinfo info = { 0 };
275         ret = ff_sctp_recvmsg(s->fd, buf + 2, size - 2, NULL, 0, &info, 0);
276         AV_WB16(buf, info.sinfo_stream);
277         ret = ret < 0 ? ret : ret + 2;
278     } else
279         ret = recv(s->fd, buf, size, 0);
280
281     return ret < 0 ? ff_neterrno() : ret;
282 }
283
284 static int sctp_write(URLContext *h, const uint8_t *buf, int size)
285 {
286     SCTPContext *s = h->priv_data;
287     int ret;
288
289     if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
290         ret = sctp_wait_fd(s->fd, 1);
291         if (ret < 0)
292             return ret;
293     }
294
295     if (s->max_streams) {
296         /*StreamId is introduced as a 2byte code into the stream*/
297         struct sctp_sndrcvinfo info = { 0 };
298         info.sinfo_stream           = AV_RB16(buf);
299         if (info.sinfo_stream > s->max_streams)
300             abort();
301         ret = ff_sctp_send(s->fd, buf + 2, size - 2, &info, MSG_EOR);
302     } else
303         ret = send(s->fd, buf, size, 0);
304
305     return ret < 0 ? ff_neterrno() : ret;
306 }
307
308 static int sctp_close(URLContext *h)
309 {
310     SCTPContext *s = h->priv_data;
311     closesocket(s->fd);
312     return 0;
313 }
314
315 static int sctp_get_file_handle(URLContext *h)
316 {
317     SCTPContext *s = h->priv_data;
318     return s->fd;
319 }
320
321 URLProtocol ff_sctp_protocol = {
322     .name                = "sctp",
323     .url_open            = sctp_open,
324     .url_read            = sctp_read,
325     .url_write           = sctp_write,
326     .url_close           = sctp_close,
327     .url_get_file_handle = sctp_get_file_handle,
328     .priv_data_size      = sizeof(SCTPContext),
329     .flags               = URL_PROTOCOL_FLAG_NETWORK,
330 };