Skip to content

Latest commit

 

History

History
400 lines (333 loc) · 10.3 KB

physfshttpd.c

File metadata and controls

400 lines (333 loc) · 10.3 KB
 
Apr 22, 2002
Apr 22, 2002
1
2
3
4
5
6
7
8
9
/*
* This is a quick and dirty HTTP server that uses PhysicsFS to retrieve
* files. It is not robust at all, probably buggy, and definitely poorly
* designed. It's just meant to show that it can be done.
*
* Basically, you compile this code, and run it:
* ./physfshttpd archive1.zip archive2.zip /path/to/a/real/dir etc...
*
* The files are appended in order to the PhysicsFS search path, and when
Jul 22, 2017
Jul 22, 2017
10
* a client request comes in, it looks for the file in said search path.
Apr 22, 2002
Apr 22, 2002
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
*
* My goal was to make this work in less than 300 lines of C, so again, it's
* not to be used for any serious purpose. Patches to make this application
* suck less will be readily and gratefully accepted.
*
* Command line I used to build this on Linux:
* gcc -Wall -Werror -g -o bin/physfshttpd extras/physfshttpd.c -lphysfs
*
* License: this code is public domain. I make no warranty that it is useful,
* correct, harmless, or environmentally safe.
*
* This particular file may be used however you like, including copying it
* verbatim into a closed-source project, exploiting it commercially, and
* removing any trace of my name from the source (although I hope you won't
* do that). I welcome enhancements and corrections to this file, but I do
Jul 20, 2003
Jul 20, 2003
26
27
* not require you to send me patches if you make changes. This code has
* NO WARRANTY.
Apr 22, 2002
Apr 22, 2002
28
*
Jul 20, 2003
Jul 20, 2003
29
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
Mar 11, 2007
Mar 11, 2007
30
* Please see LICENSE.txt in the root of the source tree.
Apr 22, 2002
Apr 22, 2002
31
*
Jan 1, 2006
Jan 1, 2006
32
* This file was written by Ryan C. Gordon. (icculus@icculus.org).
Apr 22, 2002
Apr 22, 2002
33
34
35
36
*/
#include <stdio.h>
#include <stdlib.h>
Aug 15, 2017
Aug 15, 2017
37
#include <stdarg.h>
Apr 22, 2002
Apr 22, 2002
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifndef LACKING_SIGNALS
#include <signal.h>
#endif
#ifndef LACKING_PROTOENT
#include <netdb.h>
#endif
#include "physfs.h"
Jul 22, 2017
Jul 22, 2017
58
#define DEFAULT_PORTNUM 8080
Apr 22, 2002
Apr 22, 2002
59
60
61
62
63
64
65
66
67
typedef struct
{
int sock;
struct sockaddr *addr;
socklen_t addrlen;
} http_args;
Aug 15, 2017
Aug 15, 2017
68
69
70
71
72
73
74
#define txt404 \
"HTTP/1.0 404 Not Found\n" \
"Connection: close\n" \
"Content-Type: text/html; charset=utf-8\n" \
"\n" \
"<html><head><title>404 Not Found</title></head>\n" \
"<body>Can't find '%s'.</body></html>\n\n" \
Apr 22, 2002
Apr 22, 2002
75
Aug 15, 2017
Aug 15, 2017
76
77
78
79
80
#define txt200 \
"HTTP/1.0 200 OK\n" \
"Connection: close\n" \
"Content-Type: %s\n" \
"\n"
Apr 22, 2002
Apr 22, 2002
81
Aug 15, 2017
Aug 15, 2017
82
83
84
85
86
static const char *lastError(void)
{
return PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode());
} /* lastError */
Aug 15, 2017
Aug 15, 2017
87
static int writeAll(const char *ipstr, const int sock, void *buf, const size_t len)
Aug 6, 2017
Aug 6, 2017
88
{
Aug 15, 2017
Aug 15, 2017
89
90
91
92
93
94
95
if (write(sock, buf, len) != len)
{
printf("%s: Write error to socket.\n", ipstr);
return 0;
} /* if */
return 1;
Aug 6, 2017
Aug 6, 2017
96
97
} /* writeAll */
Aug 15, 2017
Aug 15, 2017
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
static int writeString(const char *ipstr, const int sock, const char *fmt, ...)
{
/* none of this is robust against large strings or HTML escaping. */
char buffer[1024];
int len;
va_list ap;
va_start(ap, fmt);
len = vsnprintf(buffer, sizeof (buffer), fmt, ap);
va_end(ap);
if (len < 0)
{
printf("uhoh, vsnprintf() failed!\n");
return 0;
} /* if */
return writeAll(ipstr, sock, buffer, len);
} /* writeString */
Apr 22, 2002
Apr 22, 2002
117
118
static void feed_file_http(const char *ipstr, int sock, const char *fname)
{
Sep 26, 2004
Sep 26, 2004
119
PHYSFS_File *in = PHYSFS_openRead(fname);
Aug 6, 2017
Aug 6, 2017
120
Apr 22, 2002
Apr 22, 2002
121
122
if (in == NULL)
{
Aug 15, 2017
Aug 15, 2017
123
124
printf("%s: Can't open [%s]: %s.\n", ipstr, fname, lastError());
writeString(ipstr, sock, txt404, fname);
Aug 6, 2017
Aug 6, 2017
125
return;
Apr 22, 2002
Apr 22, 2002
126
} /* if */
Aug 6, 2017
Aug 6, 2017
127
Aug 15, 2017
Aug 15, 2017
128
129
/* !!! FIXME: mimetype */
if (writeString(ipstr, sock, txt200, "text/plain; charset=utf-8"))
Apr 22, 2002
Apr 22, 2002
130
131
132
{
do
{
Aug 6, 2017
Aug 6, 2017
133
char buffer[1024];
Jul 22, 2017
Jul 22, 2017
134
PHYSFS_sint64 br = PHYSFS_readBytes(in, buffer, sizeof (buffer));
Apr 22, 2002
Apr 22, 2002
135
136
if (br == -1)
{
Aug 15, 2017
Aug 15, 2017
137
printf("%s: Read error: %s.\n", ipstr, lastError());
Apr 22, 2002
Apr 22, 2002
138
139
140
break;
} /* if */
Aug 15, 2017
Aug 15, 2017
141
else if (!writeAll(ipstr, sock, buffer, (size_t) br))
Aug 6, 2017
Aug 6, 2017
142
143
144
{
break;
} /* else if */
Apr 22, 2002
Apr 22, 2002
145
} while (!PHYSFS_eof(in));
Aug 6, 2017
Aug 6, 2017
146
} /* if */
Apr 22, 2002
Apr 22, 2002
147
Aug 6, 2017
Aug 6, 2017
148
PHYSFS_close(in);
Apr 22, 2002
Apr 22, 2002
149
150
151
} /* feed_file_http */
Aug 15, 2017
Aug 15, 2017
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
static void feed_dirlist_http(const char *ipstr, int sock,
const char *dname, char **list)
{
int i;
if (!writeString(ipstr, sock, txt200, "text/html; charset=utf-8"))
return;
else if (!writeString(ipstr, sock,
"<html><head><title>Directory %s</title></head>"
"<body><p><h1>Directory %s</h1></p><p><ul>\n",
dname, dname))
return;
if (strcmp(dname, "/") == 0)
dname = "";
for (i = 0; list[i]; i++)
{
const char *fname = list[i];
if (!writeString(ipstr, sock,
"<li><a href='%s/%s'>%s</a></li>\n", dname, fname, fname))
break;
} /* for */
writeString(ipstr, sock, "</ul></body></html>\n");
} /* feed_dirlist_http */
static void feed_dir_http(const char *ipstr, int sock, const char *dname)
{
char **list = PHYSFS_enumerateFiles(dname);
if (list == NULL)
{
printf("%s: Can't enumerate directory [%s]: %s.\n",
ipstr, dname, lastError());
writeString(ipstr, sock, txt404, dname);
return;
} /* if */
feed_dirlist_http(ipstr, sock, dname, list);
PHYSFS_freeList(list);
} /* feed_dir_http */
static void feed_http_request(const char *ipstr, int sock, const char *fname)
{
PHYSFS_Stat statbuf;
printf("%s: requested [%s].\n", ipstr, fname);
if (!PHYSFS_stat(fname, &statbuf))
{
printf("%s: Can't stat [%s]: %s.\n", ipstr, fname, lastError());
writeString(ipstr, sock, txt404, fname);
return;
} /* if */
if (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY)
feed_dir_http(ipstr, sock, fname);
else
feed_file_http(ipstr, sock, fname);
} /* feed_http_request */
Apr 22, 2002
Apr 22, 2002
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
static void *do_http(void *_args)
{
http_args *args = (http_args *) _args;
char ipstr[128];
char buffer[512];
char *ptr;
strncpy(ipstr, inet_ntoa(((struct sockaddr_in *) args->addr)->sin_addr),
sizeof (ipstr));
ipstr[sizeof (ipstr) - 1] = '\0';
printf("%s: connected.\n", ipstr);
read(args->sock, buffer, sizeof (buffer));
buffer[sizeof (buffer) - 1] = '\0';
ptr = strchr(buffer, '\n');
if (!ptr)
printf("%s: potentially bogus request.\n", ipstr);
else
{
*ptr = '\0';
ptr = strchr(buffer, '\r');
if (ptr != NULL)
*ptr = '\0';
if ((toupper(buffer[0]) == 'G') &&
(toupper(buffer[1]) == 'E') &&
(toupper(buffer[2]) == 'T') &&
(toupper(buffer[3]) == ' ') &&
(toupper(buffer[4]) == '/'))
{
ptr = strchr(buffer + 5, ' ');
if (ptr != NULL)
*ptr = '\0';
Aug 15, 2017
Aug 15, 2017
247
feed_http_request(ipstr, args->sock, buffer + 4);
Apr 22, 2002
Apr 22, 2002
248
249
250
251
252
253
254
255
} /* if */
} /* else */
/* !!! FIXME: Time the transfer. */
printf("%s: closing connection.\n", ipstr);
close(args->sock);
free(args->addr);
free(args);
Jan 28, 2010
Jan 28, 2010
256
return NULL;
Apr 22, 2002
Apr 22, 2002
257
258
259
260
261
262
263
264
265
266
267
} /* do_http */
static void serve_http_request(int sock, struct sockaddr *addr,
socklen_t addrlen)
{
http_args *args = (http_args *) malloc(sizeof (http_args));
if (args == NULL)
{
printf("out of memory.\n");
return;
Jan 28, 2010
Jan 28, 2010
268
} /* if */
Apr 22, 2002
Apr 22, 2002
269
270
271
272
273
274
args->addr = (struct sockaddr *) malloc(addrlen);
if (args->addr == NULL)
{
free(args);
printf("out of memory.\n");
return;
Jan 28, 2010
Jan 28, 2010
275
} /* if */
Apr 22, 2002
Apr 22, 2002
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
args->sock = sock;
args->addrlen = addrlen;
memcpy(args->addr, addr, addrlen);
/* !!! FIXME: optionally spin a thread... */
do_http((void *) args);
} /* server_http_request */
static int create_listen_socket(short portnum)
{
int retval = -1;
int protocol = 0; /* pray this is right. */
#ifndef LACKING_PROTOENT
struct protoent *prot;
setprotoent(0);
prot = getprotobyname("tcp");
if (prot != NULL)
protocol = prot->p_proto;
#endif
retval = socket(PF_INET, SOCK_STREAM, protocol);
if (retval >= 0)
{
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(portnum);
addr.sin_addr.s_addr = INADDR_ANY;
Jul 22, 2017
Jul 22, 2017
306
if ((bind(retval, (struct sockaddr *) &addr, (socklen_t) sizeof (addr)) == -1) ||
Apr 22, 2002
Apr 22, 2002
307
308
309
310
311
312
313
(listen(retval, 5) == -1))
{
close(retval);
retval = -1;
} /* if */
} /* if */
Jan 28, 2010
Jan 28, 2010
314
return retval;
Apr 22, 2002
Apr 22, 2002
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
} /* create_listen_socket */
static int listensocket = -1;
void at_exit_cleanup(void)
{
/*
* !!! FIXME: If thread support, signal threads to terminate and
* !!! FIXME: wait for them to clean up.
*/
if (listensocket >= 0)
close(listensocket);
if (!PHYSFS_deinit())
Aug 15, 2017
Aug 15, 2017
331
printf("PHYSFS_deinit() failed: %s\n", lastError());
Apr 22, 2002
Apr 22, 2002
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
} /* at_exit_cleanup */
int main(int argc, char **argv)
{
int i;
int portnum = DEFAULT_PORTNUM;
setbuf(stdout, NULL);
setbuf(stderr, NULL);
#ifndef LACKING_SIGNALS
/* I'm not sure if this qualifies as a cheap trick... */
signal(SIGTERM, exit);
signal(SIGINT, exit);
signal(SIGFPE, exit);
signal(SIGSEGV, exit);
signal(SIGPIPE, exit);
signal(SIGILL, exit);
#endif
if (argc == 1)
{
printf("USAGE: %s <archive1> [archive2 [... archiveN]]\n", argv[0]);
Jan 28, 2010
Jan 28, 2010
356
return 42;
Apr 22, 2002
Apr 22, 2002
357
358
359
360
} /* if */
if (!PHYSFS_init(argv[0]))
{
Aug 15, 2017
Aug 15, 2017
361
printf("PHYSFS_init() failed: %s\n", lastError());
Jan 28, 2010
Jan 28, 2010
362
return 42;
Apr 22, 2002
Apr 22, 2002
363
364
365
366
367
368
369
} /* if */
/* normally, this is bad practice, but oh well. */
atexit(at_exit_cleanup);
for (i = 1; i < argc; i++)
{
Jul 22, 2017
Jul 22, 2017
370
if (!PHYSFS_mount(argv[i], NULL, 1))
Apr 22, 2002
Apr 22, 2002
371
372
373
374
375
376
377
printf(" WARNING: failed to add [%s] to search path.\n", argv[i]);
} /* else */
listensocket = create_listen_socket(portnum);
if (listensocket < 0)
{
printf("listen socket failed to create.\n");
Jan 28, 2010
Jan 28, 2010
378
return 42;
Apr 22, 2002
Apr 22, 2002
379
380
381
382
383
384
385
386
387
388
389
} /* if */
while (1) /* infinite loop for now. */
{
struct sockaddr addr;
socklen_t len;
int s = accept(listensocket, &addr, &len);
if (s < 0)
{
printf("accept() failed: %s\n", strerror(errno));
close(listensocket);
Jan 28, 2010
Jan 28, 2010
390
return 42;
Apr 22, 2002
Apr 22, 2002
391
392
393
394
395
} /* if */
serve_http_request(s, &addr, len);
} /* while */
Jan 28, 2010
Jan 28, 2010
396
return 0;
Apr 22, 2002
Apr 22, 2002
397
398
399
} /* main */
/* end of physfshttpd.c ... */