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 <unistd.h> 24#include "internal.h" 25#include "network.h" 26#include "os_support.h" 27#include "url.h" 28#if HAVE_POLL_H 29#include <poll.h> 30#endif 31#include <sys/time.h> 32 33typedef struct TCPContext { 34 int fd; 35} TCPContext; 36 37/* return non zero if error */ 38static int tcp_open(URLContext *h, const char *uri, int flags) 39{ 40 struct addrinfo hints, *ai, *cur_ai; 41 int port, fd = -1; 42 TCPContext *s = h->priv_data; 43 int listen_socket = 0; 44 const char *p; 45 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 &port, path, sizeof(path), uri); 54 if (strcmp(proto,"tcp") || port <= 0 || port >= 65536) 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 } 65 memset(&hints, 0, sizeof(hints)); 66 hints.ai_family = AF_UNSPEC; 67 hints.ai_socktype = SOCK_STREAM; 68 snprintf(portstr, sizeof(portstr), "%d", port); 69 ret = getaddrinfo(hostname, portstr, &hints, &ai); 70 if (ret) { 71 av_log(h, AV_LOG_ERROR, 72 "Failed to resolve hostname %s: %s\n", 73 hostname, gai_strerror(ret)); 74 return AVERROR(EIO); 75 } 76 77 cur_ai = ai; 78 79 restart: 80 ret = AVERROR(EIO); 81 fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol); 82 if (fd < 0) 83 goto fail; 84 85 if (listen_socket) { 86 int fd1; 87 ret = bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); 88 listen(fd, 1); 89 fd1 = accept(fd, NULL, NULL); 90 closesocket(fd); 91 fd = fd1; 92 ff_socket_nonblock(fd, 1); 93 } else { 94 redo: 95 ff_socket_nonblock(fd, 1); 96 ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); 97 } 98 99 if (ret < 0) { 100 struct pollfd p = {fd, POLLOUT, 0}; 101 ret = ff_neterrno(); 102 if (ret == AVERROR(EINTR)) { 103 if (ff_check_interrupt(&h->interrupt_callback)) { 104 ret = AVERROR_EXIT; 105 goto fail1; 106 } 107 goto redo; 108 } 109 if (ret != AVERROR(EINPROGRESS) && 110 ret != AVERROR(EAGAIN)) 111 goto fail; 112 113 /* wait until we are connected or until abort */ 114 while(timeout--) { 115 if (ff_check_interrupt(&h->interrupt_callback)) { 116 ret = AVERROR_EXIT; 117 goto fail1; 118 } 119 ret = poll(&p, 1, 100); 120 if (ret > 0) 121 break; 122 } 123 if (ret <= 0) { 124 ret = AVERROR(ETIMEDOUT); 125 goto fail; 126 } 127 /* test error */ 128 optlen = sizeof(ret); 129 getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen); 130 if (ret != 0) { 131 av_log(h, AV_LOG_ERROR, 132 "TCP connection to %s:%d failed: %s\n", 133 hostname, port, strerror(ret)); 134 ret = AVERROR(ret); 135 goto fail; 136 } 137 } 138 h->is_streamed = 1; 139 s->fd = fd; 140 freeaddrinfo(ai); 141 return 0; 142 143 fail: 144 if (cur_ai->ai_next) { 145 /* Retry with the next sockaddr */ 146 cur_ai = cur_ai->ai_next; 147 if (fd >= 0) 148 closesocket(fd); 149 goto restart; 150 } 151 fail1: 152 if (fd >= 0) 153 closesocket(fd); 154 freeaddrinfo(ai); 155 return ret; 156} 157 158static int tcp_read(URLContext *h, uint8_t *buf, int size) 159{ 160 TCPContext *s = h->priv_data; 161 int ret; 162 163 if (!(h->flags & AVIO_FLAG_NONBLOCK)) { 164 ret = ff_network_wait_fd(s->fd, 0); 165 if (ret < 0) 166 return ret; 167 } 168 ret = recv(s->fd, buf, size, 0); 169 return ret < 0 ? ff_neterrno() : ret; 170} 171 172static int tcp_write(URLContext *h, const uint8_t *buf, int size) 173{ 174 TCPContext *s = h->priv_data; 175 int ret; 176 177 if (!(h->flags & AVIO_FLAG_NONBLOCK)) { 178 ret = ff_network_wait_fd(s->fd, 1); 179 if (ret < 0) 180 return ret; 181 } 182 ret = send(s->fd, buf, size, 0); 183 return ret < 0 ? ff_neterrno() : ret; 184} 185 186static int tcp_close(URLContext *h) 187{ 188 TCPContext *s = h->priv_data; 189 closesocket(s->fd); 190 return 0; 191} 192 193static int tcp_get_file_handle(URLContext *h) 194{ 195 TCPContext *s = h->priv_data; 196 return s->fd; 197} 198 199URLProtocol ff_tcp_protocol = { 200 .name = "tcp", 201 .url_open = tcp_open, 202 .url_read = tcp_read, 203 .url_write = tcp_write, 204 .url_close = tcp_close, 205 .url_get_file_handle = tcp_get_file_handle, 206 .priv_data_size = sizeof(TCPContext), 207 .flags = URL_PROTOCOL_FLAG_NETWORK, 208}; 209