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 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
60  * for the SCTP kernel Implementation. The main purpose of this
61  * code is to provide the SCTP Socket API mappings for user
62  * application to interface with the SCTP in 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 = (void*)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;
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 = 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  av_log(h, AV_LOG_ERROR, "bad input data\n");
301  return AVERROR(EINVAL);
302  }
303  ret = ff_sctp_send(s->fd, buf + 2, size - 2, &info, MSG_EOR);
304  } else
305  ret = send(s->fd, buf, size, 0);
306 
307  return ret < 0 ? ff_neterrno() : ret;
308 }
309 
310 static int sctp_close(URLContext *h)
311 {
312  SCTPContext *s = h->priv_data;
313  closesocket(s->fd);
314  return 0;
315 }
316 
318 {
319  SCTPContext *s = h->priv_data;
320  return s->fd;
321 }
322 
324  .name = "sctp",
325  .url_open = sctp_open,
326  .url_read = sctp_read,
327  .url_write = sctp_write,
328  .url_close = sctp_close,
329  .url_get_file_handle = sctp_get_file_handle,
330  .priv_data_size = sizeof(SCTPContext),
332 };
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.
struct sockaddr_storage dest_addr
Definition: sctp.c:152
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:73
const char * s
Definition: avisynth_c.h:668
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:35
int is_streamed
true if streamed (no seek possible), default = false
Definition: url.h:48
struct SCTPContext SCTPContext
static int sctp_write(URLContext *h, const uint8_t *buf, int size)
Definition: sctp.c:284
int flags
Definition: url.h:46
#define freeaddrinfo
Definition: network.h:195
socklen_t dest_addr_len
Definition: sctp.c:153
URLProtocol ff_sctp_protocol
Definition: sctp.c:323
uint8_t
miscellaneous OS support macros and functions.
const char * from
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:647
static int sctp_close(URLContext *h)
Definition: sctp.c:310
#define closesocket
Definition: ffserver.c:28
#define AV_RB16
void av_log(void *avcl, int level, const char *fmt,...)
Definition: log.c:246
int size
int ai_addrlen
Definition: network.h:119
static int sctp_get_file_handle(URLContext *h)
Definition: sctp.c:317
static int sctp_wait_fd(int fd, int write)
Definition: sctp.c:251
ret
Definition: avfilter.c:821
#define ff_neterrno()
Definition: network.h:63
int ff_socket_nonblock(int socket, int enable)
static int ff_sctp_send(int s, const void *msg, size_t len, const struct sctp_sndrcvinfo *sinfo, int flags)
Definition: sctp.c:115
NULL
Definition: eval.c:55
#define AVIO_FLAG_NONBLOCK
Use non-blocking mode.
Definition: avio.h:351
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:148
void * buf
Definition: avisynth_c.h:594
Definition: url.h:41
#define AV_WB16(p, darg)
Definition: intreadwrite.h:237
void * priv_data
Definition: url.h:44
#define gai_strerror
Definition: network.h:201
#define snprintf
Definition: snprintf.h:34
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFilterBuffer 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.Buffer references ownership and permissions
misc parsing utilities
static int sctp_open(URLContext *h, const char *uri, int flags)
Definition: sctp.c:156
const char * name
Definition: url.h:55
int ai_socktype
Definition: network.h:117
int fd
Definition: sctp.c:150
static int flags
Definition: cpu.c:23
static int sctp_read(URLContext *h, uint8_t *buf, int size)
Definition: sctp.c:261
#define getaddrinfo
Definition: network.h:194
Main libavformat public API header.
int len
unbuffered private I/O API
struct sockaddr * ai_addr
Definition: network.h:120
int max_streams
Definition: sctp.c:151
int ai_family
Definition: network.h:116