tcp.c 5.04 KB
Newer Older
Fabrice Bellard's avatar
Fabrice Bellard committed
1 2 3 4
/*
 * TCP protocol
 * Copyright (c) 2002 Fabrice Bellard.
 *
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 22
 */
#include "avformat.h"
#include <unistd.h>
23
#include "network.h"
24
#include <sys/time.h>
Fabrice Bellard's avatar
Fabrice Bellard committed
25 26 27 28 29 30 31 32 33 34 35

typedef struct TCPContext {
    int fd;
} TCPContext;

/* return non zero if error */
static int tcp_open(URLContext *h, const char *uri, int flags)
{
    struct sockaddr_in dest_addr;
    char hostname[1024], *q;
    int port, fd = -1;
36
    TCPContext *s = NULL;
37 38 39 40
    fd_set wfds;
    int fd_max, ret;
    struct timeval tv;
    socklen_t optlen;
41 42 43 44 45 46
    char proto[1024],path[1024],tmp[1024];  // PETR: protocol and path strings

    url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
      &port, path, sizeof(path), uri);  // PETR: use url_split
    if (strcmp(proto,"tcp")) goto fail; // PETR: check protocol
    if ((q = strchr(hostname,'@'))) { strcpy(tmp,q+1); strcpy(hostname,tmp); } // PETR: take only the part after '@' for tcp protocol
47

Fabrice Bellard's avatar
Fabrice Bellard committed
48 49
    s = av_malloc(sizeof(TCPContext));
    if (!s)
50
        return AVERROR(ENOMEM);
Fabrice Bellard's avatar
Fabrice Bellard committed
51
    h->priv_data = s;
52

Fabrice Bellard's avatar
Fabrice Bellard committed
53 54
    if (port <= 0 || port >= 65536)
        goto fail;
55

Fabrice Bellard's avatar
Fabrice Bellard committed
56 57 58 59 60
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(port);
    if (resolve_host(&dest_addr.sin_addr, hostname) < 0)
        goto fail;

61
    fd = socket(AF_INET, SOCK_STREAM, 0);
Fabrice Bellard's avatar
Fabrice Bellard committed
62 63
    if (fd < 0)
        goto fail;
64
    ff_socket_nonblock(fd, 1);
65

66
 redo:
67
    ret = connect(fd, (struct sockaddr *)&dest_addr,
68 69
                  sizeof(dest_addr));
    if (ret < 0) {
70
        if (ff_neterrno() == FF_NETERROR(EINTR))
71
            goto redo;
72
        if (ff_neterrno() != FF_NETERROR(EINPROGRESS))
73
            goto fail;
Fabrice Bellard's avatar
Fabrice Bellard committed
74

75 76 77
        /* wait until we are connected or until abort */
        for(;;) {
            if (url_interrupt_cb()) {
78
                ret = AVERROR(EINTR);
79 80 81 82 83 84 85 86 87 88 89
                goto fail1;
            }
            fd_max = fd;
            FD_ZERO(&wfds);
            FD_SET(fd, &wfds);
            tv.tv_sec = 0;
            tv.tv_usec = 100 * 1000;
            ret = select(fd_max + 1, NULL, &wfds, NULL, &tv);
            if (ret > 0 && FD_ISSET(fd, &wfds))
                break;
        }
90

91 92 93 94 95 96
        /* test error */
        optlen = sizeof(ret);
        getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
        if (ret != 0)
            goto fail;
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
97 98 99 100
    s->fd = fd;
    return 0;

 fail:
101
    ret = AVERROR_IO;
102
 fail1:
Fabrice Bellard's avatar
Fabrice Bellard committed
103
    if (fd >= 0)
104
        closesocket(fd);
Fabrice Bellard's avatar
Fabrice Bellard committed
105
    av_free(s);
106
    return ret;
Fabrice Bellard's avatar
Fabrice Bellard committed
107 108
}

109
static int tcp_read(URLContext *h, uint8_t *buf, int size)
Fabrice Bellard's avatar
Fabrice Bellard committed
110 111
{
    TCPContext *s = h->priv_data;
112
    int len, fd_max, ret;
113 114
    fd_set rfds;
    struct timeval tv;
Fabrice Bellard's avatar
Fabrice Bellard committed
115

116
    for (;;) {
117
        if (url_interrupt_cb())
118
            return AVERROR(EINTR);
119 120 121 122 123
        fd_max = s->fd;
        FD_ZERO(&rfds);
        FD_SET(s->fd, &rfds);
        tv.tv_sec = 0;
        tv.tv_usec = 100 * 1000;
124 125 126 127
        ret = select(fd_max + 1, &rfds, NULL, NULL, &tv);
        if (ret > 0 && FD_ISSET(s->fd, &rfds)) {
            len = recv(s->fd, buf, size, 0);
            if (len < 0) {
128 129
                if (ff_neterrno() != FF_NETERROR(EINTR) &&
                    ff_neterrno() != FF_NETERROR(EAGAIN))
130
                    return AVERROR(errno);
131 132 133 134
            } else return len;
        } else if (ret < 0) {
            return -1;
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
135 136 137
    }
}

138
static int tcp_write(URLContext *h, uint8_t *buf, int size)
Fabrice Bellard's avatar
Fabrice Bellard committed
139 140
{
    TCPContext *s = h->priv_data;
141
    int ret, size1, fd_max, len;
142 143
    fd_set wfds;
    struct timeval tv;
Fabrice Bellard's avatar
Fabrice Bellard committed
144 145 146

    size1 = size;
    while (size > 0) {
147
        if (url_interrupt_cb())
148
            return AVERROR(EINTR);
149 150 151 152 153
        fd_max = s->fd;
        FD_ZERO(&wfds);
        FD_SET(s->fd, &wfds);
        tv.tv_sec = 0;
        tv.tv_usec = 100 * 1000;
154 155 156 157
        ret = select(fd_max + 1, NULL, &wfds, NULL, &tv);
        if (ret > 0 && FD_ISSET(s->fd, &wfds)) {
            len = send(s->fd, buf, size, 0);
            if (len < 0) {
158 159
                if (ff_neterrno() != FF_NETERROR(EINTR) &&
                    ff_neterrno() != FF_NETERROR(EAGAIN))
160
                    return AVERROR(errno);
161
                continue;
162
            }
163 164 165 166
            size -= len;
            buf += len;
        } else if (ret < 0) {
            return -1;
167
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
168 169 170 171 172 173 174
    }
    return size1 - size;
}

static int tcp_close(URLContext *h)
{
    TCPContext *s = h->priv_data;
175
    closesocket(s->fd);
Fabrice Bellard's avatar
Fabrice Bellard committed
176 177 178 179 180 181 182 183 184 185 186 187
    av_free(s);
    return 0;
}

URLProtocol tcp_protocol = {
    "tcp",
    tcp_open,
    tcp_read,
    tcp_write,
    NULL, /* seek */
    tcp_close,
};