2 * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
5 * "THE BEER-WARE LICENSE" (Revision 42):
6 * Sergey Lyubka wrote this file. As long as you retain this notice you
7 * can do whatever you want with this stuff. If we meet some day, and you think
8 * this stuff is worth it, you can buy me a beer in return.
14 fix_directory_separators(char *path)
16 for (; *path != '\0'; path++) {
20 while (path[1] == '\\' || path[1] == '/')
21 (void) memmove(path + 1,
22 path + 2, strlen(path + 2) + 1);
27 protect_against_code_disclosure(const char *path)
34 * Protect against CGI code disclosure under Windows.
35 * This is very nasty hole. Windows happily opens files with
36 * some garbage in the end of file name. So fopen("a.cgi ", "r")
37 * actually opens "a.cgi", and does not return an error! And since
38 * "a.cgi " does not have valid CGI extension, this leads to
39 * the CGI code disclosure.
40 * To protect, here we delete all fishy characters from the
44 if ((handle = FindFirstFile(path, &data)) == INVALID_HANDLE_VALUE)
49 for (p = path + strlen(path); p > path && p[-1] != '\\';)
52 if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
53 strcmp(data.cFileName, p) != 0)
60 my_open(const char *path, int flags, int mode)
62 char buf[FILENAME_MAX];
63 wchar_t wbuf[FILENAME_MAX];
65 my_strlcpy(buf, path, sizeof(buf));
66 fix_directory_separators(buf);
67 MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
69 if (protect_against_code_disclosure(buf) == FALSE)
72 return (_wopen(wbuf, flags));
76 my_stat(const char *path, struct stat *stp)
78 char buf[FILENAME_MAX], *p;
79 wchar_t wbuf[FILENAME_MAX];
81 my_strlcpy(buf, path, sizeof(buf));
82 fix_directory_separators(buf);
84 p = buf + strlen(buf) - 1;
85 while (p > buf && *p == '\\' && p[-1] != ':')
88 MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
90 return (_wstat(wbuf, (struct _stat *) stp));
94 my_remove(const char *path)
96 char buf[FILENAME_MAX];
97 wchar_t wbuf[FILENAME_MAX];
99 my_strlcpy(buf, path, sizeof(buf));
100 fix_directory_separators(buf);
102 MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
104 return (_wremove(wbuf));
108 my_rename(const char *path1, const char *path2)
110 char buf1[FILENAME_MAX];
111 char buf2[FILENAME_MAX];
112 wchar_t wbuf1[FILENAME_MAX];
113 wchar_t wbuf2[FILENAME_MAX];
115 my_strlcpy(buf1, path1, sizeof(buf1));
116 my_strlcpy(buf2, path2, sizeof(buf2));
117 fix_directory_separators(buf1);
118 fix_directory_separators(buf2);
120 MultiByteToWideChar(CP_UTF8, 0, buf1, -1, wbuf1, sizeof(wbuf1));
121 MultiByteToWideChar(CP_UTF8, 0, buf2, -1, wbuf2, sizeof(wbuf2));
123 return (_wrename(wbuf1, wbuf2));
127 my_mkdir(const char *path, int mode)
129 char buf[FILENAME_MAX];
130 wchar_t wbuf[FILENAME_MAX];
132 my_strlcpy(buf, path, sizeof(buf));
133 fix_directory_separators(buf);
135 MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
137 return (_wmkdir(wbuf));
141 wide_to_utf8(const wchar_t *str)
145 int nchar = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
150 else if (!WideCharToMultiByte(CP_UTF8, 0, str, -1, buf, nchar, NULL, NULL)) {
163 my_getcwd(char *buffer, int maxlen)
166 wchar_t *wbuffer, *wresult;
169 /* User-supplied buffer */
170 wbuffer = malloc(maxlen * sizeof(wchar_t));
174 /* Dynamically allocated buffer */
176 wresult = _wgetcwd(wbuffer, maxlen);
180 /* User-supplied buffer */
181 int n = WideCharToMultiByte(CP_UTF8, 0, wresult, -1, buffer, maxlen, NULL, NULL);
187 /* Buffer allocated by _wgetcwd() */
188 result = wide_to_utf8(wresult);
198 opendir(const char *name)
201 char path[FILENAME_MAX];
202 wchar_t wpath[FILENAME_MAX];
204 if (name == NULL || name[0] == '\0') {
206 } else if ((dir = malloc(sizeof(*dir))) == NULL) {
209 my_snprintf(path, sizeof(path), "%s/*", name);
210 fix_directory_separators(path);
211 MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, sizeof(wpath));
212 dir->handle = FindFirstFileW(wpath, &dir->info);
214 if (dir->handle != INVALID_HANDLE_VALUE) {
215 dir->result.d_name[0] = '\0';
231 if (dir->handle != INVALID_HANDLE_VALUE)
232 result = FindClose(dir->handle) ? 0 : -1;
246 struct dirent *result = 0;
248 if (dir && dir->handle != INVALID_HANDLE_VALUE) {
249 if(!dir->result.d_name ||
250 FindNextFileW(dir->handle, &dir->info)) {
251 result = &dir->result;
253 WideCharToMultiByte(CP_UTF8, 0, dir->info.cFileName,
255 sizeof(result->d_name), NULL, NULL);
265 set_non_blocking_mode(int fd)
267 unsigned long on = 1;
269 return (ioctlsocket(fd, FIONBIO, &on));
273 set_close_on_exec(int fd)
275 fd = 0; /* Do nothing. There is no FD_CLOEXEC on Windows */
283 big_int_t content_len;
287 * Thread function that reads POST data from the socket pair
288 * and writes it to the CGI process.
290 static void//DWORD WINAPI
293 struct threadparam *tp = arg;
294 int n, sent, stop = 0;
300 max_recv = min(sizeof(buf), tp->content_len - total);
301 while (!stop && max_recv > 0 && (n = recv(tp->s, buf, max_recv, 0)) > 0) {
302 for (sent = 0; !stop && sent < n; sent += k)
303 if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0))
306 max_recv = min(sizeof(buf), tp->content_len - total);
309 CloseHandle(tp->hPipe); /* Suppose we have POSTed everything */
314 * Thread function that reads CGI output and pushes it to the socket pair.
319 struct threadparam *tp = arg;
325 while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) {
327 for (sent = 0; !stop && sent < n; sent += k)
328 if ((k = send(tp->s, buf + sent, n - sent, 0)) <= 0)
331 CloseHandle(tp->hPipe);
334 * Windows is a piece of crap. When this thread closes its end
335 * of the socket pair, the other end (get_cgi() function) may loose
336 * some data. I presume, this happens if get_cgi() is not fast enough,
337 * and the data written by this end does not "push-ed" to the other
338 * end socket buffer. So after closesocket() the remaining data is
339 * gone. If I put shutdown() before closesocket(), that seems to
340 * fix the problem, but I am not sure this is the right fix.
341 * XXX (submitted by James Marshall) we do not do shutdown() on UNIX.
342 * If fork() is called from user callback, shutdown() messes up things.
353 spawn_stdio_thread(int sock, HANDLE hPipe, void (*func)(void *),
354 big_int_t content_len)
356 struct threadparam *tp;
359 tp = malloc(sizeof(*tp));
364 tp->content_len = content_len;
365 _beginthread(func, 0, tp);
369 spawn_process(struct conn *c, const char *prog, char *envblk,
370 char *envp[], int sock, const char *dir)
372 HANDLE a[2], b[2], h[2], me;
374 char *p, cmdline[FILENAME_MAX], line[FILENAME_MAX];
377 PROCESS_INFORMATION pi;
379 me = GetCurrentProcess();
380 flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS;
382 /* FIXME add error checking code here */
383 CreatePipe(&a[0], &a[1], NULL, 0);
384 CreatePipe(&b[0], &b[1], NULL, 0);
385 DuplicateHandle(me, a[0], me, &h[0], 0, TRUE, flags);
386 DuplicateHandle(me, b[1], me, &h[1], 0, TRUE, flags);
388 (void) memset(&si, 0, sizeof(si));
389 (void) memset(&pi, 0, sizeof(pi));
391 /* XXX redirect CGI errors to the error log file */
393 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
394 si.wShowWindow = SW_HIDE;
395 si.hStdOutput = si.hStdError = h[1];
398 /* If CGI file is a script, try to read the interpreter line */
399 if (c->ctx->options[OPT_CGI_INTERPRETER] == NULL) {
400 if ((fp = fopen(prog, "r")) != NULL) {
401 (void) fgets(line, sizeof(line), fp);
402 if (memcmp(line, "#!", 2) != 0)
404 /* Trim whitespaces from interpreter name */
405 for (p = &line[strlen(line) - 1]; p > line &&
410 (void) my_snprintf(cmdline, sizeof(cmdline), "%s%s%s",
411 line + 2, line[2] == '\0' ? "" : " ", prog);
413 (void) my_snprintf(cmdline, sizeof(cmdline), "%s %s",
414 c->ctx->options[OPT_CGI_INTERPRETER], prog);
417 (void) my_snprintf(line, sizeof(line), "%s", dir);
418 fix_directory_separators(line);
419 fix_directory_separators(cmdline);
422 * Spawn reader & writer threads before we create CGI process.
423 * Otherwise CGI process may die too quickly, loosing the data
425 spawn_stdio_thread(sock, b[0], stdinput, 0);
426 spawn_stdio_thread(sock, a[1], stdoutput, c->rem.content_len);
428 if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
429 CREATE_NEW_PROCESS_GROUP, envblk, line, &si, &pi) == 0) {
430 elog(E_LOG, c,"redirect: CreateProcess(%s): %d",cmdline,ERRNO);
435 CloseHandle(pi.hThread);
436 CloseHandle(pi.hProcess);