network: uniform ff_listen_bind and ff_listen_connect
[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 = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol);
88     if (fd < 0) {
89         ret = ff_neterrno();
90         goto fail;
91     }
92
93     if (listen_socket) {
94         if ((fd = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
95                                  listen_timeout, h)) < 0) {
96             ret = fd;
97             goto fail1;
98         }
99     } else {
100         if ((ret = ff_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
101                                      timeout * 100, h)) < 0) {
102
103             if (ret == AVERROR_EXIT)
104                 goto fail1;
105             else
106                 goto fail;
107         }
108     }
109
110     h->is_streamed = 1;
111     s->fd = fd;
112     freeaddrinfo(ai);
113     return 0;
114
115  fail:
116     if (cur_ai->ai_next) {
117         /* Retry with the next sockaddr */
118         cur_ai = cur_ai->ai_next;
119         if (fd >= 0)
120             closesocket(fd);
121         ret = 0;
122         goto restart;
123     }
124  fail1:
125     if (fd >= 0)
126         closesocket(fd);
127     freeaddrinfo(ai);
128     return ret;
129 }
130
131 static int tcp_read(URLContext *h, uint8_t *buf, int size)
132 {
133     TCPContext *s = h->priv_data;
134     int ret;
135
136     if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
137         ret = ff_network_wait_fd(s->fd, 0);
138         if (ret < 0)
139             return ret;
140     }
141     ret = recv(s->fd, buf, size, 0);
142     return ret < 0 ? ff_neterrno() : ret;
143 }
144
145 static int tcp_write(URLContext *h, const uint8_t *buf, int size)
146 {
147     TCPContext *s = h->priv_data;
148     int ret;
149
150     if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
151         ret = ff_network_wait_fd(s->fd, 1);
152         if (ret < 0)
153             return ret;
154     }
155     ret = send(s->fd, buf, size, 0);
156     return ret < 0 ? ff_neterrno() : ret;
157 }
158
159 static int tcp_shutdown(URLContext *h, int flags)
160 {
161     TCPContext *s = h->priv_data;
162     int how;
163
164     if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) {
165         how = SHUT_RDWR;
166     } else if (flags & AVIO_FLAG_WRITE) {
167         how = SHUT_WR;
168     } else {
169         how = SHUT_RD;
170     }
171
172     return shutdown(s->fd, how);
173 }
174
175 static int tcp_close(URLContext *h)
176 {
177     TCPContext *s = h->priv_data;
178     closesocket(s->fd);
179     return 0;
180 }
181
182 static int tcp_get_file_handle(URLContext *h)
183 {
184     TCPContext *s = h->priv_data;
185     return s->fd;
186 }
187
188 URLProtocol ff_tcp_protocol = {
189     .name                = "tcp",
190     .url_open            = tcp_open,
191     .url_read            = tcp_read,
192     .url_write           = tcp_write,
193     .url_close           = tcp_close,
194     .url_get_file_handle = tcp_get_file_handle,
195     .url_shutdown        = tcp_shutdown,
196     .priv_data_size      = sizeof(TCPContext),
197     .flags               = URL_PROTOCOL_FLAG_NETWORK,
198 };