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);
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) {
251  av_log(h, AV_LOG_ERROR,
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) {
261  av_log(h, AV_LOG_ERROR,
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  ret = AVERROR(EIO);
286  freeaddrinfo(ai);
287  return ret;
288 }
289 
290 static int sctp_wait_fd(int fd, int write)
291 {
292  int ev = write ? POLLOUT : POLLIN;
293  struct pollfd p = { .fd = fd, .events = ev, .revents = 0 };
294  int ret;
295 
296  ret = poll(&p, 1, 100);
297  return ret < 0 ? ff_neterrno() : p.revents & ev ? 0 : AVERROR(EAGAIN);
298 }
299 
300 static int sctp_read(URLContext *h, uint8_t *buf, int size)
301 {
302  SCTPContext *s = h->priv_data;
303  int ret;
304 
305  if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
306  ret = sctp_wait_fd(s->fd, 0);
307  if (ret < 0)
308  return ret;
309  }
310 
311  if (s->max_streams) {
312  /*StreamId is introduced as a 2byte code into the stream*/
313  struct sctp_sndrcvinfo info = { 0 };
314  ret = ff_sctp_recvmsg(s->fd, buf + 2, size - 2, NULL, 0, &info, 0);
315  AV_WB16(buf, info.sinfo_stream);
316  ret = ret < 0 ? ret : ret + 2;
317  } else
318  ret = recv(s->fd, buf, size, 0);
319 
320  return ret < 0 ? ff_neterrno() : ret;
321 }
322 
323 static int sctp_write(URLContext *h, const uint8_t *buf, int size)
324 {
325  SCTPContext *s = h->priv_data;
326  int ret;
327 
328  if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
329  ret = sctp_wait_fd(s->fd, 1);
330  if (ret < 0)
331  return ret;
332  }
333 
334  if (s->max_streams) {
335  /*StreamId is introduced as a 2byte code into the stream*/
336  struct sctp_sndrcvinfo info = { 0 };
337  info.sinfo_stream = AV_RB16(buf);
338  if (info.sinfo_stream > s->max_streams) {
339  av_log(h, AV_LOG_ERROR, "bad input data\n");
340  return AVERROR_BUG;
341  }
342  ret = ff_sctp_send(s->fd, buf + 2, size - 2, &info, MSG_EOR);
343  } else
344  ret = send(s->fd, buf, size, MSG_NOSIGNAL);
345 
346  return ret < 0 ? ff_neterrno() : ret;
347 }
348 
349 static int sctp_close(URLContext *h)
350 {
351  SCTPContext *s = h->priv_data;
352  closesocket(s->fd);
353  return 0;
354 }
355 
357 {
358  SCTPContext *s = h->priv_data;
359  return s->fd;
360 }
361 
363  .name = "sctp",
364  .url_open = sctp_open,
365  .url_read = sctp_read,
366  .url_write = sctp_write,
367  .url_close = sctp_close,
368  .url_get_file_handle = sctp_get_file_handle,
369  .priv_data_size = sizeof(SCTPContext),
371  .priv_data_class = &sctp_class,
372 };
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:4775
#define NULL
Definition: coverity.c:32
struct sockaddr_storage dest_addr
Definition: sctp.c:157
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
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:34
AVOption.
Definition: opt.h:246
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
int is_streamed
true if streamed (no seek possible), default = false
Definition: url.h:45
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:191
static const AVClass sctp_class
Definition: sctp.c:171
static int sctp_write(URLContext *h, const uint8_t *buf, int size)
Definition: sctp.c:323
int flags
Definition: url.h:43
#define freeaddrinfo
Definition: network.h:215
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:87
int ff_socket(int af, int type, int proto)
Definition: network.c:183
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:72
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:246
uint8_t
int timeout
Definition: sctp.c:154
AVOptions.
miscellaneous OS support macros and functions.
const char * from
Definition: jacosubdec.c:65
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:258
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:749
#define D
Definition: sctp.c:161
ptrdiff_t size
Definition: opengl_enc.c:100
#define AV_WB16(p, v)
Definition: intreadwrite.h:405
#define av_log(a,...)
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
static int sctp_close(URLContext *h)
Definition: sctp.c:349
#define fail()
Definition: checkasm.h:122
#define E
Definition: sctp.c:162
int ai_addrlen
Definition: network.h:139
static int sctp_get_file_handle(URLContext *h)
Definition: sctp.c:356
static int sctp_wait_fd(int fd, int write)
Definition: sctp.c:290
#define ff_neterrno()
Definition: network.h:65
int listen
Definition: sctp.c:153
#define s(width, name)
Definition: cbs_vp9.c:257
#define AVERROR_EXIT
Immediate exit was requested; the called function should not be restarted.
Definition: error.h:56
static int ff_sctp_send(int s, const void *msg, size_t len, const struct sctp_sndrcvinfo *sinfo, int flags)
Definition: sctp.c:116
#define AVIO_FLAG_NONBLOCK
Use non-blocking mode.
Definition: avio.h:693
void * buf
Definition: avisynth_c.h:766
Definition: url.h:38
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition: error.h:50
Describe the class of an AVClass context structure.
Definition: log.h:67
void * priv_data
Definition: url.h:41
#define gai_strerror
Definition: network.h:222
#define snprintf
Definition: snprintf.h:34
int listen_timeout
Definition: sctp.c:155
static const AVOption options[]
Definition: sctp.c:163
misc parsing utilities
static int sctp_open(URLContext *h, const char *uri, int flags)
Definition: sctp.c:178
const URLProtocol ff_sctp_protocol
Definition: sctp.c:362
const char * name
Definition: url.h:55
int ai_socktype
Definition: network.h:137
int fd
Definition: sctp.c:152
#define flags(name, subs,...)
Definition: cbs_av1.c:561
static int sctp_read(URLContext *h, uint8_t *buf, int size)
Definition: sctp.c:300
#define getaddrinfo
Definition: network.h:214
Main libavformat public API header.
struct addrinfo * ai_next
Definition: network.h:142
int len
#define MSG_NOSIGNAL
Definition: network.h:130
unbuffered private I/O API
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
struct sockaddr * ai_addr
Definition: network.h:140
#define OFFSET(x)
Definition: sctp.c:160
int max_streams
Definition: sctp.c:156
int ai_family
Definition: network.h:136