tcp.c 6.52 KB
Newer Older
Fabrice Bellard's avatar
Fabrice Bellard committed
1 2
/*
 * TCP protocol
3
 * Copyright (c) 2002 Fabrice Bellard
Fabrice Bellard's avatar
Fabrice Bellard committed
4
 *
5 6 7
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
Fabrice Bellard's avatar
Fabrice Bellard committed
8 9
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
Fabrice Bellard's avatar
Fabrice Bellard committed
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
Fabrice Bellard's avatar
Fabrice Bellard committed
13 14 15 16 17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * 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
Fabrice Bellard's avatar
Fabrice Bellard committed
20 21
 */
#include "avformat.h"
22
#include "libavutil/parseutils.h"
23
#include "libavutil/opt.h"
24
#include "libavutil/time.h"
25
#include "internal.h"
26
#include "network.h"
27
#include "os_support.h"
28
#include "url.h"
29 30
#if HAVE_POLL_H
#include <poll.h>
31
#endif
Fabrice Bellard's avatar
Fabrice Bellard committed
32 33

typedef struct TCPContext {
34
    const AVClass *class;
Fabrice Bellard's avatar
Fabrice Bellard committed
35
    int fd;
36
    int listen;
37
    int open_timeout;
38 39
    int rw_timeout;
    int listen_timeout;
Fabrice Bellard's avatar
Fabrice Bellard committed
40 41
} TCPContext;

42 43 44 45 46
#define OFFSET(x) offsetof(TCPContext, x)
#define D AV_OPT_FLAG_DECODING_PARAM
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
{"listen", "listen on port instead of connecting", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E },
47 48
{"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
{"listen_timeout", "set connection awaiting timeout", OFFSET(listen_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E },
49 50 51 52 53 54 55 56 57 58
{NULL}
};

static const AVClass tcp_context_class = {
    .class_name = "tcp",
    .item_name  = av_default_item_name,
    .option     = options,
    .version    = LIBAVUTIL_VERSION_INT,
};

Fabrice Bellard's avatar
Fabrice Bellard committed
59 60 61
/* return non zero if error */
static int tcp_open(URLContext *h, const char *uri, int flags)
{
62
    struct addrinfo hints = { 0 }, *ai, *cur_ai;
Fabrice Bellard's avatar
Fabrice Bellard committed
63
    int port, fd = -1;
64
    TCPContext *s = h->priv_data;
65 66
    const char *p;
    char buf[256];
67
    int ret;
68
    char hostname[1024],proto[1024],path[1024];
69
    char portstr[10];
70
    s->open_timeout = 5000000;
71

72
    av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
73
        &port, path, sizeof(path), uri);
74
    if (strcmp(proto, "tcp"))
75
        return AVERROR(EINVAL);
76 77 78 79
    if (port <= 0 || port >= 65536) {
        av_log(h, AV_LOG_ERROR, "Port missing in uri\n");
        return AVERROR(EINVAL);
    }
80 81
    p = strchr(uri, '?');
    if (p) {
82 83 84 85 86 87 88
        if (av_find_info_tag(buf, sizeof(buf), "listen", p)) {
            char *endptr = NULL;
            s->listen = strtol(buf, &endptr, 10);
            /* assume if no digits were found it is a request to enable it */
            if (buf == endptr)
                s->listen = 1;
        }
89
        if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
90
            s->rw_timeout = strtol(buf, NULL, 10);
91
        }
92
        if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) {
93
            s->listen_timeout = strtol(buf, NULL, 10);
94
        }
95
    }
96 97 98 99
    if (s->rw_timeout >= 0) {
        s->open_timeout =
        h->rw_timeout   = s->rw_timeout;
    }
100
    hints.ai_family = AF_UNSPEC;
101 102
    hints.ai_socktype = SOCK_STREAM;
    snprintf(portstr, sizeof(portstr), "%d", port);
103
    if (s->listen)
104
        hints.ai_flags |= AI_PASSIVE;
105 106 107 108
    if (!hostname[0])
        ret = getaddrinfo(NULL, portstr, &hints, &ai);
    else
        ret = getaddrinfo(hostname, portstr, &hints, &ai);
109
    if (ret) {
110
        av_log(h, AV_LOG_ERROR,
111 112
               "Failed to resolve hostname %s: %s\n",
               hostname, gai_strerror(ret));
113
        return AVERROR(EIO);
114
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
115

116 117 118
    cur_ai = ai;

 restart:
119 120 121
    fd = ff_socket(cur_ai->ai_family,
                   cur_ai->ai_socktype,
                   cur_ai->ai_protocol);
122 123
    if (fd < 0) {
        ret = ff_neterrno();
124
        goto fail;
125
    }
126

127
    if (s->listen) {
128
        if ((fd = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
129
                                 s->listen_timeout, h)) < 0) {
130
            ret = fd;
131 132
            goto fail1;
        }
133
    } else {
134
        if ((ret = ff_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
135
                                     s->open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) {
136

137
            if (ret == AVERROR_EXIT)
138
                goto fail1;
139 140
            else
                goto fail;
141
        }
142
    }
143

144
    h->is_streamed = 1;
Fabrice Bellard's avatar
Fabrice Bellard committed
145
    s->fd = fd;
146
    freeaddrinfo(ai);
Fabrice Bellard's avatar
Fabrice Bellard committed
147 148 149
    return 0;

 fail:
150 151 152 153 154
    if (cur_ai->ai_next) {
        /* Retry with the next sockaddr */
        cur_ai = cur_ai->ai_next;
        if (fd >= 0)
            closesocket(fd);
155
        ret = 0;
156 157
        goto restart;
    }
158
 fail1:
Fabrice Bellard's avatar
Fabrice Bellard committed
159
    if (fd >= 0)
160
        closesocket(fd);
161
    freeaddrinfo(ai);
162
    return ret;
Fabrice Bellard's avatar
Fabrice Bellard committed
163 164
}

165
static int tcp_read(URLContext *h, uint8_t *buf, int size)
Fabrice Bellard's avatar
Fabrice Bellard committed
166 167
{
    TCPContext *s = h->priv_data;
168
    int ret;
Fabrice Bellard's avatar
Fabrice Bellard committed
169

170
    if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
171 172
        ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback);
        if (ret)
173
            return ret;
Fabrice Bellard's avatar
Fabrice Bellard committed
174
    }
175 176
    ret = recv(s->fd, buf, size, 0);
    return ret < 0 ? ff_neterrno() : ret;
Fabrice Bellard's avatar
Fabrice Bellard committed
177 178
}

179
static int tcp_write(URLContext *h, const uint8_t *buf, int size)
Fabrice Bellard's avatar
Fabrice Bellard committed
180 181
{
    TCPContext *s = h->priv_data;
182
    int ret;
Fabrice Bellard's avatar
Fabrice Bellard committed
183

184
    if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
185 186
        ret = ff_network_wait_fd_timeout(s->fd, 1, h->rw_timeout, &h->interrupt_callback);
        if (ret)
187
            return ret;
Fabrice Bellard's avatar
Fabrice Bellard committed
188
    }
189 190
    ret = send(s->fd, buf, size, 0);
    return ret < 0 ? ff_neterrno() : ret;
Fabrice Bellard's avatar
Fabrice Bellard committed
191 192
}

193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
static int tcp_shutdown(URLContext *h, int flags)
{
    TCPContext *s = h->priv_data;
    int how;

    if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) {
        how = SHUT_RDWR;
    } else if (flags & AVIO_FLAG_WRITE) {
        how = SHUT_WR;
    } else {
        how = SHUT_RD;
    }

    return shutdown(s->fd, how);
}

Fabrice Bellard's avatar
Fabrice Bellard committed
209 210 211
static int tcp_close(URLContext *h)
{
    TCPContext *s = h->priv_data;
212
    closesocket(s->fd);
Fabrice Bellard's avatar
Fabrice Bellard committed
213 214 215
    return 0;
}

216 217 218 219 220 221
static int tcp_get_file_handle(URLContext *h)
{
    TCPContext *s = h->priv_data;
    return s->fd;
}

222
URLProtocol ff_tcp_protocol = {
223 224 225 226 227
    .name                = "tcp",
    .url_open            = tcp_open,
    .url_read            = tcp_read,
    .url_write           = tcp_write,
    .url_close           = tcp_close,
228
    .url_get_file_handle = tcp_get_file_handle,
229
    .url_shutdown        = tcp_shutdown,
230
    .priv_data_size      = sizeof(TCPContext),
231
    .priv_data_class     = &tcp_context_class,
232
    .flags               = URL_PROTOCOL_FLAG_NETWORK,
Fabrice Bellard's avatar
Fabrice Bellard committed
233
};