3 * Copyright (c) 2012 Luca Barbato
5 * This file is part of Libav.
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.
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.
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
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]
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.
40 #include <netinet/in.h>
41 #include <netinet/sctp.h>
49 #include "libavutil/intreadwrite.h"
50 #include "libavutil/parseutils.h"
54 #include "os_support.h"
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.
64 * This implementation is based on the Socket API Extensions for SCTP
65 * defined in <draft-ietf-tsvwg-sctpsocket-10.txt>
67 * Copyright (c) 2003 International Business Machines, Corp.
69 * Written or modified by:
70 * Ryan Layer <rmlayer@us.ibm.com>
73 static int ff_sctp_recvmsg(int s
, void *msg
, size_t len
, struct sockaddr
*from
,
74 socklen_t
*fromlen
, struct sctp_sndrcvinfo
*sinfo
,
79 char incmsg
[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo
))];
80 struct msghdr inmsg
= { 0 };
81 struct cmsghdr
*cmsg
= NULL
;
86 inmsg
.msg_name
= from
;
87 inmsg
.msg_namelen
= fromlen
? *fromlen
: 0;
90 inmsg
.msg_control
= incmsg
;
91 inmsg
.msg_controllen
= sizeof(incmsg
);
93 if ((recvb
= recvmsg(s
, &inmsg
, msg_flags
? *msg_flags
: 0)) < 0)
97 *fromlen
= inmsg
.msg_namelen
;
99 *msg_flags
= inmsg
.msg_flags
;
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
))
110 memcpy(sinfo
, CMSG_DATA(cmsg
), sizeof(struct sctp_sndrcvinfo
));
115 static int ff_sctp_send(int s
, const void *msg
, size_t len
,
116 const struct sctp_sndrcvinfo
*sinfo
, int flags
)
118 struct msghdr outmsg
;
121 outmsg
.msg_name
= NULL
;
122 outmsg
.msg_namelen
= 0;
123 outmsg
.msg_iov
= &iov
;
126 outmsg
.msg_iovlen
= 1;
127 outmsg
.msg_controllen
= 0;
130 char outcmsg
[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo
))];
131 struct cmsghdr
*cmsg
;
133 outmsg
.msg_control
= outcmsg
;
134 outmsg
.msg_controllen
= sizeof(outcmsg
);
135 outmsg
.msg_flags
= 0;
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
));
142 outmsg
.msg_controllen
= cmsg
->cmsg_len
;
143 memcpy(CMSG_DATA(cmsg
), sinfo
, sizeof(struct sctp_sndrcvinfo
));
146 return sendmsg(s
, &outmsg
, flags
);
149 typedef struct SCTPContext
{
152 struct sockaddr_storage dest_addr
;
153 socklen_t dest_addr_len
;
156 static int sctp_open(URLContext
*h
, const char *uri
, int flags
)
158 struct addrinfo
*ai
, *cur_ai
;
159 struct addrinfo hints
= { 0 };
160 struct sctp_event_subscribe event
= { 0 };
161 struct sctp_initmsg initparams
= { 0 };
164 SCTPContext
*s
= h
->priv_data
;
167 int ret
, listen_socket
= 0;
168 char hostname
[1024], proto
[1024], path
[1024];
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
);
181 p
= strchr(uri
, '?');
183 if (av_find_info_tag(buf
, sizeof(buf
), "listen", p
))
185 if (av_find_info_tag(buf
, sizeof(buf
), "max_streams", p
))
186 s
->max_streams
= strtol(buf
, NULL
, 10);
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
);
194 av_log(h
, AV_LOG_ERROR
, "Failed to resolve hostname %s: %s\n",
195 hostname
, gai_strerror(ret
));
201 fd
= socket(cur_ai
->ai_family
, SOCK_STREAM
, IPPROTO_SCTP
);
205 s
->dest_addr_len
= sizeof(s
->dest_addr
);
209 ret
= bind(fd
, cur_ai
->ai_addr
, cur_ai
->ai_addrlen
);
211 fd1
= accept(fd
, NULL
, NULL
);
215 ret
= connect(fd
, cur_ai
->ai_addr
, cur_ai
->ai_addrlen
);
217 ff_socket_nonblock(fd
, 1);
219 event
.sctp_data_io_event
= 1;
220 /* TODO: Subscribe to more event types and handle them */
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");
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",
251 static int sctp_wait_fd(int fd
, int write
)
253 int ev
= write
? POLLOUT
: POLLIN
;
254 struct pollfd p
= { .fd
= fd
, .events
= ev
, .revents
= 0 };
257 ret
= poll(&p
, 1, 100);
258 return ret
< 0 ? ff_neterrno() : p
.revents
& ev
? 0 : AVERROR(EAGAIN
);
261 static int sctp_read(URLContext
*h
, uint8_t *buf
, int size
)
263 SCTPContext
*s
= h
->priv_data
;
266 if (!(h
->flags
& AVIO_FLAG_NONBLOCK
)) {
267 ret
= sctp_wait_fd(s
->fd
, 0);
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;
279 ret
= recv(s
->fd
, buf
, size
, 0);
281 return ret
< 0 ? ff_neterrno() : ret
;
284 static int sctp_write(URLContext
*h
, const uint8_t *buf
, int size
)
286 SCTPContext
*s
= h
->priv_data
;
289 if (!(h
->flags
& AVIO_FLAG_NONBLOCK
)) {
290 ret
= sctp_wait_fd(s
->fd
, 1);
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
)
301 ret
= ff_sctp_send(s
->fd
, buf
+ 2, size
- 2, &info
, MSG_EOR
);
303 ret
= send(s
->fd
, buf
, size
, 0);
305 return ret
< 0 ? ff_neterrno() : ret
;
308 static int sctp_close(URLContext
*h
)
310 SCTPContext
*s
= h
->priv_data
;
315 static int sctp_get_file_handle(URLContext
*h
)
317 SCTPContext
*s
= h
->priv_data
;
321 URLProtocol ff_sctp_protocol
= {
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
,