mirror of
https://github.com/clearlinux/uwsgi.git
synced 2026-06-16 02:15:48 +00:00
0dd07763b2
Coverity reports a null pointer dereference (CID 1231243) after check of ub. After auditing the callers it looks like it is always called with ub defined so add a guard againt it being null at the top just to be sure. While at it fixup indentation of the function.
1637 lines
36 KiB
C
1637 lines
36 KiB
C
#include "uwsgi.h"
|
|
|
|
extern struct uwsgi_server uwsgi;
|
|
|
|
/*
|
|
|
|
poll based fd waiter.
|
|
Use it for blocking areas (like startup functions)
|
|
DO NOT USE IN REQUEST PLUGINS !!!
|
|
|
|
*/
|
|
int uwsgi_waitfd_event(int fd, int timeout, int event) {
|
|
|
|
int ret;
|
|
struct pollfd upoll;
|
|
|
|
if (!timeout)
|
|
timeout = uwsgi.socket_timeout;
|
|
|
|
timeout = timeout * 1000;
|
|
if (timeout < 0)
|
|
timeout = -1;
|
|
|
|
upoll.fd = fd;
|
|
upoll.events = event;
|
|
upoll.revents = 0;
|
|
ret = poll(&upoll, 1, timeout);
|
|
|
|
if (ret < 0) {
|
|
uwsgi_error("uwsgi_waitfd_event()/poll()");
|
|
}
|
|
else if (ret > 0) {
|
|
if (upoll.revents & event) {
|
|
return ret;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
consume data from an fd (blocking)
|
|
*/
|
|
char *uwsgi_read_fd(int fd, size_t *size, int add_zero) {
|
|
|
|
char stack_buf[4096];
|
|
ssize_t len;
|
|
char *buffer = NULL;
|
|
|
|
len = 1;
|
|
while (len > 0) {
|
|
len = read(fd, stack_buf, 4096);
|
|
if (len > 0) {
|
|
*size += len;
|
|
char *tmp = realloc(buffer, *size);
|
|
if (!tmp) {
|
|
uwsgi_error("uwsgi_read_fd()/realloc()");
|
|
exit(1);
|
|
}
|
|
buffer = tmp;
|
|
memcpy(buffer + (*size - len), stack_buf, len);
|
|
}
|
|
}
|
|
|
|
if (add_zero) {
|
|
*size = *size + 1;
|
|
buffer = realloc(buffer, *size);
|
|
if (!buffer) {
|
|
uwsgi_error("uwsgi_read_fd()/realloc()");
|
|
exit(1);
|
|
}
|
|
buffer[*size - 1] = 0;
|
|
}
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
// simply read the whole content of a file
|
|
char *uwsgi_simple_file_read(char *filename) {
|
|
|
|
struct stat sb;
|
|
char *buffer;
|
|
ssize_t len;
|
|
int fd = open(filename, O_RDONLY);
|
|
if (fd < 0) {
|
|
uwsgi_error_open(filename);
|
|
goto end;
|
|
}
|
|
|
|
if (fstat(fd, &sb)) {
|
|
uwsgi_error("uwsgi_simple_file_read()/fstat()");
|
|
close(fd);
|
|
goto end;
|
|
}
|
|
|
|
buffer = uwsgi_malloc(sb.st_size + 1);
|
|
|
|
len = read(fd, buffer, sb.st_size);
|
|
if (len != sb.st_size) {
|
|
uwsgi_error("uwsgi_simple_file_read()/read()");
|
|
free(buffer);
|
|
close(fd);
|
|
goto end;
|
|
}
|
|
|
|
close(fd);
|
|
if (buffer[sb.st_size - 1] == '\n' || buffer[sb.st_size - 1] == '\r') {
|
|
buffer[sb.st_size - 1] = 0;
|
|
}
|
|
buffer[sb.st_size] = 0;
|
|
return buffer;
|
|
end:
|
|
return NULL;
|
|
|
|
}
|
|
|
|
static char *uwsgi_scheme_fd(char *url, size_t *size, int add_zero) {
|
|
int fd = atoi(url);
|
|
return uwsgi_read_fd(fd, size, add_zero);
|
|
}
|
|
|
|
static char *uwsgi_scheme_exec(char *url, size_t *size, int add_zero) {
|
|
int cpipe[2];
|
|
if (pipe(cpipe)) {
|
|
uwsgi_error("pipe()");
|
|
exit(1);
|
|
}
|
|
uwsgi_run_command(url, NULL, cpipe[1]);
|
|
char *buffer = uwsgi_read_fd(cpipe[0], size, add_zero);
|
|
close(cpipe[0]);
|
|
close(cpipe[1]);
|
|
return buffer;
|
|
}
|
|
|
|
static char *uwsgi_scheme_http(char *url, size_t *size, int add_zero) {
|
|
char byte;
|
|
int body = 0;
|
|
char *buffer = NULL;
|
|
|
|
char *domain = url;
|
|
char *uri = strchr(domain, '/');
|
|
if (!uri) {
|
|
uwsgi_log("invalid http url\n");
|
|
exit(1);
|
|
}
|
|
uri[0] = 0;
|
|
uwsgi_log("domain: %s\n", domain);
|
|
|
|
char *colon = uwsgi_get_last_char(domain, ':');
|
|
|
|
if (colon) {
|
|
colon[0] = 0;
|
|
}
|
|
|
|
|
|
char *ip = uwsgi_resolve_ip(domain);
|
|
if (!ip) {
|
|
uwsgi_log("unable to resolve address %s\n", domain);
|
|
exit(1);
|
|
}
|
|
|
|
if (colon) {
|
|
colon[0] = ':';
|
|
ip = uwsgi_concat2(ip, colon);
|
|
}
|
|
else {
|
|
ip = uwsgi_concat2(ip, ":80");
|
|
}
|
|
|
|
int fd = uwsgi_connect(ip, 0, 0);
|
|
|
|
if (fd < 0) {
|
|
uwsgi_error("uwsgi_scheme_http()/connect()");
|
|
exit(1);
|
|
}
|
|
|
|
free(ip);
|
|
|
|
uri[0] = '/';
|
|
|
|
if (write(fd, "GET ", 4) != 4) { uwsgi_error("uwsgi_scheme_http()/write()"); exit(1);}
|
|
if (write(fd, uri, strlen(uri)) != (ssize_t) strlen(uri)) { uwsgi_error("uwsgi_scheme_http()/write()"); exit(1);}
|
|
if (write(fd, " HTTP/1.0\r\n", 11) != 11) { uwsgi_error("uwsgi_scheme_http()/write()"); exit(1);}
|
|
if (write(fd, "Host: ", 6) != 6) { uwsgi_error("uwsgi_scheme_http()/write()"); exit(1);}
|
|
|
|
uri[0] = 0;
|
|
if (write(fd, domain, strlen(domain)) != (ssize_t) strlen(domain)) { uwsgi_error("uwsgi_scheme_http()/write()"); exit(1);}
|
|
uri[0] = '/';
|
|
|
|
if (write(fd, "\r\nUser-Agent: uWSGI on ", 23) != 23) { uwsgi_error("uwsgi_scheme_http()/write()"); exit(1);}
|
|
if (write(fd, uwsgi.hostname, uwsgi.hostname_len) != uwsgi.hostname_len) { uwsgi_error("uwsgi_scheme_http()/write()"); exit(1);}
|
|
if (write(fd, "\r\n\r\n", 4) != 4) { uwsgi_error("uwsgi_scheme_http()/write()"); exit(1);}
|
|
|
|
int http_status_code_ptr = 0;
|
|
|
|
while (read(fd, &byte, 1) == 1) {
|
|
if (byte == '\r' && body == 0) {
|
|
body = 1;
|
|
}
|
|
else if (byte == '\n' && body == 1) {
|
|
body = 2;
|
|
}
|
|
else if (byte == '\r' && body == 2) {
|
|
body = 3;
|
|
}
|
|
else if (byte == '\n' && body == 3) {
|
|
body = 4;
|
|
}
|
|
else if (body == 4) {
|
|
*size = *size + 1;
|
|
char *tmp = realloc(buffer, *size);
|
|
if (!tmp) {
|
|
uwsgi_error("uwsgi_open_and_read()/realloc()");
|
|
exit(1);
|
|
}
|
|
buffer = tmp;
|
|
buffer[*size - 1] = byte;
|
|
}
|
|
else {
|
|
body = 0;
|
|
http_status_code_ptr++;
|
|
if (http_status_code_ptr == 10) {
|
|
if (byte != '2') {
|
|
uwsgi_log("Not usable HTTP response: %cxx\n", byte);
|
|
if (uwsgi.has_emperor) {
|
|
exit(UWSGI_EXILE_CODE);
|
|
}
|
|
else {
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
|
|
if (add_zero) {
|
|
*size = *size + 1;
|
|
char *tmp = realloc(buffer, *size);
|
|
if (!tmp) {
|
|
uwsgi_error("uwsgi_open_and_read()/realloc()");
|
|
exit(1);
|
|
}
|
|
buffer = tmp;
|
|
buffer[*size - 1] = 0;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
|
|
static char *uwsgi_scheme_emperor(char *url, size_t *size, int add_zero) {
|
|
|
|
if (uwsgi.emperor_fd_config < 0) {
|
|
uwsgi_log("this is not a vassal instance\n");
|
|
exit(1);
|
|
}
|
|
ssize_t rlen;
|
|
struct uwsgi_header uh;
|
|
size_t remains = 4;
|
|
char *ptr = (char *) &uh;
|
|
while(remains) {
|
|
int ret = uwsgi_waitfd(uwsgi.emperor_fd_config, 5);
|
|
if (ret <= 0) {
|
|
uwsgi_log("[uwsgi-vassal] error waiting for config header %s !!!\n", url);
|
|
exit(1);
|
|
}
|
|
rlen = read(uwsgi.emperor_fd_config, ptr, remains);
|
|
if (rlen <= 0) {
|
|
uwsgi_log("[uwsgi-vassal] error reading config header from %s !!!\n", url);
|
|
exit(1);
|
|
}
|
|
ptr+=rlen;
|
|
remains-=rlen;
|
|
}
|
|
|
|
remains = uh._pktsize;
|
|
if (!remains) {
|
|
uwsgi_log("[uwsgi-vassal] invalid config from %s\n", url);
|
|
exit(1);
|
|
}
|
|
|
|
char *buffer = uwsgi_calloc(remains + add_zero);
|
|
ptr = buffer;
|
|
while (remains) {
|
|
int ret = uwsgi_waitfd(uwsgi.emperor_fd_config, 5);
|
|
if (ret <= 0) {
|
|
uwsgi_log("[uwsgi-vassal] error waiting for config %s !!!\n", url);
|
|
exit(1);
|
|
}
|
|
rlen = read(uwsgi.emperor_fd_config, ptr, remains);
|
|
if (rlen <= 0) {
|
|
uwsgi_log("[uwsgi-vassal] error reading config from %s !!!\n", url);
|
|
exit(1);
|
|
}
|
|
ptr+=rlen;
|
|
remains-=rlen;
|
|
}
|
|
|
|
*size = uh._pktsize + add_zero;
|
|
return buffer;
|
|
}
|
|
|
|
static char *uwsgi_scheme_data(char *url, size_t *size, int add_zero) {
|
|
char *buffer = NULL;
|
|
int fd = open(uwsgi.binary_path, O_RDONLY);
|
|
if (fd < 0) {
|
|
uwsgi_error_open(uwsgi.binary_path);
|
|
exit(1);
|
|
}
|
|
int slot = atoi(url);
|
|
if (slot < 0) {
|
|
uwsgi_log("invalid binary data slot requested\n");
|
|
exit(1);
|
|
}
|
|
uwsgi_log("requesting binary data slot %d\n", slot);
|
|
off_t fo = lseek(fd, 0, SEEK_END);
|
|
if (fo < 0) {
|
|
uwsgi_error("lseek()");
|
|
uwsgi_log("invalid binary data slot requested\n");
|
|
exit(1);
|
|
}
|
|
int i = 0;
|
|
uint64_t datasize = 0;
|
|
for (i = 0; i <= slot; i++) {
|
|
fo = lseek(fd, -8, SEEK_CUR);
|
|
if (fo < 0) {
|
|
uwsgi_error("lseek()");
|
|
uwsgi_log("invalid binary data slot requested\n");
|
|
exit(1);
|
|
}
|
|
ssize_t len = read(fd, &datasize, 8);
|
|
if (len != 8) {
|
|
uwsgi_error("read()");
|
|
uwsgi_log("invalid binary data slot requested\n");
|
|
exit(1);
|
|
}
|
|
if (datasize == 0) {
|
|
uwsgi_log("0 size binary data !!!\n");
|
|
exit(1);
|
|
}
|
|
if (datasize > SIZE_MAX) {
|
|
uwsgi_log("size binary data bigger than SIZE_MAX !!!\n");
|
|
exit(1);
|
|
}
|
|
fo = lseek(fd, -(datasize + 8), SEEK_CUR);
|
|
if (fo < 0) {
|
|
uwsgi_error("lseek()");
|
|
uwsgi_log("invalid binary data slot requested\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (i == slot) {
|
|
*size = datasize;
|
|
if (add_zero) {
|
|
*size += 1;
|
|
}
|
|
buffer = uwsgi_malloc(*size);
|
|
memset(buffer, 0, *size);
|
|
len = read(fd, buffer, datasize);
|
|
if (len != (ssize_t) datasize) {
|
|
uwsgi_error("read()");
|
|
uwsgi_log("invalid binary data slot requested\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
close(fd);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
static char *uwsgi_scheme_call(char *url, size_t *size, int add_zero) {
|
|
char *(*func)(void) = dlsym(RTLD_DEFAULT, url);
|
|
if (!func) {
|
|
uwsgi_log("unable to find symbol %s\n", url);
|
|
exit(1);
|
|
}
|
|
|
|
char *s = func();
|
|
if (!s) {
|
|
uwsgi_log("called symbol %s did not return a string\n", url);
|
|
exit(1);
|
|
}
|
|
*size = strlen(s);
|
|
if (add_zero) {
|
|
*size += 1;
|
|
}
|
|
char *buffer = uwsgi_malloc(*size);
|
|
memset(buffer, 0, *size);
|
|
memcpy(buffer, s, strlen(s));
|
|
|
|
return buffer;
|
|
}
|
|
|
|
static char *uwsgi_scheme_callint(char *url, size_t *size, int add_zero) {
|
|
int (*func)(void) = dlsym(RTLD_DEFAULT, url);
|
|
if (!func) {
|
|
uwsgi_log("unable to find symbol %s\n", url);
|
|
exit(1);
|
|
}
|
|
|
|
char *s = uwsgi_num2str(func());
|
|
*size = strlen(s);
|
|
if (add_zero) {
|
|
*size += 1;
|
|
}
|
|
char *buffer = uwsgi_malloc(*size);
|
|
memset(buffer, 0, *size);
|
|
memcpy(buffer, s, strlen(s));
|
|
free(s);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
|
|
static char *uwsgi_scheme_sym(char *url, size_t *size, int add_zero) {
|
|
void *sym_start_ptr = NULL, *sym_end_ptr = NULL;
|
|
char **raw_symbol = dlsym(RTLD_DEFAULT, url);
|
|
if (raw_symbol) {
|
|
sym_start_ptr = *raw_symbol;
|
|
sym_end_ptr = sym_start_ptr + strlen(sym_start_ptr);
|
|
goto found;
|
|
}
|
|
char *symbol = uwsgi_concat3("_binary_", url, "_start");
|
|
sym_start_ptr = dlsym(RTLD_DEFAULT, symbol);
|
|
if (!sym_start_ptr) {
|
|
uwsgi_log("unable to find symbol %s\n", symbol);
|
|
exit(1);
|
|
}
|
|
free(symbol);
|
|
symbol = uwsgi_concat3("_binary_", url, "_end");
|
|
sym_end_ptr = dlsym(RTLD_DEFAULT, symbol);
|
|
if (!sym_end_ptr) {
|
|
uwsgi_log("unable to find symbol %s\n", symbol);
|
|
exit(1);
|
|
}
|
|
free(symbol);
|
|
|
|
found:
|
|
|
|
*size = sym_end_ptr - sym_start_ptr;
|
|
if (add_zero) {
|
|
*size += 1;
|
|
}
|
|
char *buffer = uwsgi_malloc(*size);
|
|
memset(buffer, 0, *size);
|
|
memcpy(buffer, sym_start_ptr, sym_end_ptr - sym_start_ptr);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
static char *uwsgi_scheme_section(char *url, size_t *size, int add_zero) {
|
|
#ifdef UWSGI_ELF
|
|
size_t s_len = 0;
|
|
char *buffer = uwsgi_elf_section(uwsgi.binary_path, url, &s_len);
|
|
if (!buffer) {
|
|
uwsgi_log("unable to find section %s in %s\n", url + 10, uwsgi.binary_path);
|
|
exit(1);
|
|
}
|
|
*size = s_len;
|
|
if (add_zero)
|
|
*size += 1;
|
|
return buffer;
|
|
#else
|
|
uwsgi_log("section:// scheme not supported on this platform\n");
|
|
exit(1);
|
|
#endif
|
|
}
|
|
|
|
struct uwsgi_string_list *uwsgi_register_scheme(char *name, char * (*func)(char *, size_t *, int)) {
|
|
struct uwsgi_string_list *usl = NULL;
|
|
uwsgi_foreach(usl, uwsgi.schemes) {
|
|
if (!strcmp(usl->value, name)) goto found;
|
|
}
|
|
|
|
usl = uwsgi_string_new_list(&uwsgi.schemes, name);
|
|
|
|
found:
|
|
usl->custom_ptr = func;
|
|
return usl;
|
|
}
|
|
|
|
char *uwsgi_open_and_read(char *url, size_t *size, int add_zero, char *magic_table[]) {
|
|
|
|
struct stat sb;
|
|
char *buffer = NULL;
|
|
ssize_t len;
|
|
char *magic_buf;
|
|
int fd;
|
|
|
|
*size = 0;
|
|
|
|
// stdin ?
|
|
if (!strcmp(url, "-")) {
|
|
buffer = uwsgi_read_fd(0, size, add_zero);
|
|
goto end;
|
|
}
|
|
#ifdef UWSGI_EMBED_CONFIG
|
|
else if (url[0] == 0) {
|
|
*size = &UWSGI_EMBED_CONFIG_END - &UWSGI_EMBED_CONFIG;
|
|
if (add_zero) {
|
|
*size += 1;
|
|
}
|
|
buffer = uwsgi_malloc(*size);
|
|
memset(buffer, 0, *size);
|
|
memcpy(buffer, &UWSGI_EMBED_CONFIG, &UWSGI_EMBED_CONFIG_END - &UWSGI_EMBED_CONFIG);
|
|
goto end;
|
|
}
|
|
#endif
|
|
|
|
struct uwsgi_string_list *usl = uwsgi_check_scheme(url);
|
|
if (!usl) goto fallback;
|
|
|
|
char *(*func)(char *, size_t *, int) = (char *(*)(char *, size_t *, int)) usl->custom_ptr;
|
|
buffer = func(url + usl->len + 3, size, add_zero);
|
|
if (buffer) goto end;
|
|
// never here !!!
|
|
uwsgi_log("unable to parse config file %s\n", url);
|
|
exit(1);
|
|
|
|
// fallback to file
|
|
fallback:
|
|
fd = open(url, O_RDONLY);
|
|
if (fd < 0) {
|
|
uwsgi_error_open(url);
|
|
exit(1);
|
|
}
|
|
|
|
if (fstat(fd, &sb)) {
|
|
uwsgi_error("fstat()");
|
|
exit(1);
|
|
}
|
|
|
|
if (S_ISFIFO(sb.st_mode)) {
|
|
buffer = uwsgi_read_fd(fd, size, add_zero);
|
|
close(fd);
|
|
goto end;
|
|
}
|
|
|
|
// is it a potential virtual file (/proc, /sys...) ?
|
|
int is_virtual = 0;
|
|
if (sb.st_size == 0) {
|
|
is_virtual = 1;
|
|
sb.st_size = 4096;
|
|
}
|
|
|
|
buffer = uwsgi_malloc(sb.st_size + add_zero);
|
|
|
|
len = read(fd, buffer, sb.st_size);
|
|
if (!is_virtual) {
|
|
if (len != sb.st_size) {
|
|
}
|
|
}
|
|
else {
|
|
if (len >= 0) {
|
|
sb.st_size = len;
|
|
}
|
|
else {
|
|
uwsgi_error("read()");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
|
|
*size = sb.st_size + add_zero;
|
|
|
|
if (add_zero)
|
|
buffer[sb.st_size] = 0;
|
|
|
|
end:
|
|
|
|
if (magic_table) {
|
|
// here we inject blobs
|
|
struct uwsgi_string_list *usl = NULL;
|
|
if (uwsgi.inject_before || uwsgi.inject_after) {
|
|
struct uwsgi_buffer *ub = uwsgi_buffer_new(uwsgi.page_size);
|
|
uwsgi_foreach(usl, uwsgi.inject_before) {
|
|
size_t rlen = 0;
|
|
char *before = uwsgi_open_and_read(usl->value, &rlen, 0, NULL);
|
|
if (uwsgi_buffer_append(ub, before, rlen)) {
|
|
uwsgi_log("unable to inject data in the config file\n");
|
|
exit(1);
|
|
}
|
|
free(before);
|
|
}
|
|
if (uwsgi_buffer_append(ub, buffer, *size - add_zero)) {
|
|
uwsgi_log("unable to inject data in the config file\n");
|
|
exit(1);
|
|
}
|
|
uwsgi_foreach(usl, uwsgi.inject_after) {
|
|
size_t rlen = 0;
|
|
char *after = uwsgi_open_and_read(usl->value, &rlen, 0, NULL);
|
|
if (uwsgi_buffer_append(ub, after, rlen)) {
|
|
uwsgi_log("unable to inject data in the config file\n");
|
|
exit(1);
|
|
}
|
|
free(after);
|
|
}
|
|
if (add_zero) {
|
|
if (uwsgi_buffer_append(ub, "\0", 1)) {
|
|
uwsgi_log("unable to inject data in the config file\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
*size = ub->pos;
|
|
// free resources
|
|
free(buffer);
|
|
buffer = ub->buf;
|
|
// aboid destroying buffer
|
|
ub->buf = NULL;
|
|
uwsgi_buffer_destroy(ub);
|
|
}
|
|
magic_buf = magic_sub(buffer, *size, size, magic_table);
|
|
free(buffer);
|
|
return magic_buf;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
// attach an fd using UNIX sockets
|
|
int *uwsgi_attach_fd(int fd, int *count_ptr, char *code, size_t code_len) {
|
|
|
|
struct msghdr msg;
|
|
ssize_t len;
|
|
char *id = NULL;
|
|
|
|
struct iovec iov;
|
|
struct cmsghdr *cmsg;
|
|
int *ret;
|
|
int i;
|
|
int count = *count_ptr;
|
|
|
|
void *msg_control = uwsgi_malloc(CMSG_SPACE(sizeof(int) * count));
|
|
|
|
memset(msg_control, 0, CMSG_SPACE(sizeof(int) * count));
|
|
|
|
if (code && code_len > 0) {
|
|
// allocate space for code and num_sockets
|
|
id = uwsgi_malloc(code_len + sizeof(int));
|
|
memset(id, 0, code_len);
|
|
iov.iov_len = code_len + sizeof(int);
|
|
}
|
|
|
|
iov.iov_base = id;
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
|
|
msg.msg_control = msg_control;
|
|
msg.msg_controllen = CMSG_SPACE(sizeof(int) * count);
|
|
|
|
msg.msg_flags = 0;
|
|
|
|
len = recvmsg(fd, &msg, 0);
|
|
if (len <= 0) {
|
|
uwsgi_error("recvmsg()");
|
|
free(msg_control);
|
|
return NULL;
|
|
}
|
|
|
|
if (code && code_len > 0) {
|
|
if (uwsgi_strncmp(id, code_len, code, code_len)) {
|
|
free(msg_control);
|
|
return NULL;
|
|
}
|
|
|
|
if ((size_t) len == code_len + sizeof(int)) {
|
|
memcpy(&i, id + code_len, sizeof(int));
|
|
if (i > count) {
|
|
*count_ptr = i;
|
|
free(msg_control);
|
|
free(id);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msg);
|
|
if (!cmsg) {
|
|
free(msg_control);
|
|
return NULL;
|
|
}
|
|
|
|
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
|
|
free(msg_control);
|
|
return NULL;
|
|
}
|
|
|
|
if ((size_t) (cmsg->cmsg_len - ((char *) CMSG_DATA(cmsg) - (char *) cmsg)) > (size_t) (sizeof(int) * (count + 1))) {
|
|
uwsgi_log("not enough space for sockets data, consider increasing it\n");
|
|
free(msg_control);
|
|
return NULL;
|
|
}
|
|
|
|
ret = uwsgi_malloc(sizeof(int) * (count + 1));
|
|
for (i = 0; i < count + 1; i++) {
|
|
ret[i] = -1;
|
|
}
|
|
|
|
memcpy(ret, CMSG_DATA(cmsg), cmsg->cmsg_len - ((char *) CMSG_DATA(cmsg) - (char *) cmsg));
|
|
|
|
free(msg_control);
|
|
if (code && code_len > 0) {
|
|
free(id);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// signal free close
|
|
void uwsgi_protected_close(int fd) {
|
|
|
|
sigset_t mask, oset;
|
|
sigfillset(&mask);
|
|
if (sigprocmask(SIG_BLOCK, &mask, &oset)) {
|
|
uwsgi_error("sigprocmask()");
|
|
exit(1);
|
|
}
|
|
close(fd);
|
|
if (sigprocmask(SIG_SETMASK, &oset, NULL)) {
|
|
uwsgi_error("sigprocmask()");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
// signal free read
|
|
ssize_t uwsgi_protected_read(int fd, void *buf, size_t len) {
|
|
|
|
sigset_t mask, oset;
|
|
sigfillset(&mask);
|
|
if (sigprocmask(SIG_BLOCK, &mask, &oset)) {
|
|
uwsgi_error("sigprocmask()");
|
|
exit(1);
|
|
}
|
|
|
|
ssize_t ret = read(fd, buf, len);
|
|
|
|
if (sigprocmask(SIG_SETMASK, &oset, NULL)) {
|
|
uwsgi_error("sigprocmask()");
|
|
exit(1);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
// pipe datas from a fd to another (blocking)
|
|
ssize_t uwsgi_pipe(int src, int dst, int timeout) {
|
|
char buf[8192];
|
|
size_t written = -1;
|
|
ssize_t len;
|
|
|
|
for (;;) {
|
|
int ret = uwsgi_waitfd(src, timeout);
|
|
if (ret > 0) {
|
|
len = read(src, buf, 8192);
|
|
if (len == 0) {
|
|
return written;
|
|
}
|
|
else if (len < 0) {
|
|
uwsgi_error("read()");
|
|
return -1;
|
|
}
|
|
|
|
size_t remains = len;
|
|
while (remains > 0) {
|
|
int ret = uwsgi_waitfd_write(dst, timeout);
|
|
if (ret > 0) {
|
|
len = write(dst, buf, remains);
|
|
if (len > 0) {
|
|
remains -= len;
|
|
written += len;
|
|
}
|
|
else if (len == 0) {
|
|
return written;
|
|
}
|
|
else {
|
|
uwsgi_error("write()");
|
|
return -1;
|
|
}
|
|
}
|
|
else if (ret == 0) {
|
|
goto timeout;
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
else if (ret == 0) {
|
|
goto timeout;
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return written;
|
|
timeout:
|
|
uwsgi_log("timeout while piping from %d to %d !!!\n", src, dst);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
even if it is marked as non-blocking, so not use in request plugins as it uses poll() and not the hooks
|
|
*/
|
|
int uwsgi_write_nb(int fd, char *buf, size_t remains, int timeout) {
|
|
char *ptr = buf;
|
|
while(remains > 0) {
|
|
int ret = uwsgi_waitfd_write(fd, timeout);
|
|
if (ret > 0) {
|
|
ssize_t len = write(fd, ptr, remains);
|
|
if (len <= 0) {
|
|
return -1;
|
|
}
|
|
ptr += len;
|
|
remains -= len;
|
|
continue;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
this is like uwsgi_write_nb() but with fast initial write and hooked wait (use it in request plugin)
|
|
*/
|
|
int uwsgi_write_true_nb(int fd, char *buf, size_t remains, int timeout) {
|
|
char *ptr = buf;
|
|
int ret;
|
|
|
|
while(remains > 0) {
|
|
errno = 0;
|
|
ssize_t len = write(fd, ptr, remains);
|
|
if (len > 0) goto written;
|
|
if (len == 0) return -1;
|
|
if (len < 0) {
|
|
if (uwsgi_is_again()) goto wait;
|
|
return -1;
|
|
}
|
|
wait:
|
|
ret = uwsgi.wait_write_hook(fd, timeout);
|
|
if (ret > 0) {
|
|
len = write(fd, ptr, remains);
|
|
if (len > 0) goto written;
|
|
}
|
|
return -1;
|
|
written:
|
|
ptr += len;
|
|
remains -= len;
|
|
continue;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
// like uwsgi_pipe but with fixed size
|
|
ssize_t uwsgi_pipe_sized(int src, int dst, size_t required, int timeout) {
|
|
char buf[8192];
|
|
size_t written = 0;
|
|
ssize_t len;
|
|
|
|
while (written < required) {
|
|
int ret = uwsgi_waitfd(src, timeout);
|
|
if (ret > 0) {
|
|
len = read(src, buf, UMIN(8192, required - written));
|
|
if (len == 0) {
|
|
return written;
|
|
}
|
|
else if (len < 0) {
|
|
uwsgi_error("read()");
|
|
return -1;
|
|
}
|
|
|
|
size_t remains = len;
|
|
while (remains > 0) {
|
|
int ret = uwsgi_waitfd_write(dst, timeout);
|
|
if (ret > 0) {
|
|
len = write(dst, buf, remains);
|
|
if (len > 0) {
|
|
remains -= len;
|
|
written += len;
|
|
}
|
|
else if (len == 0) {
|
|
return written;
|
|
}
|
|
else {
|
|
uwsgi_error("write()");
|
|
return -1;
|
|
}
|
|
}
|
|
else if (ret == 0) {
|
|
goto timeout;
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
else if (ret == 0) {
|
|
goto timeout;
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return written;
|
|
timeout:
|
|
uwsgi_log("timeout while piping from %d to %d !!!\n", src, dst);
|
|
return -1;
|
|
}
|
|
|
|
|
|
// check if an fd is valid
|
|
int uwsgi_valid_fd(int fd) {
|
|
int ret = fcntl(fd, F_GETFL);
|
|
if (ret == -1) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void uwsgi_close_all_fds(void) {
|
|
int i;
|
|
for (i = 3; i < (int) uwsgi.max_fd; i++) {
|
|
#ifdef __APPLE__
|
|
fcntl(i, F_SETFD, FD_CLOEXEC);
|
|
#else
|
|
close(i);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
int uwsgi_read_uh(int fd, struct uwsgi_header *uh, int timeout) {
|
|
char *ptr = (char *) uh;
|
|
size_t remains = 4;
|
|
while(remains > 0) {
|
|
int ret = uwsgi_waitfd(fd, timeout);
|
|
if (ret > 0) {
|
|
ssize_t len = read(fd, ptr, remains);
|
|
if (len <= 0) {
|
|
return -1;
|
|
}
|
|
remains -=len;
|
|
ptr +=len;
|
|
continue;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int uwsgi_read_nb(int fd, char *buf, size_t remains, int timeout) {
|
|
char *ptr = buf;
|
|
while(remains > 0) {
|
|
int ret = uwsgi_waitfd(fd, timeout);
|
|
if (ret > 0) {
|
|
ssize_t len = read(fd, ptr, remains);
|
|
if (len <= 0) {
|
|
return -1;
|
|
}
|
|
remains -=len;
|
|
ptr +=len;
|
|
continue;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
this is like uwsgi_read_nb() but with fast initial read and hooked wait (use it in request plugin)
|
|
*/
|
|
ssize_t uwsgi_read_true_nb(int fd, char *buf, size_t len, int timeout) {
|
|
int ret;
|
|
|
|
errno = 0;
|
|
ssize_t rlen = read(fd, buf, len);
|
|
if (rlen > 0) {
|
|
return rlen;
|
|
}
|
|
if (rlen == 0) return -1;
|
|
if (rlen < 0) {
|
|
if (uwsgi_is_again()) goto wait;
|
|
}
|
|
return -1;
|
|
wait:
|
|
errno = 0;
|
|
ret = uwsgi.wait_read_hook(fd, timeout);
|
|
if (ret > 0) {
|
|
errno = 0;
|
|
rlen = read(fd, buf, len);
|
|
if (rlen > 0) {
|
|
return rlen;
|
|
}
|
|
return -1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
like the previous one but consume the whole len (if possibile)
|
|
*/
|
|
|
|
int uwsgi_read_whole_true_nb(int fd, char *buf, size_t remains, int timeout) {
|
|
char *ptr = buf;
|
|
while(remains > 0) {
|
|
ssize_t len = uwsgi_read_true_nb(fd, ptr, remains, timeout);
|
|
if (len <= 0) return -1;
|
|
ptr += len;
|
|
remains -= len;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
this is a pretty magic function used for reading a full uwsgi response
|
|
it is true non blocking, so you can use it in request plugins
|
|
buffer is expected to be at least 4 bytes, rlen is a get/set value
|
|
*/
|
|
|
|
int uwsgi_read_with_realloc(int fd, char **buffer, size_t *rlen, int timeout, uint8_t *modifier1, uint8_t *modifier2) {
|
|
if (*rlen < 4) return -1;
|
|
char *buf = *buffer;
|
|
int ret;
|
|
|
|
// start reading the header
|
|
char *ptr = buf;
|
|
size_t remains = 4;
|
|
while(remains > 0) {
|
|
ssize_t len = read(fd, ptr, remains);
|
|
if (len > 0) goto readok;
|
|
if (len == 0) return -1;
|
|
if (len < 0) {
|
|
if (uwsgi_is_again()) goto wait;
|
|
return -1;
|
|
}
|
|
wait:
|
|
ret = uwsgi.wait_read_hook(fd, timeout);
|
|
if (ret > 0) {
|
|
len = read(fd, ptr, remains);
|
|
if (len > 0) goto readok;
|
|
}
|
|
return -1;
|
|
readok:
|
|
ptr += len;
|
|
remains -= len;
|
|
continue;
|
|
}
|
|
|
|
struct uwsgi_header *uh = (struct uwsgi_header *) buf;
|
|
uint16_t pktsize = uh->_pktsize;
|
|
if (modifier1)
|
|
*modifier1 = uh->modifier1;
|
|
if (modifier2)
|
|
*modifier2 = uh->modifier2;
|
|
|
|
if (pktsize > *rlen) {
|
|
char *tmp_buf = realloc(buf, pktsize);
|
|
if (!tmp_buf) {
|
|
uwsgi_error("uwsgi_read_with_realloc()/realloc()");
|
|
return -1;
|
|
}
|
|
*buffer = tmp_buf;
|
|
buf = *buffer;
|
|
}
|
|
|
|
*rlen = pktsize;
|
|
// read the body
|
|
remains = pktsize;
|
|
ptr = buf;
|
|
while(remains > 0) {
|
|
ssize_t len = read(fd, ptr, remains);
|
|
if (len > 0) goto readok2;
|
|
if (len == 0) return -1;
|
|
if (len < 0) {
|
|
if (uwsgi_is_again()) goto wait2;
|
|
return -1;
|
|
}
|
|
wait2:
|
|
ret = uwsgi.wait_read_hook(fd, timeout);
|
|
if (ret > 0) {
|
|
len = read(fd, ptr, remains);
|
|
if (len > 0) goto readok2;
|
|
}
|
|
return -1;
|
|
readok2:
|
|
ptr += len;
|
|
remains -= len;
|
|
continue;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
this is a commodity (big) function to send a buffer and wsgi_req body to a socket
|
|
and to receive back data (and send them to the client)
|
|
|
|
*/
|
|
|
|
int uwsgi_proxy_nb(struct wsgi_request *wsgi_req, char *addr, struct uwsgi_buffer *ub, size_t remains, int timeout) {
|
|
|
|
struct uwsgi_buffer *headers = NULL;
|
|
|
|
if (!ub) {
|
|
return -1;
|
|
}
|
|
|
|
int fd = uwsgi_connect(addr, 0, 1);
|
|
if (fd < 0) {
|
|
return -1;
|
|
}
|
|
|
|
int ret = uwsgi.wait_write_hook(fd, timeout);
|
|
if (ret <= 0) {
|
|
goto end;
|
|
}
|
|
|
|
// send the request (+ remaining data)
|
|
if (uwsgi_write_true_nb(fd, ub->buf, ub->pos, timeout)) {
|
|
goto end;
|
|
}
|
|
|
|
// send the body
|
|
while(remains > 0) {
|
|
ssize_t rlen = 0;
|
|
char *buf = uwsgi_request_body_read(wsgi_req, 8192, &rlen);
|
|
if (!buf) {
|
|
goto end;
|
|
}
|
|
if (buf == uwsgi.empty) break;
|
|
// write data to the node
|
|
if (uwsgi_write_true_nb(fd, buf, rlen, timeout)) {
|
|
goto end;
|
|
}
|
|
remains -= rlen;
|
|
}
|
|
|
|
// read the response
|
|
headers = uwsgi_buffer_new(8192);
|
|
// max 64k headers
|
|
ub->limit = UMAX16;
|
|
for(;;) {
|
|
char buf[8192];
|
|
ssize_t rlen = uwsgi_read_true_nb(fd, buf, 8192, timeout);
|
|
if (rlen > 0) {
|
|
if (headers) {
|
|
if (uwsgi_buffer_append(headers, buf, rlen)) {
|
|
goto end;
|
|
}
|
|
// check if we have a full HTTP response
|
|
if (uwsgi_is_full_http(headers)) {
|
|
int ret = uwsgi_blob_to_response(wsgi_req, headers->buf, headers->pos);
|
|
if (ret) continue;
|
|
uwsgi_buffer_destroy(headers);
|
|
headers = NULL;
|
|
}
|
|
}
|
|
else {
|
|
if (uwsgi_response_write_body_do(wsgi_req, buf, rlen)) {
|
|
break;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (headers) uwsgi_buffer_destroy(headers);
|
|
|
|
close(fd);
|
|
return 0;
|
|
end:
|
|
if (headers) uwsgi_buffer_destroy(headers);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
void uwsgi_file_write_do(struct uwsgi_string_list *usl) {
|
|
|
|
struct uwsgi_string_list *fl = usl;
|
|
while(fl) {
|
|
char *equal = strchr(fl->value, '=');
|
|
if (equal) {
|
|
*equal = 0;
|
|
FILE *f = fopen(fl->value, "w");
|
|
if (!f) {
|
|
uwsgi_error_open("uwsgi_file_write_do()");
|
|
exit(1);
|
|
}
|
|
uwsgi_log("writing \"%s\" to \"%s\" ...\n", equal+1, fl->value);
|
|
if (fprintf(f, "%s\n", equal+1) <= 0 || ferror(f) || fclose(f)) {
|
|
uwsgi_error("uwsgi_file_write_do()");
|
|
exit(1);
|
|
}
|
|
}
|
|
else {
|
|
uwsgi_log("unable to write empty value for \"%s\"\n", fl->value);
|
|
exit(1);
|
|
}
|
|
*equal = '=';
|
|
fl = fl->next;
|
|
}
|
|
}
|
|
|
|
int uwsgi_fd_is_safe(int fd) {
|
|
int i;
|
|
for(i=0;i<uwsgi.safe_fds_cnt;i++) {
|
|
if (uwsgi.safe_fds[i] == fd) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
void uwsgi_add_safe_fd(int fd) {
|
|
// check if the fd is already safe
|
|
if (uwsgi_fd_is_safe(fd)) return;
|
|
|
|
size_t len = sizeof(int) * (uwsgi.safe_fds_cnt+1);
|
|
int *tmp = realloc(uwsgi.safe_fds, len);
|
|
if (!tmp) {
|
|
uwsgi_error("uwsgi_add_safe_fd()/realloc()");
|
|
exit(1);
|
|
}
|
|
uwsgi.safe_fds = tmp;
|
|
uwsgi.safe_fds[uwsgi.safe_fds_cnt] = fd;
|
|
uwsgi.safe_fds_cnt++;
|
|
}
|
|
|
|
int uwsgi_is_again() {
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void uwsgi_disconnect(struct wsgi_request *wsgi_req) {
|
|
if (wsgi_req->socket) {
|
|
wsgi_req->socket->proto_close(wsgi_req);
|
|
}
|
|
wsgi_req->fd_closed = 1;
|
|
}
|
|
|
|
int uwsgi_ready_fd(struct wsgi_request *wsgi_req) {
|
|
if (wsgi_req->async_ready_fd) return wsgi_req->async_last_ready_fd;
|
|
return -1;
|
|
}
|
|
|
|
void uwsgi_setup_schemes() {
|
|
uwsgi_register_scheme("emperor", uwsgi_scheme_emperor);
|
|
uwsgi_register_scheme("http", uwsgi_scheme_http);
|
|
uwsgi_register_scheme("data", uwsgi_scheme_data);
|
|
uwsgi_register_scheme("sym", uwsgi_scheme_sym);
|
|
uwsgi_register_scheme("section", uwsgi_scheme_section);
|
|
uwsgi_register_scheme("fd", uwsgi_scheme_fd);
|
|
uwsgi_register_scheme("exec", uwsgi_scheme_exec);
|
|
uwsgi_register_scheme("call", uwsgi_scheme_call);
|
|
uwsgi_register_scheme("callint", uwsgi_scheme_callint);
|
|
}
|
|
|
|
struct uwsgi_string_list *uwsgi_check_scheme(char *file) {
|
|
struct uwsgi_string_list *usl;
|
|
uwsgi_foreach(usl, uwsgi.schemes) {
|
|
char *url = uwsgi_concat2(usl->value, "://");
|
|
int ret = uwsgi_startswith(file, url, strlen(url));
|
|
free(url);
|
|
if (!ret) return usl;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void uwsgi_remap_fd(int fd, char *filename) {
|
|
|
|
int fdin = open(filename, O_RDWR);
|
|
if (fdin < 0) {
|
|
uwsgi_error_open(filename);
|
|
exit(1);
|
|
}
|
|
|
|
/* stdin */
|
|
if (fdin != fd) {
|
|
if (dup2(fdin, fd) < 0) {
|
|
uwsgi_error("uwsgi_remap_fd()/dup2()");
|
|
exit(1);
|
|
}
|
|
close(fdin);
|
|
}
|
|
}
|
|
|
|
int uwsgi_is_connected(int fd) {
|
|
int soopt;
|
|
socklen_t solen = sizeof(int);
|
|
|
|
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *) (&soopt), &solen) < 0) {
|
|
return 0;
|
|
}
|
|
/* is something bad ? */
|
|
if (soopt) return 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
int uwsgi_pass_cred(int fd, char *code, size_t code_len) {
|
|
#ifdef SCM_CREDENTIALS
|
|
struct msghdr cr_msg;
|
|
struct cmsghdr *cmsg;
|
|
struct iovec cr_iov;
|
|
void *cr_msg_control = uwsgi_calloc(CMSG_SPACE(sizeof(struct ucred)));
|
|
|
|
cr_iov.iov_base = code;
|
|
cr_iov.iov_len = code_len;
|
|
|
|
cr_msg.msg_name = NULL;
|
|
cr_msg.msg_namelen = 0;
|
|
|
|
cr_msg.msg_iov = &cr_iov;
|
|
cr_msg.msg_iovlen = 1;
|
|
|
|
cr_msg.msg_flags = 0;
|
|
cr_msg.msg_control = cr_msg_control;
|
|
cr_msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred));
|
|
|
|
cmsg = CMSG_FIRSTHDR(&cr_msg);
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_CREDENTIALS;
|
|
|
|
struct ucred *u = (struct ucred*) CMSG_DATA(cmsg);
|
|
u->pid = getpid();
|
|
u->uid = getuid();
|
|
u->gid = getgid();
|
|
|
|
if (sendmsg(fd, &cr_msg, 0) < 0) {
|
|
uwsgi_error("uwsgi_pass_cred()/sendmsg()");
|
|
free(cr_msg_control);
|
|
return -1;
|
|
}
|
|
|
|
free(cr_msg_control);
|
|
return 0;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int uwsgi_pass_cred2(int fd, char *code, size_t code_len, struct sockaddr *addr, size_t addr_len) {
|
|
#ifdef SCM_CREDENTIALS
|
|
struct msghdr cr_msg;
|
|
struct cmsghdr *cmsg;
|
|
struct iovec cr_iov;
|
|
void *cr_msg_control = uwsgi_calloc(CMSG_SPACE(sizeof(struct ucred)));
|
|
|
|
cr_iov.iov_base = code;
|
|
cr_iov.iov_len = code_len;
|
|
|
|
cr_msg.msg_name = addr;
|
|
cr_msg.msg_namelen = addr_len;
|
|
|
|
cr_msg.msg_iov = &cr_iov;
|
|
cr_msg.msg_iovlen = 1;
|
|
|
|
cr_msg.msg_flags = 0;
|
|
cr_msg.msg_control = cr_msg_control;
|
|
cr_msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred));
|
|
|
|
cmsg = CMSG_FIRSTHDR(&cr_msg);
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_CREDENTIALS;
|
|
|
|
struct ucred *u = (struct ucred*) CMSG_DATA(cmsg);
|
|
u->pid = getpid();
|
|
u->uid = getuid();
|
|
u->gid = getgid();
|
|
|
|
if (sendmsg(fd, &cr_msg, 0) < 0) {
|
|
uwsgi_error("uwsgi_pass_cred2()/sendmsg()");
|
|
free(cr_msg_control);
|
|
return -1;
|
|
}
|
|
|
|
free(cr_msg_control);
|
|
return 0;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
|
|
int uwsgi_recv_cred(int fd, char *code, size_t code_len, pid_t *pid, uid_t *uid, gid_t *gid) {
|
|
#ifdef SCM_CREDENTIALS
|
|
struct iovec iov;
|
|
int ret = -1;
|
|
|
|
void *msg_control = uwsgi_calloc(CMSG_SPACE(sizeof(struct ucred)));
|
|
|
|
iov.iov_base = uwsgi_malloc(code_len);
|
|
iov.iov_len = code_len;
|
|
|
|
struct msghdr msg;
|
|
memset(&msg, 0, sizeof(msg));
|
|
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
|
|
msg.msg_control = msg_control;
|
|
msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred));
|
|
|
|
ssize_t len = recvmsg(fd, &msg, 0);
|
|
if (len <= 0) {
|
|
uwsgi_error("uwsgi_recv_cred()/recvmsg()");
|
|
goto clear;
|
|
}
|
|
|
|
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
|
if (!cmsg) goto clear;
|
|
|
|
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_CREDENTIALS) {
|
|
goto clear;
|
|
}
|
|
|
|
if (uwsgi_strncmp(code, code_len, iov.iov_base, iov.iov_len)) goto clear;
|
|
|
|
struct ucred *u = (struct ucred *) CMSG_DATA(cmsg);
|
|
*pid = u->pid;
|
|
*uid = u->uid;
|
|
*gid = u->gid;
|
|
ret = 0;
|
|
|
|
clear:
|
|
free(msg_control);
|
|
free(iov.iov_base);
|
|
return ret;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
ssize_t uwsgi_recv_cred2(int fd, char *buf, size_t buf_len, pid_t *pid, uid_t *uid, gid_t *gid) {
|
|
#ifdef SCM_CREDENTIALS
|
|
struct iovec iov;
|
|
ssize_t ret = -1;
|
|
|
|
void *msg_control = uwsgi_calloc(CMSG_SPACE(sizeof(struct ucred)));
|
|
|
|
iov.iov_base = buf;
|
|
iov.iov_len = buf_len;
|
|
|
|
struct msghdr msg;
|
|
memset(&msg, 0, sizeof(msg));
|
|
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
|
|
msg.msg_control = msg_control;
|
|
msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred));
|
|
|
|
ssize_t len = recvmsg(fd, &msg, 0);
|
|
if (len <= 0) {
|
|
uwsgi_error("uwsgi_recv_cred2()/recvmsg()");
|
|
goto clear;
|
|
}
|
|
|
|
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
|
if (!cmsg) goto clear;
|
|
|
|
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_CREDENTIALS) {
|
|
goto clear;
|
|
}
|
|
|
|
struct ucred *u = (struct ucred *) CMSG_DATA(cmsg);
|
|
*pid = u->pid;
|
|
*uid = u->uid;
|
|
*gid = u->gid;
|
|
ret = len;
|
|
|
|
clear:
|
|
free(msg_control);
|
|
return ret;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
ssize_t uwsgi_recv_cred_and_fds(int fd, char *buf, size_t buf_len, pid_t *pid, uid_t *uid, gid_t *gid, int *fds, int *fds_count) {
|
|
#if defined(SCM_CREDENTIALS) && defined(SCM_RIGHTS)
|
|
ssize_t ret = -1;
|
|
|
|
size_t msg_len = CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(int) * (*fds_count));
|
|
|
|
// allocate space for credentials and file descriptors
|
|
void *msg_control = uwsgi_calloc(msg_len);
|
|
|
|
// read into buf
|
|
struct iovec iov;
|
|
iov.iov_base = buf;
|
|
iov.iov_len = buf_len;
|
|
|
|
struct msghdr msg;
|
|
memset(&msg, 0, sizeof(msg));
|
|
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
|
|
// set cmsg
|
|
msg.msg_control = msg_control;
|
|
msg.msg_controllen = msg_len;
|
|
|
|
ssize_t len = recvmsg(fd, &msg, 0);
|
|
if (len <= 0) {
|
|
uwsgi_error("uwsgi_recv_cred_and_fds()/recvmsg()");
|
|
goto clear;
|
|
}
|
|
|
|
// reset the number of fds
|
|
*fds_count = 0;
|
|
|
|
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
|
while(cmsg) {
|
|
if (cmsg->cmsg_level != SOL_SOCKET) goto next;
|
|
if (cmsg->cmsg_type == SCM_RIGHTS) {
|
|
size_t fds_len = cmsg->cmsg_len - ((char *) CMSG_DATA(cmsg) - (char *) cmsg);
|
|
memcpy(fds, CMSG_DATA(cmsg), fds_len);
|
|
*fds_count = fds_len/sizeof(int);
|
|
}
|
|
else if (cmsg->cmsg_type == SCM_CREDENTIALS) {
|
|
struct ucred *u = (struct ucred *) CMSG_DATA(cmsg);
|
|
*pid = u->pid;
|
|
*uid = u->uid;
|
|
*gid = u->gid;
|
|
}
|
|
next:
|
|
cmsg=CMSG_NXTHDR(&msg,cmsg);
|
|
}
|
|
|
|
ret = len;
|
|
|
|
clear:
|
|
free(msg_control);
|
|
return ret;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
|
|
int uwsgi_send_fds_and_body(int fd, int *fds, int fds_count, char *body, size_t len) {
|
|
|
|
int ret = -1;
|
|
|
|
struct msghdr msg;
|
|
void *msg_control = uwsgi_malloc(CMSG_SPACE(sizeof(int) * fds_count));
|
|
struct iovec iov;
|
|
struct cmsghdr *cmsg;
|
|
|
|
iov.iov_base = body;
|
|
iov.iov_len = len;
|
|
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
|
|
msg.msg_flags = 0;
|
|
msg.msg_control = msg_control;
|
|
msg.msg_controllen = CMSG_SPACE(sizeof(int) * fds_count);
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msg);
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fds_count);
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_RIGHTS;
|
|
|
|
unsigned char *fd_ptr = CMSG_DATA(cmsg);
|
|
|
|
memcpy(fd_ptr, fds, sizeof(int) * fds_count);
|
|
|
|
ssize_t rlen = sendmsg(fd, &msg, 0);
|
|
if (rlen <= 0) {
|
|
uwsgi_error("uwsgi_send_fds_and_body()/sendmsg()");
|
|
goto end;
|
|
}
|
|
else {
|
|
size_t remains = len - rlen;
|
|
while(remains > 0) {
|
|
char *buf = body + rlen;
|
|
ssize_t wlen = write(fd, buf, remains);
|
|
if (wlen == 0) goto end;
|
|
if (wlen < 0) {
|
|
if (uwsgi_is_again()) {
|
|
// wait for write
|
|
continue;
|
|
}
|
|
uwsgi_error("uwsgi_send_fds_and_body()/write()");
|
|
goto end;
|
|
}
|
|
rlen += wlen;
|
|
remains -= wlen;
|
|
}
|
|
}
|
|
ret = 0;
|
|
|
|
end:
|
|
free(msg_control);
|
|
return ret;
|
|
}
|
|
|