cbd51422a8c8616eea2fc4b7aa9892a721cf094e
[ffmpeg.git] / libavformat / tcp.c
1 /*
2  * TCP protocol
3  * Copyright (c) 2002 Fabrice Bellard
4  *
5  * This file is part of Libav.
6  *
7  * Libav 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  * Libav 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 Libav; 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 "libavutil/parseutils.h"
23 #include "internal.h"
24 #include "network.h"
25 #include "os_support.h"
26 #include "url.h"
27 #if HAVE_POLL_H
28 #include <poll.h>
29 #endif
30
31 typedef struct TCPContext {
32     int fd;
33 } TCPContext;
34
35 /* return non zero if error */
36 static int tcp_open(URLContext *h, const char *uri, int flags)
37 {
38     struct addrinfo hints = { 0 }, *ai, *cur_ai;
39     int port, fd = -1;
40     TCPContext *s = h->priv_data;
41     int listen_socket = 0;
42     const char *p;
43     char buf[256];
44     int ret;
45     int timeout = 100, listen_timeout = -1;
46     char hostname[1024],proto[1024],path[1024];
47     char portstr[10];
48
49     av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
50         &port, path, sizeof(path), uri);
51     if (strcmp(proto, "tcp"))
52         return AVERROR(EINVAL);
53     if (port <= 0 || port >= 65536) {
54         av_log(h, AV_LOG_ERROR, "Port missing in uri\n");
55         return AVERROR(EINVAL);
56     }
57     p = strchr(uri, '?');
58     if (p) {
59         if (av_find_info_tag(buf, sizeof(buf), "listen", p))
60             listen_socket = 1;
61         if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
62             timeout = strtol(buf, NULL, 10);
63         }
64         if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) {
65             listen_timeout = strtol(buf, NULL, 10);
66         }
67     }
68     hints.ai_family = AF_UNSPEC;
69     hints.ai_socktype = SOCK_STREAM;
70     snprintf(portstr, sizeof(portstr), "%d", port);
71     if (listen_socket)
72         hints.ai_flags |= AI_PASSIVE;
73     if (!hostname[0])
74         ret = getaddrinfo(NULL, portstr, &hints, &ai);
75     else
76         ret = getaddrinfo(hostname, portstr, &hints, &ai);
77     if (ret) {
78         av_log(h, AV_LOG_ERROR,
79                "Failed to resolve hostname %s: %s\n",
80                hostname, gai_strerror(ret));
81         return AVERROR(EIO);
82     }
83
84     cur_ai = ai;
85
86  restart:
87     fd = ff_socket(cur_ai->ai_family,
88                    cur_ai->ai_socktype,
89                    cur_ai->ai_protocol);
90     if (fd < 0) {
91         ret = ff_neterrno();
92         goto fail;
93     }
94
95     if (listen_socket) {
96         if ((fd = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
97                                  listen_timeout, h)) < 0) {
98             ret = fd;
99             goto fail1;
100         }
101     } else {
102         if ((ret = ff_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
103                                      timeout * 100, h, !!cur_ai->ai_next)) < 0) {
104
105             if (ret == AVERROR_EXIT)
106                 goto fail1;
107             else
108                 goto fail;
109         }
110     }
111
112     h->is_streamed = 1;
113     s->fd = fd;
114     freeaddrinfo(ai);
115     return 0;
116
117  fail:
118     if (cur_ai->ai_next) {
119         /* Retry with the next sockaddr */
120         cur_ai = cur_ai->ai_next;
121         if (fd >= 0)
122             closesocket(fd);
123         ret = 0;
124         goto restart;
125     }
126  fail1:
127     if (fd >= 0)
128         closesocket(fd);
129     freeaddrinfo(ai);
130     return ret;
131 }
132
133 static int tcp_read(URLContext *h, uint8_t *buf, int size)
134 {
135     TCPContext *s = h->priv_data;
136     int ret;
137
138     if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
139         ret = ff_network_wait_fd(s->fd, 0);
140         if (ret < 0)
141             return ret;
142     }
143     ret = recv(s->fd, buf, size, 0);
144     return ret < 0 ? ff_neterrno() : ret;
145 }
146
147 static int tcp_write(URLContext *h, const uint8_t *buf, int size)
148 {
149     TCPContext *s = h->priv_data;
150     int ret;
151
152     if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
153         ret = ff_network_wait_fd(s->fd, 1);
154         if (ret < 0)
155             return ret;
156     }
157     ret = send(s->fd, buf, size, 0);
158     return ret < 0 ? ff_neterrno() : ret;
159 }
160
161 static int tcp_shutdown(URLContext *h, int flags)
162 {
163     TCPContext *s = h->priv_data;
164     int how;
165
166     if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) {
167         how = SHUT_RDWR;
168     } else if (flags & AVIO_FLAG_WRITE) {
169         how = SHUT_WR;
170     } else {
171         how = SHUT_RD;
172     }
173
174     return shutdown(s->fd, how);
175 }
176
177 static int tcp_close(URLContext *h)
178 {
179     TCPContext *s = h->priv_data;
180     closesocket(s->fd);
181     return 0;
182 }
183
184 static int tcp_get_file_handle(URLContext *h)
185 {
186     TCPContext *s = h->priv_data;
187     return s->fd;
188 }
189
190 URLProtocol ff_tcp_protocol = {
191     .name                = "tcp",
192     .url_open            = tcp_open,
193     .url_read            = tcp_read,
194     .url_write           = tcp_write,
195     .url_close           = tcp_close,
196     .url_get_file_handle = tcp_get_file_handle,
197     .url_shutdown        = tcp_shutdown,
198     .priv_data_size      = sizeof(TCPContext),
199     .flags               = URL_PROTOCOL_FLAG_NETWORK,
200 };