1/* 2 * TCP protocol 3 * Copyright (c) 2002 Fabrice Bellard 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg 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 * FFmpeg 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 FFmpeg; 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 <unistd.h> 23#include "internal.h" 24#include "network.h" 25#include "os_support.h" 26#if HAVE_SYS_SELECT_H 27#include <sys/select.h> 28#endif 29#include <sys/time.h> 30 31typedef struct TCPContext { 32 int fd; 33} TCPContext; 34 35/* return non zero if error */ 36static int tcp_open(URLContext *h, const char *uri, int flags) 37{ 38 struct addrinfo hints, *ai, *cur_ai; 39 int port, fd = -1; 40 TCPContext *s = NULL; 41 fd_set wfds; 42 int fd_max, ret; 43 struct timeval tv; 44 socklen_t optlen; 45 char hostname[1024],proto[1024],path[1024]; 46 char portstr[10]; 47 48 ff_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), 49 &port, path, sizeof(path), uri); 50 if (strcmp(proto,"tcp") || port <= 0 || port >= 65536) 51 return AVERROR(EINVAL); 52 53 memset(&hints, 0, sizeof(hints)); 54 hints.ai_family = AF_UNSPEC; 55 hints.ai_socktype = SOCK_STREAM; 56 snprintf(portstr, sizeof(portstr), "%d", port); 57 if (getaddrinfo(hostname, portstr, &hints, &ai)) 58 return AVERROR(EIO); 59 60 cur_ai = ai; 61 62 restart: 63 fd = socket(cur_ai->ai_family, cur_ai->ai_socktype, cur_ai->ai_protocol); 64 if (fd < 0) 65 goto fail; 66 ff_socket_nonblock(fd, 1); 67 68 redo: 69 ret = connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen); 70 if (ret < 0) { 71 if (ff_neterrno() == FF_NETERROR(EINTR)) 72 goto redo; 73 if (ff_neterrno() != FF_NETERROR(EINPROGRESS) && 74 ff_neterrno() != FF_NETERROR(EAGAIN)) 75 goto fail; 76 77 /* wait until we are connected or until abort */ 78 for(;;) { 79 if (url_interrupt_cb()) { 80 ret = AVERROR(EINTR); 81 goto fail1; 82 } 83 fd_max = fd; 84 FD_ZERO(&wfds); 85 FD_SET(fd, &wfds); 86 tv.tv_sec = 0; 87 tv.tv_usec = 100 * 1000; 88 ret = select(fd_max + 1, NULL, &wfds, NULL, &tv); 89 if (ret > 0 && FD_ISSET(fd, &wfds)) 90 break; 91 } 92 93 /* test error */ 94 optlen = sizeof(ret); 95 getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen); 96 if (ret != 0) 97 goto fail; 98 } 99 s = av_malloc(sizeof(TCPContext)); 100 if (!s) { 101 freeaddrinfo(ai); 102 return AVERROR(ENOMEM); 103 } 104 h->priv_data = s; 105 h->is_streamed = 1; 106 s->fd = fd; 107 freeaddrinfo(ai); 108 return 0; 109 110 fail: 111 if (cur_ai->ai_next) { 112 /* Retry with the next sockaddr */ 113 cur_ai = cur_ai->ai_next; 114 if (fd >= 0) 115 closesocket(fd); 116 goto restart; 117 } 118 ret = AVERROR(EIO); 119 fail1: 120 if (fd >= 0) 121 closesocket(fd); 122 freeaddrinfo(ai); 123 return ret; 124} 125 126static int tcp_read(URLContext *h, uint8_t *buf, int size) 127{ 128 TCPContext *s = h->priv_data; 129 int len, fd_max, ret; 130 fd_set rfds; 131 struct timeval tv; 132 133 for (;;) { 134 if (url_interrupt_cb()) 135 return AVERROR(EINTR); 136 fd_max = s->fd; 137 FD_ZERO(&rfds); 138 FD_SET(s->fd, &rfds); 139 tv.tv_sec = 0; 140 tv.tv_usec = 100 * 1000; 141 ret = select(fd_max + 1, &rfds, NULL, NULL, &tv); 142 if (ret > 0 && FD_ISSET(s->fd, &rfds)) { 143 len = recv(s->fd, buf, size, 0); 144 if (len < 0) { 145 if (ff_neterrno() != FF_NETERROR(EINTR) && 146 ff_neterrno() != FF_NETERROR(EAGAIN)) 147 return AVERROR(ff_neterrno()); 148 } else return len; 149 } else if (ret < 0) { 150 if (ff_neterrno() == FF_NETERROR(EINTR)) 151 continue; 152 return -1; 153 } 154 } 155} 156 157static int tcp_write(URLContext *h, uint8_t *buf, int size) 158{ 159 TCPContext *s = h->priv_data; 160 int ret, size1, fd_max, len; 161 fd_set wfds; 162 struct timeval tv; 163 164 size1 = size; 165 while (size > 0) { 166 if (url_interrupt_cb()) 167 return AVERROR(EINTR); 168 fd_max = s->fd; 169 FD_ZERO(&wfds); 170 FD_SET(s->fd, &wfds); 171 tv.tv_sec = 0; 172 tv.tv_usec = 100 * 1000; 173 ret = select(fd_max + 1, NULL, &wfds, NULL, &tv); 174 if (ret > 0 && FD_ISSET(s->fd, &wfds)) { 175 len = send(s->fd, buf, size, 0); 176 if (len < 0) { 177 if (ff_neterrno() != FF_NETERROR(EINTR) && 178 ff_neterrno() != FF_NETERROR(EAGAIN)) 179 return AVERROR(ff_neterrno()); 180 continue; 181 } 182 size -= len; 183 buf += len; 184 } else if (ret < 0) { 185 if (ff_neterrno() == FF_NETERROR(EINTR)) 186 continue; 187 return -1; 188 } 189 } 190 return size1 - size; 191} 192 193static int tcp_close(URLContext *h) 194{ 195 TCPContext *s = h->priv_data; 196 closesocket(s->fd); 197 av_free(s); 198 return 0; 199} 200 201static int tcp_get_file_handle(URLContext *h) 202{ 203 TCPContext *s = h->priv_data; 204 return s->fd; 205} 206 207URLProtocol tcp_protocol = { 208 "tcp", 209 tcp_open, 210 tcp_read, 211 tcp_write, 212 NULL, /* seek */ 213 tcp_close, 214 .url_get_file_handle = tcp_get_file_handle, 215}; 216