PS2SDK
PS2 Homebrew Libraries
Loading...
Searching...
No Matches
ps2http.c
1/*
2# _____ ___ ____ ___ ____
3# ____| | ____| | | |____|
4# | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
5#-----------------------------------------------------------------------
6# Copyright 2001-2004, ps2dev - http://www.ps2dev.org
7# Licenced under Academic Free License version 2.0
8# Review ps2sdk README & LICENSE files for further details.
9*/
10
11/*
12 * @file
13 * HTTP CLIENT FILE SYSTEM DRIVER.
14 * The HTTP file io driver is a read only driver that slots into the PS2
15 * IO subsystem and provides access to HTTP.
16 *
17 * For each open request a file handle is allocated and any read request
18 * is directed to the socket. After close has been called the file
19 * handle slot is free'd for the next request.
20 *
21 * No header information normally returned from a HTTP request is returned.
22 * The client must know the content of the data stream and how to deal with it.
23 *
24 * lseek is currently only supported in order to get the size of a file. With
25 * the current implimentation, it is not possible to seek to different positions
26 * in a file.
27 *
28 * With the current implimentation, hostnames may only be specified as an IP
29 * address. This will change once we get a DNS resolver up & running with lwip.
30 */
31
32#include <types.h>
33#include <irx.h>
34#include <loadcore.h>
35#include <stdio.h>
36#include <thbase.h>
37#include <sysclib.h>
38#include <errno.h>
39#include <ioman_mod.h>
40#include <sysmem.h>
41#include <ioman.h>
42
43#include "ps2ip.h"
44
45//#define DEBUG
46
47#define M_PRINTF(format, args...) \
48 printf("PS2HTTP: " format, ##args)
49
50#ifdef DEBUG
51#define M_DEBUG M_PRINTF
52#else
53#define M_DEBUG(format, args...)
54#endif
55
56#define MODNAME "ps2http"
57
58IRX_ID(MODNAME, 1, 1);
59
60typedef struct
61{
62 int sockFd;
63 int fileSize;
64 int filePos;
66
67
68// example of basic HTTP 1.0 protocol request.
69char strTest[] = "GET /blah HTTP/1.0\n\n";
70
71char HTTPGET[] = "GET ";
72char HTTPHOST[] = "Host: ";
73char HTTPGETEND[] = " HTTP/1.0\r\n";
74char HTTPUSERAGENT[] = "User-Agent: PS2IP HTTP Client\r\n";
75char HTTPENDHEADER[] = "\r\n";
76
80int parseContentLength(char *mimeBuffer)
81{
82 char *line;
83
84 line = strstr(mimeBuffer, "CONTENT-LENGTH:");
85 line += strlen("CONTENT-LENGTH:");
86
87 // Advance past any whitepace characters
88 while((*line == ' ') || (*line == '\t')) line++;
89
90 return (int)strtol(line,NULL, 10);
91}
92
97int isErrorHeader(char *mimeBuffer)
98{
99 char *line;
100 int i;
101 int code;
102
103 line = strstr(mimeBuffer, "HTTP/1.");
104 line += strlen("HTTP/1.");
105
106 // Advance past minor protocol version number
107 line++;
108
109 // Advance past any whitespace characters
110 while((*line == ' ') || (*line == '\t')) line++;
111
112 // Terminate string after status code
113 for(i = 0; ((line[i] != ' ') && (line[i] != '\t')); i++);
114 line[i] = '\0';
115
116 code = (int)strtol(line,NULL, 10);
117 if( code == 200 )
118 return 0;
119 else
120 return code;
121}
122
128int readLine( int socket, char * buffer, int size )
129{
130 char * ptr = buffer;
131 int count = 0;
132
133 // Keep reading until we fill the buffer.
134
135 while ( count < size )
136 {
137 int rc;
138
139 rc = recv( socket, ptr, 1, 0 );
140
141 if ( rc <= 0 ) return rc;
142
143 if ( (*ptr == '\n') ) break;
144
145 // increment after check for cr. Don't want to count the cr.
146 count++;
147 ptr++;
148 }
149
150 // Terminate string
151 *ptr = '\0';
152
153 // return how many bytes read.
154 return count;
155}
156
157
163int httpConnect( struct sockaddr_in * server, char *hostAddr, const char * url, t_fioPrivData *pHandle )
164{
165 int sockHandle;
166 int peerHandle;
167 int rc;
168 char mimeBuffer[100];
169
170 M_DEBUG( "create socket\n" );
171
172 if((sockHandle = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP )) < 0)
173 {
174 M_PRINTF( "SOCKET FAILED\n" );
175 return -1;
176 }
177
178 M_DEBUG( "connect\n" );
179
180 rc = connect( sockHandle, (struct sockaddr *) server, sizeof(*server));
181 if ( rc < 0 )
182 {
183 M_PRINTF( "CONNECT FAILED %i\n", sockHandle );
184 return -1;
185 }
186 peerHandle = sockHandle;
187
188 M_DEBUG( "send\n" );
189
190 rc = send( peerHandle, HTTPGET, sizeof( HTTPGET ) - 1, 0 );
191 if (rc < 0) goto fail_send;
192 rc = send( peerHandle, (void*) url, strlen( url ), 0 );
193 if (rc < 0) goto fail_send;
194 rc = send( peerHandle, HTTPGETEND, sizeof( HTTPGETEND ) - 1, 0 );
195 if (rc < 0) goto fail_send;
196
197 rc = send( peerHandle, HTTPHOST, sizeof( HTTPHOST ) - 1, 0 );
198 if (rc < 0) goto fail_send;
199 rc = send( peerHandle, hostAddr, strlen( hostAddr ), 0 );
200 if (rc < 0) goto fail_send;
201 rc = send( peerHandle, HTTPENDHEADER, sizeof( HTTPENDHEADER ) - 1, 0 ); // "\r\n"
202 if (rc < 0) goto fail_send;
203
204 rc = send( peerHandle, HTTPUSERAGENT, sizeof( HTTPUSERAGENT ) - 1, 0 );
205 if (rc < 0) goto fail_send;
206 rc = send( peerHandle, HTTPENDHEADER, sizeof( HTTPENDHEADER ) - 1, 0 );
207 if (rc < 0) goto fail_send;
208
209 // We now need to read the header information
210 while ( 1 )
211 {
212 int i;
213
214 // read a line from the header information.
215 rc = readLine( peerHandle, mimeBuffer, 100 );
216
217 M_DEBUG(">> %s", mimeBuffer);
218
219 if ( rc < 0 ) return rc;
220
221 // End of headers is a blank line. exit.
222 if ( rc == 0 ) break;
223 if ( (rc == 1) && (mimeBuffer[0] == '\r') ) break;
224
225 // Convert mimeBuffer to upper case, so we can do string comps
226 for(i = 0; (unsigned int)i < strlen(mimeBuffer); i++)
227 mimeBuffer[i] = toupper(mimeBuffer[i]);
228
229 if(strstr(mimeBuffer, "HTTP/1.")) // First line of header, contains status code. Check for an error code
230 if((rc = isErrorHeader(mimeBuffer))) {
231 M_PRINTF("status code = %d!\n", rc);
232 return -rc;
233 }
234
235 if(strstr(mimeBuffer, "CONTENT-LENGTH:"))
236 {
237 pHandle->fileSize = parseContentLength(mimeBuffer);
238 M_DEBUG("fileSize = %d\n", pHandle->fileSize);
239 }
240 }
241
242 // We've sent the request, and read the headers. SockHandle is
243 // now at the start of the main data read for a file io read.
244 return peerHandle;
245
246fail_send:
247 printf( "HTTP: SEND FAILED %i\n", peerHandle );
248 return -1;
249}
250
251char *strnchr(char *str, char ch, int max) {
252 int i;
253
254 for(i = 0; (i < max) && (str[i] != '\0'); i++)
255 if(str[i] == ch)
256 return(&str[i]);
257
258 return(NULL);
259}
260
261static int _ResolveHostname(const char *hostname , struct in_addr* ip)
262{
263 struct hostent *HostEntry;
264 struct in_addr **addr_list;
265
266 if((HostEntry = gethostbyname(hostname)) == NULL)
267 return 1;
268
269 for(addr_list = (struct in_addr **) HostEntry->h_addr_list; addr_list != NULL; addr_list++)
270 {
271 ip->s_addr = (*addr_list)->s_addr;
272 return 0;
273 }
274
275 return 1;
276}
277
289const char *resolveAddress( struct sockaddr_in *server, const char * url, char *hostAddr )
290{
291 unsigned char w;
292 const char *char_ptr;
293 char addr[128];
294 char port[6] = "80"; // default port of 80(HTTP)
295 int i = 0, rv;
296 int isDomain = 0;
297
298 // eg url= //192.168.0.1/fred.elf (the http: is already stripped)
299
300 // NOTE: Need more error checking in parsing code
301
302 // URL must start with double forward slashes.
303 while(url[0] == '/') {
304 url++;
305 }
306
307 for(i = 0; ((url[i] != '\0') && (url[i] != '/')) && (i < 127); i++)
308 if((((addr[i] = url[i]) < '0') || (url[i] > '9')) && (url[i] != '.')) {
309
310 if(url[i] == ':') {// allow specification of port in URL like http://www.server.net:8080/
311 for(w = 0; ((w + i + 1) < 127) && (w < 5) && (url[w + i + 1] != '/') && (url[w + i + 1] != '\0'); w++)
312 port[w] = url[w + i + 1];
313 port[w] = '\0';
314
315 M_DEBUG("using port %s for connection\n", port);
316 break;
317 }
318 else // it's a domain name if a non-numeric char is contained in the "server" part of the URL.
319 isDomain = 1;
320 }
321 addr[i] = '\0'; // overwrite last char copied(should be '/', '\0' or ':') with a '\0'
322 strcpy(hostAddr, addr);
323
324 if(isDomain) {
325 // resolve the host name.
326 rv = _ResolveHostname(addr, &server->sin_addr);
327 if(rv != 0) {
328 M_PRINTF("failed to resolve domain '%s'\n", addr);
329 return NULL;
330 }
331 }
332 else {
333 server->sin_addr.s_addr = inet_addr(addr);
334 }
335
336 i = (int) strtol(port, NULL, 10); // set the port
337 server->sin_port = htons(i);
338
339 server->sin_family = AF_INET;
340
341#if 1
342 char_ptr = url;
343 while(*char_ptr != '/') char_ptr++;
344
345 return char_ptr;
346#else
347 return url;
348#endif
349}
350
360int httpOpen(iop_io_file_t *f, const char *name, int mode)
361{
362 int peerHandle = 0;
363 struct sockaddr_in server;
364 const char *getName;
365 t_fioPrivData *privData;
366 char hostAddr[100];
367
368 (void)mode;
369
370 M_DEBUG("httpOpen(-, %s, %d)\n", name, mode);
371
372 if((privData = AllocSysMemory(ALLOC_FIRST, sizeof(t_fioPrivData), NULL)) == NULL)
373 return -EPERM;
374
375 f->privdata = privData;
376
377 privData->fileSize = 0;
378 privData->filePos = 0;
379
380 memset(&server, 0, sizeof(server));
381 // Check valid IP address and URL
382 if((getName = resolveAddress( &server, name, hostAddr )) == NULL)
383 {
384 FreeSysMemory(privData);
385 return -ENOENT;
386 }
387
388 // Now we connect and initiate the transfer by sending a
389 // request header to the server, and receiving the response header
390 if((peerHandle = httpConnect( &server, hostAddr, getName, privData )) < 0)
391 {
392 M_PRINTF("failed to connect to '%s'!\n", hostAddr);
393 FreeSysMemory(privData);
394 return peerHandle;
395 }
396
397 // http connect returns valid socket. Save in handle list.
398 privData->sockFd = peerHandle;
399
400 // return success. We got it all ready. :)
401 return 0;
402}
403
404
409int httpRead(iop_io_file_t *f, void *buffer, int size)
410{
411 t_fioPrivData *privData = (t_fioPrivData *)f->privdata;
412 int left = size;
413 int totalRead = 0;
414
415 M_DEBUG("httpRead(-, 0x%X, %d)\n", (int)buffer, size);
416
417 // Read until: there is an error, we've read "size" bytes or the remote
418 // side has closed the connection.
419 do {
420
421 int bytesRead = recv( privData->sockFd, (void *)((u8 *)buffer + totalRead), left, 0 );
422
423// M_DEBUG("bytesRead = %d\n", bytesRead);
424
425 if(bytesRead <= 0) break;
426
427 left -= bytesRead;
428 totalRead += bytesRead;
429
430 } while(left);
431
432 return totalRead;
433}
434
435
440int httpClose(iop_io_file_t *f)
441{
442 t_fioPrivData *privData = (t_fioPrivData *)f->privdata;
443
444 M_DEBUG("httpClose(-)\n");
445
446 lwip_close(privData->sockFd);
447 FreeSysMemory(privData);
448
449 return 0;
450}
451
457int httpLseek(iop_io_file_t *f, int offset, int mode)
458{
459 t_fioPrivData *privData = (t_fioPrivData *)f->privdata;
460
461 M_DEBUG("httpLseek(-, %d, %d)\n", (int)offset, mode);
462
463 switch(mode)
464 {
465 case SEEK_SET:
466 privData->filePos = offset;
467 break;
468
469 case SEEK_CUR:
470 privData->filePos += offset;
471 break;
472
473 case SEEK_END:
474 privData->filePos = privData->fileSize + offset;
475 break;
476
477 default:
478 return -EPERM;
479 }
480
481 return privData->filePos;
482}
483
484IOMAN_RETURN_VALUE_IMPL(0);
485IOMAN_RETURN_VALUE_IMPL(EIO);
486
487static iop_io_device_ops_t ps2httpOps = {
488 IOMAN_RETURN_VALUE(0), // init
489 IOMAN_RETURN_VALUE(0), // deinit
490 IOMAN_RETURN_VALUE(EIO), // format
491 &httpOpen, // open
492 &httpClose, // close
493 &httpRead, // read
494 IOMAN_RETURN_VALUE(EIO), // write
495 &httpLseek, // lseek
496 IOMAN_RETURN_VALUE(EIO), // ioctl
497 IOMAN_RETURN_VALUE(EIO), // remove
498 IOMAN_RETURN_VALUE(EIO), // mkdir
499 IOMAN_RETURN_VALUE(EIO), // rmdir
500 IOMAN_RETURN_VALUE(EIO), // dopen
501 IOMAN_RETURN_VALUE(EIO), // dclose
502 IOMAN_RETURN_VALUE(EIO), // dread
503 IOMAN_RETURN_VALUE(EIO), // getstat
504 IOMAN_RETURN_VALUE(EIO), // chstat
505};
506
507static iop_io_device_t ps2httpDev = {
508 "http",
509 IOP_DT_FS,
510 1,
511 "HTTP client file driver",
512 &ps2httpOps,
513};
514
515
519int _start( int argc, char *argv[])
520{
521 (void)argc;
522 (void)argv;
523
524 M_PRINTF("Module Loaded\n");
525
526 M_PRINTF("Adding 'http' driver into io system\n");
527 io_DelDrv( "http");
528 io_AddDrv(&ps2httpDev);
529
530 return MODULE_RESIDENT_END;
531}
532
#define ENOENT
Definition errno.h:23
#define EIO
Definition errno.h:29
#define EPERM
Definition errno.h:21
void * privdata
Definition ioman_mod.h:63
u32 count
start sector of fragmented bd/file