1/*
2 * TCP protocol
3 * Copyright (c) 2002 Fabrice Bellard
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#include "avformat.h"
22#include <unistd.h>
23#include "network.h"
24#include "os_support.h"
25#if HAVE_SYS_SELECT_H
26#include <sys/select.h>
27#endif
28#include <sys/time.h>
29
30typedef struct TCPContext {
31    int fd;
32} TCPContext;
33
34/* return non zero if error */
35static int tcp_open(URLContext *h, const char *uri, int flags)
36{
37    struct sockaddr_in dest_addr;
38    int port, fd = -1;
39    TCPContext *s = NULL;
40    fd_set wfds;
41    int fd_max, ret;
42    struct timeval tv;
43    socklen_t optlen;
44    char hostname[1024],proto[1024],path[1024];
45
46    if(!ff_network_init())
47        return AVERROR(EIO);
48
49    url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
50        &port, path, sizeof(path), uri);
51    if (strcmp(proto,"tcp") || port <= 0 || port >= 65536)
52        return AVERROR(EINVAL);
53
54    dest_addr.sin_family = AF_INET;
55    dest_addr.sin_port = htons(port);
56    if (resolve_host(&dest_addr.sin_addr, hostname) < 0)
57        return AVERROR(EIO);
58
59    fd = socket(AF_INET, SOCK_STREAM, 0);
60    if (fd < 0)
61        return AVERROR(EIO);
62    ff_socket_nonblock(fd, 1);
63
64 redo:
65    ret = connect(fd, (struct sockaddr *)&dest_addr,
66                  sizeof(dest_addr));
67    if (ret < 0) {
68        if (ff_neterrno() == FF_NETERROR(EINTR))
69            goto redo;
70        if (ff_neterrno() != FF_NETERROR(EINPROGRESS) &&
71            ff_neterrno() != FF_NETERROR(EAGAIN))
72            goto fail;
73
74        /* wait until we are connected or until abort */
75        for(;;) {
76            if (url_interrupt_cb()) {
77                ret = AVERROR(EINTR);
78                goto fail1;
79            }
80            fd_max = fd;
81            FD_ZERO(&wfds);
82            FD_SET(fd, &wfds);
83            tv.tv_sec = 0;
84            tv.tv_usec = 100 * 1000;
85            ret = select(fd_max + 1, NULL, &wfds, NULL, &tv);
86            if (ret > 0 && FD_ISSET(fd, &wfds))
87                break;
88        }
89
90        /* test error */
91        optlen = sizeof(ret);
92        getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
93        if (ret != 0)
94            goto fail;
95    }
96    s = av_malloc(sizeof(TCPContext));
97    if (!s)
98        return AVERROR(ENOMEM);
99    h->priv_data = s;
100    h->is_streamed = 1;
101    s->fd = fd;
102    return 0;
103
104 fail:
105    ret = AVERROR(EIO);
106 fail1:
107    if (fd >= 0)
108        closesocket(fd);
109    return ret;
110}
111
112static int tcp_read(URLContext *h, uint8_t *buf, int size)
113{
114    TCPContext *s = h->priv_data;
115    int len, fd_max, ret;
116    fd_set rfds;
117    struct timeval tv;
118
119    for (;;) {
120        if (url_interrupt_cb())
121            return AVERROR(EINTR);
122        fd_max = s->fd;
123        FD_ZERO(&rfds);
124        FD_SET(s->fd, &rfds);
125        tv.tv_sec = 0;
126        tv.tv_usec = 100 * 1000;
127        ret = select(fd_max + 1, &rfds, NULL, NULL, &tv);
128        if (ret > 0 && FD_ISSET(s->fd, &rfds)) {
129            len = recv(s->fd, buf, size, 0);
130            if (len < 0) {
131                if (ff_neterrno() != FF_NETERROR(EINTR) &&
132                    ff_neterrno() != FF_NETERROR(EAGAIN))
133                    return AVERROR(errno);
134            } else return len;
135        } else if (ret < 0) {
136            return -1;
137        }
138    }
139}
140
141static int tcp_write(URLContext *h, uint8_t *buf, int size)
142{
143    TCPContext *s = h->priv_data;
144    int ret, size1, fd_max, len;
145    fd_set wfds;
146    struct timeval tv;
147
148    size1 = size;
149    while (size > 0) {
150        if (url_interrupt_cb())
151            return AVERROR(EINTR);
152        fd_max = s->fd;
153        FD_ZERO(&wfds);
154        FD_SET(s->fd, &wfds);
155        tv.tv_sec = 0;
156        tv.tv_usec = 100 * 1000;
157        ret = select(fd_max + 1, NULL, &wfds, NULL, &tv);
158        if (ret > 0 && FD_ISSET(s->fd, &wfds)) {
159            len = send(s->fd, buf, size, 0);
160            if (len < 0) {
161                if (ff_neterrno() != FF_NETERROR(EINTR) &&
162                    ff_neterrno() != FF_NETERROR(EAGAIN))
163                    return AVERROR(errno);
164                continue;
165            }
166            size -= len;
167            buf += len;
168        } else if (ret < 0) {
169            return -1;
170        }
171    }
172    return size1 - size;
173}
174
175static int tcp_close(URLContext *h)
176{
177    TCPContext *s = h->priv_data;
178    closesocket(s->fd);
179    ff_network_close();
180    av_free(s);
181    return 0;
182}
183
184URLProtocol tcp_protocol = {
185    "tcp",
186    tcp_open,
187    tcp_read,
188    tcp_write,
189    NULL, /* seek */
190    tcp_close,
191};
192