tcp.c 5.53 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
 * This file is part of Libav.
6
 *
7
 * Libav 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
 * Libav 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 Libav; 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"
Fabrice Bellard's avatar
Fabrice Bellard committed
23
#include <unistd.h>
24
#include "internal.h"
25
#include "network.h"
26
#include "os_support.h"
27
#include "url.h"
28 29
#if HAVE_POLL_H
#include <poll.h>
30
#endif
31
#include <sys/time.h>
Fabrice Bellard's avatar
Fabrice Bellard committed
32 33 34 35 36 37 38 39

typedef struct TCPContext {
    int fd;
} TCPContext;

/* return non zero if error */
static int tcp_open(URLContext *h, const char *uri, int flags)
{
40
    struct addrinfo hints, *ai, *cur_ai;
Fabrice Bellard's avatar
Fabrice Bellard committed
41
    int port, fd = -1;
42
    TCPContext *s = NULL;
43 44 45
    int listen_socket = 0;
    const char *p;
    char buf[256];
46
    int ret;
47
    socklen_t optlen;
48
    int timeout = 100;
49
    char hostname[1024],proto[1024],path[1024];
50
    char portstr[10];
51

52
    av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
53 54
        &port, path, sizeof(path), uri);
    if (strcmp(proto,"tcp") || port <= 0 || port >= 65536)
55
        return AVERROR(EINVAL);
56

57 58 59 60
    p = strchr(uri, '?');
    if (p) {
        if (av_find_info_tag(buf, sizeof(buf), "listen", p))
            listen_socket = 1;
61 62 63
        if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
            timeout = strtol(buf, NULL, 10);
        }
64
    }
65
    memset(&hints, 0, sizeof(hints));
66
    hints.ai_family = AF_UNSPEC;
67 68
    hints.ai_socktype = SOCK_STREAM;
    snprintf(portstr, sizeof(portstr), "%d", port);
69 70
    ret = getaddrinfo(hostname, portstr, &hints, &ai);
    if (ret) {
71
        av_log(h, AV_LOG_ERROR,
72 73
               "Failed to resolve hostname %s: %s\n",
               hostname, gai_strerror(ret));
74
        return AVERROR(EIO);
75
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
76

77 78 79
    cur_ai = ai;

 restart:
80
    ret = AVERROR(EIO);
81
    fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol);
Fabrice Bellard's avatar
Fabrice Bellard committed
82
    if (fd < 0)
83
        goto fail;
84

85 86 87 88 89 90 91
    if (listen_socket) {
        int fd1;
        ret = bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen);
        listen(fd, 1);
        fd1 = accept(fd, NULL, NULL);
        closesocket(fd);
        fd = fd1;
92
        ff_socket_nonblock(fd, 1);
93
    } else {
94
 redo:
95
        ff_socket_nonblock(fd, 1);
96 97 98
        ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen);
    }

99
    if (ret < 0) {
100
        struct pollfd p = {fd, POLLOUT, 0};
101 102
        ret = ff_neterrno();
        if (ret == AVERROR(EINTR)) {
103 104
            if (url_interrupt_cb()) {
                ret = AVERROR_EXIT;
105
                goto fail1;
106
            }
107
            goto redo;
108
        }
109 110
        if (ret != AVERROR(EINPROGRESS) &&
            ret != AVERROR(EAGAIN))
111
            goto fail;
Fabrice Bellard's avatar
Fabrice Bellard committed
112

113
        /* wait until we are connected or until abort */
114
        while(timeout--) {
115
            if (url_interrupt_cb()) {
116
                ret = AVERROR_EXIT;
117 118
                goto fail1;
            }
119 120
            ret = poll(&p, 1, 100);
            if (ret > 0)
121 122
                break;
        }
123 124 125 126
        if (ret <= 0) {
            ret = AVERROR(ETIMEDOUT);
            goto fail;
        }
127 128 129
        /* test error */
        optlen = sizeof(ret);
        getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
130
        if (ret != 0) {
131
            av_log(h, AV_LOG_ERROR,
132 133
                   "TCP connection to %s:%d failed: %s\n",
                   hostname, port, strerror(ret));
134
            ret = AVERROR(ret);
135
            goto fail;
136
        }
137
    }
138
    s = av_malloc(sizeof(TCPContext));
139 140
    if (!s) {
        freeaddrinfo(ai);
141
        return AVERROR(ENOMEM);
142
    }
143 144
    h->priv_data = s;
    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 155 156
    if (cur_ai->ai_next) {
        /* Retry with the next sockaddr */
        cur_ai = cur_ai->ai_next;
        if (fd >= 0)
            closesocket(fd);
        goto restart;
    }
157
 fail1:
Fabrice Bellard's avatar
Fabrice Bellard committed
158
    if (fd >= 0)
159
        closesocket(fd);
160
    freeaddrinfo(ai);
161
    return ret;
Fabrice Bellard's avatar
Fabrice Bellard committed
162 163
}

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

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

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

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

static int tcp_close(URLContext *h)
{
    TCPContext *s = h->priv_data;
195
    closesocket(s->fd);
Fabrice Bellard's avatar
Fabrice Bellard committed
196 197 198 199
    av_free(s);
    return 0;
}

200 201 202 203 204 205
static int tcp_get_file_handle(URLContext *h)
{
    TCPContext *s = h->priv_data;
    return s->fd;
}

206
URLProtocol ff_tcp_protocol = {
207 208 209 210 211
    .name                = "tcp",
    .url_open            = tcp_open,
    .url_read            = tcp_read,
    .url_write           = tcp_write,
    .url_close           = tcp_close,
212
    .url_get_file_handle = tcp_get_file_handle,
Fabrice Bellard's avatar
Fabrice Bellard committed
213
};