Change license headers to say 'FFmpeg' instead of 'this program/this library'
[ffmpeg.git] / libavformat / tcp.c
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 <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #if defined(__BEOS__) || defined(__INNOTEK_LIBC__)
27 typedef int socklen_t;
28 #endif
29 #ifndef __BEOS__
30 # include <arpa/inet.h>
31 #else
32 # include "barpainet.h"
33 #endif
34 #include <netdb.h>
35 #include <sys/time.h>
36 #include <fcntl.h>
37
38 typedef struct TCPContext {
39     int fd;
40 } TCPContext;
41
42 /* resolve host with also IP address parsing */
43 int resolve_host(struct in_addr *sin_addr, const char *hostname)
44 {
45     struct hostent *hp;
46
47     if ((inet_aton(hostname, sin_addr)) == 0) {
48         hp = gethostbyname(hostname);
49         if (!hp)
50             return -1;
51         memcpy (sin_addr, hp->h_addr, sizeof(struct in_addr));
52     }
53     return 0;
54 }
55
56 /* return non zero if error */
57 static int tcp_open(URLContext *h, const char *uri, int flags)
58 {
59     struct sockaddr_in dest_addr;
60     char hostname[1024], *q;
61     int port, fd = -1;
62     TCPContext *s = NULL;
63     fd_set wfds;
64     int fd_max, ret;
65     struct timeval tv;
66     socklen_t optlen;
67     char proto[1024],path[1024],tmp[1024];  // PETR: protocol and path strings
68
69     url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname),
70       &port, path, sizeof(path), uri);  // PETR: use url_split
71     if (strcmp(proto,"tcp")) goto fail; // PETR: check protocol
72     if ((q = strchr(hostname,'@'))) { strcpy(tmp,q+1); strcpy(hostname,tmp); } // PETR: take only the part after '@' for tcp protocol
73
74     s = av_malloc(sizeof(TCPContext));
75     if (!s)
76         return -ENOMEM;
77     h->priv_data = s;
78
79     if (port <= 0 || port >= 65536)
80         goto fail;
81
82     dest_addr.sin_family = AF_INET;
83     dest_addr.sin_port = htons(port);
84     if (resolve_host(&dest_addr.sin_addr, hostname) < 0)
85         goto fail;
86
87     fd = socket(PF_INET, SOCK_STREAM, 0);
88     if (fd < 0)
89         goto fail;
90     fcntl(fd, F_SETFL, O_NONBLOCK);
91
92  redo:
93     ret = connect(fd, (struct sockaddr *)&dest_addr,
94                   sizeof(dest_addr));
95     if (ret < 0) {
96         if (errno == EINTR)
97             goto redo;
98         if (errno != EINPROGRESS)
99             goto fail;
100
101         /* wait until we are connected or until abort */
102         for(;;) {
103             if (url_interrupt_cb()) {
104                 ret = -EINTR;
105                 goto fail1;
106             }
107             fd_max = fd;
108             FD_ZERO(&wfds);
109             FD_SET(fd, &wfds);
110             tv.tv_sec = 0;
111             tv.tv_usec = 100 * 1000;
112             ret = select(fd_max + 1, NULL, &wfds, NULL, &tv);
113             if (ret > 0 && FD_ISSET(fd, &wfds))
114                 break;
115         }
116
117         /* test error */
118         optlen = sizeof(ret);
119         getsockopt (fd, SOL_SOCKET, SO_ERROR, &ret, &optlen);
120         if (ret != 0)
121             goto fail;
122     }
123     s->fd = fd;
124     return 0;
125
126  fail:
127     ret = AVERROR_IO;
128  fail1:
129     if (fd >= 0)
130         close(fd);
131     av_free(s);
132     return ret;
133 }
134
135 static int tcp_read(URLContext *h, uint8_t *buf, int size)
136 {
137     TCPContext *s = h->priv_data;
138     int len, fd_max, ret;
139     fd_set rfds;
140     struct timeval tv;
141
142     for (;;) {
143         if (url_interrupt_cb())
144             return -EINTR;
145         fd_max = s->fd;
146         FD_ZERO(&rfds);
147         FD_SET(s->fd, &rfds);
148         tv.tv_sec = 0;
149         tv.tv_usec = 100 * 1000;
150         ret = select(fd_max + 1, &rfds, NULL, NULL, &tv);
151         if (ret > 0 && FD_ISSET(s->fd, &rfds)) {
152 #ifdef __BEOS__
153             len = recv(s->fd, buf, size, 0);
154 #else
155             len = read(s->fd, buf, size);
156 #endif
157             if (len < 0) {
158                 if (errno != EINTR && errno != EAGAIN)
159 #ifdef __BEOS__
160                     return errno;
161 #else
162                     return -errno;
163 #endif
164             } else return len;
165         } else if (ret < 0) {
166             return -1;
167         }
168     }
169 }
170
171 static int tcp_write(URLContext *h, uint8_t *buf, int size)
172 {
173     TCPContext *s = h->priv_data;
174     int ret, size1, fd_max, len;
175     fd_set wfds;
176     struct timeval tv;
177
178     size1 = size;
179     while (size > 0) {
180         if (url_interrupt_cb())
181             return -EINTR;
182         fd_max = s->fd;
183         FD_ZERO(&wfds);
184         FD_SET(s->fd, &wfds);
185         tv.tv_sec = 0;
186         tv.tv_usec = 100 * 1000;
187         ret = select(fd_max + 1, NULL, &wfds, NULL, &tv);
188         if (ret > 0 && FD_ISSET(s->fd, &wfds)) {
189 #ifdef __BEOS__
190             len = send(s->fd, buf, size, 0);
191 #else
192             len = write(s->fd, buf, size);
193 #endif
194             if (len < 0) {
195                 if (errno != EINTR && errno != EAGAIN) {
196 #ifdef __BEOS__
197                     return errno;
198 #else
199                     return -errno;
200 #endif
201                 }
202                 continue;
203             }
204             size -= len;
205             buf += len;
206         } else if (ret < 0) {
207             return -1;
208         }
209     }
210     return size1 - size;
211 }
212
213 static int tcp_close(URLContext *h)
214 {
215     TCPContext *s = h->priv_data;
216 #ifdef CONFIG_BEOS_NETSERVER
217     closesocket(s->fd);
218 #else
219     close(s->fd);
220 #endif
221     av_free(s);
222     return 0;
223 }
224
225 URLProtocol tcp_protocol = {
226     "tcp",
227     tcp_open,
228     tcp_read,
229     tcp_write,
230     NULL, /* seek */
231     tcp_close,
232 };