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