Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
mongoose
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
esp
mongoose
Commits
47606a71
Commit
47606a71
authored
Oct 02, 2013
by
Sergey Lyubka
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Moved unix and win32 code to different files
parent
eedfedd1
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
667 additions
and
666 deletions
+667
-666
Makefile
build/Makefile
+2
-1
internal.h
build/src/internal.h
+6
-1
mongoose.c
build/src/mongoose.c
+0
-510
unix.c
build/src/unix.c
+105
-0
win32.c
build/src/win32.c
+401
-0
mongoose.c
mongoose.c
+153
-154
No files found.
build/Makefile
View file @
47606a71
...
...
@@ -27,7 +27,8 @@ VERSION = $(shell perl -lne \
# The order in which files are listed is important
SOURCES
=
src/internal.h src/string.c src/parse_date.c src/options.c
\
src/crypto.c src/auth.c src/mongoose.c src/lua.c
src/crypto.c src/auth.c src/win32.c src/unix.c src/mongoose.c
\
src/lua.c
TINY_SOURCES
=
../mongoose.c main.c
LUA_SOURCES
=
$(TINY_SOURCES)
sqlite3.c lsqlite3.c lua_5.2.1.c
...
...
build/src/internal.h
View file @
47606a71
...
...
@@ -323,7 +323,7 @@ struct ssl_func {
void
(
*
ptr
)(
void
);
// Function pointer
};
static
struct
ssl_func
ssl_sw
[];
static
struct
ssl_func
ssl_sw
[
30
];
#define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr)
#define SSL_accept (* (int (*)(SSL *)) ssl_sw[1].ptr)
...
...
@@ -460,6 +460,11 @@ struct de {
static
FILE
*
mg_fopen
(
const
char
*
path
,
const
char
*
mode
);
static
int
mg_stat
(
const
char
*
path
,
struct
file
*
filep
);
static
void
send_http_error
(
struct
mg_connection
*
,
int
,
const
char
*
,
PRINTF_FORMAT_STRING
(
const
char
*
fmt
),
...)
PRINTF_ARGS
(
4
,
5
);
static
void
cry
(
struct
mg_connection
*
conn
,
PRINTF_FORMAT_STRING
(
const
char
*
fmt
),
...)
PRINTF_ARGS
(
2
,
3
);
#ifdef USE_LUA
#include "lua_5.2.1.h"
...
...
build/src/mongoose.c
View file @
47606a71
...
...
@@ -43,9 +43,6 @@ static void sockaddr_to_string(char *buf, size_t len,
#endif
}
static
void
cry
(
struct
mg_connection
*
conn
,
PRINTF_FORMAT_STRING
(
const
char
*
fmt
),
...)
PRINTF_ARGS
(
2
,
3
);
// Print error message to the opened error log stream.
static
void
cry
(
struct
mg_connection
*
conn
,
const
char
*
fmt
,
...)
{
char
buf
[
MG_BUF_LEN
],
src_addr
[
IP_ADDR_STR_LEN
];
...
...
@@ -119,11 +116,6 @@ static const char *suggest_connection_header(const struct mg_connection *conn) {
return
should_keep_alive
(
conn
)
?
"keep-alive"
:
"close"
;
}
static
void
send_http_error
(
struct
mg_connection
*
,
int
,
const
char
*
,
PRINTF_FORMAT_STRING
(
const
char
*
fmt
),
...)
PRINTF_ARGS
(
4
,
5
);
static
void
send_http_error
(
struct
mg_connection
*
conn
,
int
status
,
const
char
*
reason
,
const
char
*
fmt
,
...)
{
char
buf
[
MG_BUF_LEN
];
...
...
@@ -151,508 +143,6 @@ static void send_http_error(struct mg_connection *conn, int status,
conn
->
num_bytes_sent
+=
mg_printf
(
conn
,
"%s"
,
buf
);
}
#if defined(_WIN32) && !defined(__SYMBIAN32__)
static
pthread_t
pthread_self
(
void
)
{
return
GetCurrentThreadId
();
}
static
int
pthread_mutex_init
(
pthread_mutex_t
*
mutex
,
void
*
unused
)
{
(
void
)
unused
;
*
mutex
=
CreateMutex
(
NULL
,
FALSE
,
NULL
);
return
*
mutex
==
NULL
?
-
1
:
0
;
}
static
int
pthread_mutex_destroy
(
pthread_mutex_t
*
mutex
)
{
return
CloseHandle
(
*
mutex
)
==
0
?
-
1
:
0
;
}
static
int
pthread_mutex_lock
(
pthread_mutex_t
*
mutex
)
{
return
WaitForSingleObject
(
*
mutex
,
INFINITE
)
==
WAIT_OBJECT_0
?
0
:
-
1
;
}
static
int
pthread_mutex_unlock
(
pthread_mutex_t
*
mutex
)
{
return
ReleaseMutex
(
*
mutex
)
==
0
?
-
1
:
0
;
}
static
int
pthread_cond_init
(
pthread_cond_t
*
cv
,
const
void
*
unused
)
{
(
void
)
unused
;
cv
->
signal
=
CreateEvent
(
NULL
,
FALSE
,
FALSE
,
NULL
);
cv
->
broadcast
=
CreateEvent
(
NULL
,
TRUE
,
FALSE
,
NULL
);
return
cv
->
signal
!=
NULL
&&
cv
->
broadcast
!=
NULL
?
0
:
-
1
;
}
static
int
pthread_cond_wait
(
pthread_cond_t
*
cv
,
pthread_mutex_t
*
mutex
)
{
HANDLE
handles
[]
=
{
cv
->
signal
,
cv
->
broadcast
};
ReleaseMutex
(
*
mutex
);
WaitForMultipleObjects
(
2
,
handles
,
FALSE
,
INFINITE
);
return
WaitForSingleObject
(
*
mutex
,
INFINITE
)
==
WAIT_OBJECT_0
?
0
:
-
1
;
}
static
int
pthread_cond_signal
(
pthread_cond_t
*
cv
)
{
return
SetEvent
(
cv
->
signal
)
==
0
?
-
1
:
0
;
}
static
int
pthread_cond_broadcast
(
pthread_cond_t
*
cv
)
{
// Implementation with PulseEvent() has race condition, see
// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
return
PulseEvent
(
cv
->
broadcast
)
==
0
?
-
1
:
0
;
}
static
int
pthread_cond_destroy
(
pthread_cond_t
*
cv
)
{
return
CloseHandle
(
cv
->
signal
)
&&
CloseHandle
(
cv
->
broadcast
)
?
0
:
-
1
;
}
// For Windows, change all slashes to backslashes in path names.
static
void
change_slashes_to_backslashes
(
char
*
path
)
{
int
i
;
for
(
i
=
0
;
path
[
i
]
!=
'\0'
;
i
++
)
{
if
(
path
[
i
]
==
'/'
)
path
[
i
]
=
'\\'
;
// i > 0 check is to preserve UNC paths, like \\server\file.txt
if
(
path
[
i
]
==
'\\'
&&
i
>
0
)
while
(
path
[
i
+
1
]
==
'\\'
||
path
[
i
+
1
]
==
'/'
)
(
void
)
memmove
(
path
+
i
+
1
,
path
+
i
+
2
,
strlen
(
path
+
i
+
1
));
}
}
// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
// wbuf and wbuf_len is a target buffer and its length.
static
void
to_unicode
(
const
char
*
path
,
wchar_t
*
wbuf
,
size_t
wbuf_len
)
{
char
buf
[
PATH_MAX
*
2
],
buf2
[
PATH_MAX
*
2
];
mg_strlcpy
(
buf
,
path
,
sizeof
(
buf
));
change_slashes_to_backslashes
(
buf
);
// Convert to Unicode and back. If doubly-converted string does not
// match the original, something is fishy, reject.
memset
(
wbuf
,
0
,
wbuf_len
*
sizeof
(
wchar_t
));
MultiByteToWideChar
(
CP_UTF8
,
0
,
buf
,
-
1
,
wbuf
,
(
int
)
wbuf_len
);
WideCharToMultiByte
(
CP_UTF8
,
0
,
wbuf
,
(
int
)
wbuf_len
,
buf2
,
sizeof
(
buf2
),
NULL
,
NULL
);
if
(
strcmp
(
buf
,
buf2
)
!=
0
)
{
wbuf
[
0
]
=
L'\0'
;
}
}
#if defined(_WIN32_WCE)
static
time_t
time
(
time_t
*
ptime
)
{
time_t
t
;
SYSTEMTIME
st
;
FILETIME
ft
;
GetSystemTime
(
&
st
);
SystemTimeToFileTime
(
&
st
,
&
ft
);
t
=
SYS2UNIX_TIME
(
ft
.
dwLowDateTime
,
ft
.
dwHighDateTime
);
if
(
ptime
!=
NULL
)
{
*
ptime
=
t
;
}
return
t
;
}
static
struct
tm
*
localtime
(
const
time_t
*
ptime
,
struct
tm
*
ptm
)
{
int64_t
t
=
((
int64_t
)
*
ptime
)
*
RATE_DIFF
+
EPOCH_DIFF
;
FILETIME
ft
,
lft
;
SYSTEMTIME
st
;
TIME_ZONE_INFORMATION
tzinfo
;
if
(
ptm
==
NULL
)
{
return
NULL
;
}
*
(
int64_t
*
)
&
ft
=
t
;
FileTimeToLocalFileTime
(
&
ft
,
&
lft
);
FileTimeToSystemTime
(
&
lft
,
&
st
);
ptm
->
tm_year
=
st
.
wYear
-
1900
;
ptm
->
tm_mon
=
st
.
wMonth
-
1
;
ptm
->
tm_wday
=
st
.
wDayOfWeek
;
ptm
->
tm_mday
=
st
.
wDay
;
ptm
->
tm_hour
=
st
.
wHour
;
ptm
->
tm_min
=
st
.
wMinute
;
ptm
->
tm_sec
=
st
.
wSecond
;
ptm
->
tm_yday
=
0
;
// hope nobody uses this
ptm
->
tm_isdst
=
GetTimeZoneInformation
(
&
tzinfo
)
==
TIME_ZONE_ID_DAYLIGHT
?
1
:
0
;
return
ptm
;
}
static
struct
tm
*
gmtime
(
const
time_t
*
ptime
,
struct
tm
*
ptm
)
{
// FIXME(lsm): fix this.
return
localtime
(
ptime
,
ptm
);
}
static
size_t
strftime
(
char
*
dst
,
size_t
dst_size
,
const
char
*
fmt
,
const
struct
tm
*
tm
)
{
(
void
)
snprintf
(
dst
,
dst_size
,
"implement strftime() for WinCE"
);
return
0
;
}
#endif
// Windows happily opens files with some garbage at the end of file name.
// For example, fopen("a.cgi ", "r") on Windows successfully opens
// "a.cgi", despite one would expect an error back.
// This function returns non-0 if path ends with some garbage.
static
int
path_cannot_disclose_cgi
(
const
char
*
path
)
{
static
const
char
*
allowed_last_characters
=
"_-"
;
int
last
=
path
[
strlen
(
path
)
-
1
];
return
isalnum
(
last
)
||
strchr
(
allowed_last_characters
,
last
)
!=
NULL
;
}
static
int
mg_stat
(
const
char
*
path
,
struct
file
*
filep
)
{
wchar_t
wbuf
[
PATH_MAX
]
=
L"
\\\\
?
\\
"
;
WIN32_FILE_ATTRIBUTE_DATA
info
;
filep
->
modification_time
=
0
;
to_unicode
(
path
,
wbuf
+
4
,
ARRAY_SIZE
(
wbuf
)
-
4
);
if
(
GetFileAttributesExW
(
wbuf
,
GetFileExInfoStandard
,
&
info
)
!=
0
)
{
filep
->
size
=
MAKEUQUAD
(
info
.
nFileSizeLow
,
info
.
nFileSizeHigh
);
filep
->
modification_time
=
SYS2UNIX_TIME
(
info
.
ftLastWriteTime
.
dwLowDateTime
,
info
.
ftLastWriteTime
.
dwHighDateTime
);
filep
->
is_directory
=
info
.
dwFileAttributes
&
FILE_ATTRIBUTE_DIRECTORY
;
// If file name is fishy, reset the file structure and return error.
// Note it is important to reset, not just return the error, cause
// functions like is_file_opened() check the struct.
if
(
!
filep
->
is_directory
&&
!
path_cannot_disclose_cgi
(
path
))
{
memset
(
filep
,
0
,
sizeof
(
*
filep
));
}
}
return
filep
->
modification_time
!=
0
;
}
static
int
mg_remove
(
const
char
*
path
)
{
wchar_t
wbuf
[
PATH_MAX
];
to_unicode
(
path
,
wbuf
,
ARRAY_SIZE
(
wbuf
));
return
DeleteFileW
(
wbuf
)
?
0
:
-
1
;
}
static
int
mg_mkdir
(
const
char
*
path
,
int
mode
)
{
char
buf
[
PATH_MAX
];
wchar_t
wbuf
[
PATH_MAX
];
(
void
)
mode
;
mg_strlcpy
(
buf
,
path
,
sizeof
(
buf
));
change_slashes_to_backslashes
(
buf
);
(
void
)
MultiByteToWideChar
(
CP_UTF8
,
0
,
buf
,
-
1
,
wbuf
,
ARRAY_SIZE
(
wbuf
));
return
CreateDirectoryW
(
wbuf
,
NULL
)
?
0
:
-
1
;
}
// Implementation of POSIX opendir/closedir/readdir for Windows.
static
DIR
*
opendir
(
const
char
*
name
)
{
DIR
*
dir
=
NULL
;
wchar_t
wpath
[
PATH_MAX
];
DWORD
attrs
;
if
(
name
==
NULL
)
{
SetLastError
(
ERROR_BAD_ARGUMENTS
);
}
else
if
((
dir
=
(
DIR
*
)
malloc
(
sizeof
(
*
dir
)))
==
NULL
)
{
SetLastError
(
ERROR_NOT_ENOUGH_MEMORY
);
}
else
{
to_unicode
(
name
,
wpath
,
ARRAY_SIZE
(
wpath
));
attrs
=
GetFileAttributesW
(
wpath
);
if
(
attrs
!=
0xFFFFFFFF
&&
((
attrs
&
FILE_ATTRIBUTE_DIRECTORY
)
==
FILE_ATTRIBUTE_DIRECTORY
))
{
(
void
)
wcscat
(
wpath
,
L"
\\
*"
);
dir
->
handle
=
FindFirstFileW
(
wpath
,
&
dir
->
info
);
dir
->
result
.
d_name
[
0
]
=
'\0'
;
}
else
{
free
(
dir
);
dir
=
NULL
;
}
}
return
dir
;
}
static
int
closedir
(
DIR
*
dir
)
{
int
result
=
0
;
if
(
dir
!=
NULL
)
{
if
(
dir
->
handle
!=
INVALID_HANDLE_VALUE
)
result
=
FindClose
(
dir
->
handle
)
?
0
:
-
1
;
free
(
dir
);
}
else
{
result
=
-
1
;
SetLastError
(
ERROR_BAD_ARGUMENTS
);
}
return
result
;
}
static
struct
dirent
*
readdir
(
DIR
*
dir
)
{
struct
dirent
*
result
=
0
;
if
(
dir
)
{
if
(
dir
->
handle
!=
INVALID_HANDLE_VALUE
)
{
result
=
&
dir
->
result
;
(
void
)
WideCharToMultiByte
(
CP_UTF8
,
0
,
dir
->
info
.
cFileName
,
-
1
,
result
->
d_name
,
sizeof
(
result
->
d_name
),
NULL
,
NULL
);
if
(
!
FindNextFileW
(
dir
->
handle
,
&
dir
->
info
))
{
(
void
)
FindClose
(
dir
->
handle
);
dir
->
handle
=
INVALID_HANDLE_VALUE
;
}
}
else
{
SetLastError
(
ERROR_FILE_NOT_FOUND
);
}
}
else
{
SetLastError
(
ERROR_BAD_ARGUMENTS
);
}
return
result
;
}
#ifndef HAVE_POLL
static
int
poll
(
struct
pollfd
*
pfd
,
int
n
,
int
milliseconds
)
{
struct
timeval
tv
;
fd_set
set
;
int
i
,
result
;
SOCKET
maxfd
=
0
;
tv
.
tv_sec
=
milliseconds
/
1000
;
tv
.
tv_usec
=
(
milliseconds
%
1000
)
*
1000
;
FD_ZERO
(
&
set
);
for
(
i
=
0
;
i
<
n
;
i
++
)
{
FD_SET
((
SOCKET
)
pfd
[
i
].
fd
,
&
set
);
pfd
[
i
].
revents
=
0
;
if
(
pfd
[
i
].
fd
>
maxfd
)
{
maxfd
=
pfd
[
i
].
fd
;
}
}
if
((
result
=
select
(
maxfd
+
1
,
&
set
,
NULL
,
NULL
,
&
tv
))
>
0
)
{
for
(
i
=
0
;
i
<
n
;
i
++
)
{
if
(
FD_ISSET
(
pfd
[
i
].
fd
,
&
set
))
{
pfd
[
i
].
revents
=
POLLIN
;
}
}
}
return
result
;
}
#endif // HAVE_POLL
static
void
set_close_on_exec
(
SOCKET
sock
)
{
(
void
)
SetHandleInformation
((
HANDLE
)
sock
,
HANDLE_FLAG_INHERIT
,
0
);
}
int
mg_start_thread
(
mg_thread_func_t
f
,
void
*
p
)
{
return
(
long
)
_beginthread
((
void
(
__cdecl
*
)(
void
*
))
f
,
0
,
p
)
==
-
1L
?
-
1
:
0
;
}
static
HANDLE
dlopen
(
const
char
*
dll_name
,
int
flags
)
{
wchar_t
wbuf
[
PATH_MAX
];
(
void
)
flags
;
to_unicode
(
dll_name
,
wbuf
,
ARRAY_SIZE
(
wbuf
));
return
LoadLibraryW
(
wbuf
);
}
#if !defined(NO_CGI)
#define SIGKILL 0
static
int
kill
(
pid_t
pid
,
int
sig_num
)
{
(
void
)
TerminateProcess
(
pid
,
sig_num
);
(
void
)
CloseHandle
(
pid
);
return
0
;
}
static
void
trim_trailing_whitespaces
(
char
*
s
)
{
char
*
e
=
s
+
strlen
(
s
)
-
1
;
while
(
e
>
s
&&
isspace
(
*
(
unsigned
char
*
)
e
))
{
*
e
--
=
'\0'
;
}
}
static
pid_t
spawn_process
(
struct
mg_connection
*
conn
,
const
char
*
prog
,
char
*
envblk
,
char
*
envp
[],
int
fdin
,
int
fdout
,
const
char
*
dir
)
{
HANDLE
me
;
char
*
interp
,
full_interp
[
PATH_MAX
],
full_dir
[
PATH_MAX
],
cmdline
[
PATH_MAX
],
buf
[
PATH_MAX
];
FILE
*
fp
;
STARTUPINFOA
si
;
PROCESS_INFORMATION
pi
=
{
0
};
(
void
)
envp
;
memset
(
&
si
,
0
,
sizeof
(
si
));
si
.
cb
=
sizeof
(
si
);
// TODO(lsm): redirect CGI errors to the error log file
si
.
dwFlags
=
STARTF_USESTDHANDLES
|
STARTF_USESHOWWINDOW
;
si
.
wShowWindow
=
SW_HIDE
;
me
=
GetCurrentProcess
();
DuplicateHandle
(
me
,
(
HANDLE
)
_get_osfhandle
(
fdin
),
me
,
&
si
.
hStdInput
,
0
,
TRUE
,
DUPLICATE_SAME_ACCESS
);
DuplicateHandle
(
me
,
(
HANDLE
)
_get_osfhandle
(
fdout
),
me
,
&
si
.
hStdOutput
,
0
,
TRUE
,
DUPLICATE_SAME_ACCESS
);
// If CGI file is a script, try to read the interpreter line
interp
=
conn
->
ctx
->
config
[
CGI_INTERPRETER
];
if
(
interp
==
NULL
)
{
buf
[
0
]
=
buf
[
1
]
=
'\0'
;
// Read the first line of the script into the buffer
snprintf
(
cmdline
,
sizeof
(
cmdline
),
"%s%c%s"
,
dir
,
'/'
,
prog
);
if
((
fp
=
mg_fopen
(
cmdline
,
"r"
))
!=
NULL
)
{
fgets
(
buf
,
sizeof
(
buf
),
fp
);
fclose
(
fp
);
buf
[
sizeof
(
buf
)
-
1
]
=
'\0'
;
}
if
(
buf
[
0
]
==
'#'
&&
buf
[
1
]
==
'!'
)
{
trim_trailing_whitespaces
(
buf
+
2
);
}
else
{
buf
[
2
]
=
'\0'
;
}
interp
=
buf
+
2
;
}
if
(
interp
[
0
]
!=
'\0'
)
{
GetFullPathNameA
(
interp
,
sizeof
(
full_interp
),
full_interp
,
NULL
);
interp
=
full_interp
;
}
GetFullPathNameA
(
dir
,
sizeof
(
full_dir
),
full_dir
,
NULL
);
mg_snprintf
(
cmdline
,
sizeof
(
cmdline
),
"%s%s
\"
%s
\\
%s
\"
"
,
interp
,
interp
[
0
]
==
'\0'
?
""
:
" "
,
full_dir
,
prog
);
DEBUG_TRACE
((
"Running [%s]"
,
cmdline
));
if
(
CreateProcessA
(
NULL
,
cmdline
,
NULL
,
NULL
,
TRUE
,
CREATE_NEW_PROCESS_GROUP
,
envblk
,
NULL
,
&
si
,
&
pi
)
==
0
)
{
cry
(
conn
,
"%s: CreateProcess(%s): %ld"
,
__func__
,
cmdline
,
ERRNO
);
pi
.
hProcess
=
(
pid_t
)
-
1
;
}
(
void
)
CloseHandle
(
si
.
hStdOutput
);
(
void
)
CloseHandle
(
si
.
hStdInput
);
(
void
)
CloseHandle
(
pi
.
hThread
);
return
(
pid_t
)
pi
.
hProcess
;
}
#endif // !NO_CGI
static
int
set_non_blocking_mode
(
SOCKET
sock
)
{
unsigned
long
on
=
1
;
return
ioctlsocket
(
sock
,
FIONBIO
,
&
on
);
}
#else
static
int
mg_stat
(
const
char
*
path
,
struct
file
*
filep
)
{
struct
stat
st
;
filep
->
modification_time
=
(
time_t
)
0
;
if
(
stat
(
path
,
&
st
)
==
0
)
{
filep
->
size
=
st
.
st_size
;
filep
->
modification_time
=
st
.
st_mtime
;
filep
->
is_directory
=
S_ISDIR
(
st
.
st_mode
);
// See https://github.com/cesanta/mongoose/issues/109
// Some filesystems report modification time as 0. Artificially
// bump it up to mark mg_stat() success.
if
(
filep
->
modification_time
==
(
time_t
)
0
)
{
filep
->
modification_time
=
(
time_t
)
1
;
}
}
return
filep
->
modification_time
!=
(
time_t
)
0
;
}
static
void
set_close_on_exec
(
int
fd
)
{
fcntl
(
fd
,
F_SETFD
,
FD_CLOEXEC
);
}
int
mg_start_thread
(
mg_thread_func_t
func
,
void
*
param
)
{
pthread_t
thread_id
;
pthread_attr_t
attr
;
int
result
;
(
void
)
pthread_attr_init
(
&
attr
);
(
void
)
pthread_attr_setdetachstate
(
&
attr
,
PTHREAD_CREATE_DETACHED
);
#if USE_STACK_SIZE > 1
// Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384
(
void
)
pthread_attr_setstacksize
(
&
attr
,
USE_STACK_SIZE
);
#endif
result
=
pthread_create
(
&
thread_id
,
&
attr
,
func
,
param
);
pthread_attr_destroy
(
&
attr
);
return
result
;
}
#ifndef NO_CGI
static
pid_t
spawn_process
(
struct
mg_connection
*
conn
,
const
char
*
prog
,
char
*
envblk
,
char
*
envp
[],
int
fdin
,
int
fdout
,
const
char
*
dir
)
{
pid_t
pid
;
const
char
*
interp
;
(
void
)
envblk
;
if
((
pid
=
fork
())
==
-
1
)
{
// Parent
send_http_error
(
conn
,
500
,
http_500_error
,
"fork(): %s"
,
strerror
(
ERRNO
));
}
else
if
(
pid
==
0
)
{
// Child
if
(
chdir
(
dir
)
!=
0
)
{
cry
(
conn
,
"%s: chdir(%s): %s"
,
__func__
,
dir
,
strerror
(
ERRNO
));
}
else
if
(
dup2
(
fdin
,
0
)
==
-
1
)
{
cry
(
conn
,
"%s: dup2(%d, 0): %s"
,
__func__
,
fdin
,
strerror
(
ERRNO
));
}
else
if
(
dup2
(
fdout
,
1
)
==
-
1
)
{
cry
(
conn
,
"%s: dup2(%d, 1): %s"
,
__func__
,
fdout
,
strerror
(
ERRNO
));
}
else
{
// Not redirecting stderr to stdout, to avoid output being littered
// with the error messages.
(
void
)
close
(
fdin
);
(
void
)
close
(
fdout
);
// After exec, all signal handlers are restored to their default values,
// with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
// implementation, SIGCHLD's handler will leave unchanged after exec
// if it was set to be ignored. Restore it to default action.
signal
(
SIGCHLD
,
SIG_DFL
);
interp
=
conn
->
ctx
->
config
[
CGI_INTERPRETER
];
if
(
interp
==
NULL
)
{
(
void
)
execle
(
prog
,
prog
,
NULL
,
envp
);
cry
(
conn
,
"%s: execle(%s): %s"
,
__func__
,
prog
,
strerror
(
ERRNO
));
}
else
{
(
void
)
execle
(
interp
,
interp
,
prog
,
NULL
,
envp
);
cry
(
conn
,
"%s: execle(%s %s): %s"
,
__func__
,
interp
,
prog
,
strerror
(
ERRNO
));
}
}
exit
(
EXIT_FAILURE
);
}
return
pid
;
}
#endif // !NO_CGI
static
int
set_non_blocking_mode
(
SOCKET
sock
)
{
int
flags
;
flags
=
fcntl
(
sock
,
F_GETFL
,
0
);
(
void
)
fcntl
(
sock
,
F_SETFL
,
flags
|
O_NONBLOCK
);
return
0
;
}
#endif // _WIN32
// Write data to the IO channel - opened file descriptor, socket or SSL
// descriptor. Return number of bytes written.
static
int64_t
push
(
FILE
*
fp
,
SOCKET
sock
,
SSL
*
ssl
,
const
char
*
buf
,
...
...
build/src/unix.c
0 → 100644
View file @
47606a71
#include "internal.h"
#if !defined(_WIN32)
static
int
mg_stat
(
const
char
*
path
,
struct
file
*
filep
)
{
struct
stat
st
;
filep
->
modification_time
=
(
time_t
)
0
;
if
(
stat
(
path
,
&
st
)
==
0
)
{
filep
->
size
=
st
.
st_size
;
filep
->
modification_time
=
st
.
st_mtime
;
filep
->
is_directory
=
S_ISDIR
(
st
.
st_mode
);
// See https://github.com/cesanta/mongoose/issues/109
// Some filesystems report modification time as 0. Artificially
// bump it up to mark mg_stat() success.
if
(
filep
->
modification_time
==
(
time_t
)
0
)
{
filep
->
modification_time
=
(
time_t
)
1
;
}
}
return
filep
->
modification_time
!=
(
time_t
)
0
;
}
static
void
set_close_on_exec
(
int
fd
)
{
fcntl
(
fd
,
F_SETFD
,
FD_CLOEXEC
);
}
int
mg_start_thread
(
mg_thread_func_t
func
,
void
*
param
)
{
pthread_t
thread_id
;
pthread_attr_t
attr
;
int
result
;
(
void
)
pthread_attr_init
(
&
attr
);
(
void
)
pthread_attr_setdetachstate
(
&
attr
,
PTHREAD_CREATE_DETACHED
);
#if USE_STACK_SIZE > 1
// Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384
(
void
)
pthread_attr_setstacksize
(
&
attr
,
USE_STACK_SIZE
);
#endif
result
=
pthread_create
(
&
thread_id
,
&
attr
,
func
,
param
);
pthread_attr_destroy
(
&
attr
);
return
result
;
}
#ifndef NO_CGI
static
pid_t
spawn_process
(
struct
mg_connection
*
conn
,
const
char
*
prog
,
char
*
envblk
,
char
*
envp
[],
int
fdin
,
int
fdout
,
const
char
*
dir
)
{
pid_t
pid
;
const
char
*
interp
;
(
void
)
envblk
;
if
((
pid
=
fork
())
==
-
1
)
{
// Parent
send_http_error
(
conn
,
500
,
http_500_error
,
"fork(): %s"
,
strerror
(
ERRNO
));
}
else
if
(
pid
==
0
)
{
// Child
if
(
chdir
(
dir
)
!=
0
)
{
cry
(
conn
,
"%s: chdir(%s): %s"
,
__func__
,
dir
,
strerror
(
ERRNO
));
}
else
if
(
dup2
(
fdin
,
0
)
==
-
1
)
{
cry
(
conn
,
"%s: dup2(%d, 0): %s"
,
__func__
,
fdin
,
strerror
(
ERRNO
));
}
else
if
(
dup2
(
fdout
,
1
)
==
-
1
)
{
cry
(
conn
,
"%s: dup2(%d, 1): %s"
,
__func__
,
fdout
,
strerror
(
ERRNO
));
}
else
{
// Not redirecting stderr to stdout, to avoid output being littered
// with the error messages.
(
void
)
close
(
fdin
);
(
void
)
close
(
fdout
);
// After exec, all signal handlers are restored to their default values,
// with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
// implementation, SIGCHLD's handler will leave unchanged after exec
// if it was set to be ignored. Restore it to default action.
signal
(
SIGCHLD
,
SIG_DFL
);
interp
=
conn
->
ctx
->
config
[
CGI_INTERPRETER
];
if
(
interp
==
NULL
)
{
(
void
)
execle
(
prog
,
prog
,
NULL
,
envp
);
cry
(
conn
,
"%s: execle(%s): %s"
,
__func__
,
prog
,
strerror
(
ERRNO
));
}
else
{
(
void
)
execle
(
interp
,
interp
,
prog
,
NULL
,
envp
);
cry
(
conn
,
"%s: execle(%s %s): %s"
,
__func__
,
interp
,
prog
,
strerror
(
ERRNO
));
}
}
exit
(
EXIT_FAILURE
);
}
return
pid
;
}
#endif // !NO_CGI
static
int
set_non_blocking_mode
(
SOCKET
sock
)
{
int
flags
;
flags
=
fcntl
(
sock
,
F_GETFL
,
0
);
(
void
)
fcntl
(
sock
,
F_SETFL
,
flags
|
O_NONBLOCK
);
return
0
;
}
#endif // _WIN32
build/src/win32.c
0 → 100644
View file @
47606a71
#include "internal.h"
#if defined(_WIN32)
static
pthread_t
pthread_self
(
void
)
{
return
GetCurrentThreadId
();
}
static
int
pthread_mutex_init
(
pthread_mutex_t
*
mutex
,
void
*
unused
)
{
(
void
)
unused
;
*
mutex
=
CreateMutex
(
NULL
,
FALSE
,
NULL
);
return
*
mutex
==
NULL
?
-
1
:
0
;
}
static
int
pthread_mutex_destroy
(
pthread_mutex_t
*
mutex
)
{
return
CloseHandle
(
*
mutex
)
==
0
?
-
1
:
0
;
}
static
int
pthread_mutex_lock
(
pthread_mutex_t
*
mutex
)
{
return
WaitForSingleObject
(
*
mutex
,
INFINITE
)
==
WAIT_OBJECT_0
?
0
:
-
1
;
}
static
int
pthread_mutex_unlock
(
pthread_mutex_t
*
mutex
)
{
return
ReleaseMutex
(
*
mutex
)
==
0
?
-
1
:
0
;
}
static
int
pthread_cond_init
(
pthread_cond_t
*
cv
,
const
void
*
unused
)
{
(
void
)
unused
;
cv
->
signal
=
CreateEvent
(
NULL
,
FALSE
,
FALSE
,
NULL
);
cv
->
broadcast
=
CreateEvent
(
NULL
,
TRUE
,
FALSE
,
NULL
);
return
cv
->
signal
!=
NULL
&&
cv
->
broadcast
!=
NULL
?
0
:
-
1
;
}
static
int
pthread_cond_wait
(
pthread_cond_t
*
cv
,
pthread_mutex_t
*
mutex
)
{
HANDLE
handles
[]
=
{
cv
->
signal
,
cv
->
broadcast
};
ReleaseMutex
(
*
mutex
);
WaitForMultipleObjects
(
2
,
handles
,
FALSE
,
INFINITE
);
return
WaitForSingleObject
(
*
mutex
,
INFINITE
)
==
WAIT_OBJECT_0
?
0
:
-
1
;
}
static
int
pthread_cond_signal
(
pthread_cond_t
*
cv
)
{
return
SetEvent
(
cv
->
signal
)
==
0
?
-
1
:
0
;
}
static
int
pthread_cond_broadcast
(
pthread_cond_t
*
cv
)
{
// Implementation with PulseEvent() has race condition, see
// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
return
PulseEvent
(
cv
->
broadcast
)
==
0
?
-
1
:
0
;
}
static
int
pthread_cond_destroy
(
pthread_cond_t
*
cv
)
{
return
CloseHandle
(
cv
->
signal
)
&&
CloseHandle
(
cv
->
broadcast
)
?
0
:
-
1
;
}
// For Windows, change all slashes to backslashes in path names.
static
void
change_slashes_to_backslashes
(
char
*
path
)
{
int
i
;
for
(
i
=
0
;
path
[
i
]
!=
'\0'
;
i
++
)
{
if
(
path
[
i
]
==
'/'
)
path
[
i
]
=
'\\'
;
// i > 0 check is to preserve UNC paths, like \\server\file.txt
if
(
path
[
i
]
==
'\\'
&&
i
>
0
)
while
(
path
[
i
+
1
]
==
'\\'
||
path
[
i
+
1
]
==
'/'
)
(
void
)
memmove
(
path
+
i
+
1
,
path
+
i
+
2
,
strlen
(
path
+
i
+
1
));
}
}
// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
// wbuf and wbuf_len is a target buffer and its length.
static
void
to_unicode
(
const
char
*
path
,
wchar_t
*
wbuf
,
size_t
wbuf_len
)
{
char
buf
[
PATH_MAX
*
2
],
buf2
[
PATH_MAX
*
2
];
mg_strlcpy
(
buf
,
path
,
sizeof
(
buf
));
change_slashes_to_backslashes
(
buf
);
// Convert to Unicode and back. If doubly-converted string does not
// match the original, something is fishy, reject.
memset
(
wbuf
,
0
,
wbuf_len
*
sizeof
(
wchar_t
));
MultiByteToWideChar
(
CP_UTF8
,
0
,
buf
,
-
1
,
wbuf
,
(
int
)
wbuf_len
);
WideCharToMultiByte
(
CP_UTF8
,
0
,
wbuf
,
(
int
)
wbuf_len
,
buf2
,
sizeof
(
buf2
),
NULL
,
NULL
);
if
(
strcmp
(
buf
,
buf2
)
!=
0
)
{
wbuf
[
0
]
=
L'\0'
;
}
}
#if defined(_WIN32_WCE)
static
time_t
time
(
time_t
*
ptime
)
{
time_t
t
;
SYSTEMTIME
st
;
FILETIME
ft
;
GetSystemTime
(
&
st
);
SystemTimeToFileTime
(
&
st
,
&
ft
);
t
=
SYS2UNIX_TIME
(
ft
.
dwLowDateTime
,
ft
.
dwHighDateTime
);
if
(
ptime
!=
NULL
)
{
*
ptime
=
t
;
}
return
t
;
}
static
struct
tm
*
localtime
(
const
time_t
*
ptime
,
struct
tm
*
ptm
)
{
int64_t
t
=
((
int64_t
)
*
ptime
)
*
RATE_DIFF
+
EPOCH_DIFF
;
FILETIME
ft
,
lft
;
SYSTEMTIME
st
;
TIME_ZONE_INFORMATION
tzinfo
;
if
(
ptm
==
NULL
)
{
return
NULL
;
}
*
(
int64_t
*
)
&
ft
=
t
;
FileTimeToLocalFileTime
(
&
ft
,
&
lft
);
FileTimeToSystemTime
(
&
lft
,
&
st
);
ptm
->
tm_year
=
st
.
wYear
-
1900
;
ptm
->
tm_mon
=
st
.
wMonth
-
1
;
ptm
->
tm_wday
=
st
.
wDayOfWeek
;
ptm
->
tm_mday
=
st
.
wDay
;
ptm
->
tm_hour
=
st
.
wHour
;
ptm
->
tm_min
=
st
.
wMinute
;
ptm
->
tm_sec
=
st
.
wSecond
;
ptm
->
tm_yday
=
0
;
// hope nobody uses this
ptm
->
tm_isdst
=
GetTimeZoneInformation
(
&
tzinfo
)
==
TIME_ZONE_ID_DAYLIGHT
?
1
:
0
;
return
ptm
;
}
static
struct
tm
*
gmtime
(
const
time_t
*
ptime
,
struct
tm
*
ptm
)
{
// FIXME(lsm): fix this.
return
localtime
(
ptime
,
ptm
);
}
static
size_t
strftime
(
char
*
dst
,
size_t
dst_size
,
const
char
*
fmt
,
const
struct
tm
*
tm
)
{
(
void
)
snprintf
(
dst
,
dst_size
,
"implement strftime() for WinCE"
);
return
0
;
}
#endif
// Windows happily opens files with some garbage at the end of file name.
// For example, fopen("a.cgi ", "r") on Windows successfully opens
// "a.cgi", despite one would expect an error back.
// This function returns non-0 if path ends with some garbage.
static
int
path_cannot_disclose_cgi
(
const
char
*
path
)
{
static
const
char
*
allowed_last_characters
=
"_-"
;
int
last
=
path
[
strlen
(
path
)
-
1
];
return
isalnum
(
last
)
||
strchr
(
allowed_last_characters
,
last
)
!=
NULL
;
}
static
int
mg_stat
(
const
char
*
path
,
struct
file
*
filep
)
{
wchar_t
wbuf
[
PATH_MAX
]
=
L"
\\\\
?
\\
"
;
WIN32_FILE_ATTRIBUTE_DATA
info
;
filep
->
modification_time
=
0
;
to_unicode
(
path
,
wbuf
+
4
,
ARRAY_SIZE
(
wbuf
)
-
4
);
if
(
GetFileAttributesExW
(
wbuf
,
GetFileExInfoStandard
,
&
info
)
!=
0
)
{
filep
->
size
=
MAKEUQUAD
(
info
.
nFileSizeLow
,
info
.
nFileSizeHigh
);
filep
->
modification_time
=
SYS2UNIX_TIME
(
info
.
ftLastWriteTime
.
dwLowDateTime
,
info
.
ftLastWriteTime
.
dwHighDateTime
);
filep
->
is_directory
=
info
.
dwFileAttributes
&
FILE_ATTRIBUTE_DIRECTORY
;
// If file name is fishy, reset the file structure and return error.
// Note it is important to reset, not just return the error, cause
// functions like is_file_opened() check the struct.
if
(
!
filep
->
is_directory
&&
!
path_cannot_disclose_cgi
(
path
))
{
memset
(
filep
,
0
,
sizeof
(
*
filep
));
}
}
return
filep
->
modification_time
!=
0
;
}
static
int
mg_remove
(
const
char
*
path
)
{
wchar_t
wbuf
[
PATH_MAX
];
to_unicode
(
path
,
wbuf
,
ARRAY_SIZE
(
wbuf
));
return
DeleteFileW
(
wbuf
)
?
0
:
-
1
;
}
static
int
mg_mkdir
(
const
char
*
path
,
int
mode
)
{
char
buf
[
PATH_MAX
];
wchar_t
wbuf
[
PATH_MAX
];
(
void
)
mode
;
mg_strlcpy
(
buf
,
path
,
sizeof
(
buf
));
change_slashes_to_backslashes
(
buf
);
(
void
)
MultiByteToWideChar
(
CP_UTF8
,
0
,
buf
,
-
1
,
wbuf
,
ARRAY_SIZE
(
wbuf
));
return
CreateDirectoryW
(
wbuf
,
NULL
)
?
0
:
-
1
;
}
// Implementation of POSIX opendir/closedir/readdir for Windows.
static
DIR
*
opendir
(
const
char
*
name
)
{
DIR
*
dir
=
NULL
;
wchar_t
wpath
[
PATH_MAX
];
DWORD
attrs
;
if
(
name
==
NULL
)
{
SetLastError
(
ERROR_BAD_ARGUMENTS
);
}
else
if
((
dir
=
(
DIR
*
)
malloc
(
sizeof
(
*
dir
)))
==
NULL
)
{
SetLastError
(
ERROR_NOT_ENOUGH_MEMORY
);
}
else
{
to_unicode
(
name
,
wpath
,
ARRAY_SIZE
(
wpath
));
attrs
=
GetFileAttributesW
(
wpath
);
if
(
attrs
!=
0xFFFFFFFF
&&
((
attrs
&
FILE_ATTRIBUTE_DIRECTORY
)
==
FILE_ATTRIBUTE_DIRECTORY
))
{
(
void
)
wcscat
(
wpath
,
L"
\\
*"
);
dir
->
handle
=
FindFirstFileW
(
wpath
,
&
dir
->
info
);
dir
->
result
.
d_name
[
0
]
=
'\0'
;
}
else
{
free
(
dir
);
dir
=
NULL
;
}
}
return
dir
;
}
static
int
closedir
(
DIR
*
dir
)
{
int
result
=
0
;
if
(
dir
!=
NULL
)
{
if
(
dir
->
handle
!=
INVALID_HANDLE_VALUE
)
result
=
FindClose
(
dir
->
handle
)
?
0
:
-
1
;
free
(
dir
);
}
else
{
result
=
-
1
;
SetLastError
(
ERROR_BAD_ARGUMENTS
);
}
return
result
;
}
static
struct
dirent
*
readdir
(
DIR
*
dir
)
{
struct
dirent
*
result
=
0
;
if
(
dir
)
{
if
(
dir
->
handle
!=
INVALID_HANDLE_VALUE
)
{
result
=
&
dir
->
result
;
(
void
)
WideCharToMultiByte
(
CP_UTF8
,
0
,
dir
->
info
.
cFileName
,
-
1
,
result
->
d_name
,
sizeof
(
result
->
d_name
),
NULL
,
NULL
);
if
(
!
FindNextFileW
(
dir
->
handle
,
&
dir
->
info
))
{
(
void
)
FindClose
(
dir
->
handle
);
dir
->
handle
=
INVALID_HANDLE_VALUE
;
}
}
else
{
SetLastError
(
ERROR_FILE_NOT_FOUND
);
}
}
else
{
SetLastError
(
ERROR_BAD_ARGUMENTS
);
}
return
result
;
}
#ifndef HAVE_POLL
static
int
poll
(
struct
pollfd
*
pfd
,
int
n
,
int
milliseconds
)
{
struct
timeval
tv
;
fd_set
set
;
int
i
,
result
;
SOCKET
maxfd
=
0
;
tv
.
tv_sec
=
milliseconds
/
1000
;
tv
.
tv_usec
=
(
milliseconds
%
1000
)
*
1000
;
FD_ZERO
(
&
set
);
for
(
i
=
0
;
i
<
n
;
i
++
)
{
FD_SET
((
SOCKET
)
pfd
[
i
].
fd
,
&
set
);
pfd
[
i
].
revents
=
0
;
if
(
pfd
[
i
].
fd
>
maxfd
)
{
maxfd
=
pfd
[
i
].
fd
;
}
}
if
((
result
=
select
(
maxfd
+
1
,
&
set
,
NULL
,
NULL
,
&
tv
))
>
0
)
{
for
(
i
=
0
;
i
<
n
;
i
++
)
{
if
(
FD_ISSET
(
pfd
[
i
].
fd
,
&
set
))
{
pfd
[
i
].
revents
=
POLLIN
;
}
}
}
return
result
;
}
#endif // HAVE_POLL
static
void
set_close_on_exec
(
SOCKET
sock
)
{
(
void
)
SetHandleInformation
((
HANDLE
)
sock
,
HANDLE_FLAG_INHERIT
,
0
);
}
int
mg_start_thread
(
mg_thread_func_t
f
,
void
*
p
)
{
return
(
long
)
_beginthread
((
void
(
__cdecl
*
)(
void
*
))
f
,
0
,
p
)
==
-
1L
?
-
1
:
0
;
}
static
HANDLE
dlopen
(
const
char
*
dll_name
,
int
flags
)
{
wchar_t
wbuf
[
PATH_MAX
];
(
void
)
flags
;
to_unicode
(
dll_name
,
wbuf
,
ARRAY_SIZE
(
wbuf
));
return
LoadLibraryW
(
wbuf
);
}
#if !defined(NO_CGI)
#define SIGKILL 0
static
int
kill
(
pid_t
pid
,
int
sig_num
)
{
(
void
)
TerminateProcess
(
pid
,
sig_num
);
(
void
)
CloseHandle
(
pid
);
return
0
;
}
static
void
trim_trailing_whitespaces
(
char
*
s
)
{
char
*
e
=
s
+
strlen
(
s
)
-
1
;
while
(
e
>
s
&&
isspace
(
*
(
unsigned
char
*
)
e
))
{
*
e
--
=
'\0'
;
}
}
static
pid_t
spawn_process
(
struct
mg_connection
*
conn
,
const
char
*
prog
,
char
*
envblk
,
char
*
envp
[],
int
fdin
,
int
fdout
,
const
char
*
dir
)
{
HANDLE
me
;
char
*
interp
,
full_interp
[
PATH_MAX
],
full_dir
[
PATH_MAX
],
cmdline
[
PATH_MAX
],
buf
[
PATH_MAX
];
FILE
*
fp
;
STARTUPINFOA
si
;
PROCESS_INFORMATION
pi
=
{
0
};
(
void
)
envp
;
memset
(
&
si
,
0
,
sizeof
(
si
));
si
.
cb
=
sizeof
(
si
);
// TODO(lsm): redirect CGI errors to the error log file
si
.
dwFlags
=
STARTF_USESTDHANDLES
|
STARTF_USESHOWWINDOW
;
si
.
wShowWindow
=
SW_HIDE
;
me
=
GetCurrentProcess
();
DuplicateHandle
(
me
,
(
HANDLE
)
_get_osfhandle
(
fdin
),
me
,
&
si
.
hStdInput
,
0
,
TRUE
,
DUPLICATE_SAME_ACCESS
);
DuplicateHandle
(
me
,
(
HANDLE
)
_get_osfhandle
(
fdout
),
me
,
&
si
.
hStdOutput
,
0
,
TRUE
,
DUPLICATE_SAME_ACCESS
);
// If CGI file is a script, try to read the interpreter line
interp
=
conn
->
ctx
->
config
[
CGI_INTERPRETER
];
if
(
interp
==
NULL
)
{
buf
[
0
]
=
buf
[
1
]
=
'\0'
;
// Read the first line of the script into the buffer
snprintf
(
cmdline
,
sizeof
(
cmdline
),
"%s%c%s"
,
dir
,
'/'
,
prog
);
if
((
fp
=
mg_fopen
(
cmdline
,
"r"
))
!=
NULL
)
{
fgets
(
buf
,
sizeof
(
buf
),
fp
);
fclose
(
fp
);
buf
[
sizeof
(
buf
)
-
1
]
=
'\0'
;
}
if
(
buf
[
0
]
==
'#'
&&
buf
[
1
]
==
'!'
)
{
trim_trailing_whitespaces
(
buf
+
2
);
}
else
{
buf
[
2
]
=
'\0'
;
}
interp
=
buf
+
2
;
}
if
(
interp
[
0
]
!=
'\0'
)
{
GetFullPathNameA
(
interp
,
sizeof
(
full_interp
),
full_interp
,
NULL
);
interp
=
full_interp
;
}
GetFullPathNameA
(
dir
,
sizeof
(
full_dir
),
full_dir
,
NULL
);
mg_snprintf
(
cmdline
,
sizeof
(
cmdline
),
"%s%s
\"
%s
\\
%s
\"
"
,
interp
,
interp
[
0
]
==
'\0'
?
""
:
" "
,
full_dir
,
prog
);
DEBUG_TRACE
((
"Running [%s]"
,
cmdline
));
if
(
CreateProcessA
(
NULL
,
cmdline
,
NULL
,
NULL
,
TRUE
,
CREATE_NEW_PROCESS_GROUP
,
envblk
,
NULL
,
&
si
,
&
pi
)
==
0
)
{
cry
(
conn
,
"%s: CreateProcess(%s): %ld"
,
__func__
,
cmdline
,
ERRNO
);
pi
.
hProcess
=
(
pid_t
)
-
1
;
}
(
void
)
CloseHandle
(
si
.
hStdOutput
);
(
void
)
CloseHandle
(
si
.
hStdInput
);
(
void
)
CloseHandle
(
pi
.
hThread
);
return
(
pid_t
)
pi
.
hProcess
;
}
#endif // !NO_CGI
static
int
set_non_blocking_mode
(
SOCKET
sock
)
{
unsigned
long
on
=
1
;
return
ioctlsocket
(
sock
,
FIONBIO
,
&
on
);
}
#endif
mongoose.c
View file @
47606a71
...
...
@@ -323,7 +323,7 @@ struct ssl_func {
void
(
*
ptr
)(
void
);
// Function pointer
};
static
struct
ssl_func
ssl_sw
[];
static
struct
ssl_func
ssl_sw
[
30
];
#define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr)
#define SSL_accept (* (int (*)(SSL *)) ssl_sw[1].ptr)
...
...
@@ -460,6 +460,11 @@ struct de {
static
FILE
*
mg_fopen
(
const
char
*
path
,
const
char
*
mode
);
static
int
mg_stat
(
const
char
*
path
,
struct
file
*
filep
);
static
void
send_http_error
(
struct
mg_connection
*
,
int
,
const
char
*
,
PRINTF_FORMAT_STRING
(
const
char
*
fmt
),
...)
PRINTF_ARGS
(
4
,
5
);
static
void
cry
(
struct
mg_connection
*
conn
,
PRINTF_FORMAT_STRING
(
const
char
*
fmt
),
...)
PRINTF_ARGS
(
2
,
3
);
#ifdef USE_LUA
#include "lua_5.2.1.h"
...
...
@@ -1311,158 +1316,7 @@ int mg_modify_passwords_file(const char *fname, const char *domain,
return
1
;
}
// Return number of bytes left to read for this connection
static
int64_t
left_to_read
(
const
struct
mg_connection
*
conn
)
{
return
conn
->
content_len
+
conn
->
request_len
-
conn
->
num_bytes_read
;
}
static
int
call_user
(
int
type
,
struct
mg_connection
*
conn
,
void
*
p
)
{
if
(
conn
!=
NULL
&&
conn
->
ctx
!=
NULL
)
{
conn
->
event
.
user_data
=
conn
->
ctx
->
user_data
;
conn
->
event
.
type
=
type
;
conn
->
event
.
event_param
=
p
;
conn
->
event
.
request_info
=
&
conn
->
request_info
;
conn
->
event
.
conn
=
conn
;
}
return
conn
==
NULL
||
conn
->
ctx
==
NULL
||
conn
->
ctx
->
event_handler
==
NULL
?
0
:
conn
->
ctx
->
event_handler
(
&
conn
->
event
);
}
static
FILE
*
mg_fopen
(
const
char
*
path
,
const
char
*
mode
)
{
#ifdef _WIN32
wchar_t
wbuf
[
PATH_MAX
],
wmode
[
20
];
to_unicode
(
path
,
wbuf
,
ARRAY_SIZE
(
wbuf
));
MultiByteToWideChar
(
CP_UTF8
,
0
,
mode
,
-
1
,
wmode
,
ARRAY_SIZE
(
wmode
));
return
_wfopen
(
wbuf
,
wmode
);
#else
return
fopen
(
path
,
mode
);
#endif
}
static
void
sockaddr_to_string
(
char
*
buf
,
size_t
len
,
const
union
usa
*
usa
)
{
buf
[
0
]
=
'\0'
;
#if defined(USE_IPV6)
inet_ntop
(
usa
->
sa
.
sa_family
,
usa
->
sa
.
sa_family
==
AF_INET
?
(
void
*
)
&
usa
->
sin
.
sin_addr
:
(
void
*
)
&
usa
->
sin6
.
sin6_addr
,
buf
,
len
);
#elif defined(_WIN32)
// Only Windoze Vista (and newer) have inet_ntop()
strncpy
(
buf
,
inet_ntoa
(
usa
->
sin
.
sin_addr
),
len
);
#else
inet_ntop
(
usa
->
sa
.
sa_family
,
(
void
*
)
&
usa
->
sin
.
sin_addr
,
buf
,
len
);
#endif
}
static
void
cry
(
struct
mg_connection
*
conn
,
PRINTF_FORMAT_STRING
(
const
char
*
fmt
),
...)
PRINTF_ARGS
(
2
,
3
);
// Print error message to the opened error log stream.
static
void
cry
(
struct
mg_connection
*
conn
,
const
char
*
fmt
,
...)
{
char
buf
[
MG_BUF_LEN
],
src_addr
[
IP_ADDR_STR_LEN
];
va_list
ap
;
FILE
*
fp
;
time_t
timestamp
;
va_start
(
ap
,
fmt
);
(
void
)
vsnprintf
(
buf
,
sizeof
(
buf
),
fmt
,
ap
);
va_end
(
ap
);
// Do not lock when getting the callback value, here and below.
// I suppose this is fine, since function cannot disappear in the
// same way string option can.
if
(
call_user
(
MG_EVENT_LOG
,
conn
,
buf
)
==
0
)
{
fp
=
conn
->
ctx
==
NULL
||
conn
->
ctx
->
config
[
ERROR_LOG_FILE
]
==
NULL
?
NULL
:
fopen
(
conn
->
ctx
->
config
[
ERROR_LOG_FILE
],
"a+"
);
if
(
fp
!=
NULL
)
{
flockfile
(
fp
);
timestamp
=
time
(
NULL
);
sockaddr_to_string
(
src_addr
,
sizeof
(
src_addr
),
&
conn
->
client
.
rsa
);
fprintf
(
fp
,
"[%010lu] [error] [client %s] "
,
(
unsigned
long
)
timestamp
,
src_addr
);
if
(
conn
->
request_info
.
request_method
!=
NULL
)
{
fprintf
(
fp
,
"%s %s: "
,
conn
->
request_info
.
request_method
,
conn
->
request_info
.
uri
);
}
fprintf
(
fp
,
"%s"
,
buf
);
fputc
(
'\n'
,
fp
);
funlockfile
(
fp
);
fclose
(
fp
);
}
}
}
// Return fake connection structure. Used for logging, if connection
// is not applicable at the moment of logging.
static
struct
mg_connection
*
fc
(
struct
mg_context
*
ctx
)
{
static
struct
mg_connection
fake_connection
;
fake_connection
.
ctx
=
ctx
;
// See https://github.com/cesanta/mongoose/issues/236
fake_connection
.
event
.
user_data
=
ctx
->
user_data
;
return
&
fake_connection
;
}
const
char
*
mg_version
(
void
)
{
return
MONGOOSE_VERSION
;
}
// HTTP 1.1 assumes keep alive if "Connection:" header is not set
// This function must tolerate situations when connection info is not
// set up, for example if request parsing failed.
static
int
should_keep_alive
(
const
struct
mg_connection
*
conn
)
{
const
char
*
http_version
=
conn
->
request_info
.
http_version
;
const
char
*
header
=
mg_get_header
(
conn
,
"Connection"
);
if
(
conn
->
must_close
||
conn
->
status_code
==
401
||
mg_strcasecmp
(
conn
->
ctx
->
config
[
ENABLE_KEEP_ALIVE
],
"yes"
)
!=
0
||
(
header
!=
NULL
&&
mg_strcasecmp
(
header
,
"keep-alive"
)
!=
0
)
||
(
header
==
NULL
&&
http_version
&&
strcmp
(
http_version
,
"1.1"
)))
{
return
0
;
}
return
1
;
}
static
const
char
*
suggest_connection_header
(
const
struct
mg_connection
*
conn
)
{
return
should_keep_alive
(
conn
)
?
"keep-alive"
:
"close"
;
}
static
void
send_http_error
(
struct
mg_connection
*
,
int
,
const
char
*
,
PRINTF_FORMAT_STRING
(
const
char
*
fmt
),
...)
PRINTF_ARGS
(
4
,
5
);
static
void
send_http_error
(
struct
mg_connection
*
conn
,
int
status
,
const
char
*
reason
,
const
char
*
fmt
,
...)
{
char
buf
[
MG_BUF_LEN
];
va_list
ap
;
int
len
=
0
;
conn
->
status_code
=
status
;
buf
[
0
]
=
'\0'
;
// Errors 1xx, 204 and 304 MUST NOT send a body
if
(
status
>
199
&&
status
!=
204
&&
status
!=
304
)
{
len
=
mg_snprintf
(
buf
,
sizeof
(
buf
),
"Error %d: %s"
,
status
,
reason
);
buf
[
len
++
]
=
'\n'
;
va_start
(
ap
,
fmt
);
len
+=
mg_vsnprintf
(
buf
+
len
,
sizeof
(
buf
)
-
len
,
fmt
,
ap
);
va_end
(
ap
);
}
DEBUG_TRACE
((
"[%s]"
,
buf
));
mg_printf
(
conn
,
"HTTP/1.1 %d %s
\r\n
"
"Content-Length: %d
\r\n
"
"Connection: %s
\r\n\r\n
"
,
status
,
reason
,
len
,
suggest_connection_header
(
conn
));
conn
->
num_bytes_sent
+=
mg_printf
(
conn
,
"%s"
,
buf
);
}
#if defined(_WIN32) && !defined(__SYMBIAN32__)
#if defined(_WIN32)
static
pthread_t
pthread_self
(
void
)
{
return
GetCurrentThreadId
();
}
...
...
@@ -1860,8 +1714,9 @@ static int set_non_blocking_mode(SOCKET sock) {
unsigned
long
on
=
1
;
return
ioctlsocket
(
sock
,
FIONBIO
,
&
on
);
}
#endif
#
else
#
if !defined(_WIN32)
static
int
mg_stat
(
const
char
*
path
,
struct
file
*
filep
)
{
struct
stat
st
;
...
...
@@ -1964,6 +1819,150 @@ static int set_non_blocking_mode(SOCKET sock) {
}
#endif // _WIN32
// Return number of bytes left to read for this connection
static
int64_t
left_to_read
(
const
struct
mg_connection
*
conn
)
{
return
conn
->
content_len
+
conn
->
request_len
-
conn
->
num_bytes_read
;
}
static
int
call_user
(
int
type
,
struct
mg_connection
*
conn
,
void
*
p
)
{
if
(
conn
!=
NULL
&&
conn
->
ctx
!=
NULL
)
{
conn
->
event
.
user_data
=
conn
->
ctx
->
user_data
;
conn
->
event
.
type
=
type
;
conn
->
event
.
event_param
=
p
;
conn
->
event
.
request_info
=
&
conn
->
request_info
;
conn
->
event
.
conn
=
conn
;
}
return
conn
==
NULL
||
conn
->
ctx
==
NULL
||
conn
->
ctx
->
event_handler
==
NULL
?
0
:
conn
->
ctx
->
event_handler
(
&
conn
->
event
);
}
static
FILE
*
mg_fopen
(
const
char
*
path
,
const
char
*
mode
)
{
#ifdef _WIN32
wchar_t
wbuf
[
PATH_MAX
],
wmode
[
20
];
to_unicode
(
path
,
wbuf
,
ARRAY_SIZE
(
wbuf
));
MultiByteToWideChar
(
CP_UTF8
,
0
,
mode
,
-
1
,
wmode
,
ARRAY_SIZE
(
wmode
));
return
_wfopen
(
wbuf
,
wmode
);
#else
return
fopen
(
path
,
mode
);
#endif
}
static
void
sockaddr_to_string
(
char
*
buf
,
size_t
len
,
const
union
usa
*
usa
)
{
buf
[
0
]
=
'\0'
;
#if defined(USE_IPV6)
inet_ntop
(
usa
->
sa
.
sa_family
,
usa
->
sa
.
sa_family
==
AF_INET
?
(
void
*
)
&
usa
->
sin
.
sin_addr
:
(
void
*
)
&
usa
->
sin6
.
sin6_addr
,
buf
,
len
);
#elif defined(_WIN32)
// Only Windoze Vista (and newer) have inet_ntop()
strncpy
(
buf
,
inet_ntoa
(
usa
->
sin
.
sin_addr
),
len
);
#else
inet_ntop
(
usa
->
sa
.
sa_family
,
(
void
*
)
&
usa
->
sin
.
sin_addr
,
buf
,
len
);
#endif
}
// Print error message to the opened error log stream.
static
void
cry
(
struct
mg_connection
*
conn
,
const
char
*
fmt
,
...)
{
char
buf
[
MG_BUF_LEN
],
src_addr
[
IP_ADDR_STR_LEN
];
va_list
ap
;
FILE
*
fp
;
time_t
timestamp
;
va_start
(
ap
,
fmt
);
(
void
)
vsnprintf
(
buf
,
sizeof
(
buf
),
fmt
,
ap
);
va_end
(
ap
);
// Do not lock when getting the callback value, here and below.
// I suppose this is fine, since function cannot disappear in the
// same way string option can.
if
(
call_user
(
MG_EVENT_LOG
,
conn
,
buf
)
==
0
)
{
fp
=
conn
->
ctx
==
NULL
||
conn
->
ctx
->
config
[
ERROR_LOG_FILE
]
==
NULL
?
NULL
:
fopen
(
conn
->
ctx
->
config
[
ERROR_LOG_FILE
],
"a+"
);
if
(
fp
!=
NULL
)
{
flockfile
(
fp
);
timestamp
=
time
(
NULL
);
sockaddr_to_string
(
src_addr
,
sizeof
(
src_addr
),
&
conn
->
client
.
rsa
);
fprintf
(
fp
,
"[%010lu] [error] [client %s] "
,
(
unsigned
long
)
timestamp
,
src_addr
);
if
(
conn
->
request_info
.
request_method
!=
NULL
)
{
fprintf
(
fp
,
"%s %s: "
,
conn
->
request_info
.
request_method
,
conn
->
request_info
.
uri
);
}
fprintf
(
fp
,
"%s"
,
buf
);
fputc
(
'\n'
,
fp
);
funlockfile
(
fp
);
fclose
(
fp
);
}
}
}
// Return fake connection structure. Used for logging, if connection
// is not applicable at the moment of logging.
static
struct
mg_connection
*
fc
(
struct
mg_context
*
ctx
)
{
static
struct
mg_connection
fake_connection
;
fake_connection
.
ctx
=
ctx
;
// See https://github.com/cesanta/mongoose/issues/236
fake_connection
.
event
.
user_data
=
ctx
->
user_data
;
return
&
fake_connection
;
}
const
char
*
mg_version
(
void
)
{
return
MONGOOSE_VERSION
;
}
// HTTP 1.1 assumes keep alive if "Connection:" header is not set
// This function must tolerate situations when connection info is not
// set up, for example if request parsing failed.
static
int
should_keep_alive
(
const
struct
mg_connection
*
conn
)
{
const
char
*
http_version
=
conn
->
request_info
.
http_version
;
const
char
*
header
=
mg_get_header
(
conn
,
"Connection"
);
if
(
conn
->
must_close
||
conn
->
status_code
==
401
||
mg_strcasecmp
(
conn
->
ctx
->
config
[
ENABLE_KEEP_ALIVE
],
"yes"
)
!=
0
||
(
header
!=
NULL
&&
mg_strcasecmp
(
header
,
"keep-alive"
)
!=
0
)
||
(
header
==
NULL
&&
http_version
&&
strcmp
(
http_version
,
"1.1"
)))
{
return
0
;
}
return
1
;
}
static
const
char
*
suggest_connection_header
(
const
struct
mg_connection
*
conn
)
{
return
should_keep_alive
(
conn
)
?
"keep-alive"
:
"close"
;
}
static
void
send_http_error
(
struct
mg_connection
*
conn
,
int
status
,
const
char
*
reason
,
const
char
*
fmt
,
...)
{
char
buf
[
MG_BUF_LEN
];
va_list
ap
;
int
len
=
0
;
conn
->
status_code
=
status
;
buf
[
0
]
=
'\0'
;
// Errors 1xx, 204 and 304 MUST NOT send a body
if
(
status
>
199
&&
status
!=
204
&&
status
!=
304
)
{
len
=
mg_snprintf
(
buf
,
sizeof
(
buf
),
"Error %d: %s"
,
status
,
reason
);
buf
[
len
++
]
=
'\n'
;
va_start
(
ap
,
fmt
);
len
+=
mg_vsnprintf
(
buf
+
len
,
sizeof
(
buf
)
-
len
,
fmt
,
ap
);
va_end
(
ap
);
}
DEBUG_TRACE
((
"[%s]"
,
buf
));
mg_printf
(
conn
,
"HTTP/1.1 %d %s
\r\n
"
"Content-Length: %d
\r\n
"
"Connection: %s
\r\n\r\n
"
,
status
,
reason
,
len
,
suggest_connection_header
(
conn
));
conn
->
num_bytes_sent
+=
mg_printf
(
conn
,
"%s"
,
buf
);
}
// Write data to the IO channel - opened file descriptor, socket or SSL
// descriptor. Return number of bytes written.
static
int64_t
push
(
FILE
*
fp
,
SOCKET
sock
,
SSL
*
ssl
,
const
char
*
buf
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment