FFmpeg
sctp.c
Go to the documentation of this file.
1 /*
2  * SCTP protocol
3  * Copyright (c) 2012 Luca Barbato
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg 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  * FFmpeg 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 FFmpeg; 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 "libavutil/opt.h"
52 #include "avformat.h"
53 #include "internal.h"
54 #include "network.h"
55 #include "os_support.h"
56 #include "url.h"
57 
58 /*
59  * The sctp_recvmsg and sctp_sendmsg functions are part of the user
60  * library that offers support for the SCTP kernel Implementation.
61  * To avoid build-time clashes the functions sport an ff_-prefix here.
62  * The main purpose of this code is to provide the SCTP Socket API
63  * mappings for user applications to interface with SCTP in the kernel.
64  *
65  * This implementation is based on the Socket API Extensions for SCTP
66  * defined in <draft-ietf-tsvwg-sctpsocket-10.txt>
67  *
68  * Copyright (c) 2003 International Business Machines, Corp.
69  *
70  * Written or modified by:
71  * Ryan Layer <rmlayer@us.ibm.com>
72  */
73 
74 static int ff_sctp_recvmsg(int s, void *msg, size_t len, struct sockaddr *from,
75  socklen_t *fromlen, struct sctp_sndrcvinfo *sinfo,
76  int *msg_flags)
77 {
78  int recvb;
79  struct iovec iov;
80  char incmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
81  struct msghdr inmsg = { 0 };
82  struct cmsghdr *cmsg = NULL;
83 
84  iov.iov_base = msg;
85  iov.iov_len = len;
86 
87  inmsg.msg_name = from;
88  inmsg.msg_namelen = fromlen ? *fromlen : 0;
89  inmsg.msg_iov = &iov;
90  inmsg.msg_iovlen = 1;
91  inmsg.msg_control = incmsg;
92  inmsg.msg_controllen = sizeof(incmsg);
93 
94  if ((recvb = recvmsg(s, &inmsg, msg_flags ? *msg_flags : 0)) < 0)
95  return recvb;
96 
97  if (fromlen)
98  *fromlen = inmsg.msg_namelen;
99  if (msg_flags)
100  *msg_flags = inmsg.msg_flags;
101 
102  for (cmsg = CMSG_FIRSTHDR(&inmsg); cmsg;
103  cmsg = CMSG_NXTHDR(&inmsg, cmsg)) {
104  if ((IPPROTO_SCTP == cmsg->cmsg_level) &&
105  (SCTP_SNDRCV == cmsg->cmsg_type))
106  break;
107  }
108 
109  /* Copy sinfo. */
110  if (cmsg)
111  memcpy(sinfo, CMSG_DATA(cmsg), sizeof(struct sctp_sndrcvinfo));
112 
113  return recvb;
114 }
115 
116 static int ff_sctp_send(int s, const void *msg, size_t len,
117  const struct sctp_sndrcvinfo *sinfo, int flags)
118 {
119  struct msghdr outmsg = { 0 };
120  struct iovec iov;
121 
122  outmsg.msg_name = NULL;
123  outmsg.msg_namelen = 0;
124  outmsg.msg_iov = &iov;
125  iov.iov_base = (void*)msg;
126  iov.iov_len = len;
127  outmsg.msg_iovlen = 1;
128  outmsg.msg_controllen = 0;
129 
130  if (sinfo) {
131  char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
132  struct cmsghdr *cmsg;
133 
134  outmsg.msg_control = outcmsg;
135  outmsg.msg_controllen = sizeof(outcmsg);
136  outmsg.msg_flags = 0;
137 
138  cmsg = CMSG_FIRSTHDR(&outmsg);
139  cmsg->cmsg_level = IPPROTO_SCTP;
140  cmsg->cmsg_type = SCTP_SNDRCV;
141  cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
142 
143  outmsg.msg_controllen = cmsg->cmsg_len;
144  memcpy(CMSG_DATA(cmsg), sinfo, sizeof(struct sctp_sndrcvinfo));
145  }
146 
147  return sendmsg(s, &outmsg, flags | MSG_NOSIGNAL);
148 }
149 
150 typedef struct SCTPContext {
151  const AVClass *class;
152  int fd;
153  int listen;
154  int timeout;
158 } SCTPContext;
159 
160 #define OFFSET(x) offsetof(SCTPContext, x)
161 #define D AV_OPT_FLAG_DECODING_PARAM
162 #define E AV_OPT_FLAG_ENCODING_PARAM
163 static const AVOption options[] = {
164  { "listen", "Listen for incoming connections", OFFSET(listen), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, .flags = D|E },
165  { "timeout", "Connection timeout (in milliseconds)", OFFSET(timeout), AV_OPT_TYPE_INT, { .i64 = 10000 }, INT_MIN, INT_MAX, .flags = D|E },
166  { "listen_timeout", "Bind timeout (in milliseconds)", OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, INT_MIN, INT_MAX, .flags = D|E },
167  { "max_streams", "Max stream to allocate", OFFSET(max_streams), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT16_MAX, .flags = D|E },
168  { NULL }
169 };
170 
171 static const AVClass sctp_class = {
172  .class_name = "sctp",
173  .item_name = av_default_item_name,
174  .option = options,
175  .version = LIBAVUTIL_VERSION_INT,
176 };
177 
178 static int sctp_open(URLContext *h, const char *uri, int flags)
179 {
180  struct addrinfo *ai, *cur_ai;
181  struct addrinfo hints = { 0 };
182  struct sctp_event_subscribe event = { 0 };
183  struct sctp_initmsg initparams = { 0 };
184  int port;
185  int fd = -1;
186  SCTPContext *s = h->priv_data;
187  const char *p;
188  char buf[256];
189  int ret;
190  char hostname[1024], proto[1024], path[1024];
191  char portstr[10];
192 
193  av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
194  &port, path, sizeof(path), uri);
195  if (strcmp(proto, "sctp"))
196  return AVERROR(EINVAL);
197  if (port <= 0 || port >= 65536) {
198  av_log(s, AV_LOG_ERROR, "Port missing in uri\n");
199  return AVERROR(EINVAL);
200  }
201 
202  p = strchr(uri, '?');
203  if (p) {
204  if (av_find_info_tag(buf, sizeof(buf), "listen", p))
205  s->listen = 1;
206  if (av_find_info_tag(buf, sizeof(buf), "max_streams", p))
207  s->max_streams = strtol(buf, NULL, 10);
208  }
209 
210  hints.ai_family = AF_UNSPEC;
211  hints.ai_socktype = SOCK_STREAM;
212  snprintf(portstr, sizeof(portstr), "%d", port);
213  ret = getaddrinfo(hostname, portstr, &hints, &ai);
214  if (ret) {
215  av_log(h, AV_LOG_ERROR, "Failed to resolve hostname %s: %s\n",
216  hostname, gai_strerror(ret));
217  return AVERROR(EIO);
218  }
219 
220  cur_ai = ai;
221 
222 restart:
223  fd = ff_socket(cur_ai->ai_family, SOCK_STREAM, IPPROTO_SCTP, h);
224  if (fd < 0) {
225  ret = ff_neterrno();
226  goto fail;
227  }
228 
229  if (s->listen) {
230  if ((fd = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
231  s->listen_timeout, h)) < 0) {
232  ret = fd;
233  goto fail1;
234  }
235  } else {
236  if ((ret = ff_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
237  s->timeout, h, !!cur_ai->ai_next)) < 0) {
238 
239  if (ret == AVERROR_EXIT)
240  goto fail1;
241  else
242  goto fail;
243  }
244  }
245 
246  event.sctp_data_io_event = 1;
247  /* TODO: Subscribe to more event types and handle them */
248 
249  if (setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &event,
250  sizeof(event)) != 0) {
252  "SCTP ERROR: Unable to subscribe to events\n");
253  goto fail1;
254  }
255 
256  if (s->max_streams) {
257  initparams.sinit_max_instreams = s->max_streams;
258  initparams.sinit_num_ostreams = s->max_streams;
259  if (setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &initparams,
260  sizeof(initparams)) < 0) {
262  "SCTP ERROR: Unable to initialize socket max streams %d\n",
263  s->max_streams);
264  ret = ff_neterrno();
265  goto fail1;
266  }
267  }
268 
269  h->priv_data = s;
270  h->is_streamed = 1;
271  s->fd = fd;
272  freeaddrinfo(ai);
273  return 0;
274 
275 fail:
276  if (cur_ai->ai_next) {
277  /* Retry with the next sockaddr */
278  cur_ai = cur_ai->ai_next;
279  if (fd >= 0)
280  closesocket(fd);
281  ret = 0;
282  goto restart;
283  }
284 fail1:
285  if (fd >= 0)
286  closesocket(fd);
287  ret = AVERROR(EIO);
288  freeaddrinfo(ai);
289  return ret;
290 }
291 
292 static int sctp_wait_fd(int fd, int write)
293 {
294  int ev = write ? POLLOUT : POLLIN;
295  struct pollfd p = { .fd = fd, .events = ev, .revents = 0 };
296  int ret;
297 
298  ret = poll(&p, 1, 100);
299  return ret < 0 ? ff_neterrno() : p.revents & ev ? 0 : AVERROR(EAGAIN);
300 }
301 
302 static int sctp_read(URLContext *h, uint8_t *buf, int size)
303 {
304  SCTPContext *s = h->priv_data;
305  int ret;
306 
307  if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
308  ret = sctp_wait_fd(s->fd, 0);
309  if (ret < 0)
310  return ret;
311  }
312 
313  if (s->max_streams) {
314  /*StreamId is introduced as a 2byte code into the stream*/
315  struct sctp_sndrcvinfo info = { 0 };
316  ret = ff_sctp_recvmsg(s->fd, buf + 2, size - 2, NULL, 0, &info, 0);
317  AV_WB16(buf, info.sinfo_stream);
318  ret = ret < 0 ? ret : ret + 2;
319  } else
320  ret = recv(s->fd, buf, size, 0);
321 
322  return ret < 0 ? ff_neterrno() : ret;
323 }
324 
325 static int sctp_write(URLContext *h, const uint8_t *buf, int size)
326 {
327  SCTPContext *s = h->priv_data;
328  int ret;
329 
330  if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
331  ret = sctp_wait_fd(s->fd, 1);
332  if (ret < 0)
333  return ret;
334  }
335 
336  if (s->max_streams) {
337  /*StreamId is introduced as a 2byte code into the stream*/
338  struct sctp_sndrcvinfo info = { 0 };
339  info.sinfo_stream = AV_RB16(buf);
340  if (info.sinfo_stream > s->max_streams) {
341  av_log(h, AV_LOG_ERROR, "bad input data\n");
342  return AVERROR_BUG;
343  }
344  ret = ff_sctp_send(s->fd, buf + 2, size - 2, &info, MSG_EOR);
345  } else
346  ret = send(s->fd, buf, size, MSG_NOSIGNAL);
347 
348  return ret < 0 ? ff_neterrno() : ret;
349 }
350 
351 static int sctp_close(URLContext *h)
352 {
353  SCTPContext *s = h->priv_data;
354  closesocket(s->fd);
355  return 0;
356 }
357 
359 {
360  SCTPContext *s = h->priv_data;
361  return s->fd;
362 }
363 
365  .name = "sctp",
366  .url_open = sctp_open,
367  .url_read = sctp_read,
368  .url_write = sctp_write,
369  .url_close = sctp_close,
370  .url_get_file_handle = sctp_get_file_handle,
371  .priv_data_size = sizeof(SCTPContext),
373  .priv_data_class = &sctp_class,
374 };
D
#define D
Definition: sctp.c:161
options
static const AVOption options[]
Definition: sctp.c:163
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
av_find_info_tag
int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
Attempt to find a specific tag in a URL.
Definition: parseutils.c:756
URL_PROTOCOL_FLAG_NETWORK
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:33
AVOption
AVOption.
Definition: opt.h:429
URLProtocol
Definition: url.h:51
os_support.h
sockaddr_storage
Definition: network.h:111
E
#define E
Definition: sctp.c:162
freeaddrinfo
#define freeaddrinfo
Definition: network.h:218
sctp_get_file_handle
static int sctp_get_file_handle(URLContext *h)
Definition: sctp.c:358
fail
#define fail()
Definition: checkasm.h:193
OFFSET
#define OFFSET(x)
Definition: sctp.c:160
ff_listen_bind
int ff_listen_bind(int fd, const struct sockaddr *addr, socklen_t addrlen, int timeout, URLContext *h)
Bind to a file descriptor and poll for a connection.
Definition: network.c:251
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:209
SCTPContext::timeout
int timeout
Definition: sctp.c:154
intreadwrite.h
s
#define s(width, name)
Definition: cbs_vp9.c:198
sctp_wait_fd
static int sctp_wait_fd(int fd, int write)
Definition: sctp.c:292
info
MIPS optimizations info
Definition: mips.txt:2
from
const char * from
Definition: jacosubdec.c:66
SCTPContext::listen_timeout
int listen_timeout
Definition: sctp.c:155
ff_sctp_recvmsg
static int ff_sctp_recvmsg(int s, void *msg, size_t len, struct sockaddr *from, socklen_t *fromlen, struct sctp_sndrcvinfo *sinfo, int *msg_flags)
Definition: sctp.c:74
internal.h
LIBAVUTIL_VERSION_INT
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:75
NULL
#define NULL
Definition: coverity.c:32
ff_sctp_protocol
const URLProtocol ff_sctp_protocol
Definition: sctp.c:364
ff_sctp_send
static int ff_sctp_send(int s, const void *msg, size_t len, const struct sctp_sndrcvinfo *sinfo, int flags)
Definition: sctp.c:116
AV_WB16
#define AV_WB16(p, v)
Definition: intreadwrite.h:401
SCTPContext::dest_addr
struct sockaddr_storage dest_addr
Definition: sctp.c:157
av_default_item_name
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:237
parseutils.h
options
Definition: swscale.c:42
ff_listen_connect
int ff_listen_connect(int fd, const struct sockaddr *addr, socklen_t addrlen, int timeout, URLContext *h, int will_try_next)
Connect to a file descriptor and poll for result.
Definition: network.c:263
ff_neterrno
#define ff_neterrno()
Definition: network.h:68
addrinfo::ai_addr
struct sockaddr * ai_addr
Definition: network.h:143
sctp_read
static int sctp_read(URLContext *h, uint8_t *buf, int size)
Definition: sctp.c:302
SCTPContext
Definition: sctp.c:150
sctp_close
static int sctp_close(URLContext *h)
Definition: sctp.c:351
addrinfo::ai_family
int ai_family
Definition: network.h:139
SCTPContext::fd
int fd
Definition: sctp.c:152
size
int size
Definition: twinvq_data.h:10344
URLProtocol::name
const char * name
Definition: url.h:52
gai_strerror
#define gai_strerror
Definition: network.h:225
addrinfo::ai_next
struct addrinfo * ai_next
Definition: network.h:145
addrinfo::ai_addrlen
int ai_addrlen
Definition: network.h:142
getaddrinfo
#define getaddrinfo
Definition: network.h:217
URLContext
Definition: url.h:35
av_url_split
void av_url_split(char *proto, int proto_size, char *authorization, int authorization_size, char *hostname, int hostname_size, int *port_ptr, char *path, int path_size, const char *url)
Split a URL string into components.
Definition: utils.c:351
url.h
SCTPContext::listen
int listen
Definition: sctp.c:153
len
int len
Definition: vorbis_enc_data.h:426
ret
ret
Definition: filter_design.txt:187
AVClass::class_name
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:80
addrinfo::ai_socktype
int ai_socktype
Definition: network.h:140
avformat.h
network.h
AV_OPT_TYPE_INT
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition: opt.h:259
sctp_class
static const AVClass sctp_class
Definition: sctp.c:171
MSG_NOSIGNAL
#define MSG_NOSIGNAL
Definition: network.h:133
sctp_write
static int sctp_write(URLContext *h, const uint8_t *buf, int size)
Definition: sctp.c:325
AV_OPT_TYPE_BOOL
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
Definition: opt.h:327
AVIO_FLAG_NONBLOCK
#define AVIO_FLAG_NONBLOCK
Use non-blocking mode.
Definition: avio.h:636
sctp_open
static int sctp_open(URLContext *h, const char *uri, int flags)
Definition: sctp.c:178
SCTPContext::max_streams
int max_streams
Definition: sctp.c:156
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:482
AVERROR_BUG
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition: error.h:52
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
h
h
Definition: vp9dsp_template.c:2070
AVERROR_EXIT
#define AVERROR_EXIT
Immediate exit was requested; the called function should not be restarted.
Definition: error.h:58
ff_socket
int ff_socket(int af, int type, int proto, void *logctx)
Definition: network.c:188
addrinfo
Definition: network.h:137
snprintf
#define snprintf
Definition: snprintf.h:34
AV_RB16
uint64_t_TMPL AV_WL64 unsigned int_TMPL AV_WL32 unsigned int_TMPL AV_WL24 unsigned int_TMPL AV_WL16 uint64_t_TMPL AV_WB64 unsigned int_TMPL AV_WB32 unsigned int_TMPL AV_WB24 unsigned int_TMPL AV_RB16
Definition: bytestream.h:98