1 /*
2 mpg321 - a fully free clone of mpg123.
3 Copyright (C) 2001 Joe Drew
4
5 Network code based heavily upon:
6 plaympeg - Sample MPEG player using the SMPEG library
7 Copyright (C) 1999 Loki Entertainment Software
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include <stdio.h>
25 #include <string.h>
26
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/ioctl.h>
31 #include <sys/time.h>
32 #include <fcntl.h>
33
34 #include <netinet/in.h>
35 #include <netdb.h>
36 #include <sys/socket.h>
37 #include <arpa/inet.h>
38
39 #include <unistd.h>
40
41 #include <limits.h>
42
43 #include "mpg321.h"
44
is_address_multicast(unsigned long address)45 int is_address_multicast(unsigned long address)
46 {
47 if ((address & 255) >= 224 && (address & 255) <= 239)
48 return (1);
49 return (0);
50 }
51
tcp_open(char * address,int port)52 int tcp_open(char *address, int port)
53 {
54 struct sockaddr_in stAddr;
55 struct hostent *host;
56 int sock;
57 struct linger l;
58
59 memset(&stAddr, 0, sizeof(stAddr));
60 stAddr.sin_family = AF_INET;
61 stAddr.sin_port = htons(port);
62
63 if ((host = gethostbyname(address)) == NULL)
64 return (0);
65
66 stAddr.sin_addr = *((struct in_addr *)host->h_addr_list[0]);
67
68 if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
69 return (0);
70
71 l.l_onoff = 1;
72 l.l_linger = 5;
73 if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)&l, sizeof(l)) < 0)
74 return (0);
75
76 if (connect(sock, (struct sockaddr *)&stAddr, sizeof(stAddr)) < 0)
77 return (0);
78
79 return (sock);
80 }
81
udp_open(char * address,int port)82 int udp_open(char *address, int port)
83 {
84 int enable = 1L;
85 struct sockaddr_in stAddr;
86 struct sockaddr_in stLclAddr;
87 struct ip_mreq stMreq;
88 struct hostent *host;
89 int sock;
90
91 stAddr.sin_family = AF_INET;
92 stAddr.sin_port = htons(port);
93
94 if ((host = gethostbyname(address)) == NULL)
95 return (0);
96
97 stAddr.sin_addr = *((struct in_addr *)host->h_addr_list[0]);
98
99 /* Create a UDP socket */
100 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
101 return (0);
102
103 /* Allow multiple instance of the client to share the same address and port */
104 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&enable, sizeof(unsigned long int)) < 0)
105 return (0);
106
107 /* If the address is multicast, register to the multicast group */
108 if (is_address_multicast(stAddr.sin_addr.s_addr))
109 {
110 /* Bind the socket to port */
111 stLclAddr.sin_family = AF_INET;
112 stLclAddr.sin_addr.s_addr = htonl(INADDR_ANY);
113 stLclAddr.sin_port = stAddr.sin_port;
114 if (bind(sock, (struct sockaddr *)&stLclAddr, sizeof(stLclAddr)) < 0)
115 return (0);
116
117 /* Register to a multicast address */
118 stMreq.imr_multiaddr.s_addr = stAddr.sin_addr.s_addr;
119 stMreq.imr_interface.s_addr = INADDR_ANY;
120 if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&stMreq, sizeof(stMreq)) < 0)
121 return (0);
122 }
123 else
124 {
125 /* Bind the socket to port */
126 stLclAddr.sin_family = AF_INET;
127 stLclAddr.sin_addr.s_addr = htonl(INADDR_ANY);
128 stLclAddr.sin_port = htons(0);
129 if (bind(sock, (struct sockaddr *)&stLclAddr, sizeof(stLclAddr)) < 0)
130 return (0);
131 }
132
133 return (sock);
134 }
135
raw_open(char * arg)136 int raw_open(char *arg)
137 {
138 char *host;
139 int port;
140 int sock;
141
142 /* Check for URL syntax */
143 if (strncmp(arg, "raw://", strlen("raw://")))
144 return (0);
145
146 /* Parse URL */
147 port = 0;
148 host = arg + strlen("raw://");
149 if (strchr(host, ':') != NULL) /* port is specified */
150 {
151 port = atoi(strchr(host, ':') + 1);
152 *strchr(host, ':') = 0;
153 }
154
155 /* Open a UDP socket */
156 if (!(sock = udp_open(host, port)))
157 perror("raw_open");
158
159 return (sock);
160 }
161
162 /**
163 * Read a http line header.
164 * This function read character by character.
165 * @param tcp_sock the socket use to read the stream
166 * @param buf a buffer to receive the data
167 * @param size size of the buffer
168 * @return the size of the stream read or -1 if an error occured
169 */
http_read_line(int tcp_sock,char * buf,int size)170 static int http_read_line(int tcp_sock, char *buf, int size)
171 {
172 int offset = 0;
173
174 do
175 {
176 if (read(tcp_sock, buf + offset, 1) < 0)
177 return -1;
178 if (buf[offset] != '\r') /* Strip \r from answer */
179 offset++;
180 }
181 while (offset < size - 1 && buf[offset - 1] != '\n');
182
183 buf[offset] = 0;
184 return offset;
185 }
186
http_open(char * arg)187 int http_open(char *arg)
188 {
189 char *host;
190 int port;
191 char *request;
192 int tcp_sock;
193 char http_request[PATH_MAX];
194 char filename[PATH_MAX];
195 char c;
196
197 /* Check for URL syntax */
198 if (strncmp(arg, "http://", strlen("http://")))
199 return (0);
200
201 /* Parse URL */
202 port = 80;
203 host = arg + strlen("http://");
204 if ((request = strchr(host, '/')) == NULL)
205 return (0);
206 *request++ = 0;
207
208 if (strchr(host, ':') != NULL) /* port is specified */
209 {
210 port = atoi(strchr(host, ':') + 1);
211 *strchr(host, ':') = 0;
212 }
213
214 /* Open a TCP socket */
215 if (!(tcp_sock = tcp_open(host, port)))
216 {
217 perror("http_open");
218 return (0);
219 }
220
221 snprintf(filename, sizeof(filename) - strlen(host) - 75, "%s", request);
222
223 /* Send HTTP GET request */
224 /* Please don't use a Agent know by shoutcast (Lynx, Mozilla) seems to be reconized and print
225 * a html page and not the stream */
226 snprintf(http_request, sizeof(http_request), "GET /%s HTTP/1.0\r\n"
227 /* "User-Agent: Mozilla/2.0 (Win95; I)\r\n" */
228 "User-Agent: mpg321/%s\r\n"
229 "Pragma: no-cache\r\n" "Host: %s\r\n" "Accept: */*\r\n" "\r\n", filename, VERSION, host);
230
231 send(tcp_sock, http_request, strlen(http_request), 0);
232
233 /* Parse server reply */
234 #if 0
235 do
236 read(tcp_sock, &c, sizeof(char));
237 while (c != ' ');
238 read(tcp_sock, http_request, 4 * sizeof(char));
239 http_request[4] = 0;
240 if (strcmp(http_request, "200 "))
241 {
242 fprintf(stderr, "http_open: ");
243 do
244 {
245 read(tcp_sock, &c, sizeof(char));
246 fprintf(stderr, "%c", c);
247 }
248 while (c != '\r');
249 fprintf(stderr, "\n");
250 return (0);
251 }
252 #endif
253
254 do
255 {
256 int len;
257
258 len = http_read_line(tcp_sock, http_request, sizeof(http_request));
259
260 if (len == -1)
261 {
262 fprintf(stderr, "http_open: %s\n", strerror(errno));
263 return 0;
264 }
265
266 if (strncmp(http_request, "Location:", 9) == 0)
267 {
268 /* redirect */
269 close(tcp_sock);
270
271 http_request[strlen(http_request) - 1] = '\0';
272
273 return http_open(&http_request[10]);
274 }
275
276 if (strncmp(http_request, "ICY ", 4) == 0)
277 {
278 /* This is icecast streaming */
279 if (strncmp(http_request + 4, "200 ", 4))
280 {
281 fprintf(stderr, "http_open: %s\n", http_request);
282 return 0;
283 }
284 }
285 else if (strncmp(http_request, "icy-", 4) == 0)
286 {
287 /* we can have: icy-noticeX, icy-name, icy-genre, icy-url, icy-pub, icy-metaint, icy-br */
288 /* Don't print these - mpg123 doesn't */
289 /* fprintf(stderr,"%s\n",http_request); */
290 }
291 }
292 while (strcmp(http_request, "\n") != 0);
293
294 return (tcp_sock);
295 }
296
ftp_get_reply(int tcp_sock)297 int ftp_get_reply(int tcp_sock)
298 {
299 int i;
300 char c;
301 char answer[1024];
302
303 do
304 {
305 /* Read a line */
306 for (i = 0, c = 0; i < 1024 && c != '\n'; i++)
307 {
308 read(tcp_sock, &c, sizeof(char));
309 answer[i] = c;
310 }
311 answer[i] = 0;
312 fprintf(stderr, "%s", answer + 4);
313 }
314 while (answer[3] == '-');
315
316 answer[3] = 0;
317
318 return (atoi(answer));
319 }
320
ftp_open(char * arg)321 int ftp_open(char *arg)
322 {
323 char *host;
324 int port;
325 char *dir;
326 char *file;
327 int tcp_sock;
328 int data_sock;
329 char ftp_request[PATH_MAX];
330 struct sockaddr_in stLclAddr;
331 socklen_t namelen;
332 int i;
333
334 /* Check for URL syntax */
335 if (strncmp(arg, "ftp://", strlen("ftp://")))
336 return (0);
337
338 /* Parse URL */
339 port = 21;
340 host = arg + strlen("ftp://");
341 if ((dir = strchr(host, '/')) == NULL)
342 return (0);
343 *dir++ = 0;
344 if ((file = strrchr(dir, '/')) == NULL)
345 {
346 file = dir;
347 dir = NULL;
348 }
349 else
350 *file++ = 0;
351
352 if (strchr(host, ':') != NULL) /* port is specified */
353 {
354 port = atoi(strchr(host, ':') + 1);
355 *strchr(host, ':') = 0;
356 }
357
358 /* Open a TCP socket */
359 if (!(tcp_sock = tcp_open(host, port)))
360 {
361 perror("ftp_open");
362 return (0);
363 }
364
365 /* Send FTP USER and PASS request */
366 ftp_get_reply(tcp_sock);
367 sprintf(ftp_request, "USER anonymous\r\n");
368 send(tcp_sock, ftp_request, strlen(ftp_request), 0);
369 if (ftp_get_reply(tcp_sock) != 331)
370 return (0);
371 sprintf(ftp_request, "PASS smpeguser@\r\n");
372 send(tcp_sock, ftp_request, strlen(ftp_request), 0);
373 if (ftp_get_reply(tcp_sock) != 230)
374 return (0);
375 sprintf(ftp_request, "TYPE I\r\n");
376 send(tcp_sock, ftp_request, strlen(ftp_request), 0);
377 if (ftp_get_reply(tcp_sock) != 200)
378 return (0);
379 if (dir != NULL)
380 {
381 snprintf(ftp_request, sizeof(ftp_request), "CWD %s\r\n", dir);
382 send(tcp_sock, ftp_request, strlen(ftp_request), 0);
383 if (ftp_get_reply(tcp_sock) != 250)
384 return (0);
385 }
386
387 /* Get interface address */
388 namelen = sizeof(stLclAddr);
389 if (getsockname(tcp_sock, (struct sockaddr *)&stLclAddr, &namelen) < 0)
390 return (0);
391
392 /* Open data socket */
393 if ((data_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
394 return (0);
395
396 stLclAddr.sin_family = AF_INET;
397
398 /* Get the first free port */
399 for (i = 0; i < 0xC000; i++)
400 {
401 stLclAddr.sin_port = htons(0x4000 + i);
402 if (bind(data_sock, (struct sockaddr *)&stLclAddr, sizeof(stLclAddr)) >= 0)
403 break;
404 }
405 port = 0x4000 + i;
406
407 if (listen(data_sock, 1) < 0)
408 return (0);
409
410 i = ntohl(stLclAddr.sin_addr.s_addr);
411 sprintf(ftp_request, "PORT %d,%d,%d,%d,%d,%d\r\n",
412 (i >> 24) & 0xFF, (i >> 16) & 0xFF,
413 (i >> 8) & 0xFF, i & 0xFF, (port >> 8) & 0xFF, port & 0xFF);
414 send(tcp_sock, ftp_request, strlen(ftp_request), 0);
415 if (ftp_get_reply(tcp_sock) != 200)
416 return (0);
417
418 snprintf(ftp_request, sizeof(ftp_request), "RETR %s\r\n", file);
419 send(tcp_sock, ftp_request, strlen(ftp_request), 0);
420 if (ftp_get_reply(tcp_sock) != 150)
421 return (0);
422
423 return (accept(data_sock, NULL, NULL));
424 }
425