Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 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 |
||
41 | #include |
||
42 | |||
43 | #include "config.h" |
||
44 | |||
45 | #if HAVE_POLL_H |
||
46 | #include |
||
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 |
||
66 | * |
||
67 | * Copyright (c) 2003 International Business Machines, Corp. |
||
68 | * |
||
69 | * Written or modified by: |
||
70 | * Ryan Layer |
||
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; |
||
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 | 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 | |||
317 | static int sctp_get_file_handle(URLContext *h) |
||
318 | { |
||
319 | SCTPContext *s = h->priv_data; |
||
320 | return s->fd; |
||
321 | } |
||
322 | |||
323 | URLProtocol ff_sctp_protocol = { |
||
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), |
||
331 | .flags = URL_PROTOCOL_FLAG_NETWORK, |
||
332 | };>>>>>>>>=>> |